CRACKING | COUNTERSTRIKE - 4

| ASM: Chapitre 2

Introduction

Bon voilà, j'ai bien bossé cette fois, puisque je vous ai écrit le chapitre 2 (en entier cette fois) du tuto sur l'assembleur. Au départ, je me suis demandé si je devais le faire jusqu'au bout, mais vu le courrier que j'ai reçu, je me suis rendu compte qu'il interessait beaucoup plus de monde que je ne l'imaginais. Donc voilà, c'est fait. Je tient à préciser que c'est un des chapitre les plus important, et qu'il est nécéssaire de le comprendre

CHAPITRE 2 - ORGANISATION ET ACCES A LA MEMOIRE

Donc comme je l'ai dit plus haut, ce chapitre est extrèmement important. Car après ça, on entrera de plein pied dans la programmation en abordant les variables et la structure des données, puis dans le chapitre 4, nous commencerons à apprendre des instructions en plus grosse quantité. Donc je le souligne bien, il FAUT comprendre ce chapitre.
Pour décrire brièvement ce chapitre, je peux déjà vous dire qu'il traitera de l'organisation de la mémoire, de la façon dont le processeur communique avec, les différent régistre des processeur 80x86, les modes d'adressage de ces processeur, etc... Enfin, nous verons les premières instructions d'assembleur, liées donc à la gestion de la mémoire.

I- Les processeurs 80x86

Pour ceux qui n'ont entendus parlé que de pentium II, ou pentium III, eh bien il faut savoir que les familles de processeur ce sont succèdé ajoutant leurs lots d'améliorations, et pas uniquement en matière de rapidité. Voici les principaux processeur x86: 8088/8086, 80188/80186, 80286, 80386, 80486, 80586/pentium.
Chaque type de processeur a ce qu'on appelle des registres (registers). Chaque génération étant un amalgame et une amélioration des précédents. Excepté pour les 8088, 8086, 80188 et 80186, pour lesquels le registre est indentique. C'est pourquoi quand je parlerai de 8086, il sera en fait question de ces 4 processeurs.
Les régistres ont été classé en trois catégories:
- les régistres généraux (general purpose resgisters): il sert en général aux opérations arythmétiques, logiques etc...
- les registres de segments (segment registers): pour accèder aux blocs de mémoire, appelés... segments
- les registres divers (miscellaneous registers): il y a deux régistres spéciaux dont nous parlerons plus tard

II- Le 8086

general purpose registers

Ils y 8 registres généraux de 16 bits pour le 8086: ax, bx, cx, dx, si, di, bp et sp. Bien qu'on puisse les utiliser comme l'on veut, certaines instructions marcheront plus efficacement avec des registre spécifique.

- le régistre ax (Accumulator): il est le plus souvent utilisé pour les calculs arythmétiques et logiques. Bien qu'il soit possible d'utiliser la plupart des autres régistres pour ça, ils sont plus efficace dans le régistre ax.

- le régistre bx (Base): ils sert à stocker les adresses indirectes (que j'expliquerai plus tard).

- le régistre cx (Count): comme son nom l'indique, il compte. Il est le plus souvent utilisé pour compter le nombre d'itération d'un boucle, ou le nombre de caractère dans une chaine.

- le régistre dx (Data): il a deux buts principaux; stocker le dépassement de capacité (overflow) de certaines opérations arythmétiques, et de contenir les adresses d'E/S lors d'un accès à des données.

-les régistres si et di (Source index et Destination Index): comme le régistre bx, ils vont pointer vers des adresses indirects. Ils sont aussi souvent utilisés pour traiter des chaines de caractères

- le régistre bp (Base pointer): il est similaire au régistre bx. Généralement utilisé pour accèder aux paramètres et aux variables locales dans une procédure.

- le régistre sp (Stack Pointer): il est a utiliser avec précautions. En temps normal, vous ne devriez pas utiliser le régistre sp pour le calcul arythmétique. l'execution correcte de la plupart des programmes dépend principalement de la bonne utilisation de ce régistre.

A coté des registres 16 bits, il y a les régistres 8 bits: al, ah, bl, bh, cl, ch, dl, dh. Vous avez sans doute remarqué une similitude avec les régistres 16 bits. En fait, al et ah seront le L.O. byte et le H.O. byte de ax. Il sera sans doute plus facile de comprendre sur le schéma récapitulatif. Il est a noté que les régistres 8 bits ne constituent pas un ensemble de régistre indépendents. En effet, si on modifie la valeur de al, on modifie par conséquent la valeur de ax. Si l'on modifie ax, on modifie à la fois al et ah. En revanche, les régistres si, di, bp et sp ne sont que 16 bits.

Si l'on schématise le régistre général du 8086:

ax
ah | al
si
bx
bh | bl
di
cx
ch | cl
bp
dx
dh | dl
sp

Segment registers

Il y a quatre régistres de segment: cs, ds, es et ss. Ils sont tous de 16 bits. Ils travaillent à la selection de blocs de mémoire (des segments). Un régistre de segment pointe au début d'un segment de mémoire.

les segments de mémoire du 8086 ne dépassent pas 65536 octets (64 Ko), ce qui a désapointé plus d'un programmeur. Nous verons les problème de cette limitation à 64 Ko plus tard.

- le régistre cs (Code Segment): il pointe vers le segment qui contient l'instruction en cours d'execution

- le régistre ds (Data Segment): il pointe vers une variable globale du programme. Bien que que le segment soit limité à 64 Ko, on peut détourner cette limitation en changeant la valeur de ds en temps voulu.

- le régistre es (Extra Segment): il est exactement ce que son nom indique: un extra-segment. Il est utilisé pour avoir accès à un segment lorsque qu'il est difficile ou impossible de modifier les autres régistre de segment.

- le régistre ss (Stack Segment): il pointe vers le segment qui contient la pile du programme. la pile sert à stocker les informations importante à l'execution du programme, comme l'adresse de retour d'une sous-routine, les paramètre de procédure, les variables locales etc... En général, on ne modifie pas le régistre ss, car trop de choses dans le système dépendent de lui. Bien qu'il reste possible de stocker des données dans le régistre ss, ce n'est jamais une bonne idée.

Miscellaneous registers

Il y a deux régistres spéciaux: ip (Instruction Pointer), et le Flags Register. On n'accède pas à ces régistre comme on fait pour les autres. En général, le processeur les modifie directement, tout seul.

- le régistre ip contient l'adresse de l'instruction en cours d'execution.

- le flag register n'est pas du tout comme les autres régistres. Bien qu'il soit lui aussi de 16 bits, chaque bit s'utilise indépendament. Seulement 9 de ces bits servent. Schéma:

1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16

1, 2, 3, 4, 11, 13 ,15: ne sont pas utilisés 5: overflow 6: direction 7: interrupt 8: trace 9: sign 10: zero 12: Auxiliary Carry 14: parity 15: carry

III- Le 80286

Le 80286 ajoute une amélioration principale au 8086: l'execution en mode protégé. Mais je ne vais pas beaucoup m'atarder sur ce mode. En effet, il n'interesse que les programmeurs qui codent leurs propre système d'exploitation ou des programme de bas-niveau. Même si vous écrivez des logiciels pour des OS en mode protégé comme Unix ou OS/2, vous ne devriez pas utiliser cette option du 80286. Il faut juste savoir que des bits additionnels sont utilisé dans le flag register, et que 5 registres additionnels sont utilisés par les sytèmes d"exploitation pour supporter la gestion de la mémoire et les processus multiples:
- machine status word (msw)
- global descriptor table register (gdtr)
- local descriptor table register (ldtr)
- interrupt descriptor table register (idtr)
- task register (tr)

IV- Le 80386/80486

Le 80386 a grandement étendu le nombre de régistres, et a aussi étendu la définition des régistres existant Le 80486 n'a pas ajouté de régistre, mais il a défini quelques bits de registres qui restaient inutilisés par le 80386.

- general purpose register: le changement le plus important pour les programmeurs, c'est l'apparition des régistre 32 bits. Le ax, bx, cx, dx, si, di, bp et sp ont été étendu à 32 bits. Le 80386 les appelles respectivement Le eax, ebx, ecx, edx, esi, edi, ebp et esp pour les différencier de leur version 16 bits (les 16 bits restent tout de même utilisables sur le 80386).

- segment register: deux nouveaux régistres de segment ont été ajouté: fs et gs, qui permet au programmeur d'accèder à 6 segments en même temps sans avoir à les recharger. Il est a noter que les registres de segment n'ont pas été étendu à 32 bits.

- miscellaneous register: là aussi, extension de flags et ip en 32 bits. Ce qui donne respectivement les registre eflags et eip. Là aussi, les 16 bits restent utilisables.

Le 80386 a ajouté 4 registres de controle (control register): CR0 -> CR3. Ces régistres étendent le registre msw du 80286. En fait, le régsitre msw est émulé pour une histoire de compatibilité, mais les données apparaissent en réalité dans le régistre CRx. Sur le 80386 et le 80486, ces régistres controlent des fonctions comme la gestion de la mémoire paginée, l'utilisation ou pas du cache, l'opération en mode protégé etc...

8 régistres de débogage ont été ajoutés. les programmes de débogage comme Softice peuvent utiliser ces régistres pour poser des breakpoints quand vous essayez de trouver une erreur dans un programme. Bien que vous n'utiliserez surement pas ces régistres dans vos programmes, vous les trouverez utiles lorsque vous utiliserez des débugger pour trouver une erreur en un temps relativement réduit. Bien sur, les débuggers ne sont utilisables qu'a partir d'un processeur 80386 et supérieur.

Enfin, le 80386 et 80486 ont ajouté une série de régistres de test, qui test le bon fonctionnement du processeur à l'allumage du système. Intel a mis ces régistres pour pouvoir tester les processeur à la sortie de l'usine, mais les programmeur en on tirer parti pour faire des test de mise en route (power-on test).

Si vous vous en tenez à la programmation courante, beaucoup de ces nouveaux régistres vous seront inutiles. En revanche, les extension 32 bits et les extra-registre de segment sont courament utilisés. le schéma suivant vous résume ce qu'il y a à connaitre le plus:

EAX
| AX
| AH | AL
ESI
| SI
EBX
| BX
| BH | BL
EDI
| DI
ECX
| CX
| CH | CL
EBP
| BP
EDX
| DX
| DH | DL
ESP
| SP
CS |  | FS
--- | --- | ---
EFLAGS
| FLAGS
DS |  | GS
--- | --- | ---
ES |  | SS
--- | --- | ---

V- Organisation physique de la mémoire d'un 80x86

Un façon simple de se représenter la mémoire (RAM evidement) est de l'imaginer sous forme d'un tableau de bits. Par exemple, en pascal, un tableau qui couvrirait toute la RAM sera défini ainsi:

Memory : array [0..RAMtotal] of byte;

Et pour mettre une valeur dans une partie du tableau, c'est très simple:

Memory[adresse] = valeur;

Pour lire une valeur c'est tout aussi simple:

valeur = Memory[adresse];

Maintenant, imaginez que "Memory" soit la mémoire, et que "adresse" soit un nombre binaire qui indique l'emplacement de la mémoire voulu, et vous avez le mode d'accès à la mémoire :o)
(bon, là je viens de me rendre compte que j'aurai du vous parler de l'architecture Von Neumann, mais c'est pas grave, je le ferai plus tard, c'est moins important)

Le premier Megaoctet de mémoire est spécial. En effet, pour des processeurs comme le 80186, la RAM ne pouvait pas dépasser 1 Mo (je vous expliquerai pourquoi dans la suite du chapitre). Donc même sur des processeurs plus recent, certains programmes MS-DOS se limitent à cette partie de la mémoire.

VI- Les Segments sur un 80x86

Les segments ont été une grande avancée à l'époque, puisque ça a permis plein de choses:
- d'augmenter la taille de mémoire adressable à 1 Mo (pour les 8086)
- de partitionner les programmes en modules indépendant
- d'implémenter des programmes orientés objets
et j'en passe. Tient, si vous voullez, j'ai une petite histoire pour vous.

En 1976, alors qu'Intel commençais à étudier le 8086, la mémoire était très chère. Les ordinateurs personnels tels qu'ils étaient à l'époque, n'avaient que 400 octets de mémoire. Même après qu'IBM ait introduit le "PC" sur le marché, 5 ans plus tard, même 64 ko était une grosse quantité de mémoire, et 1 Mo c'était enorme, et pour les riches. Les ingénieurs d'IBM pensaient que 64 ko resteraient suffisant pendant la durée de vie du 8086. mais l'erreur qu'ils ont fait a été de sous-estimer la durée de vie du 8086. Ils pensaient qu'il durerait 5 ans, comme le 8080, et ils avaient des projets pour d'autres processeurs à l'époque (l'iapx432 ça vous dit quelque chose? Non? Normal, c'est vieux). Ils pensaient aussi que même 1 Mo de mémoire serait suffisant. Mais c'était sans compter sur la quantité massive de programmes faits pour le 8086. En 1983, il était clair que Intel ne pouvait pas abandonner l'architecture 80x86. Pendant ce temps, les gens (enfin, les programmeurs et utilisateurs koi) commençaient à gueuler avec leur 1 Mo de mémoire. Intel a donc sorti le 286, qui pouvait gérer jusqu'à 16 Mo de mémoire. Mais là encore, un autre problème est apparu. Les programmes faits pour le PC d'IBM étaient ecrits d'une telle façon qu'ils ne pouvaient pas exploiter plus de 1 Mo. Et l'histoire continue, mais vu que ça vous fait chier, et moi aussi, je vais m'arrèter là.
Enfin tout ça pour dire que c'est dans ce contexte que la segmentation est apparue. En fait, le 8086 était un processeur 16 bits, avec des régistres 16 bits et des adresses 16 bits. C'est de là que vient la limitation d'adressage à 64 Ko. La segmentation permet de dépasser cette limite. Bon, vous allez finir par vous embrouiller, donc je vais m'arrèter là.

Bon, on va voir plus en détail la segmentation. D'abord on va considerer la mémoire de la manière suivante: une suite linéaire de bits (qui s'enchainent les uns à la suite des autres). Chaque bit est représenté par un index simple, un nombre compris entre 1 et [RAM_totale]. En gros, c'est un tableau à une entrée. On appelera donc ce type d'adressage: adressage linéaire (ouf, c'était compliqué ça).
La segmentation, elle, utilise 2 indexes pour spécifier un emplacement de la mémoire: un segment, et un offset à l'intérieur de ce segment. Avec ce système, on se représente donc la mémoire sous forme d'un tableau à 2 entrée

| Offsets (y)
S e g m e n t (x) |  |  |  |  |  |  |  |

Tableaux: 2 entreés, les Offsets (y) et les segments (x). La case rouge est donc l'intersection des 2 valeurs. En sachant qu'on commence par la valeur du segment. Il y en a surement qui se sentent véxés par cette précision, mais si je le fais, c'est que je sais que certains ont du mal a assimiler certaines notions :)

Voilà, mais maintenant, vous allez surement me demander: "Mais pourquoi se faire chier avec la segmentation alors que l'adressage linéaire marcherait aussi bien?". Ben là je vais vous répondre tout simplement par un exemple. Imaginons que vous soyez en train d'écrire un programme de math, qui trace des courbes. Prenons une equation simple: F(x) = Sin(x)Cos(x). Vous aurez besoin de variables temporaires pour la fonction Sin, d'une autre pour la fonction Cos, et d'un autre pour l'affichage de la courbe par exemple. Dans ce cas de figure, il serait plus simple de réserver un segment à la fonction Sin, un autre à la fonction Cos, et un autre pour l'affichage de la courbe.
De façon plus générale, la segmentation permet de séparer la mémoire en blocs, et d'attacher chaque bloc à une partie du code du programme. Ainsi, les risques d'erreur entre les emplacements de mémoires sont réduits (evidement, là je parle dans le cas ou on code en assembleur pur).

Bon, continuons. Un adresse complète sera donc composé de 2 membres: le segment et l'offset, sous la forme:
segment:offset
Du 8086 au 80286, ces 2 membres ne pouvaient être que de 16 bits. Mais à partir du 80386, l'offset pouvait être de 16 ou 32 bits. Sur un 8086, avec les offsets de 16 bits, la taille d'un segment ne pouvait pas exceder 64 Ko. Avec le 32 bits, on peut avoir des segments de plus de 4 Mo.
En revanche, le segment reste en 16 bits. Ca veut dire qu'un programme ne pourra pas utiliser plus de 65536 segments.
A ce stade vous devez vous demander combien de mémoire maximale ce système peut gerer. Eh bien on va faire un calcul très simple.

Les segments sont en 16 bits:
216 = 65536 segments

Les offsets sont en 32 bits:
232 = 4 294 967 296 bits

Donc:
65536 x 4294967296 = 281 474 976 710 700 bits
Soit environs 280 Go, y'a de la place donc :)

Certes, les programmes utilisent la segmentation. Mais la mémoire physique connectée au CPU est tout de même linéaire. Il y a donc une fonction qui convertit la valeur du segment en une adresse de mémoire physique. Ensuite il ajoute la valeur de l'offset pour avoir l'emplacement exacte des données dans la mémoire physique. Nous allons donc faire la différence entre une adresse segmentée (utilisé par les programmes) et une adresse logique (utilisé par le processeur).

Bon, pour ceux qui sont perdus, je vais récapituler le minimum à retenir de tout ça:
- il existe des adresses segmentées (utilisées par les programmes). A celles-ci s'opposent les adressent logique (utilisées par le processeur)
- une adresse segmentée sera représentée sous la forme segment:offset
- une adresse logique sera un chiffre entre 0 et [RAM_maximum]
- le processeur s'occupe de la correspondance entre adresse logique et adresse segmentée

Sur un 8086, 8088, 80186, 80188 et par extension, tout ceux qui opèrent en mode réel, la fonction qui converti une adresse segmenté en adresse logique est très simple. Le processeur multiplie la valeur du segment par 16 (10h) et ajoute la valeur de l'offset. Maintenant, prenons une valeur quelconque (attention, à partir d'ici tout sera exprimé en hexa): 1000:1F00
Pour convertir ceci en adresse logique, vous multipliez la valeur du segment (1000h) par 16 (10h). Hors multiplier par 10h est très simple, il suffit de rajouter un zéro, donc ça nous donne 10000h. Ensuite on ajoute la valeur de l'offset (1F00h) et ça nous donne 11F00. Donc 11F00 est l'adresse logique qui correspond à l'adresse segmentée 1000:1F00

Mais à partir du 80286, au lieu d'augmenter la taille des régistres de segments, Intel a décidé de changer la méthode de conversion. L'ancienne méthode pourra toujours être utilisée, mais seulement en mode réel, et ne pourra utiliser que le premier Mo de mémoire. C'est là qu'Intel a introduit le mode protégé. Je disais donc que la méthode de conversion avait changé, et qu'elle ne se faisait plus par une fonction telle que "segment x 10h + offset". Le processeur en mode protégé utilise désormais un tableau de correspondance pour calculer l'adresse physique. Bon, je ne vais pas m'étendre là-dessus, c'était juste pour parler un peu du mode protégé. M'enfin sachez quand même que votre application ne pourra pas modifier elle-même le tableau des correspondance. Les OS comme Windows, Linux, Unix, O/S 2 etc... qui opèrent en mode protégés le font bien tout seuls. Et de toute manière, vous ne devriez pas avoir à génerer des adresse de segment par vous-même, car c'est l'OS qui placera le programme en mémoire.

VII- Mode d'adressage

Il y a plusieurs mode d'adressage pour le 80x86, ce qui offre un accès flexible, et permet d'utiliser des structures de données complèxes tel que les variables, les tableaux, les pointeurs etc... La maitrise de ces différents moyen d'accèder à la mémoire c'est un premier pas vers la maitrise de l'assembleur. J'ajoute quand même pour ceux qui traivailleraient sur d'anciens processeurs, que plusieurs de ces méthodes d'accès sont apparues avec le 80386, donc sur des machine moins récentes, vous ne pourrez pas tirer parti de ces techniques. Nous allons donc voir en détails ces différents modes d'adressage.

Mode d'adressage des régistres des 8086

La plupart des instructions du 8086 peuvent opérer dans les régistres généraux (general purpose registers). En spécifiant le nom du régistre, on peut directement acceder à son contenu. Vous allez apprendre votre première instruction en assembleur: l'instrucition "mov" (move). Elle s'utilise comme ça:

mov destination, source

Cette instruction copie les données du membres source vers le membre destination. La seule restriction de cette instruction est que la source et la destination doivent être de la même contenance. Disons que ne peut pas copier un régistre 16 bits dans un de 8 bits. On va juste voir quelques exemples:

mov ax, bx ;copie la valeur de bx dans ax
mov dl, al ;copie la valeur de al dans dl
mov si, dx ;copie la valeur de dx dans si
mov sp, bp ;copie la valeur de bp dans sp
mov ax, ax ;oui oui, c'est legal

Souvenez vous que les régistres sont la meilleur place ou garder des variables souvent utilisées, car l'accès est plus facile est plus rapide que l'accès à la mémoire.

Mode d'adressage de la mémoire des 8086

Il y a 17 manières d'accèder à la mémoire. Ca peut parraitre beaucoup à première vue, mais rassurez vous, la plupart des modes sont une variante d'un autre mode. Donc c'est assez facile à apprendre, car vous DEVEZ les apprendre. Nan nan, je rigole pas là! La clé d'un bon programme en assembleur est d'abord le choix judicieux de la façon d'accèder à la mémoire.
Les modes d'addressage autorisés par la famille des 8086 inclus le déplacement seul, base, déplacement + base, base + index, et déplacement + base + index. Les 12 autres moyens sont donc une variation de ces 5 là.

Le déplacement seul

C'est surement le plus utilisé, et le plus facile à comprendre, il est autrement appelé mode d'adressage direct. Il consiste en une constante de 16 bits qui va spécifier la location ciblée (de la mémoire). Prenons un exemple:

mov al, ds:[8088h]

Cette instruction copie les données situées à la location de mémoire 8088h dans le régistre al. Evidement, l'opération inverse est possible:

mov ds:[8088h], al

Le contenu de al est copié à l'emplacement de mémoire ds:8088h

Ici, ds (data segment) est le régistre qui contient le segment par défaut. Mais il est possible de spécifier autre chose que ds.

Le mode d'adressage indirect via les régistres

Le 80x86 vous permettent d'accèder à la mémoire indirectement à travers un régistre en utilisant cette méthode. Prenons des exemples:

mov al, [bx]
mov al, [bp]
mov al, [si]
mov al, [di]

Dans ce cas, les régistres bx, bp, si ou di contiennent un emplacement de mémoire (offset). On va donc utiliser les valeur que contiennent ces régistres pour spécifier l'offset désiré. Pour le segment, [bx], [si] et [di] sont associés par défaut avec ds, et [bp] avec ss (stack segment). Mais il est possible de spécifier un autre régistre pour définir le segment, exemple:

mov al, cs:[bx]
mov al, ds:[bp]
mov al, ss:[si]
mov al, es:[di]

Vous remarquerez que pour faire nos petites additions, que ce soit pour ce mode d'accès ou les suivants, on n'utilisera pas d'autres régistres que bx / bp (b pour base) et si / di (i pour index)

Mode d'adressage indexé

Là encore on va prendre des exemples:

mov al, cst[bx]
mov al, cst[bp]
mov al, cst[si]
mov al, cst[di]

Vous l'aurez peut-être compris, cst est un nombre. C'est très simple, l'offset sera la valeur du régistre + la valeur de cst. Par exemple, si bx contient 1000h, alors l'instruction mov cl, 20h[bx] remplira le régistre cl avec le contenu de la location de mémoire ds:1020h

Comme précédement, [bx], [si] et [di] sont associés par défaut avec ds, et [bp] avec ss. Là aussi vous pouvez utiliser un autre régistre de segment:

mov al, ss:cst[bx]
mov al, es:cst[bp]
mov al, cs:cst[si]
mov al, ss:cst[di]

base + index

Ben même chose que précédement, sauf qu'on n'additionne plus une constante et un régistre, mais 2 régistres. Exemple:

mov al, [bx][si]
mov al, [bp][di]
mov al, [bp][si]
mov al, [bp][di]

Bon, je vais pas m'étendre là dessus.

base + index + déplacement

On reprend les mêmes et on recommence. Exemple:

mov al, cst[bx][si]
mov al, cst[bp][di]
mov al, cst[bp][si]
mov al, cst[bp][di]

Bon pas dur là, on addition 2 régistres et une constante.

Généralité sur le mode d'adressage

Bon, vous voyez, ce n'est pas bien sorcier. Les 17 façons d'adresser de la mémoire sont une combinaison de cst, [bx], [bp], [si] et [di]. Pour être sur de générer un mode d'adressage légal, il suffit de considerer ce tableau:

cst | [bx] [bp] | [si] [di]

Vous prenez un seul composant par colonne, et vous n'êtes pas obligé d'utiliser toutes les colonnes. Le tout c'est d'utiliser au moins un élément. Par exemple, vous prenez cst de la première colonne, rien dans la deuxième, et [di] dans la troisième, ça vous donne cst[di] (waw, c'était dur). Bon, je vais pas vous en pondre un roman, si vous n'avez pas compris, ben relisez :) Au pire, écrivez moi: Nexxt@tipiak.net.

Un dernier truc, qui est évident, mais tout de même... Je vous ai dit qu'on ne pouvait pas utiliser d'autres régistres que ceux présentés dans le tableau, donc cst[dx][si] serait illégal, coz [dx] ne fait pas parti des régistres utilisables ici. Nah

Fin mot pour les modes d'adressages du 8086

L'adresse effective est l'offset final produit par le calcul d'un de ces mode d'adressage. Par exemple, si bx contient 10h, l'adresse effective de 10h[bx] est 20h. Il existe une instruction qui peut calculer une adresse effective, c'est l'instruction lea (load effective adress), que nous verons certainement dans le chapitre suivant.
Si vous vous rappelez ce que je vous ai dit au début... non, personne ne se rappelle, donc je vais le redire. Il est important de bien choisir son mode d'adressage, ske ils ne prennent pas tous le même nombre de cycles d'horloge pour s'accomplir. Il est evident qu'un cst[bx][si] prendre plus de temps qu'un [bx]. Il est aussi à noter qu'un cst qui reste dans l'encadrement -128 / + 127 (8 bits) sera plus court à calculer qu'un cst qui sort de cet ecadrement.
(rien n'a voir: le réseau SFR est vraiment excellent, je vous le conseille)

Mode d'adressage des régistres des 80386 et supérieurs

Ici je pense que plus de monde va se sentir concerné (car je ne pense pas qu'il y ait beaucoup de personne à lire ce tuto sur un 286). On va compliquer (ou simplifier, à vous de voir) la tache, puisqu'on va introduire les régistres 32 bits. En effet, comme vous vous rappelez bien ce que je vous ai dit plus haut, les régistres généraux (general purpose registers) ont tous leur equivalents 32 bits qui sont bien sur: eax, ebx, ecx, edx, esi, edi, ebp et esp.
Bon, pour utiliser ces régistres avec l'instruction mov, ben je vais pas tout vous redire, c'est la même chose que plus haut. Si vous avez une mémoire de limace, je vous renvoie là. Je vais juste vous remettre en garde sur ce point important: la source et la destination doivent être de la même contenance (on ne copie pas un régistre de 32 bits dans un de 16, nan nan, ça ne marche pas).

Mode d'adressage de la mémoire des 80386 et supérieurs

Une des avancées notable du 386 a été la généralisation des modes d'adressage de la mémoire. En effet, au lieu de se limiter aux régistres d'index si et di, et aux régistres de base bx et bp, vous pouvez désormais utiliser tous les nouveaux régistres généraux de 32 bits.

Le mode d'adressage indirect via les régistres

Ben ça va être la même chose pratiquement, à ceci près que seuls les régistres 32 bits sont autorisés. Exemple:

mov al, [eax] ;associé par défaut avec ds
mov al, [ebx] ;associé par défaut avec ds
mov al, [cx] ;c'est pas bien de faire ça :(
mov al, [ebp] ;associé par défaut avec ss
mov al, [esp] ;associé par défaut avec ss

Bon, je décrète que vous avez compris. Sinon: Nexxt@tipiak.net

Les autres modes d'adressage

Là je vais globaliser, ske c'est exactement la même chose qu'au dessus, sauf que vous jouez avec 32 bits (arf, bandes d'esprits obscènes).

Pour savoir avec quoi les régistres sont associés par défaut, c'est très simple. Dans un exemple comme celui ci:
mov al, [ebp]
Ben pour ebp et esp, le régistre de segment par défaut est ss, pour les autres, c'est ds.

Pour un base + index style çà:
mov al, [esp][ecx]
il faut se regarder la base, en sachant que la base est en premier. Donc dans cet exemple, [esp] est en premier, donc par défaut on l'associe à ss. Ce qui donnerait en fait:
mov al, ss:[esp][ecx]
Pour les autres association, reportez vous à ce que j'ai dit quelques ligne plus haut.
Sachez qu'utiliser le même régistre en base et en index est légal. Donc vous pouvez très bien avoir ça:
mov al, [ecx][ecx]
Evidement, vous pouvez utiliser un déplacement:
mov al, cst[esp][ecx]

Enfin, sachez que esp peut être utiliser comme base, mais pas comme index.

Pour votre tite culture

Bon, puisque mon but n'est pas tout à fait de faire de vous des codeurs d'élite (mais à y contribuer :) ), je vais juste survoler cette partie. Sachez seulement que l'index peut être multiplié par 1, 2, 4 ou 8. On le représente comme ça:

cst [index*n]
[base] [index*n]
cst [base] [index*n]

suivant bien sur que vous désirez mettre ou non une base et/ou une constante. Les modes que je vous ai présentés dans le paragraphe précédents sont en fait un cas spécial de ce mode, ou n = 1. Par exemple:
mov al, cst[esp][ecx]
pourrait très bien se représenter ainsi:
mov al, cst[esp][ecx*1]

Bon, ne vous attardez pas sur ça, à moins que vous voulliez faire des trucs compliqués, ce qui m'étonnerai (du moins, pas pour l'instant). Mais bon, pour ceux qui en veulent toujours plus, sachez que si vous ajouté une multiplication, alors le régistre sur lequelle elle est opérée sera l'index, quelque soit la place de ce régistre.

le mot de la fin pour les modes d'adressages du 80386 et supérieur

mot

Conclusion

Evidement, je ne vous ai pas tout dit sur l'instruction mov et ses amis. Mais bon, j'estime que c'est suffisant pour passer à la suite (puis de toute façon j'en ai marre de parler de ça).

Bon, j'espère que vous matrisez bien ce chapitre, ou du moins, que vous le maitriserez après l'avoir relu 50 fois. Dans le chapitre suivant, je préfère vous avertir, je ne sais pas du tout ce qu'il y aura. Sans doute une brève note d'utilisation de MASM (pour ceux qui ne savent pas ce que c'est, c'est un compilateur d'assembleur). Puis on va surement voir d'autres instructions. M'enfin bon, de toute manière, vous devez absolument maitriser les régistres, car ils servent tout le temps. Je le redis une dernière fois, si vous n'avez pas compris, que le mail soit avec vous.
Nexxt@tipiak.net