AUTEUR: Mr ALLOZA David
e-mail david.alloza@wanadoo.fr
COURS N°2
LES MICROPROCESSEURS INTEL
Seules les photocopies pour un usage personnel sont autorisées
INTRODUCTION
Les choses sérieuses commencent, après avoir vu dans le cours précédent toutes les bases mathématiques dont nous auront besoin durant notre apprentissage, nous allons nous attaquer à l'assembleur proprement dit. Ce cours est très important, si vous avez du mal à le suivre, prenez votre temps, ou vous serez handicapé par la suite.
Dans un premier temps, je décrirai ce qu'est en réalité la mémoire, pour vous ce terme devra à la fin de ce cour représenter autre choses qu'une simple quantité.
Ensuite nous aborderons le cas des segments qui sont indispensables pour accéder à la mémoire dans le mode REEL ( sous DOS, ou windows 3.1), ces mêmes segments auront une toute autre signification dans le mode protégé, nous parlerons alors de descripteurs.
L'étude de l'assembleur proprement dite commencera avec la description des registres du microprocesseur, même si leur description vous parait un peu obscure, vous verrez que avec un petit peu de pratique, tous cela vous paraîtra familier.
Pour finir nous ferons un petit programme, de quelques lignes qui permet de remplir votre écran de couleurs bariolée, vous apprendrez ainsi ce qu'est la mémoire vidéo, et ferez connaissance avec le VGA.
SOMMAIRE
I) La mémoire de quoi s'agit il au juste?
II) Des segments pour se positionner dans la mémoire.
III) Un microprocesseur pour agir sur la mémoire.
IV) Des mémoires dans le microprocesseur : les registres.
V) Assembleur et langage machine.
VI) Un exemple qui clarifie les choses
I)LA MEMOIRE
Une analogie consiste à comparer la mémoire à une longue rangée de tiroirs alignes les un derrière les autres. Si on donne à chaque tiroir un numéro, en commençant par 0 pour le 1er tiroir, on dira que ce numéro est l'adresse de la mémoire, dans la suite on parlera d'adresse mémoire . La coutume est(de nombreux avantages la justifie) de noter les adresses mémoires en hexadécimal.
Dans le cours précédent un exercice vous avait amené à calculer l'adresse mémoire maximale que pouvait gérer un 80386, le nombre était très élevé ( 4*1024*1024*1024 = 4Go).
Dans un P.C., l'unité de mémoire est l'octet, ce qui signifie que chaque tiroir comportera un octet, soit 8 bits. On peut donc imaginer que notre tiroir contient 8 paragraphes, chacun représentants un bit pouvant représenter deux états 0 ou 1.
Avoir un ordinateur qui possède 8Mo de mémoire signifie qu'il possède 1024*1024*8 octets de mémoire, et donc 1024*1024*8*8 bits de mémoire.
Dans un système informatique, les deux opérations possibles sur la mémoire sont l'écriture et la lecture d'une valeur. Dans n'importe quel langage informatique:
Pour écrire dans la mémoire on doit fournir deux paramètres au microprocesseur:
a) L'adresse mémoire ou l'on va écrire.
b) La valeur que l'on vas écrire à cette adresse ( sur 8,16,ou 32bits).
Pour lire une valeur dans la mémoire on doit fournir ces deux paramètres
a) L'adresse mémoire ou on veut lire.
b) l'endroit ou on doit conserver la valeur lue (valeur sur 8, 16, ou 32 bits).
Nous verrons à la fin de ce cour , que pour lire ou écrire dans la mémoire on utilise l'instruction assembleur MOV.
II)DES SEGMENTS POUR SE POSITIONNER DANS LA MEMOIRE.
Revenons à nos tiroirs; Imaginez maintenant que l'on mette une étiquette tous les 16 tiroirs, on inscrit sur la première étiquette la valeur 0, puis 16 tiroirs plus loin la valeur 1, ainsi de suite. on appellera le numéro de l'étiquette le segment, et la distance par rapport à cette étiquette l'offset. une adresse mémoire se composera donc de deux parties:
*Un segment toujours représenté en hexadécimal non signé
*Un offset toujours représenté en hexadécimal non signé
on notera l'adresse segment:offset.
exemple A000h:0000h
On peut trouver une correspondance (en sachant qu'il y a un segment tout les 16 octets) entre l'adresse sous la forme segment:offset, et l'adresse physique ( on l'appelle ainsi car c'est cette adresse qui sera déposée sur le bus d'adresse du microprocesseur l'or d'un accès à la RAM).
adresse physique = SEGMENT*16+OFFSET.
Limitations: Une limitation importante de ce modèle est due à l'héritage 16 bit des microprocesseurs intel: Les segments et les offsets sont codés sur 16 bits non signés . De plus le bus d'adresse des premiers microprocesseurs étant sur 20 bits, l'adresse maximale possible était FFFFFh soit au maximum 1Mo adressable, cette limitation est restée et l'adresse maximale autorisée en mode réel est F000h:FFFFh
Ces considérations expliquent l'impossibilité qu'a le DOS d'utiliser la mémoire étendue, on se retrouve donc (quand on à enlevé la zone d'adressage du BIOS en ROM et la RAM vidéo) avec une mémoire vive adressable de 640Ko au maximum (mémoire conventionnelle).
L'utilisation des adresses sous la forme segment:offset, n'est pas un choix, c'est une contrainte que nous devrons supporter tant que nous programmerons en mode réel.
REMARQUE: Une remarque importante, il s'agit d'éviter une erreur présente dans la quasi totalité des livres sur la programmation assembleur, qui consiste à croire que la mémoire est divisée en segments de 64Ko, ce qui est faux.
La mémoire est divisée en segments de 16 octets, à partir desquels il est possible de couvrir une zone de 64Ko à l'aide d'un offset de 16 bits.
AUTRE REMARQUE: A partir de segments distincts on peut accéder à une même adresse mémoire en effet ces adresses sont identiques:
segment offset
0005h:0001h ==> 5h*16+1h = 80 + 1 = 81
0004h:0011h ==> 4h*16+11h = 64 + 17 = 81
0003h:0021h ==> 3h*16+21h = 48 + 33 = 81
0002h:0031h ==> 2h*16+31h = 32 + 49 = 81
0001h:0041h ==> 1h*16+41h = 16 + 65 = 81
0000h:0051h ==> 0h*16+51h = 0 + 81 = 81
On dit alors que ces segments sont entrelacés.
III) UN MICROPROCESSEUR POUR AGIR SUR LA MEMOIRE
III-A)LES BUS DE COMMUNICATION
Un bus est un groupement de conducteurs électriques, représentant chacun une variable logique, qui sert à faire transiter des informations entre plusieurs éléments.
III-A-1) Le bus d'adresse:
Ce bus, d'une taille de 20 bit sur un PC XT, à été porté à une taille de 32 bits à partir du 386. Sa tache est de fournir l'adresse visée par le microprocesseur au contrôleur de mémoire. Ce bus est unidirectionnel, l'information ne vas que dans le sens microprocesseur --> périphérique.
III-A-2) Le bus de données:
C'est par ce bus que le microprocesseur transmet des données à ses circuits périphériques, ce bus de 8 bits dans le 8088 à été porté à 32 bits dans le 386 puis à 64 bits dans le pentium. Ce bus est bidirectionnel, il permet au microprocesseur de lire des données, tous comme, il lui permet d'en écrire.
III-A-3) Le bus de contrôle:
C'est par ce bus que vas être communiqué le sens du transfert sur le bus de données (lecture ou écriture) ainsi que la taille du transfert (8, 16, 32, 64 bits). C'est aussi par ce bus que le circuit périphérique dira au microprocesseur s'il est prêt à émettre ou recevoir des données.
III-A-4)Mécanisme d'accès mémoire:
Dans un premier temps nous ignorerons le rôle joué par la mémoire cache.
a)Accès en lecture: Le microprocesseur transmet par le bus d'adresse l'adresse de la mémoire dont il veut lire le contenu, il place sur le bus de contrôle la taille de la donnée qu'il veut lire, et l'ordre LECTURE. A partir de ce moment la, le microprocesseur scrute le bus de contrôle pour savoir si la donné est arrivée, ou si il faut encore attendre. Dés que le bus de contrôle informe le microprocesseur que la donnée est disponible, le microprocesseur lit la donnée sur son bus de donnée.
b)Accès en écriture: Le microprocesseur transmet par le bus d'adresse l'adresse de la mémoire ou il veut écrire, il place sur le bus de contrôle la taille de la donnée qu'il veut écrire ainsi que l'ordre ECRITURE. Il place sur le bus de données la donnée à écrire. A partir de ce moment la, le microprocesseur scrute le bus de contrôle pour savoir si la donnée à été écrite en mémoire. Dés que le bus de contrôle informe le microprocesseur que la donnée à été écrite, le microprocesseur peut continuer son travail.
III-A-5)Temps d'accès mémoire:
C'est un chiffre exprimé généralement en ns (nano secondes) qui détermine le temps qui vas s'écouler entre la demande de lecture (ou d'écriture) mémoire et sa réalisation. On peut faire l'analogie entre un temps accès mémoire et le temps qu'il faut pour ouvrir un tiroir et ainsi accéder à son contenu. Avec de la mémoire à 80ns ( la vielle RAM avant que l' EDO n'apparaisse), il s'écoule donc 80ns entre la demande d'écriture (ou de lecture) et sa réalisation, cela peut vous paraître très peu, mais pourtant c'est énorme à l'échelle du microprocesseur.
En sachant qu'un pentium à 100Mhz, exécute une instruction (parfois 2) en 10ns, on à le temps d'exécuter 8 instructions en attendant une écriture mémoire, sur un pentium 200 c'est pire, on peut en exécuter environ 16. Dans les microprocesseurs anciens (avant le 486), le microprocesseur ne pouvait rien faire en attendant l'écriture, il bloquait, maintenant on peut exécuter des instructions en parallèle avec une demande d'accès mémoire.
III-B)LA MEMOIRE CACHE
III-B-1)La mémoire cache interne:
Il s'agit d'une mémoire un peut spéciale, quand à sa constitution, à son rôle, et à l'endroit ou elle se trouve.
Reprenons nos fameux tiroirs:
Vous êtes donc dans votre bureau, au fond de la pièce, une grande armoire qui comporte tous les tiroirs dont nous parlions précédemment. Votre entreprise ayant réalisé de gros bénéfices vous avez acheté un grand bureau et employé une secrétaire.
La taille de votre bureau vous permet de poser dessus environs une vingtaine de dossiers en plus de votre espace de travail. Si vous avez besoin d'un dossier particulier, dans un premier temps vous regardez s'il n'est pas sur votre bureau, s'il y est vous le prenez. Si votre dossier n'est pas sur le bureau, vous allez demander à votre secrétaire de vous l'apporter (car vous êtes très fainéant), Cette opération prendra un certain temps, et vous serez tenté de conserver ce dossier sur votre bureau au cas ou il serait à nouveau utile. La fin de la journée arrive et votre bureau est encombré de dossiers, il vous en faut un autre, vous appelez votre secrétaire qui vous l'apporte, et comme il ne reste plus de place sur votre bureau, vous faites ranger à votre secrétaire un dossier dont vous pensez ne plus avoir besoin.
Et bien la mémoire cache fonctionne exactement sur ce principe.
Vous êtes le microprocesseur, votre bureau la mémoire cache, et votre secrétaire le contrôleur mémoire qui gère vos tiroirs de mémoire.
Quand le microprocesseur à besoin d'une donnée stockée en mémoire:
Il regarde si cette donnée est dans le cache, si elle y est il la lit, si elle n'y est pas, il demande au contrôleur mémoire d'aller la chercher dans la mémoire centrale, il l'utilise alors tout en la gardant soigneusement dans le cache. Si maintenant le cache est plein, alors il supprime du cache la donnée qui y est restée le plus longtemps sans être utilisée, et donne la place disponible à la nouvelle donnée.
La mémoire cache interne, qui est comme son nom l'indique interne au microprocesseur, est très rapide d'accès, on peut considérer son temps de réponse comme nul. On peut se poser la question de savoir pourquoi toute la mémoire de l'ordinateur n'est pas de la mémoire cache, en fait la raison est d'ordre économique: La mémoire cache est encore très chère, un ordinateur composé uniquement de mémoire cache serait hors de prix. Un exemple intéressant de mémoire cache est celle du pentium pro dont la capacité à été portée à 256Ko, ce qui est énorme pour un cache interne.
Quelques chiffres:
MICROPROCESSEUR |
BUS DE DONNEES |
BUS D'ADRESSES |
TAILLE DU CACHE |
8088 |
8 |
20 |
0 |
8086 |
16 |
20 |
0 |
80286 |
16 |
24 |
0 |
80386SX |
16 |
32 |
0 |
80386DX |
32 |
32 |
0 |
80486SX |
32 |
32 |
4Ko |
80486DX |
32 |
32 |
4Ko |
80486DX2 |
32 |
32 |
8Ko |
80486DX4 |
32 |
32 |
16Ko |
PENTIUM |
64 |
32 |
16Ko |
CYRIX 686 |
64 |
32 |
16Ko |
PENTIUM PRO |
64 |
32 |
256Ko |
III-B-2)La mémoire cache externe (ou cache de second niveau):
Le volume de votre entreprise augmente, et décidément votre secrétaire devient insupportable, elle n'accepte plus de faire des centaines de trajets tiroirs - bureau par jour.
L'idée vous vient d'installer entre votre bureau et les tiroirs de l'armoire une table basse sur laquelle vous pourrez entreposer plus d'une centaine de dossiers. Ainsi avec cet ingénieux système les trajets bureau - tiroirs seront moins fréquents.
Si vous avez besoin d'un dossier, dans un premier temps vous regarderez s'il se trouve sur votre bureau, si c'est le cas vous le prenez et c'est fini.
Si votre dossier ne se trouve pas sur le bureau, vous pouvez regarder s'il n'est pas sur la table basse, s'il y est, il suffit de tendre le bras pour le prendre, ce qui est tout de même plus rapide que d'appeler la secrétaire. Et comble de malchance il n'est pas sur la table basse ( il s'agit sûrement d'un nouveau dossier que vous n'avez pas regardé de la semaine), la secrétaire vous l'amènera avec le sourire.
Votre table basse remplis le même rôle que votre bureau, au détail près qu'elle est un peut plus difficile d'accès et qu'on peut entreposer dessus plus de dossiers.
Et bien dans un ordinateur le cache de second niveau rempli exactement le même rôle que la table basse de votre bureau, il permet de garder de coté des données auxquelles on accède relativement souvent. On pourrait très bien imaginer que dans le futur des ordinateurs contiennent des caches de troisième ou de quatrième niveau.
La taille des caches de second niveau à augmenté ces dernieres années, ils faisaient 32Ko sur les 386, 128Ko sur les 486, il font maintenant de 256Ko à 512Ko sur les pentiums les plus récents. La technologie mémoire qu'ils utilisent, permet, actuellement, d'accéder à une donnée en environ 20ns.
En gérant la mémoire virtuelle (sur le disque dur), windows 3.1 ou windows 95 créent un niveau de cache supplémentaire, le cache de premier niveau est dans votre microprocesseur, le cache de second niveau est sur la carte mère de votre ordinateur, la RAM de votre ordinateur compose le cache de troisième niveau, et l'équivalent de la mémoire est maintenant le disque dur.
Le microprocesseur cherche la donnée dans son cache, si elle n'y est pas, on cherche dans le cache de second niveau, si elle n'y est pas on la cherche dans la mémoire centrale, et si comble de malchance, on ne la trouve pas, on va donc la chercher sur le disque dur (ce qui prend beaucoup de temps).
Ne vous affolez pas, au fond ce n'est pas vous le programmeur qui devra gérer tout cela, le microprocesseur s'en charge tout seul. Il existe cependant un intérêt certain à connaître ces mécanismes, on peut se débrouiller, quand on écrit un programme en assembleur (et même en C), pour faire la maximum d'accès mémoire dans le cache et donc le minimum dans la mémoire centrale, ce qui à pour conséquence d'accélérer notablement la vitesse d'exécution, on a alors optimisé pour le cache, nous y reviendrons.
Une imperfection réside dans l'analogie entre les tiroirs et la réalité des accès mémoire: Quand vous accédez à une donnée mémoire, elle reste évidemment présente dans la mémoire, en fait vous faites une sorte de duplication à autre vitesse, une sorte de photocopie de dossiers.
IV) les registres.
Un registre sert à stocker les données nécessaires à l'exécution d'un programme par le microprocesseur. On n'accède pas à un registre avec une adresse, mais avec son appellation.
Pour les exemples qui vont suivre, je vais vous décrire l'instruction la plus utilisée dans le microprocesseur: MOV
MOV registre1,registre2 a pour effet de copier le contenu du registre2 dans le registre1, le contenu préalable du registre1 étant écrasé. Registre1 et registre2 devant être de même taille.
IV-1)Les registres généraux:
Ils se nomment EAX,EBX,ECX,EDX ce sont des registres de 32 bits, qui servent notamment pour stocker les résultats des opérations arithmétiques.
A) Le registre EAX
On peut accéder aux bits 0 à 7 de ce registre, en utilisant la notation AL ( L pour LOW (bas en anglais)).
par exemple: MOV AL,10h
Aura pour effet de placer la valeur 10h dans les bits 0..7 du registre EAX, le reste du registre étant inchangé.
Pour accéder aux bits 8 à 15 de ce registre, on doit utiliser la notation AH (H pour HIGH (haut en anglais))
par exemple: MOV AH,31h
Aura pour effet de placer la valeur 31h dans les bits 8..15 du registre EAX, les autres bits restant inchangés.
On peut accéder aux bits 0 à 15 du registre EAX, en une seule fois, on doit alors utiliser la notation AX.
par exemple: MOV AX,1234h
Aura pour effet de placer la valeur 1234h dans les bits 0..15 du registre EAX.
et enfin pour accéder au registre EAX dans son intégralité, il suffit de l'appeler par son nom EAX.
exemple : MOV EAX,12345678h
Aura pour effet de placer la valeur 12345678h dans le registre EAX.
voyons maintenant un petit programme: ces instructions sont exécutées les unes après les autres.
INSTRUCTION
|
CONTENU DE EAX |
CONTENU DE AX |
CONTENU DE AH |
CONTENU DE AL |
MOV EAX,12345678h
|
12345678h |
5678h |
56h |
78h |
MOV AL,10h
|
12345610h |
5610h |
56h |
10h |
MOV AX,0000h
|
12340000h |
0000h |
00h |
00h |
MOV AH,31h
|
12343100h |
3100h |
31h |
00h |
MOV AL,AH
|
12343131h |
3131h |
31h |
31h |
B) Les registres EBX,ECX,et EDX: conformément au schéma du début du chapitre, se manipulent exactement comme le registre EAX.
IV-2)Les registres de segments:
Ces registres, de 16 bits, servent uniquement, à indiquer l'or d'une écriture ou d'une lecture mémoire, à partir de quel segment on veut lire ou écrire.
Pour donner un exemple, je vais vous présenter un autre aspect de l'instruction MOV.
MOV registre1 , segment: taille [OFFSET]
a pour effet de copier la valeur qui se trouve à l'adresse OFFSET+16*segment de taille 'taille' dans registre1. On dira que l'on à réalisé un adressage direct ( l'adresse de la donnée est directement comprise dans l'instruction), dans les cas précédent (pour les premiers exemples), il s'agissait d'adressage immédiat .
taille peut être:
Un octet on écrit alors BYTE PTR pour taille
Un mot de 16 bits on écrit alors WORD PTR pour taille
Un double mot (32 bits) on écrit alors DWORD PTR pour taille.
Exemple:
MOV AL , 200h:BYTE PTR [20h]
a pour effet de copier l'octet qui se trouve à l'adresse 200h:20h, dans le registre AL.
et bien, si on met la valeur 200h dans le registre de segment DS, on pourra réaliser la même chose avec:
MOV AL , DS:BYTE PTR [20h]
Rien ne nous empêche de réaliser une écriture mémoire plutôt qu'une lecture, par exemple:
MOV DS:BYTE PTR [20h], AL
Copie la valeur stockée dans AL, à l'adresse DS:20h soit DS*16+20h.
IV-3 ROLE DES REGISTRES DE SEGMENTS:
DS 'DATA SEGMENT' est généralement utilisé pour adresser des données.
CS 'CODE SEGMENT' représente le segment à partir duquel se trouvent les instructions du programme (et oui les instructions doivent êtres stockée quelques part en mémoire, tous comme les données, vous reverrez ça dans le dernier chapitre).
ES 'EXTRA SEGMENT ' On peut l'utiliser comme on veux, tous comme FS et GS.
SS 'STACK SEGMENT ' Comme je ne vous ai pas encore expliqué ce qu'est la pile, j'y reviendrai plus tard.
On ne peut pas mettre directement une valeur immédiate dans un registre de segment, le microprocesseur ne le permet pas:
MOV DS,0200h est incorrect
par contre
MOV AX,0200h
MOV DS,AX est correct.
IV-4)Les registres d'index:
Ce sont des registres de 32 bits, qui peuvent servir à faire un peu n'importe quoi (opérations arithmétiques...), mais qui à l'origine (sur les P.C. XT ) servaient à contenir l'offset d'une adresse.
par exemple:
MOV DI,20h
MOV DS:BYTE PTR [DI], AL
est équivalent à
MOV DS:BYTE PTR [20h], AL
Une exception : Dans les registres généraux EBX peut être utilisé comme un registre d'index.
Ex:
MOV BX,20h
MOV DS:BYTE PTR[BX], AL est équivalent à l'exemple précédent.
IV-5) LES DIFFERENTS REGISTRES D'INDEX (sur 32 bits):
EDI : 'DESTINATION INDEX', Tire son nom des instructions de copie de chaîne de caractères, ou il pointait sur la destination. Ces instructions ne sont quasiment plus utilisées aujourd'hui. On peut s'en servir pour faire ce que l'on veut à partir du 386.
ESI:'SOURCE INDEX ' tous comme pour EDI, il servait pour copier des chaînes de caractères, il pointait sur la source. On peut s'en servir pour faire ce que l'on veut à partir du 386.
ESP:'STACK POINTEUR' Il sert à contenir l'offset de l'adresse de la pile, mieux vaut éviter d'y toucher.
EBP:'BASE POINTEUR' On en fait, tous comme pour EDI et ESI ce que l'on veut .
Bien entendu, DI,SI,SP,BP représentent les bits de 0 à 15 de ces registres.
IV-6)Les registres de travail du microprocesseur:
A: EIP, il s'agit d'un registre de 32bits (à partir du 386, avant il s'appelait IP et faisait 16 bits), on ne peut pas y accéder, le microprocesseur s'en sert pour savoir quelle instruction il doit exécuter, ce registre contient l'offset par rapport au registre de segment CS, de l'adresse de la prochaine instruction à exécuter.
B: Registre de flag (32 bits à partir du 386): On ne peut pas non plus y accéder. Son rôle est de positionner ses bits ( à l'état 0 ou 1) selon le résultat arithmétique de la précédente instruction exécutée. Toutes les instructions conditionnelles (instructions qui ne s'exécutent que si une conditions est vérifiée, nous les verrons par la suite.. ) s'y réfèrent.
Je ne décrirai que les bits que nous utiliserons pendant ces cours.
BIT |
APPELLATION |
Rôle |
0 |
CF (Carry flag) |
passe à 1, si l'opération arithmétique à engendré une retenue
|
2 |
PF (Parity Flag) |
passe à 1 si l'opération arithmétique à engendré un résultat avec un nombre pair de bits à 1.
|
6 |
ZF (Zero Flag) |
Passe à 1 si le résultat de l'opération arithmétique précédente est nul.
|
7 |
SF (Sign Flag) |
Passe à 1 si le résultat de l'opération arithmétique précédente est négatif
|
11 |
OF(Overflow Flag) |
Passe à 1, si la somme de deux nombres positif donne par débordement un résultat négatif (voir cour 1) |
IV-7)LA PILE:
La pile est une zone mémoire, dont le programmeur fixe la taille, et qui sert à sauvegarder la valeur d'un registre, tous comme à faire passer des paramètres à un sous programme.
Pour utiliser la pile nous auront 6 instructions:
PUSH --> pour empiler une valeur
POP --> Pour dépiler une valeur
PUSHA --> Pour empiler le contenu de tous les registres du microprocesseur (en 16 bits).
POPA --> Pour dépiler le contenu de tous les registres du microprocesseur (en 16 bits).
PUSHAD --> Pour empiler le contenu de tous les registres du microprocesseur (en 32 bits).
POPAD --> Pour dépiler le contenu de tous les registres du microprocesseur (en 32 bits).
La pile dans les microprocesseurs INTEL, est une pile LIFO (Last In First Out) ce qui signifie que la première valeur dépilée, sera la dernière que l'on aura empilé.
Exemple:
MOV AX,0010h
MOV BX,1234h
PUSH AX
PUSH BX
POP AX ==> AX contient 1234h
POP BX ==> BX contient 0010h
cet exemple à permis d'échanger le contenu de deux registres, en se servant de la pile comme tampon intermédiaire.
V)ASSEMBLEUR ET LANGAGE MACHINE
Dans des discutions passionnées sur les compétences de chacun, il est rare que quelqu'un ne sorte pas l'idiotie suivante:
Le langage machine c'est plus rapide que l'assembleur !
Ou pire encore
L'assembleur, c'est génial, c'est plus rapide que le langage machine !
Rassurer vous, si vous avez à faire à ce genre de personnes, ne vous sentez pas ignorant, il s'agit de personnes qui ne savent pas de quoi elles parlent, et qui se permettent de porter des jugements.
Le langage machine c'est exactement la même chose que l'assembleur, seule l'apparence diffère, je m'explique.
Si vous voulez mettre la valeur FFFFh dans AX vous taperez
MOV AX,FFFFh
et votre assembleur transformera ça en
B8FFFF soit en binaire %1011 1111 1111 1111 1111.
quand votre microprocesseur rencontrera la valeur binaire %10110101, il saura que il s'agit de l'instruction MOV AX,? et que vous allez lui fournir à la suite une valeur immédiate sur 16 bits qu'il devra mettre dans AX.
Si vous aviez tapé directement les bits un à un, le résultat aurait été exactement le même, vous auriez simplement beaucoup souffert pour rien, vous auriez alors programmé en langage machine.
L'assembleur, se limite en fait à directement transcrire en code machine votre programme assembleur. L'assembleur ne modifiera en rien vos initiatives , la vitesse d'exécution est donc exactement la même, que le programme ait été programmé en langage machine bit par bit , ou en assembleur avec turbo assembleur (ou watcom assembleur).
Si par contre, vous programmez en basic ou en langage C, vous ne saurez pas ce que le compilateur fera de votre programme quand il le transformera en un programme machine directement compréhensible par le microprocesseur.
Ces petites précision, devraient vous permettre de comprendre maintenant pourquoi les instructions ont une adresse, elles sont pointées par CS:EIP, et c'est le microprocesseur qui les interprétera comme données ou instructions selon le contexte. Vous verrez, que, quand vous programmerez si par mégarde vous allez continuer l'exécution de votre programme dans des données, le microprocesseur se retrouvera alors avec des instructions incohérentes, et plantera assez vite.
VI) Un exemple qui clarifie les choses
; ------------------------------------------------------------- Pile ---------
.286
PILE segment stack
dw 200h dup(?)
PILE ends
; ---------------------------------------------------------- Données ------
DATA segment
OLD_VIDEO db 0
DATA ends
;---------------------------------------------------------------------------------
CODE segment
.386
assume ss:PILE
assume ds:DATA
assume cs:CODE
; ---------------------------------- CODE -------------------------------------
start:
mov ax,DATA
mov ds,ax
mov ah,0fh ; Lit le mode vidéo
int 10h ; courant
mov [OLD_VIDEO] ,al ; et le sauvegarde
mov al,13h ; passe en 320-200 256c
mov ah,00h
int 10h
; / regardez à partir d'ici vous devriez comprendre ces 6 lignes en lisant la suite /
mov ax,0A000h ;1 le début de la ram vidéo en vga
mov es,ax ;2 dans le extra segment
mov cx,320*200 ;3 pour boucler 64000 fois
boucle: mov di,cx ;4 on copie cx dans di
mov es:byte ptr[di],ch ;5 on met de la couleur sur l'ecran
loop boucle ;6 on boucle tant que cx>0
; ///// vous verrez le reste dans les prochains cours ///////
clav: mov ah,01h ; rien dans le buffer clavier ?
int 16h
je clav ; si il y a quelque chose on quitte
;************************************************************************
mov al,[OLD_VIDEO] ;restaure le mode video
mov ah,00h
int 10h
mov ah,4Ch ; quitte
int 21h
; ------------------------------------------------------------- Fin du Code --
CODE ends
end start
EXPLICATIONS DES 6 LIGNES QUE VOUS POUVEZ COMPRENDRE:
Tout d'abord il faut que je vous parle de l'instruction LOOP, cette instruction décrémente CX ( CX <--- CX-1), et teste ensuite le bit ZF du registre de flag. Si le bit ZF est égal à 1 (c'est donc que CX=0), le programme continue, sinon, le microprocesseur va se brancher au label BOUCLE.
Si au début on met 10 dans CX, la boucle vas se répéter 10 fois.
Ici on met 320*200 = 64000 dans CX, la boucle vas donc se répéter 64000 fois ce qui correspond au nombre de points sur l'écran.
Si ce programme affiche quelque chose, c'est parce que l'on écrit des valeurs dans la RAM vidéo, qui est une mémoire un peut spéciale qui commence à l'adresse A000h:0000h avec une carte VGA, et qui fait 64K. Si on écrit un octet dans la mémoire vidéo, on fixera la couleur du point de l'écran. Si on écrit la valeur 10, le point aura comme couleur la dixième couleur de la palette, il y en a 256 en tout. Je reviendrais la dessus dans le cour N°4.
Le reste du programme est 'hors programme' selon la formule consacrée dans l'enseignement.
Détails:
LIGNE 1:
MOV AX,0A000h => on met la valeur A000h dans AX, j'ai mis un 0 devant parce que turbo assembleur le demande, une valeur hexadécimale ne doit jamais commencer par une lettre, on met donc un 0 devant.
LIGNE 2:
MOV ES,AX => on à été obligé de passer par le registre AX car on ne peut pas mettre directement une valeur immédiate dans un registre de segment, à partir de maintenant ES pointera sur le segment A000h, donc ES pointera à l'adresse A000h*16+0=A0000h.
LIGNE 3:
MOV CX,320*200 => j'ai préféré écrire 320*200, plutôt que 64000, parce que 320*200 est plus parlant, on comprend que l'on à 200 lignes de 320 points. L'assembleur convertira automatiquement 320*200 en 64000, ça ne change rien au résultat.
LIGNE 4:
MOV DI,CX => On est obligé de copier CX dans DI, car CX ne peut pas servir d'index.
LIGNE 5:
MOV ES:BYTE PTR [DI],CH => on copie à l'adresse ES*16+DI, le contenu de CH, vous pouvez modifier cette ligne en remplaçant CH par CL, c'est amusant à voir.
LIGNE 6:
LOOP BOUCLE ==>Cette instruction décrémente CX et si le résultat n'est pas nul, le microprocesseur se branche à BOUCLE.
Pour finir quelques idées sur la RAM vidéo en mode 13h.
X=0 OFFSET = 0...........................X=319 OFFSET = 319
Y=0 Y=0
. .
. .
. .
. .
. .
. .
. .
. .
. .
. .
. .
. .
X=0 OFFSET = 199*320 X=319 OFFSET = 320*199+319
Y=199....................................Y=199
Pour connaître l'offset qu'il faut fournir pour atteindre un point de coordonnées X,Y on a OFFSET = X + 320*Y.
Ah, oui, au cas ou vous auriez la fainéantise de lire le manuel de turbo assembleur, pour assembler ce programme il faudra taper:
TASM PRG1 pour le watcom WASM PRG1WASM
TLINK PRG1 WLINK FILE PRG1WASM NAME PRG1.EXE
et pour le lancer
PRG1
Si vous n'avez pas encore acheté le WATCOM C, attendez, je vais essayer de trouver un compilateur dans le domaine public. Si vous l'avez déjà acheté vous avez fait une très bonne acquisition, on s'en servira intensivement dans quelques cours.
C'est fini pour ce cours, il est extrêmement difficile, mais je vous assure que si vous l'avez compris vous avez passé le plus difficile, ce cours comporte la base du raisonnement qu'il faudra maîtriser pour la programmation en assembleur. La suite devrais être, je pense plus facile pour vous, 15 jours sont peut être un peu court, n'hésitez pas à prendre 3 semaines, voire un mois, quitte à m'envoyer du courrier intermédiaire pour diverses questions. BON COURAGE!.
La disquette jointe comporte:
PRG1.EXE => cet exemple assemblé, pour ceux qui n'ont pas d'assembleur
PRG1.ASM =>le source pour turbo assembleur
PRG1WASM.ASM =>pour ceux qui utilisent l'assembleur du WATCOM
FRACAT02.EXE =>Ce que vous saurez faire à la fin du cour N°5 (motivation !).
EXERCICES:
EXERCICE 1> (facile pour celui qui à bien bossé le cours 1)
a)Si on multiplie une valeur stockée dans AX, par une valeur Stockée dans BX, sur combien de bits tiendra le résultat.
b)Donnez, un registre qui aurait une taille suffisante pour stocker le résultat.
EXERCICE 2> (purement calculatoire...)
a)Dans le programme présenté, ou si situe sur l'écran le deuxième pixel écrit (attention au piège ! ).
b)En sachant que la mémoire vidéo en VGA s'adresse sur l'espace [A0000h....AFFFFh], combien d'octets peut on écrire dans cet espace sans faire apparaitre de points sur l'écran, et dans quelle fourchette d'adresse se trouvent il ?
EXERCICE 3> ( un petit peu difficile ).
Modifiez le programme pour que l'on change de couleur toutes les lignes, la premiere ligne aura la couleur 0, la deuxième ligne la couleur 1 ......... la 200e ligne aura la couleur 199, une indication: sauvegardez CX avec un push.
EXERCICE 4> ( facile)
a)Modifiez le programme pour qu'il remplisse l'écran d'une même couleur.
b)Modifiez le programme pour qu'il change de couleur à chaque pixel.