TP & TD Assembleur 8086/8088 N°2 en Doc


Télécharger TP & TD Assembleur 8086/8088 N°2 en Doc

★★★★★★★★★★3.5 étoiles sur 5 basé sur 1 votes.
Votez ce document:

Télécharger aussi :


MicroInformatique
Informatique Industrielle

Assembleur 8086/8088

TP n°2

 

 

I.      BUT DU TP

Se familiariser avec assembleur freeware NASM associé à l'éditeur syntaxique NASMIDE.

II.      Présentation de NASM

 

a)       Qu'est ce que NASM ?

NASM ( ) est un assembleur 80x86 libre d'utilisation. NASMIDE ( ) est un environnement de développement spécialement développé pour travailler avec NASM sous DOS. (Il existe également une version de NASMIDE sous Windows permettant de développer des programmes en assembleur 32 bits).

 

b)       Structure d'un programme assembleur

Un programme en assembleur commence en général par la définition des données puis ensuite par le code.

Il s'agit tout d'abord de préciser à l'assembleur si on va écrire du code 16 bits ou 32 bits. Dans notre cas, nous générerons du code 16 bits en précisant la directive [BITS 16]. Il faut ensuite préciser l'adresse du programme dans le segment de code en utilisant la directive [ORG 0x100]. (0x100 signifie que 100 est un nombre en héxadécimal)

Ensuite, il faut préciser par [SEGMENT .data] que l'on va définir des variables et constantes dans le segment de données.

La directive [SEGMENT .text] permet ensuite d'introduire les instructions du programme en langage assembleur.

 

c)       Edition d'un programme

-          Vérifier que les options suivantes sont configurées dans le menu options|assembler

 

 

-          Vérifier que les options suivantes sont configurées dans le menu options|directories

 

 

-          Editer le programme qui affiche un caractère A à l'écran (le même celui du TP 1). (Attention, int 21 s'écrit maintenant int 0x21 ou int $21)

-          Appuyer sur la touche F2 pour sauver le programme.

-          Appuyer simultanément sur les touches alt + F9 pour assembler le programme.

-          Une fenêtre d'erreur doit normalement indiquer qu'aucune erreur n'est survenue.

 

 

-          Appuyer simultanément sur les touches Ctrl + F9 pour exécuter le programme.

 

 

En résumé, attention au format des constantes numériques:

 

100   ? décimal

100h  ? hexadécimal

0x100  ? hexadécimal aussi

$100  ? hexadécimal également mais le premier caractère doit être un chiffre

10000100b ? binaire

 

d)       Déclaration de variables dans le segment de données

Il est possible de définir des variables de type caractère, chaîne de caractères, entier, entier long, réel,.. grâce à des pseudo instruction.

 

Déclaration de variables entières de type octet: db

 

 nb  db $10

 tab  db 1, 2 ,3 ,4 ,5 ,6 ,7 ,8 ,10

 message  db 'bonjour les IUP GSI TI',13,10,'$'

 (13 ? retour chariot, 10 ? ligne suivante, $ ? fin de chaîne de caractères)

 

Déclaration de variables entières de type mot: dd

 

 R dd 1024

 

Déclaration de variables en virgule flottante: dd, dq, dt

 

 A dd  1.2

 B dd 1.e10

 C dd 1.e+10

 D dq 1.e-10

 pi dt 3.141592353589793238462

 

e)       Utilisation des variables dans le programme

r db 40

Exemple:  mov al, [r]

   mov [r], cl

?: Il est impossible d'affecter une variable directement à une autre variable, il faut obligatoirement passer par un registre.

Il est également impossible d'affecter une constante directement à une variable, il faut également passer par un registre.

 

f)        Définition d'étiquettes

Contrairement à DEBUG, il est possible de définir des étiquettes vers lesquelles certaines instructions pourraient se brancher. Ceci évite le comptage laborieux des octets lorsqu'il s'agit de réaliser un saut dans le programme.

Une étiquette de déclare par un nom de taille inférieure à 8 caractères et se terminant par :

 

Exemple:

 

 BOUCLE: ADD AX,CX ; Ax = Ax + Cx

   .

   .

   .

   JNE BOUCLE ; jump not equal

III.      Exercices

Exercice 1:

 

1)       Faire l'organigramme du programme qui calcule la somme des 11 premiers entiers
(0 + 1 + 2 +…+ 10 +11 ). Ecrire ensuite le programme en assembleur 8086. On utilisera pour cela les instruction MOV, CMP, JNE, ADD, DEC ou INC… On utilisera une variable R pour stocker le résultat et une variable N pour stocker le nombre 11.

 

2)       Afficher ensuite le résultat à l'écran en utilisant l'interruption $21 puis déduire en fonction du caractère affiché la valeur numérique du résultat.

 

3)       Même exercice mais en utilisant l'instruction LOOP

 

Exercice 2:

 

- Utilisation de l'interruption $21 en mode 9

 

L'interruption $21 avec ah à 9 permet d'afficher des chaînes de caractères qui se terminent par le caractère 13, 10 ,'$'. (13 ? retour chariot, 10 ? ligne suivante, $ ? fin de chaîne de caractères)

Pour cela, il faut définir un message avec la pseudo instruction db (voir explications de db) en terminant ce message par 13, 10, '$'.

Il faut ensuite affecter au registre dx l'adresse de ce message (mov dx, mess où mess est le nom donné au message).

 

Ecrire un programme qui affiche dix fois à l'écran le message suivant avec retour à la ligne et saut de ligne:

 

Si je travaille bien en TP, je deviendrai un dieu de l'assembleur et le prof sera fier de moi


 

 

Exercice 3:

 

Concevoir une application qui réalise le produit de deux nombres A = 100 et B = 170. Les nombre A et B sont codés sur 8 bits chacun.

-          Quelle instruction faut il utiliser ?

-          Où se trouvent les opérandes ?

-          Où se trouve le résultat de l'opération.

-          Afficher les octets du registre contenant le résultat de l'opération.

-          Déduire le résultat numérique à partir des caractères affichés.

 

Mêmes questions mais avec des nombres A et B de 16 bits. Prendre A = 1642 et B = 10.

Essayer avec A=3227 et B=5.

Comment apparaît le résultat ?

Comment faire pour que des caractères imprimables apparaissent tout le temps ?

 


ASCII Table (7-bit)

    Decimal   Octal   Hex    Binaire        Valeur

      --------------------------------------------------------------------------------------

         000      000    000   00000000      NUL    (Null char.)

         001      001    001   00000001      SOH    (Start of Header)

         002      002    002   00000010      STX    (Start of Text)

         003      003    003   00000011      ETX    (End of Text)

         004      004    004   00000100      EOT    (End of Transmission)

         005      005    005   00000101      ENQ    (Enquiry)

         006      006    006   00000110      ACK    (Acknowledgment)

         007      007    007   00000111      BEL    (Bell)

         008      010    008   00001000       BS    (Backspace)

         009      011    009   00001001       HT    (Horizontal Tab)

         010      012    00A   00001010       LF    (Line Feed)

         011      013    00B   00001011       VT    (Vertical Tab)

         012      014    00C   00001100       FF    (Form Feed)

         013      015    00D   00001101       CR    (Carriage Return)

         014      016    00E   00001110       SO    (Serial In)(Shift Out)

         015      017    00F   00001111       SI    (Serial Out)(Shift Out)

         016      020    010   00010000      DLE    (Data Link Escape)

         017      021    011   00010001      DC1 (XON) (Device Control 1)

         018      022    012   00010010      DC2       (Device Control 2)

         019      023    013   00010011      DC3 (XOFF)(Device Control 3)

         020      024    014   00010100      DC4       (Device Control 4)

         021      025    015   00010101      NAK    (Negative Acknowledgement)

         022      026    016   00010110      SYN    (Synchronous Idle)

         023      027    017   00010111      ETB    (End of Trans. Block)

         024      030    018   00011000      CAN    (Cancel)

         025      031    019   00011001       EM    (End of Medium)

         026      032    01A   00011010      SUB    (Substitute)

         027      033    01B   00011011      ESC    (Escape)

         028      034    01C   00011100       FS    (File Separator)

         029      035    01D   00011101       GS    (Group Separator)

         030      036    01E   00011110       RS    (Request to Send)

         031      037    01F   00011111       US    (Unit Separator)

         032      040    020   00100000       SP    (Space)

         033      041    021   00100001        !

         034      042    022   00100010        "

         035      043    023   00100011        #

         036      044    024   00100100        $

         037      045    025   00100101        %

         038      046    026   00100110        &

         039      047    027   00100111        '

         040      050    028   00101000        (

         041      051    029   00101001        )

         042      052    02A   00101010        *

         043      053    02B   00101011        +

         044      054    02C   00101100        ,

         045      055    02D   00101101        -

         046      056    02E   00101110        .

         047      057    02F   00101111        /

         048      060    030   00110000        0

         049      061    031   00110001        1

         050      062    032   00110010        2

         051      063    033   00110011        3

         052      064    034   00110100        4

         053      065    035   00110101        5

         054      066    036   00110110        6

         055      067    037   00110111        7

         056      070    038   00111000        8

         057      071    039   00111001        9

         058      072    03A   00111010        :

         059      073    03B   00111011        ;

         060      074    03C   00111100        <

         061      075    03D   00111101        =

         062      076    03E   00111110        >

         063      077    03F   00111111        ?

         064      100    040   01000000        @

         065      101    041   01000001        A

         066      102    042   01000010        B

         067      103    043   01000011        C

         068      104    044   01000100        D

         069      105    045   01000101        E

         070      106    046   01000110        F

         071      107    047   01000111        G

         072      110    048   01001000        H

         073      111    049   01001001        I

         074      112    04A   01001010        J

         075      113    04B   01001011        K

         076      114    04C   01001100        L



         077      115    04D   01001101        M

         078      116    04E   01001110        N

         079      117    04F   01001111        O

         080      120    050   01010000        P

         081      121    051   01010001        Q

         082      122    052   01010010        R

         083      123    053   01010011        S

         084      124    054   01010100        T

         085      125    055   01010101        U

         086      126    056   01010110        V

         087      127    057   01011111        W

         088      130    058   01011000        X

         089      131    059   01011001        Y

         090      132    05A   01011010        Z

         091      133    05B   01011011        [

         092      134    05C   01011100        \

         093      135    05D   01011101        ]

         094      136    05E   01011110        ^

         095      137    05F   01011111        _

         096      140    060   01100000        `

         097      141    061   01100001        a

         098      142    062   01100010        b

         099      143    063   01100011        c

         100      144    064   01100100        d

         101      145    065   01100101        e

         102      146    066   01100110        f

         103      147    067   01100111        g

         104      150    068   01101000        h

         105      151    069   01101001        i

         106      152    06A   01101010        j

         107      153    06B   01101011        k

         108      154    06C   01101100        l

         109      155    06D   01101101        m

         110      156    06E   01101110        n

         111      157    06F   01101111        o

         112      160    070   01110000        p

         113      161    071   01110001        q

         114      162    072   01110010        r

         115      163    073   01110011        s

         116      164    074   01110100        t

         117      165    075   01110101        u

         118      166    076   01110110        v

         119      167    077   01110111        w

         120      170    078   01111000        x

         121      171    079   01111001        y

         122      172    07A   01111010        z

         123      173    07B   01111011        {

         124      174    07C   01111100        |

         125      175    07D   01111101        }

         126      176    07E   01111110        ~

         127      177    07F   01111111      DEL


MicroInformatique
Informatique Industrielle

Assembleur 8086/8088

TP n°3

 

 

 

I.      BUT DU TP

Maîtriser les différents types d'adressage, notamment pour accéder aux éléments de tableaux.

II.      RAPPELS

Pour adresser les différents éléments d'un tableau, il faut utiliser l'adressage indexé basé en prenant le registre BX comme base et le registre DI ou SI comme indexe. Pour mettre la base du tableau (déplacement du tableau dans le segment de données) dans BX, il suffit d'écrire
MOV BX, nom du tableau.

 

Ensuite l'adressage indexée basée s'utilise de la manière suivante:

 

MOV AL, [BX+SI] ;mettre le SI ème élément du tableau dans le registre AL

 

SI représente la position de l'élément du tableau que l'on veut atteindre.

III.      EXERCICES

 

Exercice 1:

 

Soit le tableau suivant:

 

 tab db 5, 30, 20, 1,11, 8

 

Faire le somme des 6 éléments de ce tableau et mettre le résultat dans une variable S. Afficher le résultat à l'écran, en déduire la valeur numérique.

 

Exercice 2:

 

Ecrire une application qui compte le nombre de lettres 'e' dans une phrase quelconque (50 caractères minimum) se terminant par le caractère '$'.

-          Représenter l'organigramme.

-          Mettre le résultat dans le registre dl et ajouter 32.

-          Imprimer le caractère à l'écran et vérifier si le caractère qui apparaît est cohérent.

 

Exercice 3:

 

Définir une chaîne de caractères de longueur au moins 50 caractères se terminant par '$'.

Réaliser une application (organigramme + programme) qui remplace toutes les lettres d'un type donné par un autre type (par exemple tous les 'e' par des 'a').

On entrera les deux lettres au clavier en utilisant l'interruption 21,8.

Les saisies clavier seront précédées d'un message du type 'Remplacer:' et 'par:'

 

 


Exercice 4:

 

Tri d'un tableau

 

Soit le tableau tab suivant:  5, 8, 1, 7, 6

Soit l'algorithme suivant:

 

Appliquer l'algorithme suivant au tableau tab en remplissant le tableau d'état suivant:

 

Etape

i

j

tab[0]

tab[1]

tab[2]

tab[3]

tab[4]

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Soit la chaîne de caractères suivante:

 

 

message db "le but de ce probleme est de trier par ordre croissant de code ASCII cette chaîne de caractere"

 

-          Ecrire le programme qui tri par ordre croissant de code ASCII la chaîne précédente.

-          On prendra SI pour l'index i et DI pour l'index j.

-          On affichera le résultat grâce à l'interruption $21, 9.

 


MicroInformatique
Informatique Industrielle

Assembleur 8086/8088

TP n°4

I.      But du TP

Programmer l'affichage en mode Graphique MCGA.

II.      Présentation du mode MCGA

Le mode MCGA est un mode graphique créé par IBM. Sa particularité est de posséder 256 couleurs et une définition de 320 pixels par 200 pixels. De ce fait, chaque pixel est représenté par un octet. La mémoire écran est donc représentée par 320*200*1 octets contigus (64000).

 

III.      Accès au mode MCGA

Pour accéder au mode MCGA, nous allons utiliser les interruptions logicielle du BIOS. L'interruption du BIOS est l'interruption 0x10 et la fonction qui permet de choisir son mode vidéo est la fonction 0. Dans cette fonction il faut préciser le mode vidéo que l'on désire. Le mode correspondant au MCGA est le mode 0x13.

En résumé pour activer le mode MCGA il faut:

-          ah? 0

-          al ? 0x13

-          Appel de l'interruption 0x10

 

Pour se remettre en mode texte à la fin du programme, on utilisera le mode 0x03.

-          ah? 0

-          al ? 0x03

-          Appel de l'interruption 0x10

 

IV.      Accès aux pixels

Pour accéder à un pixel, il faut écrire dans la mémoire vidéo du mode MCGA. Cette mémoire vidéo commence à l'adresse 0xa000 et s'étend sur 64000 octets.

Pour accéder à un pixel de coordonnées x, y, x et y il faut donc accéder à la mémoire vidéo d'adresse y*320+x.

La couleur de ce pixel dépendra de l'octet que l'on écrira à l'adresse du pixel. La couleur associée à une valeur de l'octet est définie dans une palette qu'il est également possible de modifier. Mais ceci est une autre histoire …

Pour écrire à l'adresse 0xa000 + déplacement, il faut utiliser l'extra segment es associé à un déplacement représenté par bx. On peut éventuellement ajouter à tout cela un déplacement fixe ou variable avec les registres si et di.

 

 

 

Exemple:

 

 mov ax,0xa000

 mov es,ax

 mov dl, 0xaa

mov bx, 160

mov [es:bx], dl   ; on écrit à l'adresse 0xa000 + 160

 

ou

mov bx, 160

mov [es:bx + 320], dl  ; on écrit à l'adresse 0xa000 + 160 +320

 

ou

 

 

mov bx, 160

mov si, 320

mov [es:bx + si], dl  ; on écrit à l'adresse 0xa000 + 160 +320

 

ou

 

mov bx, 160

mov si, 320

mov [es:bx + si + 640], dl ; on écrit à l'adresse 0xa000 + 160 +320 +640

 

V.      Exercices

Exercice 1:

-          Allumer le pixel de coordonnées (200,100) avec la couleur de code 0x32.



-          Allumer les 256 premiers pixels leur associant une couleur variant de 0 à 255.

-          Même question mais en augmentant l'épaisseur du trait (5 lignes).

-          Même question mais sur toute la hauteur de l'écran (cf figure suivante).

 

 

Exercice 2:

-          Encadrer l'écran avec  un cadre d'épaisseur 5 pixels de la couleur de votre choix

-          Quadriller l'écran avec une ligne de 5 pixels d'épaisseur et un espacement de 10 pixels entre les lignes.

 

Exercice 3: (pour les meilleur(e)s)

-          Ecrire un programme qui déplace un motif rectangulaire (8*6 pixels) en bas de la fenêtre de gauche ?droite, de droite ? gauche, … tant qu'aucune touche n'est appuyée.

-          L'affichage et l'effacement devra se faire avec un ou exclusif pour ne pas effacer ce qui se trouve éventuellement déjà sur l'écran.

-          Le test de l'appui d'une touche se fera en utilisant l'interruption 0x16 (voir annexe)

-          Les variables à utiliser seront les suivantes:

 

 

 

-          entre chaque affichage, il est nécessaire (au moins sur les Pentiums) de générer une temporisation. Pour cela on écrira un sous-programme de temporisation simple du type:

 

tempo:   mov cx,0xffff

tt:  nop

     loop tt

      ret

 

-          Pour déplacer le motif vers la gauche ou vers la droite, on utilisera une variable delta qui prendra la valeur 1 ou –1 selon le sens du déplacement. Cette variable servira d'incrément pour afficher la position suivante du motif.

-          Il faudra tester si on arrive au niveau de la limite supérieure droite (320-L) pour donner à delta la valeur –1 et tester la limite inférieure gauche (0) pour mettre delta à +1.

 

 

Exercice 4: (pour les meilleur(e)s des meilleur(e)s

 

 

-          Ecrire un Casse brique en assembleur 8086 (raquette + balle + briques)


INT 16,1 - Get Keyboard Status

AH = 01

on return:

ZF = 0 if a key pressed (even Ctrl-Break)

AX = 0 if no scan code is available

AH = ~scan code~

AL = ASCII character or zero if special function key

- data code is not removed from buffer

- ~Ctrl-Break~ places a zero word in the keyboard buffer but does

register a keypress.


MicroInformatique
Informatique Industrielle

Assembleur 8086/8088

TP n°5

I.      But du TP

Se familiariser avec les instructions répétitives, les horloges temps réel, la génération de nombres pseudo aléatoires, …

(Ce TP a été largement inspiré de ce que l'on trouve sur le site

)

 

II.      Le hasard en informatique

C'est simple, le hasard n'existe pas en informatique. Les suites de nombres "au hasard" en informatique sont presque toutes basées sur un principe: On utilise un nombre générateur, on lui fait subir une opération (par exemple on multiplie les poids faibles par les poids forts et on inverse un bit sur deux) qui nous donne un nouveau générateur. On extrait tout ou une partie des bits de ce générateur pour avoir le nombre au hasard.

Dans les langages évolués, il existe une fonction qui donne un nombre et une fonction qui fixe le premier générateur. Cela signifie que si vous dessinez "au hasard" en prenant deux fois le même générateur, vous allez obtenir deux fois exactement le même dessin. Pour palier à ceci, on choisit en principe pour le premier générateur l'heure du système.

Une "bonne" série aléatoire doit être telle que chacun des nombres générés doivent être équiprobables (sur un temps de mesure suffisamment long, on doit trouver autant de 1 que de 2, que de 3 ). D'autre part on voudrait que la suite soit aussi irrégulière que possible

Si on utilise l'horloge du PC, on va avoir des valeurs qui vont changer avec le temps, d'une façon relativement régulière, mais dans la plus par des cas cela suffit. Sur un PC, le plus simple est de récupérer le temps depuis que l'on a allumé l'ordinateur. Les premiers PC avaient une horloge à 4,77 Mhz, derrière on divisait par 4 puis par 65536 (compteur sur 16 bits) en on obtenait une fréquence de 4770000/(4*65536) = 18,2 Hz.

On alertait alors ainsi 18,2 fois par seconde le PC qui remettait à l'heure son compteur de temps. Lorsque l'on a changé les vitesses des horloges des PC, on a gardé cette fréquence pour l'horloge.

Ceci signifie que si vous changez la fréquence de votre ordinateur, le comptage du temps va rester le même.

 

III.      Application à l'affichage aléatoire de points de couleurs différentes

Nous proposons dans cet exercice d'afficher en permanence les points de l'écran avec une couleur pseudo aléatoire. Pour cela nous allons utiliser le compteur temps réel du PC qui se trouve à l'adresse 0040h:006Ch.

Ecrire un programme qui affiche allume dans le désordre les points de l'écran avec une couleur que l'on incrémentera au fur et à mesure.

Pour cela il faut suivre les étapes suivantes:

-          Se mettre en mode graphique MCGA.

-          Initialiser le segment DS avec 0xA000 (passer par AX).

-          Initialiser le segment ES avec 0x0040 (passer par AX).

-          BL contiendra la couleur du point à afficher, mettre BL à 0.

-          Afficher le premier point en utilisant la notation MOV [DS:SI], BL.

-          Ajouter à SI l'état du compteur temps réel ADD SI,[ES:0x6C].

-          Insérer l'ensemble dans une boucle dont on sortira dés que l'on appui sur une touche du clavier (interruption 16,1).

-          Revenir en mode Texte à la fin du programme (voir TP 4).

 

 

 

IV.      Synchronisation du dessin des points avec le rafraîchissement de l'affichage

De manière simpliste, l'affichage est réalisé par le balayage quasi horizontal de 3 spots (voir figure suivante)

 

 

 

Quand les spots arrivent en bas de l'écran, ils reviennent en haut et la balayage recommence. Pendant la remontée, les spots sont invisibles.

Nous allons dans cet exercice synchroniser l'affichage des points avec la remontée des spots. Pour cela nous allons détecter  grâce à la lecture d'un port d'entrée sortie le moment ou les spots commencent leur remontée. Pendant cette remontée, nous allons effectuer l'affichage.

Cette information de trouve au niveaux du port E/S d'adresse 3DAh. Quand on lit ce port, le bit n°3 (quatrième bit) renseigne sur l'état des spots. Quand ce bit est à 0, on est entrain de dessiner (balayage vers le bas des spots) et quand ce bit est à 1, les spots sont entrain de remonter.

Le principe est d'attendre tant que le bit n°3 est à 1 puis d'attendre tant qu'il est à 0. Ainsi on sera certain que l'on va commencer l'affichage au début de la remontée des spots. Dans les animations graphiques, cette méthode permet d'éviter les phénomène d'instabilité au niveau de l'affichage.

 

Ajouter dans le programme précédent le code qui synchronise le dessin sur le rafraîchissement de l'écran.

Pour cela on suivra les étapes suivantes:

-          Lire le port 3DAh avec l'instruction IN AL, DX (dx contient l'adresse du port).

-          Faire un masque avec la valeur 08 (utiliser l'instruction AND AL,08).

-          Faire le saut tant que le résultat n'est pas nul.

-          Recommencer une seconde boucle qui dure maintenant tant que le résultat est nul.

 

V.      Utilisation des instructions de chaînes pour translater l'écran

Les instructions de chaînes:

 

MOVSB, MOVSW et MOVSD

 

Formes.

MOVSB
MOVSW
MOVSD
REP MOVSB
REP MOVSW
REP MOVSD

Commentaires.

Ces instructions permettent de déplacer un octet (suffixe B comme Byte), un mot (suffixe W comme Word) ou un double mot à partir du processeur 386 (suffixe D comme DWord).

En mode 16 bits, l'octet (mot ou double mot) d'origine est pris en [DS:SI], d'où le nom SI de Source Index. Le segment DS est implicite et il est possible d'en choisir un autre avec un préfixe de segment. La destination est [ES:DI], d'où le nom DI de destination index. Il n'est par contre pas possible de changer le segment ES.

En mode 32 bits, les index ESI et EDI sont utilisés en place de SI et DI.

Ces instructions permettent de faire des transfert simples ou multiples. On parle alors de chaîne de caractère, d'où le nom de l'instruction MOVE (déplacer) S (string=chaîne).

Pour être prêt à faire le transfert suivant, les index sont incrémentés si l'indicateur de direction est à 0, et décrémentés si il est à 1. Pour mettre l'indicateur de direction à 0 on utilise l'instruction CLD (clear direction, clear=effacer). Avec MOVSB, SI et DI seront incrémentés de 1. Avec MOVSW, on vient de transférer deux octets, il faut donc pointer sur le word suivant qui est deux octets plus loin. SI et DI seront donc incrémentés de 2. Avec MOVSD, les index sont incrémentés de 4. On peut aussi mettre l'indicateur de direction à 1 par l'instruction STD (set D, set se traduit par mettre), ainsi les index seront décrémentés de 1, 2 ou 4.

Si on utilise le préfixe REP devant, l'instruction est répétée CX fois en mode 16 bits (ECX fois en mode 32 bits). En fait REP va décrémenter CX et faire l'instruction tant que CX n'est pas nul. Si on met 10 dans CX avant l'instruction MOVSB, on copiera 10 octets. MOVSW copiera donc 20 octets (10 fois 2 octets) et MOVSD copiera 40 octets. Si CX est nul, la première décrémentation mettra CX à 65535, et on fait donc 65536 copies.

Utilisation

On utilise rarement cette instruction en dehors d'une boucle. Elle est utilisée en principe pour copier une zone de mémoire dans une autre. REP MOVSD est plus rapide pour copier un nombre d'octets fixe, on la préférera si le nombre d'octet est connu ou est divisible par 4. On utilise MOVSW si le processeur peut être un 8086 ou un 80286. MOVSB ne devrait que très peu être employée.

Ces instructions sont très utiles pour déplacer des tableaux, des chaînes de caractères, des buffers, des zones d'écran

Si les zones ne se recouvrent pas, on peut utiliser l'incrémentation ou la décrémentation des index. Bien sûr les valeurs de départ ne seront pas les mêmes dans les deux cas. Assurez vous aussi à positionner correctement l'indicateur de direction. Si les zones source et destination se recouvrent, on n'a pas le choix du sens de la copie. Pour déplacer par exemple 100h octets à partir de ES:0000 vers ES:0001, on est obligé de commencer par ES:0100 et il faudra décrémenter les index. Dans le cas contraire, l'octet ES:0000 serait mis en ES:0001 qui serait de nouveau copié en ES:0002 et ainsi de suite. Si c'est réellement ce que vous voulez faire, utilisez plutôt l'instruction STOSB.

Un exemple.

On vient de dessiner en mémoire une belle page que l'on veut afficher en mode 13h. La page dessinée commence en DS:0000.

 

      xor  si,si ; DS:SI pointe sur l'image en mémoire          

 

      xor  di,di ; ES:DI pointe sur l'écran (A000:0000)

      mov  ax,0A000h

      mov  es,ax

 

      cld        ; Index croissants

 

      mov  cx,65536/2 ; 64 ko à copier 2 octets par 2 octets

 

 rep  movsw  ; fait réelement la copie

 

 

 

 

 

 

 

 

Le programme à réaliser

Dans l'étape précédente, nous avions un ciel noir dans lequel apparaissait progressivement des étoiles colorées. Nous allons monter dans une fusée et nous allons voir les étoiles défiler. Cela va donner:

Pour faire ce programme, nous allons répéter quatre choses (une de plus que la dernière fois):

-          Dessiner un point "au hasard"

-          Attendre le retour du spot

-          Décaler l'écran graphique d'une ligne vers le bas (pour avoir l'impression de monter)

-          Tester si un caractère est arrivé

Décaler l'écran.

C'est la seule chose qui est nouvelle dans cette étape. Nous sommes en mode 13h et l'écran visible à 200 lignes. Quand on écrit un point au hasard, on l'écrit dans le segment entier. L'écran dans lequel on dessine dépasse un peu de quelques lignes. Nous allons utiliser la ligne 200, qui est invisible, car seules les lignes 0 à 199 sont affichées. Nous allons dérouler l'écran comme du papier à musique: pour faire descendre l'écran, il suffit de prendre toutes les lignes et de les redessiner une ligne plus bas. Pour avoir de plus en plus d'étoile comme dans l'étape précédente, nous allons mettre la dernière ligne à la place de la première. Pour décaler nos 200 lignes nous allons en utiliser 201 (une ligne sert de mémoire). Nous allons faire cette rotation en deux temps.

Premier temps: copiez les lignes 0 à 199 dans les lignes 1 à 200. En assembleur pour faire un tel travail, il n'y a pas vraiment le choix, il faut utiliser l'instruction MOVSW (pour recopier les octets 2 par 2)

Deuxième temps: copier la ligne 200 (l'ancienne 199) dans la ligne 0.

Premier temps (0..199) ? (1..200)

Les zones source et destination se recouvrent. Il va donc falloir réfléchir: si nous copions d'abord la ligne 0 dans la ligne 1, nous ne pourrons plus copier le ligne 1 dans la ligne 2 car la ligne 1 aura été écrasée et contiendrait la ligne 0. Il va donc falloir copier à l'envers. Mettons la ligne 199 dans la 200, la 198 dans la 199 et ainsi de suite.

Pour positionner les index, il faut mettre dans SI l'offset du dernier ensemble de 2 points de l'écran que l'on va copier (vu que nous commençons par copier par la fin). Chaque ligne contient 320 points, soit 320 octets. Après notre dernier ensemble de 2 points, nous allons trouver les points de la ligne 200, et donc d'offset 200*320. Le dernier couple copié est donc en 200*320-2. Nous écrirons simplement:

 

 mov si,200*320-2                                              

Pour le calcul de di, c'est exactement une ligne au dessous soit 320 octets plus loin.

 

 mov di,200*320-2+320                                          

Ne faites pas le calcul pour le propgramme, on sait ainsi comment cette valeur est obtenue.

L'initialisation des segments peut se faire au début du programme une fois pour toutes. Profitons de l'initialisation de l'un pour initialiser l'autre. Écrivons quelque chose du style:

 

 mov ax,0A000h

 mov ds,ax

 mov es,ax                                                     

      Pour déplacer 200 lignes, soit 200*320 octets, il faut initialiser CX à 200*320/2 vu que nous copions les octets 2 par 2.

      Enfin pensons à mettre l'indicateur de direction à 1, pour décrémenter (aller à l'envers). Quand tout cela est fait, appelons l'instruction MOVSW avec son préfixe REP pour faire tout le transfert.

Deuxième temps (200) ? (0).

Ici les lignes ne se recouvrent pas, on peut donc les copier dans le sens que l'on veut. Comme nous avons copié le reste en décrémentant les indices, il y a deux solutions: soit on met l'indicateur à 1 une fois pour toutes dans les initialisations et on décrémente aussi pour cette partie, soit on positionne l'indicateur à chaque fois pour chacune des deux parties et on peut utiliser l'incrémentation. Pour une fois, c'est vous qui choisissez. On aura de toutes façon l'occasion d'utiliser cette deuxième démarche dans une étape ultérieure.

Ce que l'on voit à l'écran.

Normalement si tout se passe bien, l'écran va se remplir progressivement de points tout en descendant. Tant que les points sont peu nombreux, tout est sympathique, mais quand les points seront suffisamment denses, vous apercevrez comme des barres horizontales au milieu de l'écran. Cela est dû en partie au fait que l'écran est dessiné dans le mauvais sens (bas vers le haut, contraire au balayage). Il y a des moyens de faire mieux, mais ce qui nous intéresse actuellement est la découverte des méthodes de programmation et les instructions, avec une progression pas trop rapide.

 



697