Assembleur
Niveaux de programmation
• Niveaux de programmation
circuit logiques 0/1
--- unité de traitement micro-instructions (UAL, chemins de données) = suite de 0/1
(Unité de commande) micro-pgme
= suite de micro-instructions
--- Codop
111111 000011101010101 langage machine = suite de 0/1
---
ADD A,20 assembleur = remplacer les codop
Structure d’un programme assembleur 8086
TITLE nom ; donner un nom au programme
PILE SEGMENT STACK ; déclaration d’un segment de pile ; dont le nom est pile
...
...
PILE ENDS ; fin de la déclaration de la pile
DONNEE SEGMENT ; déclaration d’un segment de données qui va ; contenir les variables
...
DONNEE ENDS ; fin de la déclaration de données
LECODE SEGMENT ; déclaration du segment de code qui va contenir ; le code
Debut: ; étiquette d’indication du point d’entrée du code ...
LECODE ENDS ; fin de la déclaration du code
Exemple
TITLE prog2.asm: Exemple sur ROL, Pile segment stack ;
dw 100 dup(?) Pile ends
DATA segment
DATA1 DW 5F97H
COMPTE DB ?
DATA ends CODE segment MAIN:
ASSUME CS:CODE, DS:DATA MOV AX,DATA
MOV DS, AX
XOR BL, BL ; Mettre BL à 0 (ou bien SUB) MOV DL, 16 ; rotation 16 fois
MOV AX, DATA1
ENCORE: ROL AX, 1 ; Rotation a gauche (a droite aussi si on préfère) JNC PROCHAIN ; Test si CF=0
INC BL ; Si CF = 1, incrémenter le compteur du nombre de ‘1’
PROCHAIN: DEC DL ; répéter 16 fois
JNZ ENCORE ; encore une fois si ce n’est pas fini
MOV COMPTE, BL ; sauvegarder le résultat dans la case mémoire COMPTE
Pourquoi les segments?
À l'origine
- Pour pouvoir adresser plus de 64 Ko de mémoire dans un programme car les registres sont sur16 bits
En pratique aujourd'hui
- Permet de séparer clairement des zones mémoires selon leur rôle
Exemple : la pile ne peut pas être écrasée par des données ou déborder sur des données / code
Mais cela est contraignant ...
Suite
Adressage sur 20 bits avec 2 registres
2 registres 16 bits : peut coder adresses sur 32 bits Pour uniquement 20 bits
- Décale le premier registre de 4 bits et l'additionne au second
- Adresse notée A:B
- Adresse réelle : A * 16 + B
Exemple (les nombres sont en hexa)
• 3100:27EE correspond à l'adresse 31000 + 27EE = 337EE
• Décaler de 4 bits en binaire revient à décaler d'un chiffre
en hexa
Suite 2
Nous avons 4 segments d'adresses : CS, DS, SS, ES
• utiliser 2 registres pour adresser des mots mémoires - Le premier est le registre de segment
- Le second un registre général On l'appelle l'offset (décalage)
Addresse : segment:offset
• Exemples
- CS:IP : adresse de la prochaine instruction à exécuter - DS:SI : adresse d'une donnée
- SS:SP : adresse du haut de la pile
• Vos programme sources, écrits en
assembleur, doivent avoir l’extension
.asm pour ASseMbler
Déclaration de variables
Les variables se déclarent de la manière suivante:
datas1 db ? ; datas1 est un byte non initialisé
datas2 db 0FFh ; datas2 est un byte initialisé à FF (255 en hexadécimal) datas3 dw ? ; datas3 est un word (16 bits)
datas4 db 5 dup (?) ; datas4 est un tableau de 5 bytes non initialisés datas5 dw 10 dup (15) ; datas5 est un tableau de 10 byte initialisés à 15 De manière générale:
DB : 1 byte (8 bits) (Declare Byte) DW : 1 word (16 bits) (Declare Word) DD : 2 words (32 bits) (Declare Double) DF,DP : 6 bytes
DQ : 8 bytes (64 bits)
DT : 10 bytes
Les constantes peuvent être écrites en:
- décimal: 1, 2, 3, 123, 45
- hexadécimal : 1h,2h,3h,12h,0Fh,0AD4h (noter la présence du 0 quand le le premier chiffre du nombre en hexadécimal commence par une lettre)
- binaire : 1b,0b,1010b,111101b
Les entrées Sorties en assembleur
• Pour réaliser les opérations standards (affichage, saisie), le système d’exploitation (ici DOS) fournit les fonctions pré-écrites suivantes:
• Affichage d’un caratère: mov DL, “A”; caractère A est transfére dans DL mov AH, 2; fonction no. 2
int 21h ; appel au DOS
• Affichage d’une chaine de caractères:
mov DX, offset chaine; pointe vers l’adresse du premier caractère de la chaîne de caractères chaine mov AH, 09h; fonction no. 9
int 21h;
• Saisie d’un caratère: mov AH, 1; fonction no. 1
• Saisie d’un caractère mov AH, 7; fonction no. 7
(sans écho) int 21h ; résultat dans AL
• Arrêt de programme: mov AX, 4C00h;
int 21h;
À mettre à la fin de chaque fin programme; c’est l’équivalent du
return (0) en C. Ces instructions ont pour effet de retourner au DOS
L’équivalent de quelques instructions du langage C en assembleur
• if then else Assembleur
If ax =1 if: CMP AX, 1 bx = 10; JNZ Else else {
bx = 0; Then: MOV BX,10 cx = 10; JMP endif
} Else: MOV BX,0 MOV CX,10 endif: ...
…
Instruction i Si condition alors
Instructions j Sinon
Instruction m Fin si
Instructions k
Instruction i
Condition ?
Instructions j
Instructions k FAUX VRAI
Instructions m
• La boucle FOR Assembleur
• For (k=0; k<=10; k++) MOV BX,0
• bx = bx + k; MOV CX,0 For: CMP CX,10 JA Endfor ADD BX,CX INC CX
JMP For Endfor:
• WHILE Assembleur
• bx = 5 MOV BX,5
• while (bx >0) while: CMP BX,0 bx = bx -1; JLE Endwhile DEC BX
JMP while Endwhile:
• SWITCH Assembleur
• switch (n) { CMP n,1 case 1: ....; break; JNE case2 case 2: ...; break; ...
default: ...; JMP endswitch } case2: CMP n,2
JNE default ...
JMP endswitch default: ...
Écrire le code de l’instruction En assembleur if (a>b) && (c <= d) if: cmp a, b
{ jng endif
...
} cmp c, d jnle endif ...
endif:
Exercice: coder en assembleur les instructions suivantes:
1. if (a >b) || (c > d)) 2. for (i=1; i < 10; i++) { {
Liste des registres les plus utilisés
A. Registres généraux
• AX (A pour accumulateur): joue le rôle d’opérande implicite dans plusieurs opérations: MUL, DIV, INC, etc.
• CX (C pour compteur): est utilisé pour les boucles (instruction LOOP).
• DX: utilisé dans les multiplications et divisions comme registre d’extension.
• SI (Source Index): souvent utilisé comme pointeur sur une adresse mémoire (exemple: MOV AL, [SI]). Il est très utilisée avec les
instructions de traitement de chaînes de caractères (LODS).
• DI (Destination Index): pareil que SI (instruction STOS)
• BP (base pointeur): sert de pointeur sur la base de la pile, et permet d’atteindre n’importe quel élément de la pile (exemple:
MOV AX,[BP+2]).
• SP (Stack pointer): pointe sur le sommet de la pile; son contenu est automatiquement changé par les instructions PUSH et POP.
B. Registres spéciaux
• IP (Instruction pointeur): contient l’adresse de l’instruction qui suit celle qui est en cours d’exécution.
• DS (Data Segment): Pointe sur le début du segment qui contient les données
• CS (Code Segment): Pointe sur le segment qui contient le code du programme.
• ES (Extended Segment) : permet de pointer sur un segment supplémentaire défini par le programmeur. Il se charge par l’intermédiaire de AX, comme pour DS.
• SS (Stack Segment): segment contenant la pile.
C. Registre indicateur : utilisé pour sauvegarder des états particuliers du microprocesseur en référence à la dernière instruction exécutée.
Quelques bits seulement de ce registre ont une signification sont nommés: CF (retenue), OF (débordement), etc.
Format standard d’une instruction
Label: Mnémonique Opérandes ;commentaire
Label: est un identificateur permettant de désigner un endroit dans le code source, soit une instruction, soit une donnée. Si le label est placé avant une instruction, on fait référence à l’adresse de cette instruction. Si c’est avant une donnée, on fait référence à l’adresse de cette instruction.
Le label de code doit se terminer par deux points (:). Il sert général comme destinations des instructions ou des retour de début des boucles de répétition.
Le label de donnée ne contient pas les deux points(:) à la fin. Il sert dans ce cas là comme identificateur.
• Mnémonique (des instructions): il sert à identifier une instruction donnée. Quelques instructions de base sont résumées dans la prochaine section.
• Opérandes: une instruction assembleur peut avoir de 0 à 3 opérandes. Chaque opérande peut être le nom d’un registre, un opérande
mémoire, une expression constante ou le nom d’un périphérique entrée/sortie.
• Commentaire: précédé du point-virgule (;). Il
sert à à ajouter des informations explicatives au
sujet du fonctionnement du programme ou de
l’instruction correspondante.
Quelques instructions de base-1
• Affectations
; Registres <-- Valeurs
MOV AX, 65535 ; (décimal) MOV Cl, 01101b ; (binaire)
MOV DH, 0FAh ; (hexa)
; Entre registres MOV AX, BX MOV CL, DH
; Entre Registres et Variables
MOV CX, variable_de_deux_octets MOV variable_de_un_octet, DL
;Registres <-- Adresses Mémoire
Mov AX, Offset variable ; AX <- adresse de variable
Quelques instructions de base- 2
• Arithmétique
; Incrémentation
INC AX ; AX <- AX + 1 Inc ma_variable
; Décrémentation DEC AX
Dec ma_variable
; Addition
ADD AX, 5 ; AX <- AX + 5 ADD BH, toto ; BH <- BH + toto Add toto, Cx ; toto <- toto + Cx
; Soustraction
SUB AX, 5 ; AX <- AX – 5 SUB BH, toto ; BH <- BH – toto
Quelques instructions de base-3
• Logique
; AND bit à bit
MOV AH, 0101b ; AH <- 5 MOV BH, 1001b ; BH <- 9
AND AH, BH ; AH <- AH AND BH; AH vaut 0001b, soit 1
; OR bit à bit
MOV AH, 0101b ; AH <- 5 MOV BH, 1001b ; BH <- 9
Or AH, BH ; AH <- AH OR BH; AH vaut 1101b, soit 13 (8+4+1)
; XOR bit à bit
MOV AH, 0101b ; AH <- 5 MOV BH, 1001b ; BH <- 9
XOR Ah, BH ; AH <- AH XOR BH; AH vaut 1100b, soit 12 (8+4)
; NOT bit à bit
Quelques instructions de base-4
• Comparaisons :
Toutes les comparaisons se font à l'aide de l'instruction CMP.
On utilise ensuite les instructions de saut conditionnel:
Jump if Equal, JMP if Greater, ...
Il faut définir des labels (étiquettes): les endroits dans le programme où va sauter si le test est vérifié (comme les GOTO en Fortran).
; Egalité (Jump if Equal) CMP AX, 5
JE label_1
; Différence (Jump if Not Equal) CMP AX, ma_variable
JNE label_2
;Inférieur, Supérieur, Inf. ou égal, Sup. ou égal
; (Jump if Lower, Greater, Lower or Equal, Greater or Equal) CMP CH, 0
JL label_1 CMP DH, Ah JG label_2
CMP AL, 01001b
CMP variable, 65
JGE label_4 Label_1: instructions...
Label_2: instructions...
Label_3: instructions...
Label_4: instructions...
; Saut non conditionnel : JMP label_1
Remarque: CMP est identique à l’instruction SUB, mais ne produit pas de résultat. Il positionne cependant les flags. Il permet de sauter à un label qui est à une adresse de 16 bits. Les sauts à un label sont toujours courts (à peu prés de 127octets). Il faut donc prendre garde que ce label puisse être atteint.
Djamal Rebaïne 29
JZ Saut si zéro.
ZF = 1
JNZ
JE Saut si égal. JNE
JC Saut si Retenue (inférieur).
CF = 1
JNC
JB Saut si inférieur. JNB
JNAE Saut si ni supérieur ni
égal. JAE
JS Saut si signe négatif. SF = 1 JNS JO Saut si débordement. OF = 1 JNO JPE Saut si parité paire.
PF = 1 JPO JP Saut si parité.
JNZ Saut si pas zéro.
ZF = 0
JZ
JNE Saut si différent. JE
JNC Saut si pas de retenue.
CF = 0
JC
JNB Saut si pas inférieur. JB
JAE Saut si supérieur ou égal. JNAE JNS Saut si aucun signe
(positif). SF = 0 JS
Saut si pas de
Modes d’adressage
Un mode d'adressage est un moyen qui permet au microprocesseur d'avoir accès à une donnée.
Cette donnée peut être un nombre quelconque dont on aura besoin dans le programme, un
nombre qui se trouve déjà dans un registre, ou encore un nombre qui se trouve écrit quelque part en mémoire.
• La connaissance des principaux modes
d'adressage est nécessaire car elle permet
d'écrire les programmes de la façon la plus
Modes d’adressage
• Mode immédiat
L’opérande est codée avec l’instruction mov AX, 568
• Mode registre
L’opérande est un registre de donnée ou d’adresse mov AX,BX
• Mode mémoire direct
L’opérande est désigné par l’adresse donnée dans l’instruction
mov [0hC040],AL
mov DS :[0hC040],AL
mov CS:var2,AX
mais pas
mov 0hFE15 :var2,AX
Modes d’adressage pour accéder aux données
• Mode mémoire indirect
L’opérande est désignée par une adresse placée dans les registres d’adresses donnée dans l’instruction
mov AX,[SI] BX,BP,SI,DI peuvent servir de registre pointeur
1. Indirect avec déplacement
L’adresse = contenu du registre d’adresse + déplacement (le registre d’adresse n’est pas modifié)
mov AX, [DI]
mov BX,[DI+6]
Modes d’adressage pour accéder aux données
2. Indirect avec index
L’adresse = contenu du registre d’adresse + contenu du registre d’index (le registre d’adresse n’est pas modifié)
mov [BP][DI],AX
les couples possibles sont BP-DI,
BP-SI, BX-DI, BX-SI
Quelques notes utiles
• La déclaration d’une chaîne de caractères est mise en '' '' ou ' ' .
• Le caractère '$' indique la fin d’une chaîne de caractères.
Son omission implique que les octets en mémoire qui
viennent après cette chaîne sont aussi affichés comme des caractères.
• L’assembleur ne fait pas de différence entre une majuscule et une minuscule dans l’écriture de ses instructions et la
notation des registres.
• La directive ASSUME permet d'indiquer à l'assembleur où se situe le segment de données et le segment de code. Puis il s'agit d'initialiser le segment de données à l’intérieur du
segment de code:
MOV AX, nom_du_segment_de_donnees
MOV DS, AX
Un petit mot sur l’instruction de transfert
MOV reg, reg (registre à registre)
reg, mem (registre à mémoire) mem, reg (mémoire à registre) reg, imed (registre à valeur)
mem, imed (mémoire à valeur)
NOTE: Pas de transfert de mémoire à mémoire
Applications de quelques
instructions sur des exemples
Compar e Opérand
es
C
F ZF
Dest. >
Src. 0 0
Dest. =
Src. 0 1
Dest. <
Src. 1 0
Instruction CMP (Comparer) CMP destination, source
L’instruction CMP affecte les indicateurs AF, OF, SF, PF, CF et ZF mais seuls CF et ZF sont utilisés.
L’opérande destination peut être dans un registre ou dans une mémoire.
L’opérande source peut être dans un registre, dans une mémoire, ou en mode immédiat.
Les opérandes (destination et source) ne changent pas.
DATA1 DW 235FH
…
MOV BX, 7888H ; 7888Hh BX MOV CX, 9FFFH
CMP BX, CX ; BX < CX CF=1 JNC est exécutée PASSE
JNC PASSE ; Note: les contenus de (BX, et CX) ne changent pas après CMP
ADD BX, 4000H
PASSE: ADD CX, DATA1 ; mais CF est toujours vérifié pour (< ou >). Pour (=) on utilise ZF.
TEMP DB ?
…
MOV AL, TEMP ; TEMP AL
CMP AL, 99 ; TEMP = 99?. Avec (SUB AL, 99), la même chose mais 0 AL JZ PROCHAIN ; Si ZF=1 (TEMP=99), Saute a PROCHAIN:
TITLE prog1.asm: Exemple sur CMP, Trouver l’octet le plus grand parmi 5 notes d’élèves PILE segment stack
dw 100 dup(?) PILE ends
;--- DATA segment
NOTES DB 18, 06, 19, 11, 08
PLUS_G DB ?
DATA ends
;--- CODE segment
main:
assume CS:CODE, DS:data ; génération de l’adresse du segment de code et de données
MOV AX, DATA ; Initialiser le registre DS pour récupérer l’adresse du segment de donnée MOV DS, AX
MOV CX, 5 ; compteur de boucle
MOV BX, OFFSET NOTES ; BX pointe vers les données NOTES
XOR AL, AL ; Initialise AL à 0; va héberger la plus grande note ENCORE: CMP AL, [BX] ; compare la note prochaine a la note la plus élevée
JA PROCHAIN ; Sauter si AL est encore la note la plus élevée MOV AL, [BX] ; sinon AL retient la plus élevée
PROCHAIN: INC BX ; pointe vers la prochaine note
LOOP ENCORE ; CX décrémente jusqu’à 0 pour sortir de la LOOP MOV PLUS_G, AL ; sauvegarde de la note la plus élevée dans PLUS_G
;---
MOV AH, 4Ch
INT 21h
MAIN ENDS
END MAIN
TITLE prog2.asm: Exemple sur ROL, Trouver le nombre de ‘1’ dans un mot
Pile segment stack ; déclaration d’un segment de pile – pas nécessaire dans notre cas dw 100 dup(?)
Pile ends
;--- DATA segment
DATA1 DW 5F97H
COMPTE DB ?
DATA ends
;--- CODE segment
MAIN:
ASSUME CS:CODE, DS:DATA MOV AX,DATA
MOV DS, AX
XOR BL, BL ; Mettre BL à 0 (ou bien SUB) MOV DL, 16 ; rotation 16 fois
MOV AX, DATA1
ENCORE: ROL AX, 1 ; Rotation a gauche (a droite aussi si on préfère) JNC PROCHAIN ; Test si CF=0
INC BL ; Si CF = 1, incrémenter le compteur du nombre de ‘1’
PROCHAIN: DEC DL ; répéter 16 fois
JNZ ENCORE ; encore une fois si ce n’est pas fini
MOV COMPTE, BL ; sauvegarder le résultat dans la case mémoire COMPTE
;--- MOV AH, 4Ch
INT 21h MAIN ENDS
END MAIN
Quelques explications
L’itération: On peut également transcrire une boucle à l’aide de
l’instruction LOOP nécessitant l’utilisation implicite du registre CX.
MOV CX, unevaleur Boucle:
…… ; le corps de la boucle LOOP Boucle
Cela signifie que le corps de la boucle est exécuté tant que la valeur de CX n’est pas nulle. A chaque itération, CX est décrémenté d’une unité.
Attention: si CX est nul au premier tour, alors il décrémenté et sa valeur devient 65535, et on va attendre un bon bout de temps pour arriver à la valeur nulle et sortir de la boucle
for (cx=5; cx>0; cx--) ax = ax + cx
MOV AX,0
MOV CX,5 ; CX est le compteur de boucle for:
ADD AX,CX ; fait le calcul
LOOP for ; décrémente d’une unité CX.
; si CX > 0 fait le saut à for
On peut aussi utiliser LOOPE/LOOPZ/LOOPNE/LOOPNZ pour signifier :
a.LOOPE (« Loop while Equal ») Monlabel
Décrémente CX, puis, si CX <> 0 et ZF = 1, fait un saut à MonLabel.
Mnémonique équivalent : LOOPZ
b. LOOPNE (« Loop while not Equal ») Monlabel
Décrémente CX, puis, si CX <> 0 et ZF = 0, fait un saut à
• MonLabel.
Buffer DB 8 DUP(0)
……..
Boucle:
MOV AH,1 ;lecture INT 21h
MOV [BX], AL; rangement de qu’on vient de lire INC BX
CMP AL, 0Dh; a-t-on lu le retour chariot?
LOOPNE Boucle; sinon on continue jusqu’à CX = 0 ????
• Décalage et rotation
SHL (Shift Left; SHR: shift right): effectue un décalage à gauche des bits. Si le deuxième opérande est une valeur, alors seule la valeur 1 est acceptée. Le bit de poids fort se retrouve dans CF; un 0 est introduit dans le bit de poids faible.
SHL AL, 1
Une façon plus élégante consiste à utiliser CL dans son rôle de compteur:
MOV CL, 4 SHL AX,CX
Pareil pour les instructions SAR, ROR, RCR et leurs équivalents à gauche.
Manipulation de données
1. Operateur offset: renvoie l’adresse à laquelle est située un label de donnée
Exemple: ……
Bval db ? Wval1 dw ?
Wval2 dd ? … …
Si Bval se trouve à l’adresse offset 00404000 (hexa), l’opérateur offset renvoie les valeurs suivantes:
MOV AX, offset bval ; AX = 00404000 MOV AX, offset Wval1 ; AX = 00404001 MOV AX, offset Wval2 ; AX = 00404002
2. Operateur PTR: Permet de passer outre la taille déclarée au départ pour un opérande. Par exemple,
…
double dd 12345678h
…
MOV AX, double; erreur
Mais si on insère la directive WORD PTR, on peut copier le mot de poids faible (5678h) dans AX; c’est-à-dire
MOV AX WORD PTR double
Un mot sur les macros
Étant donné que certaines instructions se répètent constamment dans un programme, l’écriture de macro-fonctions (ou macros) est un moyen
pratique de rendre votre code source plus lisible.
Il est possible de choisir pour certaines suites d’instructions un nom qui les représente.
Lorsque l’assembleur rencontrera ce nom dans votre code source, il le remplacera par les lignes de code qu’il désigne. Ces lignes forment une
« macro ».
Les macros, à la différence des procédures, n’ont aucune signification pour la machine. Seul l’assembleur
comprend leur signification. Elles ne sont qu’un artifice mis à la disposition du programmeur pour clarifier son programme. Lorsque l’assembleur rencontre le nom
d’une macro dans votre code, il le remplace par le code de la macro. Tout se passe exactement comme si vous aviez tapé vous-même 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. C’est toute la différence avec les fonctions
qui ne sont écrites qu’une seule fois mais peuvent être
appelées aussi souvent qu’on veut à l’aide d’un CALL
(qu’on verra plus tard dans ce cours).
Voici comment écrire une macro :
l’exemple suivant sert à écrire un message à
l’écran.
affiche macro chaine
; sauvegarder le contenu de DX, par ; exemple, en utilisant la pile
push dx ; sauvegarde de dx dans la pile mov dx,offset chaine
mov ah, 09h int 21h
pop dx ; restauration de dx
endm ;fin de la macro
• L’assembleur se chargera alors de la remplacer par les instructions comprises entre la première et la dernière ligne de cet exemple, en prenant le soin de remplacer le mot chaine par le
message fourni en paramètre.
• Supposons à présent que l’on veuille écrire à l’écran le message « Coucou ! Ceci est un essai » et revenir à la ligne à l’aide de notre macro
affiche
• La syntaxe suivante :
• affiche ‘Coucou ! Ceci est un essai !’, 10, 13, ‘$’
• 10, 13 est l’équivalent de endln en C-C++
Présentation d’autres exemples
TITLE ex3_somme; somme de deux nombres PILE SEGMENT STACK; déclaration de pile.
; Pour cet exemple, la pile n’est pas nécessaire.
DW 100 DUP (?) PILE ENDS
affiche macro chaine ; macro pour afficher une chaîne de ; caractères
MOV DX,offset chaine ; offset renvoie l’adresse de début de chaine MOV AH, 09h ; fonction qui affiche une chaîne de caractères
INT 21h
ENDM; fin de la macro
DATA SEGMENT; déclaration de variables val1 db 0
val2 db 0
recup_val1 db 10,13,'veuillez taper la valeur1',10,13,'$' ; 10 et 13=endl du C+
+
recup_val2 db 10,13,'veuillez taper la valeur2',10,13,'$‘
aff_resu db 10,13,'la valeur saisie est:',32,'$' ; $ caractère de fin de chaine
SCODE SEGMENT ; zone de code
ASSUME CS:SCODE, DS:DATA ; génération de l’adresse du segment de code et de données
DEBUT: ; entrée du code
MOV AX, DATA ; Initialiser le registre DS pour récupérer l’adresse du MOV DS, AX ; segment de donnée
; à partir d’ici on peut placer nos lignes de code
affiche recup_val1; appel de macro pour afficher un message contenu dans recup_val1
MOV AH,1 ; faire une lecture au clavier grâce à la fonction 1 le caractère tapé sera placé dans AL
INT 21h MOV val1,AL
affiche recup_val2; appel de la macro pour afficher un message sur écran MOV AH,1 ;faire une lecture au clavier
INT 21h
ADD AL,val1 ; AL = AL + val1 MOV val2,AL
affiche aff_resu; appel de la macro pour afficher un message sur écran SUB val2,30h ; les valeurs lues tantôt sont en ascii; exemple :
; si on tape les valeurs 1 et 2,
; le programme récupère 31 et 32, valeurs ; hexadécimales des caractères 1 et 2.
; Donc 31 + 32 = 63. et 63 n’est pas la valeur hexa ; du caractère 3. Sa valeur est 33
; autrement dit, on doit retirer 30 en hexa ou 48 en ; décimal.
MOV AH,2 ; afficher la valeur saisie grâce à la fonction 2 INT 21h ; qui affiche le contenu de DL
MOV DL,val2
MOV AH, 4Ch ; on termine le programme avec la fonction
MOV AL, 0 ; 4c en hexa. On place une valeur >=0 pour dire INT 21h ; que l’exécution s’est déroulée correctement.
; Équivalent en c de return 0 SCODE ENDS; fin du segment de code
TITLE ex4_max ; détermine et affiche le maximum de deux nombres ; introduits à travers le clavier
PILE SEGMENT STACK
DW 100 DUP (?) ; déclaration d’une pile de 100 éléments PILE ENDS
affiche macro chaine ; à la compilation, l’assembleur recopie l’ensemble ; de instructions de cette macro
mov dx,offset chaine ; pointe vers le début de la chaîne chaine mov ah, 09h ; pour afficher une chaîne de caractères à partir de ; l’adres de début de chaine
int 21h Endm
DATA SEGMENT
temp db 0 ; initialisation de temp à 0 val1 db 0
val2 db 0
recup_val1 db 10,13,'veuillez taper la valeur1',10,13,'$' ; 10 et 13=endl du c++
recup_val2 db 10,13,'veuillez taper la valeur2',10,13,'$'
aff_resu db 10,13,'le maximun est :',32,'$' ; $ caractère de fin de chaîne DATA ENDS
SCODE SEGMENT
ASSUME CS:SCODE, DS:DATA ; génération d,adresses pour les segments de code et de données
DEBUT: ; entrée du code
; Initialiser le registre DS par l’adresse du segment de donnée générée par ; la directive ASSUME
MOV AX, DATA MOV DS, AX
affiche recup_val1 ; permet d’afficher le message contenu dans recup_val1 MOV val1,AL
MOV AH,1 ;faire une lecture au clavier d’un caractère INT 21h
affiche recup_val2 ; afficher un message MOV AH,1 ; faire une lecture au clavier int 21h
MOV val2,AL CMP AL,val1 JG grand
MOV DL,val1 JMP sortie
grand: MOV DL,val2 sortie: MOV temp,DL
affiche aff_resu ; afficher un message
MOV DL temp; ces trois instructions servent à ; afficher le contenu du registre dl MOV AH,2
INT 21h
; Terminer le programme en retournant vers le DOS MOV AH, 4Ch
MOV AL, 0 INT 21h
SCODE ENDS ; fin du segment de code
Exemple 5: que fait cette portion de code?
MOV BX, offset Alphabet ;alphabet étant une chaîne de caractères MOV CL, 26
MOV [BX], CL ; 26 caractères MOV AL, 65h ; 65 = 'A' dans AL
MaBoucle: INC BX
MOV [BX], AL ; écrit dans le tableau
INC AL ; AL = caractère suivant DEC CL ; affecte l’inicateur ZF
JNZ MaBoucle ; test l’indicateur ZF = 0 XOR BX, BX ; permet de mettre BX à 0 MOV BX,offset alphabet
MOV CL,26 Boucle: INC BX
MOV DL, alphabet[bx]
MOV AH,2 INT 21h
MOV DL, ‘ ‘ ; permet d’afficher un blanc MOV AH, 2
INT 21h DEC CL JNZ Boucle
Exemple 6 : que fait la portion de code ci-dessous ?
MOV BX, offset Chose ; Met l‘adresse de début du tableau chose dans BX
MOV DI, 0 ; Index nul MOV AX, 1
MOV CX, 11 ; 11 éléments dans le tableau MaBoucle: MOV [BX+DI], AX ; écrit dans le tableau
SHL AX, 1 ; AX = AX * 2
ADD DI, 2 ; Chose est un tableau de Words -> 2 octets
DEC CX
JNZ MaBoucle
Remarque 1 : Lors de la saisie d’une chaîne de caractères au clavier, les deux premiers termes sont réservés: le premier étant la
dimension et le deuxième est le nombre effectif de caractères.
Remarque 2 : Les registres SI, DI, BX peuvent être utilisés pour les
TITLE sommedetroixnombres ; ce programme fait la somme des trois premiers nombres entiers i.e : 1+2+3
PILE SEGMENT STACK
DW 100 DUP (?)
PILE ENDS
affiche macro chaine; déclaration de macro mov dx,offset chaine
mov ah, 09h int 21h
endm; fin de la macro DATA SEGMENT temp db 0
val1 db 3 val db 0
aff_resu db 10,13,'la somme des termes jusqu a 3 est:',32,'$' ; $ caractère de fin de chaîne
DATA ENDS
SCODE SEGMENT
ASSUME CS:SCODE, DS:DATA DEBUT: ; Initialiser le registre DS MOV AX, DATA
MOV DS, AX MOV AX,0
so:
CMP AH,val1 JGE psorte INC AH ADD AL,AH JMP so
psorte: ADD AL,30h MOV temp,AL
affiche aff_resu; affichage à l’aide de la macro MOV AL,temp
MOV DL, AL
MOV AH,2 ; afficher la valeur saisie INT 21h
; Terminer le programme MOV AH, 4Ch
MOV AL, 0 INT 21h
SCODE ENDS
END DEBUT
• Exemple 7: Exemple sur ROL, Trouver le
nombre de ‘0’ dans un double-mot
TITLE programme.asm: Exemple sur ROL, Trouver le nombre de ‘0’ dans un double-mot PILESEGMENT STACK
DW 100 DUP (?) PILEENDS
DATA SEGMENT
DATA1 DD 0ABCD5F97H
COMPTE DB ?
DATA ENDS CODE SEGMENT
ASSUME CS:SCODE, DS:DATA ; Initialiser le registre DS MOV AX, DATA
MOV DS, AX
XOR CX, CX ; Mettre CX à 0 (ou bien SUB)
MOV DX, 1010h ; mettre 16 dans DH et DL; 00010000 000010000 en binaire MOV AX, WORD PTR DATA1 ; pointer vers le premier mot
MOV BX, WORD PTR DATA1 + 2 ; pointe vers le deuxième mot
ENCORE1: ROL AX, 1 ; Rotation à gauche (à droite aussi si on préfère) JC PROCHAIN1 Test si CF=1
INC CL ; Si CF = 0, incrémenter le compteur du nombre de ‘0’
PROCHAIN1: DEC DL ; répéter 16 fois
JNZ ENCORE1 ; encore une fois si ce n’est pas fini
MOV AL, CL ; sauvegarder le résultat dans AL, en sortant de la boucle, le contenu de AX ; nous importe peu, donc en écrasant le premier mot n’est pas grave ENCORE2: ROL BX, 1 ; Rotation à gauche
JC PROCHAIN2 ; Test si CF=1
INC CH ; Si CF = 0, incrémenter le compteur du nombre de ‘0’
PROCHAIN2: DEC DH ; répéter 16 fois
JNZ ENCORE2 ; encore une fois si ce n’est pas fini MOV AH, CH; sauvegarder le résultat dans AH. Même remarque qu’en haut ADD AH, AL ; Additionner les nombres de ‘0’ trouvés séparément dans les 2 mots MOV COMPTE, AH ; et sauvegarder le résultat dans COMPTE
MOV AH, 4Ch ; retour au DOS INT 21h
Code ENDS END MAIN
Conversion Minuscule en Majuscule
L Hex Binaire
A 41 01000001
B 42 01000010
. …………
. …………
Y 59 01011001
Z 5A 01011010
L Hex Binaire
a 61 01100001
b 62 01100010
. …………
. …………
y 79 01111001
z 7A 01111010
TITLE prog8.asm ; Conversion MINUSCULE MAJUSCULE d’un texte PILE SEGMENT STACK
DW 100 DUP (?) PILE ENDS
DATA SEGMENT
TEXTE1 DB ‘mOn Nom eST REBainE’, 13,10, ‘$’
TEXTE2 DB 21 DUP(?)
;--- CODE SEGMENT
ASSUME CS:SCODE, DS:DATA
; Initialiser le registre DS
MOV AX, DATA MOV DS, AX MAIN :
MOV SI, OFFSET TEXTE1 ; SI pointe sur le texte original
MOV BX, OFFSET TEXTE2 ; BX pointe sur le texte en MAJUSCULE MOV CX, 21 ; compteur de boucle
ARRIERE: MOV AL, byte ptr t[SI] ; prochain caractère CMP AL, 61H ; Si < ‘a’ (61H est le code ASCII de ‘a’) JB PASSE ; donc pas besoin de convertir
CMP AL, 7AH ; Si > ‘z’ (7AH est le code ASCII de ‘z’) JA PASSE ; donc pas besoin de convertir
AND AL, 11011111B ; masque le bit 5 pour convertir en MAJUSCULE
PASSE: MOV [BX], AL ; sauvegarde le caractère MAJUSCULE
INC SI ; incrémente le “pointeur vers le texte original”
INC BX ; incrémente le “pointeur vers le texte en MAJUSCULE”
LOOP ARRIERE ; Continuer à boucler tant que CX > 0
;--- MOV AX, 4C00h
INT 21h
CODE ENDS
END MAIN
Exemple 9: lit une chaîne de caractères et l’affiche à l’envers
• Programme palin
title palin
pile segment stack dw 100 dup(?) pile ends
data segment
reponse db 255 dup('$')
enter db 10,13,'$‘ ; endln en C++
temp db 0 data ends
scode segment
assume cs:scode, ds:data entree:
mov ax,data mov ds,ax
; on écrit le code à partir de là mov dx,offset reponse
mov ah,0ah ; lecture à partir du clavier d’une chaîne de caractères
;qui se termine dès qu’on tape le retour chariot (touche entrée) int 21h
mov si,dx mov cx,si
Deb: cmp BL,0dh; comparer à la touche entrée 13 en ascii car la fin de reponse contient ce caractère
je finsearch inc SI
mov BL,byte ptr[si]
jmp deb finsearch:
dec SI inc CX
mov DX,offset enter mov AH,09h
int 21h fs:
cmp SI,CX je fin_s
mov DL,byte ptr[si]
mov AH,02h int 21h dec SI jmp fs fin_s:
mov ax,4c00h int 21h
scode ends end Deb
Une autre manière de le faire
• ;debut:
• xor dx,dx
• mov DL,[si+1] ; récupération du nombre de caractères lus
• ; la taille à récupérer est obtenue par l’objet de destination
• ; car [si+1] n’a pas de taille spécifique donc obligation de la
• ; récupérer avec la destination
• ; ici DL donc récupération de 8bits ; si DX récupération de 16bits
• ; la destination décide de la taille à récupérer
• mov si,dx
• inc si
• mov cx,1
• f_s:
• cmp si,cx
• jle fin_s
• mov dl,reponse[si]
• mov ah,02h
• int 21h
• dec si
• jmp f_s
• fin_s:
• mov ax,4c00h
• int 21h
•
• scode ends
• end
Une troisième manière de le faire
• debut:
• xor DX,DX
• mov DX,reponse[si] ; récuperation du nombre de caractères lus
• ; la taille à récupérer est obtenue par l’objet de destination
• ; ici DL donc récupération de 8bits si DX récupération de 16bits
• ; la destination décide de la taille à récupérer
• mov SI,DX
• inc SI
• mov CX,1
• fs:
• cmp SI,CX
• jle fins
• mov DL,reponse[si]
• mov AH,02h
• int 21h
• dec SI
• jmp fs
• fins:
• mov AX,4c00h
• int 21h
•
• scode ends
• end
• MULTIPLICATION ET DIVISION SIGNÉE (IMUL / IDIV) reg.
(ou mém.)
• Note: Dans les manuels d’Intel IMUL et IDIV pour Integer MULtiplication et DIVision (X et / des nombres entiers) mais au fait il s’agit de
Multiplication et Division des nombres signées.
• MULTIPLICATION ET DIVISION SIGNÉE (IMUL / IDIV) reg.
(ou mém.)
• Note: Dans les manuels d’Intel IMUL et IDIV pour Integer MULtiplication et DIVision (X et / des nombres entiers) mais au fait il s’agit de
Multiplication et Division des nombres signées.
DIVISION SIGNEE NUM. (> ou <) DENOM. (> ou <) QUOTIENT RESTE
Octet/Octet AL = Octet CBW Reg. ou mem. AL AH
Mot/Mot AX = Mot CWD Reg. ou mem. AX DX
Mot/Octet AX = Mot Reg. ou mem. AL (Erreur si –128>AL>+127) AH DoubleMot/Mot DXAX = DoubleMot Reg. ou mem. AX (Erreur si –32768>AX>+32767) DX
MULTIPLICATION
SIGNEE OPERANDE 1
(> ou <)
OPERANDE 2 (> ou <)
RESULTAT
Octet/Octet AL Reg. ou mem. AX (CF=OF=1 si AH possède une partie du résultat, mais si celui-ci n’est pas large pas besoin de AH, le bit de signe est copié aux bits non utilisés de AH et la CPU force CF=OF=0 pour l’indiquer)
Mot/Mot AX Reg. ou mem. DXAX(CF=OF=1 si DX possède une partie du résultat, mais si celui-ci n’est pas large pas besoin de DX, le bit de signe est copié aux bits non utilisés de DX et la CPU force CF=OF=0 pour l’indiquer)
Title exemple pour trouver la moyenne d’un ensemble de nombres PILE segment stack
dw 100 dup (?) PILE ends
DATA segment
SIGN_DAT DB +13,-10,+19,+14,-18,-9,+12,-9,+16 MOYENNE DW ?
RESTE DW ? DATA ends
CODE segment
ASSUME CS:CODE, DS:DATA MOV AX,DATA
MOV DS,AX MAIN:
MOV CX,9 ; Charger le compteur
XOR BX,BX ; Mettre a 0 le registre BX, utilisé comme accumulateur MOV SI,OFFSET SIGN_DAT ; SI SIGN_DAT
ARRIERE: MOV AL,[SI] ; Un octet de donnée AL CBW ; Extension du signe AX ADD BX,AX ; BX+AXBX
INC SI ; SI+1 SI
LOOP ARRIERE ; Boucler tant que CX > 0
MOV AL,9 ; Le nombre totales des températures AL CBW ; Extension du signe AX
MOV CX,AX ; Sauvegarder le DENOMINATEUR dans CX MOV AX,BX ; LA somme des températures AX
; EXEMPLE D’UTILISATION DE IDIV TROUVER LA TEMPERATURE MOYENNE
• CWD ; Extension du signe AX
• IDIV CX ; Trouver la moyenne des températures (AX/CX)
• MOV MOYENNE,AX ; Sauvegarder la moyenne (QUOTIENT)
• MOV REMAINDER,DX ; Sauvegarder le reste
• MOV AH,4CH INT 21H ;Retourner au DOS
...
Data segment X dw ? A dw ? B dw ? C dw ? D dw ? Data ends
Arithmetique proc near
MOV AX, 2 ;établir la constante
IMUL A ;DX:AX = A*2
MOV BX,DX ;
MOV CX,AX ;BX:AX = A *2
MOV AX,B
IMUL C ;DX:AX = B*C
ADD AX,CX ;AX = AX + CX ! faites attention, il peut y avoir une retenue ici ADC DX,BX ;DX:AX = A*2+B*C + la retenue s’il y a lieu avec ADC
MOV CX, D
SUB CX,3 ; cx = D -3 IDIV CX ; AX =(A*2 + B*C)/(D-3)
MOV X,AX ; X = ax (A*2 +B*C)/(D-3) stocker le résultat RET
Arithmetique endp ; fin de la procedure ...
Expression arithmétique
X = (A*2 + B*C)/(D-3)
Conversion d’une chaine de caractères en une valeur décimale
Toute information introduite via le clavier est considérée comme une chaîne de
caractères. Ainsi, si on introduit le nombre 827, il sera considéré par l’assembleur
comme la chaîne de caractères ‘827’.
Pour avoir sa valeur numérique, il y a lieu
de la convertir suivant l’algorithme suivant:
Algorithme de conversion
• nbre = nombre de caractère lus
• Nombre = 0
• Repeter
• chiffre = caractère lu
• nombre = nombre *10+chiffre
• nbre = nbre -1
• Si nbre > 0 aller à repeter
•
Exemple
• ‘827’
• nombre = 0
• Chiffre = 8
• nombre = nombre * 10 +chiffre =0*10+8 = 8
• Chiffre = 2
• nombre = nombre * 10 + 2 = 8*10 + 2 = 82
• Chiffre = 7
• Nombre = nombre * 10 + 7 = 82*10+7 = 827
•
En assembleur, on aura quelque chose comme ci-dessous
TITLE caracteresversnombre SPILE SEGMENT STACK DW 100 DUP(?) SPILE ENDS SDATA SEGMENT
chaine db 255 dup('$') SDATA ENDS
SCODE SEGMENT
ASSUME CS:SCODE,DS:SDATA DEBUT:
mov AX,sdata mov DS,AX
mov DX,offset chaine mov AH,0ah
int 21h mov SI,1 mov AX,0 xor CX,CX mov CL,chaine[si]
inc SI repeter:
mov DX,10 mul DX
mov DL,chaine[si]
sub DL,48; ou bien 30h, c’est pareil. C’est pour rendre le caractère lu comme une valeur numérique mov DH,0
add AX,DX inc SI loop repeter MOV AX,4C00H INT 21H SCODE ENDS END DEBUT
Pour lire une chaine de caractères, appeler 21h fonction 0Ah qui installe les caractères tapés dans une zone repérée par DS:DX (buffer déclarée dans le segment de données). La fonction se termine quand un return (touche entréée) est détecté. Le buffer contient alors les informations suivantes:
byte contenu
0 nombre maximum de caractères à lire
1 nombre de caractères lus (sans compter le retour chariot).
cette valeur est installée par la fonction.
2 À partir de cette position, on trouve les caractères lus
Conversion d’une valeur décimale en une chaîne de caractères
Toute information affichée sur écran est considérée comme un
caractère. Ainsi, pour afficher la valeur 827, il faut dans un premier temps avoir tous le chiffres qui la composent; ensuite convertir
chacun de ces chiffres en leur équivalent ascii et ensuite passer à l’affichage proprement dit.
Ainsi, si on veut afficher le 827, on doit d’abord récupérer les chiffres 8, 2 et 7 par une succesion de divisions par dix (ces chiffres seront
récupérés dans l’ordre inverse). Ensuite, les convertir en ASCII en ajoutant la valeur 30h, et enfin passer à l’affichage proprement dit dans l’odre inverse pour avoir le bon ordre.
L’algorithme, récupérant les chiffres dans l’ordre inverse, peut être comme suit:
Algorithme
k = 0 do
quotient = nombre / 10;
reste = nombre % 10;
tab[k] = reste;
nombre = quotient;
k++
while (quotient != 0)
; on suppose que le registre AX contient le nombre
mov result,AX ; déclarer result dans le segment de données
mov dx,offset enter ; retour chariot 10, 13 dans le segment de données mov ah,09h
int 21h
mov ax,result
mov SI, offset tabconv ; tabconv est à déclarer dans le segment de données mov start, offset tabconv
; start sert à garder le début du tableau mov BX,0
mov BL,10
division: ; on suppose que la division se fait sur des nombres de 16 bits div BL
cmp AL,0 je fin_div add AH,48
mov byte ptr[si],AH mov AH,0
inc SI
jmp division
Dans ce programme, les chiffres composant le nombre contenu dans AX est affiché dans le bon ordre