TUTORIAL ASSEMBLEUR - chapitre 5 -------------------------------- Utilisations des sauts conditionnels avec CMP - Instructions mathématiques -------------------------------------------------------------------------- Utilisations des sauts conditionnels avec CMP --------------------------------------------- Les sauts conditionnels sont souvent utilisés dans des boucles. Ces boucles vont nous s ervir à exécuter des actions plusieurs fois de suite. La boucle se divise en deux parties : la partie qui fait l'action et la partie qui teste si l'action est terminée. Voici un petit exemple de code : MOV AX,0 (nous verrons plus tard que XOR AX,AX est plus petit pour le même resultat) Boucle: INC AX CMP AX,12 JE Fin JMP Boucle Fin: cette boucle n'est pas bien optimisée, nous pouvons faire mieux : MOV AX,0 Boucle: INC AX CMP AX,12 JNE Boucle Nous avons enlevé 2 instructions de saut, ici, le JNE (jump if not equal - saute s'il n'est pas égal à) va effectuer le saut tant que AX n'est pas égal à 12, des que AX=12, il va stopper de faire la boucle et va faire poursuivre l'exécution du programme. Si vous avez compris cela, vous avez à peu près tout compris des boucles. On peut bien entendu remplacer le JNZ par un JNA ou une autre condition (voir tableau des conditions dans la leçon 4). On peut aussi mettre plusieurs CMP dans une boucle ou faire plusieurs boucles dans une grande boucle. C'est facile mais ça vient aussi avec l'expérience. :) Les instructions mathématiques ------------------------------ MULTIPLICATION : MUL / IMUL Je vais continuer avec d'autres instructions pour compléter et vous permettre d'effectuer d'autres opérations. Pour l'instant, le cours est bien théorique mais attendez encore un peu... Bon, commençons par la multiplication, très utile. L'assembleur utilise l'instruction MUL pour les opérations non signées et IMUL pour les opérations signées. MUL nécessite une opérande (un paramètre). Cette opérande s'appelle la source. Le nombre qui doit être multiplié se trouve OBLIGATOIREMENT dans AX. La source est un registre (BX,CX). Si vous avez AX=2 et BX=5 et que vous faîtes MUL BX, vous obtiendrez AX=10 et BX=5. Pour IMUL, c'est pareil, les assembleurs acceptent de mettre des nombres négatifs dans un registre (MOV AX,-10). Pour ce qui est des nombres négatifs, sur 16 bits par exemple, -10 sera comme 65525 (65535-10). Le processeur soustrait à la valeur limite du registre (ici 16 bits= 65535), la valeur absolue du nombre négatif. Donc pour 16 bits, les nombres signés iront de -32768 à 32767. IMUL s'utilise comme MUL sauf qu'il faut indiquer que l'on travaille avec des nombres signés ou non-signés. C'est pourquoi, il faut toujours effacer DX (mettre DX à 0) lors d'une multiplication non-signée. Dans le cas d'un IMUL, il faut utiliser l'instruction CWD (convert word to doubleword), qui 'étend' le signe de AX dans DX. Omettre ces instructions peut conduire à des résultats erronés. De même, multiplier par DX donne de drôles de résultats. DIVISION : DIV / IDIV --------------------- Comme pour MUL et IMUL, nous avons DIV (nombres non signés) et IDIV (valeurs signées). DIV divise la valeur de AX par le source. Comme pour IMUL et MUL, il faut que DX soit correctement paramétré. Donc si nous avons AX=12 et BX=5, ensuite IDIV BX, nous aurons AX=2 et DX=2 (DX est le reste). N'oublier pas qu'on ne peut pas diviser par 0 !!! Si vous passez outre, le programme retourne au DOS et affiche 'Div by 0'. SHR et SHL ---------- SHR permet de diviser plus rapidement les registres. SHR demande deux opérandes: le nombre à diviser et le diviseur. SHR divise par une puissance de deux. Pour diviser par deux AX,BX,CX ou DX (et les autres registres aussi), on met SHR AX,1 (1 parce que 2^1=2), si on veut diviser BX par 4, on fait SHR BX,2 (2 car 2^2=4), SHR CX,3 (2^3=8) divise CX par 8. Compris ? C'est très rapide, pour diviser AX par 256, on aura SHR AX,8. SHL, c'est pareil sauf qu'on multiplie : SHL AX,4 (2^4=16) multiplie AX par 16. On peut combiner aussi des SHL pour multiplier par une valeur qui n'est pas une puissance de 2. Si nous voulons multiplier AX par 320, on remarque que X*320 = X*256+X*64, or 64 et 256 sont des puissances de 2, donc nous allons multiplier par 256 et multiplier par 64, et additionner les 2 resultats : MOV AX,12 ;on veut multiplier 12 (=X) MOV BX,AX ;on copie dans BX (AX=X ; BX=X) SHL AX,8 ;on multiplie par 256 (AX=X*256) SHL BX,6 ;on multiplie par 64 (BX=X*64) ADD AX,BX ;on additionne dans AX (AX=X*256 + BX) -> (AX=X*256+X*64=X*320) et voila. cependant dans certains cas, il est préferable d'utiliser MUL car une décomposition en puissance de 2 prendrait plus de temps... NEG --- NEG demande une opérande : la destination. Il change le signe de la destination. Par exemple si AX=12, alors NEG AX donnera AX=-12. Si CL=-3 alors NEG CL changera la valeur de CL en 3. Simple non ?. Les nombres à virgules ---------------------- Le problème en assembleur, est que nous ne pouvons utiliser les nombres à virgules directement dans les registres (sauf avec un coprocesseur mais il demande une programmation différente basée sur une pile). Nous utiliserons les nombres à virgule fixe. Il s'agit simplement d'effectuer des calculs avec des valeurs assez grandes qui seront ensuite, redivisée pour retrouver un résultat dans un certain intervalle. Nous voudrions avoir 0.5 dans AX, mais AX n'accepte pas de nombre à virgule, nous allons simplement multiplier AX par 256, or 256*0.5=128, parfait, c'est un nombre entier : MOV AX,128 maintenant, nous allons multiplier 300 par 0.5 (donc par 128) MOV CX,300 XOR DX,DX ;comme MOV DX,0 MUL CX dans AX, nous avons (300*128), mais ce nombre est 256x trop grand ! il suffit de le rediviser (qui est en fait un simple decalage peu couteux en temps de calcul) : SHR AX,8 Nous avons dans AX = (300*128)/256 = 150. Et voila ! nous avons effectué une MUL avec un nombre à virgule fixe mais seulement en utilisant des entiers. Cependant, il convient d'être prudent avec les nombres signés et les grands nombres, on arrive aisément à des dépassements. Sachez aussi que plus vous utilisez un SHL ou SHR avec une opérande grande, plus le résultat sera précis (plus de décimales disponibles). On aurait pu fort bien faire : MOV AX,64 ;(0.5*128) MOV CX,3200 XOR DX,DX MUL CX SHR AX,7 ;AX/128 Nous avons simplement une virgule fixe '128x plus grande'. ### Chapitre 5 - dake / c a l o d o x ### ### http://www.space.ch/scene/calodox ###