Maxime CHAMBREUIL - Samy FOUILLEUX - ASI3 - Année 2001 / 2002

SE : TP5 - TP6

Objectifs :

Exercice 1 : Manipulation de la pile

Programme invmess
Ce programme copie l'inverse d'une chaîne de caractères dans une autre chaîne.

Assume CS : Code, DS : Data, SS : Pile

Pile SEGMENT STACK

dw 1024 dup(0)

Pile ENDS

Data SEGMENT
msg db "chaine$"
inverse db 7 dup(0)
long equ (inverse-msg)
Data ENDS

Code SEGMENT
main:MOV AX, Data
MOV DS,AX
MOV SI,0
MOV CX,long-1
boucle1 : MOV BL,msg[SI]
PUSH BX
INC SI
LOOP boucle1
MOV SI,0

MOV CX,long-1
boucle2 : POP BX
MOV inverse[SI],BL
INC SI
LOOP boucle2
MOV inverse[SI],"$"
MOV AH,4Ch
INT 21h
Code ENDS
END main

Pour réaliser l'inversion, on envoie tout les éléments de msg dans la pile grâce à une boucle. Pour cela, on crée une constante long, qui ne sera pas stockée en memoire, et qui est égale à la différence entre l'adresse du premier élément de msg et de celle du premier élément de inverse. une fois que l'on a réalisé ce stockage dans la pile, on dépile les éléments de la pile dans long. Mais comme on met le dernier élément qui a été stocké dans la pile dans long, on met en fait le dernier élément de msg à la premiére place dans long. On réalise donc bien une inversion. Enfin on rajoute un $ à la fin de long pour marquer la fin de la chaîne de caratére.

Registres en entrée : AX=0000 BX=0000 CX=083A DX=0000 SP=0800 BP=0000 SI=0000 DI=0000 DS=20BB ES=20BB SS=20CB CS=214C IP=0000

Registres en sortie : AX=4C4B BX=0063 CX=0000 DX=0000 SP=0800 BP=0000 SI=0006 DI=0000 DS=214B ES=20BB SS=20CB CS=214C IP=0028


On voit que les registres AX et BX qui ont servit dans le programme sont modifiés. On voit aussi que le registre CX est à 0, ce qui est normal car à la fin d'une boucle CX est obligatoirement à 0.



Exercice 2 : Procédures et passage de paramètres par registre

Nous voulons écrire un programme qui affiche (en hexadécimal) la valeur d'un mot (2 octets). Pour cela, nous décomposons l'opération de façon à écrire le mot 4 bits par 4 bits. En fait on a le nombre sous la forme :
X Y Z W
On va afficher W puis Z, Y et enfin X

 

Assume CS:Code,DS: Data, SS:Pile
Pile SEGMENT STACK
dw 1024 dup(0)
Pile ENDS

Data SEGMENT
hex db "0123456789ABCDEF"
Data ENDS

Code SEGMENT

affdigit PROC
PUSH AX,BX,CX
AND BX,000Fh
MOV DL,hex[BX]
MOV AH,2
INT 21h
POP DX,Bx,AX
ret
affdigit ENDP

affoctet PROC
PUSH CX,BX,BX
MOV CL,4
SHR BX,CL
call affdigit
POP BX
AND BX,000Fh
call affdigit
POP BX,CX
ret
affoctet ENDP

affnombre PROC
PUSH BX,BX
MOV BL,BH
XOR BH,BH
CALL affoctet
POP BX
XOR BH,BH
CALL affoctet
POP bx
ret
affnombre ENDP

main:MOV AX, Data
MOV DS,AX
MOV BX,8752h
CALL affnombre
Mov AH,4Ch
INT 21h


Code ENDS

Pour expliquer le programme, nous allons détailler chaque procédure. Tout d'abord, le nombre est mis dans un registre BX (passage par registre), on fait ensuite appel à la procédure affnombre. On sauvgarde le nombre dans la pile, puis on copie sa seconde moitié pour la mettre dans sa première (MOV BL,BH) et on met la premiére moitié à 0 en faisant un ou exclusif. BX contient alors 0-0-X-Y On passe ensuite à la procédure affoctet. Celle ci effectue d'abord un décalage de 4 bits pour afficher X grâce à la procédure affdigit qui transforme BX de façon à pouvoir afficher le nombre qui se trouve dans la dernière casse. La procédure affoctet reprend BX pour avoir 0-0-X-Y. On transforme BX pour avoir 0-0-0-X et on réutilise affdigit. La procédure affnombre reprend BX pour avoir X-Y-W-Z et le transforme en 0-0-W-Z et on relance affoctet. Au final on va afficher X puis Y puis Z et enfin W, on va donc afficher le nombre contenu dans BX.

E:\SE\lasm>affnom
8752

Registres en entrée : AX=0000 BX=0000 CX=0856 DX=0000 SP=0800 BP=0000 SI=0000 DI=0000 DS=20BA ES=20BA SS=20CA CS=214B IP=0037

Registres en sortie : AX=4C4A BX=8752 CX=0856 DX=0804 SP=0800 BP=0000 SI=0000 DI=0000 DS=214A ES=20BA SS=20CA CS=214B IP=0044

On voit que AX a été modifié, c'est normal on s'en sert. On voit surtout que BX a la valeur qui s'est affichée. C'est aussi normal car c'est dans BX que nous avons mis la valeur à afficher. Le programme marche.

 

Exercice 3: Procédures et passage de paramètres par la pile

1- On fait pareil que précédemment mais en passant les arguments par la pile et non par les registres.

 
Assume CS : Code,DS : Data, SS : Pile
Pile SEGMENT STACK
dw 1024 dup(0)
Pile ENDS

Data SEGMENT
hex db "0123456789ABCDEF"
Data ENDS


Code SEGMENT
affdigpi PROC
PUSH BP
MOV BP,SP
PUSH AX,DX,BX
MOV BX,[BP+4]
AND BX,000Fh
MOV DL,hex[BX]
MOV AH,2
INT 21h
POP BX,DX,AX,BP
ret 2
affdigpi ENDP

affoctpi PROC
PUSH BP
MOV BP,SP
PUSH CX,BX
MOV BX,[BP+4]
MOV CL,4h
SHR BX,CL
PUSH BX
CALL affdigpi
POP BX
AND BX,000Fh
PUSH BX
CALL affdigpi
POP CX,BP
ret 2
affoctpi ENDP

 

affnbepi PROC
PUSH BP
MOV BP,SP
PUSH CX,BX
MOV BX,[BP+4]
MOV CL,8h
SHR BX,CL
PUSH BX
CALL affoctpi
POP BX
AND BX,00FFh
PUSH BX
CALL affoctpi
POP CX,BP
ret 2

affnbepi ENDP
main:MOV AX, Data
MOV DS,AX
PUSH BX
MOV BX,3456h
PUSH BX
CALL affnbepi
POP BX
MOV AH,4Ch
INT 21h
Code ENDS
END main

 

La différence ne se passe qu'au niveau des passage de paramétre aux procédures, le programme faisant la même chose. Les résultats sont donc les mêmes.

E:\SE\lasm>affnbepi
3456

Registres en entrée : AX=0000 BX=0000 CX=0878 DX=0000 SP=0800 BP=0000 SI=0000 DI=0000 DS=20BB ES=20BB SS=20CB CS=214C IP=0056

Registres en sortie : AX=4C4B BX=0000 CX=0878 DX=0000 SP=0800 BP=0000 SI=0000 DI=0000 DS=214B ES=20BB SS=20CB CS=214C IP=0066

On voit encore que AX change car il est utilisé sans être sauvgardé au début. On voit aussi que BX a la valeur de départ, car nous l'avons sauvgardée dans la pile au début du programme.

2 - L'espace maximal pris dans la pille lors de l'éxécution de affnombre se profuit dans le cas ou l'on est au niveau de la procédure affdigpi. A la fin de cette procédure, avant de commencer à dépiler, il y a dans la pile : BX - BX - IP - BP - CX - BX -BX - IP - BP - CX - BX - BX - IP - BP - AX - DX - BX. De ce fait, on utilise 17 étages de la pile, ce qui fait 34 octets.

 

Exercice 4: Comparaisons de chaînes de caractères

1 - Nous réalisons une procédure retourner dans SI l'adresse de la première occurence d'un caractère dans une chaîne.

Assume CS : Code,DS : Data, SS : Pile

Pile SEGMENT STACK

Pile ENDS

Data SEGMENT
chaine db "Vite",0
Data ENDS

Code SEGMENT
premOcc PROC
boucle : MOV AH,[SI]
CMP AL,AH
JZ test
CMP AH,0
JZ retour
INC SI
JMP boucle
test : MOV AL,'1'
MOV AH,'1'
CMP AL,AH
JMP fin
retour : MOV AL,'1'
MOV AH,'0'
CMP AL,AH
fin : ret
premOcc ENDP

main : MOV AX, Data
MOV DS,AX
MOV SI,offset chaine
MOV AL,'w'
call premOcc

MOV AH,4Ch
INT 21h
Code ENDS

Tout d'abord nous mettons l'adresse du premier élément de chaine dans SI, nous mettons le caratére à rechercher dans AL. Nous allons ensuite réaliser la procédure premOcc. Celle ci va rélaiser une boucle, à chaque fois on regarde si le caratére de chaine choisit et identique à celui dans AL. Si c'est le cas alors on réalise une opération pour mettre ZF à 0. Si ce n'est pas le cas on regarde si le code du caractére est égal à 0, dans ce cas on met ZF à 1. Sinon on incrémente SI pour passer au caractére suivant de chaine.

Registres en entrée : AX=20CB BX=0000 CX=0040 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000 DS=20BB ES=20BB SS=20CB CS=20CC IP=0022

Registres en sortie : AX=4C31 BX=0000 CX=0040 DX=0000 SP=0000 BP=0000 SI=0004 DI=0000 DS=20CB ES=20BB SS=20CB CS=20CC IP=002E

Le seul registre utilisé dans le programme est AX, et c'est bien le seul des 6 premiers registres a être modifié. En outre, on s'aperçoit que SI est à 4, et comme le caractére n'apparait pas, il est normal que SI soit égal à la taille de chaine.

Pour le drapeaux ZF, voyons ce que nous donne symdeb :


AX=0077 BX=0000 CX=0040 DX=0000 SP=FFFE BP=0000 SI=0004 DI=0000 DS=20CB ES=20BB SS=20CB CS=20CC IP=0018 NV UP EI PL ZR NA PE NC
20CC:0018 B031 MOV AL,31 ;'1'
-t
AX=0031 BX=0000 CX=0040 DX=0000 SP=FFFE BP=0000 SI=0004 DI=0000 DS=20CB ES=20BB SS=20CB CS=20CC IP=001A NV UP EI PL ZR NA PE NC
20CC:001A B430 MOV AH,30 ;'0'
-t
AX=3031 BX=0000 CX=0040 DX=0000 SP=FFFE BP=0000 SI=0004 DI=0000 DS=20CB ES=20BB SS=20CB CS=20CC IP=001C NV UP EI PL ZR NA PE NC
20CC:001C 3AC4 CMP AL,AH
-t
AX=3031 BX=0000 CX=0040 DX=0000 SP=FFFE BP=0000 SI=0004 DI=0000 DS=20CB ES=20BB SS=20CB CS=20CC IP=001E NV UP EI PL NZ NA PO NC
20CC:001E C3 RET

On voit que le drapeau est égal à 1, avant qu'on ne réalise l'opération qui doit le mettre à 0. Lorsque que celle ci est réalisé, le drapeau est égal 0. Ceci montre que le caractére n'est pas dans la chaine, ce qui est bien le cas. Si on réalise le programme avec le caractére dans AL apparaisant dans la chaine, on obtient ZF=1, ce que nous montre les extraits suivants (on remplace w par i dans le programme) :

AX=6969 BX=0000 CX=0040 DX=0000 SP=FFFE BP=0000 SI=0001 DI=0000 DS=20CB ES=20BB SS=20CB CS=20CC IP=000F NV UP EI PL ZR NA PE NC
20CC:000F B031 MOV AL,31 ;'1'
-t
AX=6931 BX=0000 CX=0040 DX=0000 SP=FFFE BP=0000 SI=0001 DI=0000 DS=20CB ES=20BB SS=20CB CS=20CC IP=0011 NV UP EI PL ZR NA PE NC
20CC:0011 B431 MOV AH,31 ;'1'
-t
AX=3131 BX=0000 CX=0040 DX=0000 SP=FFFE BP=0000 SI=0001 DI=0000 DS=20CB ES=20BB SS=20CB CS=20CC IP=0013 NV UP EI PL ZR NA PE NC
20CC:0013 3AC4 CMP AL,AH
-t
AX=3131 BX=0000 CX=0040 DX=0000 SP=FFFE BP=0000 SI=0001 DI=0000 DS=20CB ES=20BB SS=20CB CS=20CC IP=0015 NV UP EI PL ZR NA PE NC
20CC:0015 E90600 JMP 001E

 

2 - On veut ensuite réaliser une procèdure qui rende la longueur de la plus longue sous-chaîne initiale commune à deux chaînes de caractères.

Assume CS : Code,DS : Data, SS : Pile

Pile SEGMENT STACK
Pile ENDS

Data SEGMENT
chaine db "Vite",0
chaine2 db "Vite il faut finir ce TP",0
Data ENDS

Code SEGMENT
sousChaine PROC
PUSH AX
boucle : MOV AH,[SI]
MOV AL,[DI]
CMP AL,AH
JZ boucle2
JMP fin
boucle2 : CMP AL,0
JZ test
INC SI
INC DI
JMP boucle
test : CMP AH,0
JZ egal
JMP fin
egal : MOV AL,'1'
MOV AH,'1'
CMP AL,AH
fin : MOV BX,SI
POP AX
ret
sousChaine ENDP

main: MOV AX,Data
MOV DS,AX
MOV SI,offset chaine
MOV DI,offset chaine2
CALL sousChaine
MOV AH,4Ch
INT 21h
Code ENDS
END main

Tout d'abord, nous mettons les adresses du premier element de chaque chaine dans SI et DI. Nous réalisons ensuite une boucle dans la procédure sousChaine. On met le caratére de chaine désigné par SI et le caractére de chaine2 désigné par DI dans AH et AL. On compare ensuite les deux caratéres. Si ils sont égaux on regarde si le code n'est pas égal à 0 pour le caratére de chaine. Si oui, on refait la même chose pour le caractére de chaine2. Si son code est aussi égal à 0, les deux chaines sont égales, on réalise une opération qui met ZF à 1, et on met SI dans BX. Si les caratéres sont égaux, et qu'aucun n'a son code égal à 0, on incrémente SI et DI, et on refait le test. Sinon on met SI dans BX. Si les caratéres sont différents, on met SI dans BX. Il faut mettre SI dans BX et non DI car au début de la procédure SI=0 alors que DI=5. Ceci est du au fait que, comme chaine est le premier élément déclaré, on le place au début, c'est à dire en 0.

Registres en entrée : AX=0000 BX=0000 CX=0041 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000 DS=20BA ES=20BA SS=20CA CS=20CB IP=001F

Registres en sortie : AX=4CCA BX=0004 CX=0041 DX=0000 SP=0000 BP=0000 SI=0004 DI=0009 DS=20CA ES=20BA SS=20CA CS=20CB IP=002F

On voit que AX a changé, c'est le registre qu'on utilise pour les calculs. On voit que SI et BX on la même valeur, et que celle ci est égale à 4. Or la plus longue sous-chaine initiale de l'exemple utilisé est justement 4. Le programme semble donc bien marcher. Vérifions maintenant que Zf est bien lis à 1 quand les listes sont égales. On relance le programme avec chaine=chaine2="Vite" :

AX=0000 BX=0000 CX=0049 DX=0000 SP=FFFC BP=0000 SI=0004 DI=0009 DS=20CA ES=20BA SS=20CA CS=20CB IP=001D NV UP EI PL ZR NA PE NC
20CB:001D B031 MOV AL,31 ;'1'
-t
AX=0031 BX=0000 CX=0049 DX=0000 SP=FFFC BP=0000 SI=0004 DI=0009 DS=20CA ES=20BA SS=20CA CS=20CB IP=001F NV UP EI PL ZR NA PE NC
20CB:001F B431 MOV AH,31 ;'1'
-t
AX=3131 BX=0000 CX=0049 DX=0000 SP=FFFC BP=0000 SI=0004 DI=0009 DS=20CA ES=20BA SS=20CA CS=20CB IP=0021 NV UP EI PL ZR NA PE NC
20CB:0021 3AC4 CMP AL,AH
-t
AX=3131 BX=0000 CX=0049 DX=0000 SP=FFFC BP=0000 SI=0004 DI=0009 DS=20CA ES=20BA SS=20CA CS=20CB IP=0023 NV UP EI PL ZR NA PE NC
20CB:0023 8BDE MOV BX,SI

Zf est bien mis à 1. Le programme semble donc marcher correctement.

 

Exercice 5: Procédure FAR

On veut tester une procédure de type FAR. Pour cela on réalise une procédure qui met en majuscule les minuscules d'une chaîne de caractère.

Data SEGMENT
message1 db "uv d'ArcHitEcture et de SySteme",10,13,0
message2 db "oN chaNGE de CHainE avEc des 012 %% §.",10,13,0
Data ENDS

Pile SEGMENT STACK
dw 100 dup(0)
Pile ENDS
Code2 SEGMENT
Assume CS:Code2, DS:Data, SS:Pile
Majuscule PROC FAR

PUSH BP

MOV BP,SP
PUSH SI,AX,CX
MOV SI,[BP+6]
boucle : MOV AH,[SI]
CMP AH,0
JZ fin
MOV AL,61h
CMP AH,AL
JB faux
MOV AL,7Ah
CMP AH,AL
JA faux
MOV CX,20h
plus : DEC AH
LOOP plus

MOV [SI],AH
faux : INC SI
JMP boucle
fin : POP CX,AX,SI,BP
ret 2
Majuscule ENDP
Code2 ENDS

Code SEGMENT
Assume CS:Code, DS:Data, SS:Pile
main: MOV AX,Data
MOV DS,AX
MOV AX,offset message1
PUSH AX
Call Majuscule
MOV AX,offset message2
PUSH AX
Call Majuscule
MOV AH,4Ch
Int 21h
Code ENDS
END main

Nous envoyons l'adresse du premier carctére de SI dans la pile. Puis, nous mettons l'adresse du premier caractére de la chaine dans SI. Ensuite nous mettons le caractére correspondant dans AH. Nous vérifions que le code de ce caratére est différent de 0, sinon la procédure est finie. Nous regardons ensuite si le code ASCII de ce caratére est inférieur à 61h (code de a) et si oui si il est supérieur à 7Ah (celui de z). Si oui alors le caractére est une minuscule. On incrémente alors AH de 20h (pour arriver entre A et Z) de façon à obtenir la majuscule. Ensuite on incrémente SI et on recommence. A la fin de chaque boucle on met AH dans [SI] de façon à remplacer les minuscules de la chaine par des majuscules. La procédure affichage permet de vérifier que le programme donne le résultat escompté.

Registres en entrée : AX=0000 BX=0000 CX=016B DX=0000 SP=00C8 BP=0000 SI=0000 DI=0000 DS=20BA ES=20BA SS=20CF CS=20DF IP=0000

Registres en sortie : AX=4C22 BX=0000 CX=016B DX=0000 SP=00C8 BP=0000 SI=0000 DI=0000 DS=20CA ES=20BA SS=20CF CS=20DF IP=0019

On voit que seul AX a changé dans les premiers registres, ce qui est normal comme on ne sert que de lui. On remarque aussi que CF est le même en entrée et en sortie. Mais comment cela se passe pendant l'éxecution du programme.

AX=0000 BX=0000 CX=016B DX=0000 SP=00C6 BP=0000 SI=0000 DI=0000 DS=20CA ES=20BA SS=20CF CS=20DF IP=0009 NV UP EI PL NZ NA PO NC
20DF:0009 9A0000DC20 CALL 20DC:0000
-t
AX=0000 BX=0000 CX=016B DX=0000 SP=00C2 BP=0000 SI=0000 DI=0000 DS=20CA ES=20BA SS=20CF CS=20DC IP=0000 NV UP EI PL NZ NA PO NC
20DC:0000 55 PUSH BP

On remarque que lorsque la procédure est appelée, IP change comme à chaque fois, mais CS change aussi. Ceci siginifie que le programme principal et la procédure ne sont pas dans le même segement de code.



Exercice 6: Architecture PC

Voici l'architecture de mon ordinateur.

 

Exercice 7: Programmation Assembleur

On tape le programme donné.

Ce programme assez complexe part de plusieurs éléments qui ne semblent rien dire. Ensuite, grâce à de nombreux décalages au niveau des registres, on affiche au fur et à mesure un caractére correspondant au code obtenu à chaque boucle. Au final on obtient :

E:\SE\lasm>fin
BoNNes VacancEs



 

 


 


 


Maxime CHAMBREUIL - Samy FOUILLEUX - ASI3 - Année 2001 / 2002