|
LA MEMOIRE VIVE |
Sommaire du chapitre :
Réservation de la mémoire
Types de donnée
L'adressage
Terminologie des adresses
Modèles mémoire
Le Bus Interface Unit (BIU)
Alignement des données
La pile - Stack -
Le mode réél
Le mode réél avec MS-DOS
Le mode protégé
La mémoire vive est une unité de stockage à l'instar des registres du microprocesseur. La capacité de stockage de cette unité est bien plus vaste que celle proposée par le processeur au travers de ses registres. Comme les registres, la mémoire est volatile. A cause de cette volatilité, nous sommes obligés de transférer les octets qui s'y trouvent, vers le disque dur, sous forme de fichier. La mémoire vive est située, comme vous l'aurez compris, hors du microprocesseur. La communication entre le microprocesseur et la mémoire est opérée par le BIU (Bus Interface Unit). Les cases mémoire (octets) sont organisées de telle façon qu'elles sont situées les unes derrière les autres.
Un autre élément influence la quantité de mémoire adressable. Cet élément, c'est le mode dans lequel se trouve le microprocesseur. Avec les microprocesseurs d'aujourd'hui deux modes sont possibles : le mode réél et le mode protégé. Le premier permet un accés à 1 mégaoctets de mémoire alors que le second autorise un adressage jusqu'àEt enfin, la mémoire est organisée selon un modèle que l'on appelle : modèle mémoire.
Il existe deux types de réservation :
La réservation statique et la réservation dynamique.
La réservation statique
Elle est définie une fois pour toute au moment de la conception du programme. Elle ne varie jamais. Elle est définie dans le programme exécutable.
Le programmeur utilisera la mémoire sous forme de variables, de constantes, de taille différente.
Pour faire une réservation mémoire on utilisera, la syntaxe suivante :
NomVariable TypeDonnée Valeur
NomVariable : Identificateur de la zone mémoire utilisée. Pour simplifier, considérez le nom d'une variable comme une adresse.
TypeDonnée : Indique au compilateur combien d'octets seront réservés pour stocker la Valeur.
Valeur : Donnée avec laquelle la zone mémoire sera initialisée. Initialiser une zone mémoire avec une donnée précise n'est pas obligatoire, on peut parfaitement laisser la zone mémoire indéfinie.
Exemple :
MonNombre DWORD 0B452FF45h
Dans cet exemple nous demandons au système d'exploitation de créer une zone mémoire de 4 octets (DWORD) contenant la valeur B452FF45 et portant le nom de MonNombre.
Les données initialisées sont stockées dans le fichier exécutable. Il faut faire très attention de ne pas créer de données inutiles qui engorgeront le fichier exécutable. La taille en serait alors volumineuse et le système d'exploitation mettrait un certain temps pour initialiser le bloc mémoire.
Les données non initialisées n'ont aucune incidence sur la taille du fichier exécutable et les performances de chargement de ce dernier. L'ensemble de ces données représente une certaine taille. Une référence à cette taille est stockée dans le fichier exécutable sous la forme : Virtual Size = Nombre d'octets.
Pour en savoir plus au sujet des fichiers exécutables : EXE, DLL, etc...
La réservation dynamique
Elle s'effectue en utilisant les fonctions du système d'exploitation au moment de l'exécution. Ce type de réservation est utilisé lorsque le programmeur ne sait pas au moment de la conception de combien d'octets il aura besoin pour stocker une portion de données en provenance d'un fichier où d'autres sources. Ces fonctions retournent une adresse du bloc mémoire demandé.
Exemple :
A un moment donné durant l'exécution, le programme voudra lire un fichier de 400 octets et copier le tout en mémoire. Il procédera de la façon suivante :
Buffer = GlobalAlloc(GMEM_FIXED, 400) ;Création d'un bloc mémoire de 400 octets.
LectureFichier(hFile, Buffer, 400) ;Place le contenu du fichier dans le bloc mémoire pointé par Buffer.
GlobalFree(Buffer) ;Libère la mémoire.
Buffer contient l'adresse du bloc mémoire demandé. Après utilisation il faut absolument détruire le bloc mémoire et pour cela on utilise la fonction GlobalFree(Buffer).
Les types de donnée ont pour but d'indiquer au compilateur le nombre d'octets à réserver en mémoire. Ils participent également à simplifier la tâche du programmeur et d'identifier plus facilement quel type de donnée le programme traite.
TYPES DE DONNEE ENTIERS NON SIGNES
Types fondamentaux :
| TYPE | TAILLE (en bits) | PLAGE DE VALEURS |
|---|---|---|
| BYTE | 8 | 0...255 |
| WORD | 16 | 0...65535 |
| DWORD | 32 | 0...4 294 967 295 |
Autres types :
| TYPE | TAILLE (en bits) | PLAGE DE VALEURS |
|---|---|---|
| QWORD | 64 | 0...2^64-1 |
| TBYTE | 80 | 0...2^80-1 |
TYPES DE DONNEE ENTIERS SIGNES
L'ensemble des types de donnée peuvent également être signé. Un nombre signé est considéré par le programmeur. Si le programmeur veut qu'à un moment dans le code une valeur soit traitée en tant que nombre signé, il interprétera l'état du bit de signe qui est toujours le bit de poids fort (celui situé à l'extrême gauche).
Types fondamentaux :
| TYPE | TAILLE (en bits) | PLAGE DE VALEURS |
|---|---|---|
| BYTE | 8 | -1...-128 et 0...127 |
| WORD | 16 | -1...-32768 et 0...32767 |
| DWORD | 32 | -1...-2 147 483 648 et 0...2 147 483 647 |
Autres types :
| TYPE | TAILLE (en bits) | PLAGE DE VALEURS |
|---|---|---|
| QWORD | 64 | -1...-2^63 et 0...2^63-1 |
| TBYTE | 80 | -1...-2^79 et 0...2^79-1 |
TYPES DE DONNEE FLOTTANTS SIGNES ET NON SIGNES
Types flottants - Processeur mathématique - :
| TYPE | TAILLE (en bits) | PLAGE DE VALEURS |
|---|---|---|
| REAL4 | 32 | 3.4 * 10^-38 à 3.4 * 10^38 |
| REAL8 | 64 | 1.7 * 10^-308 à 1.7 * 10^308 |
| REAL10 | 80 | 1.2 * 10^-4932 à 1.2 * 10^4932 |
Structure des flottants :
La structure des nombres à virgule flottante ce traduit par la formule suivante : S.E.M. (Signe, Exposant, Mantisse).
Structure du REAL4 :
Signe : 1 bit, Exposant : 8 bits, Mantisse : 23 bits
Structure du REAL8 :
Signe : 1 bit, Exposant : 10 bits, Mantisse : 53 bits
Structure du REAL10 :
Signe : 1 bit, Exposant : 15 bits, Mantisse : 64 bits

En mode protégé (32 bits) :
L’accés à une case (octet) , se fait par le biais d’une adresse numérotée de 1 à 2^32-1. Le microprocesseur peut adresser 4 GO de mémoire. Sans cette adresse toute possibilité de récupérer un octet en mémoire est impossible.
Le programmeur ne connaît pas l’adresse physique d’une donnée, pas même le programme une fois compilé, et c'est normal. Personne ne peut à priori affirmer où sera implanté le code, les données en mémoire. L'adresse ne sera connue qu'après le chargement de l'application par le système d'exploitation. Cependant, il y a bien des adresses placées dans tout fichier exécutable. Ces adresses portent le nom d'adresse offset.
C'est le lieur qui est responsable du calcul de ces adresses. Le lieur ne se pose aucune question quand à la valeur du registre de segment de code : CS. Il s'en fiche royalement ! Tous ses calculs sont des offsets ! Il considère tous les registres de segment à la valeur de zéro. Ce qui lui importe le plus, c'est que tout segment soit aligné sur une frontière de 1000h (4096, une page) octets. Les douzes bits de poids faible sont systématiquement mis à zéro.
Une adresse offset est un décalage par rapport au début d'un segment.
Le format global d'une adresse est : CS:OFFSET, DS:OFFSET, etc...
Exemple :
Imaginons un code d'une taille de 100h (256) octets et des données d'égales taille.
Les segments sont alignés sur une frontière de 1000h (4096) octets.
Le segment de code sera placé à l'adresse offset virtuelle de 1000h et le segment de données qui vient juste après est quant à lui placé à la prochaine adresse offset virtuelle multiple de 1000h soit 2000h.
Ces deux segments ont une taille en mémoire de 1000h (4096) octets chacun.
Les informations contenues dans chacun de ces deux segments sont d'une taille de 100h (256) octets.
Il faut également savoir que tout programme Windows possède une adresse offset de base privilégiée.
Cette adresse vaut toujours 00400000h et a pour nom : IMAGE BASE.
A cette IMAGE BASE est ajoutée, l'adresse offset virtuelle de base de chaque segment.
Le segment de code débutera à l'adresse offset de : 00400000h + 1000h = 00401000h, et le segment de données à l'adresse offset de : 00400000h + 2000h = 00402000h.
Quand le système d’exploitation chargera l’image de l’exécutable en mémoire, il créera le segment de code et CS sera initialisé par l’adresse physique de ce segment, puis tous les autres registres de segment se verront attribuer la valeur de CS. En reprenant l'exemple ci-dessus, on peut imaginer que le système d'exploitation a obtenu une adresse de base du segment de code : CS de 01000000h.
Alors, le registre de segment DS aura pour valeur : 01000000h, SS aura : 01000000h, ainsi que tous les autres. A cela s'ajoutera l'adresse offset :
CS = 01000000h + 401000h = 01401000h
DS = 01000000h + 402000h = 01402000h
Pour atteindre l'octet 45h (69) dans le segment de données nous aurons l'adresse linéaire : 01402045h.
Voilà comment le lieur procède pour la confection d'adresse offset.
Ce que nous venons de voir s'applique à tout programme Windows en mode protégé.
Les registres de segment ne contiennent pas d'adresse physique. Ils contiennent un sélecteur de segment. Ce dernier est un index dans la table des descripteurs de segment. C'est dans un descripteur de segment que l'on trouvera l'adresse linéaire. Si le système de pagination n'est pas activé, l'adresse linéaire est l'adresse physique et c'est elle qui sera placée sur le bus d'adresse. Si le système de pagination est activé, l'adresse linéaire subira un traitement supplémentaire afin de déterminer l'adresse physique.
En mode réél ( 16 bits ) :
C'est dans ce mode que le système
d'exploitation MS-DOS s'exécute ainsi, que tous les programmes
conçus pour ce système.
Dans ce mode le multi-tâches est inexistant. Un seul programme
s'exécute à la fois.
Toutes les adresses offset sont codées sur 16 bits. Et le
microprocesseur ne peut adresser que 2^20 de mémoire soit :
1048576 octets.
Chaque registre de segment contient, contrairement au mode
protégé, une adresse physique de 16 bits qui correspond
à la base (début) du segment de code et/ou de
données. Vous vous demandez, à juste titre, comment le
microprocesseur peut adresser 1 MO de mémoire alors qu'un
registre de segment contient une adresse 16 bits correspondant à
une valeur maximale de FFFF (65535).
Le microprocesseur, par défaut, décale de 4 bits vers la
gauche le contenu d'un registre de segment afin d'obtenir une adresse
sur 20 bits (1 MO). Le dernier chiffre est toujours aligné sur
une valeur multiple de 10h (16) donc toujours égal à
zéro.
Exemple :
Le registre de segment de code CS contient la valeur de 1EAFh (16 bits) pour obtenir l'adresse physique sur 20 bits le microprocesseur décale de 4 bits vers la gauche 1EAFh afin d'obtenir la valeur 20 bits suivante : 1EAF0h. Le dernier nombre d'une adresse 20 bits est toujours égal à zéro. Ce qui prouve que nous obtenons une adresse de segment tous les 10h (16) octets. La valeur du prochain segment sera alignée sur une valeur multiple de 10h (16). En reprenant la valeur 1EAFh le prochain segment sera 1EB00h le suivant à 1EB10h, 1EB20h etc... A cette adresse de 20 bits nous ajoutons l'adresse offset (16 bits) : B1F5h afin d'obtenir l'adresse physqiue finale : 29CE5h, qui correspond à la donnée que l'on veut atteindre. Un segment de code ou de données est d'une taille maximale de 10000h (65536) octets. Le format d'une adresse est le même qu'en mode protégé : CS:OFFSET ( 16 bits )
Les modes d’adressage
L’adressage Direct
L’adressage direct consiste à utiliser une adresse offset directement. C'est-à-dire, une valeur numérique relative au début du segment, qui correspond à la donnée à atteindre, que vous aurez calculée. Pour vous rassurer vous n’aurez jamais à utiliser ce type d’adressage parce que cela serait catastrophique en matière de maintenance et d’évolution du programme. C’est le lieur qui en aura la responsabilité. L’exemple ci-dessous montre comment copier quatre octets situés à l’adresse offset 403000h dans le registre eax :
mov eax, ds:[00403000h]
L’adressage indirect
L'adresse offset calculée par le lieur est disponible sous forme de nom de variable et de label, que l'on place dans un registre. L’exemple ci-dessous, montre comment copier le premier octet de la variable Msg dans le registre al. Pour obtenir l’adresse offset, on utilisera la directive OFFSET. Cette dernière sera copiée dans le registre ebx avec l'instruction mov. ebx sera utilisé pour adresser la mémoire indirectement.
Msg DB «Hello World ! »,0
mov ebx, offset Msg
mov al, [ebx] ;Adressage indirect.
C’est à ce mode que nous allons nous intéresser.
L'adressage indirect basé, utilise les registre ebx et ebp (base) pour stocker les adresses de bases.
Exemple :
mov ax, [ebx]
Le registre ebx contient l'adresse de base à partir de laquelle deux octets seront copiés de la mémoire dans le registre ax.
L'adressage indirect basé indexé
C'est l'utilisation conjointe des registres de base (ebx, ebp) et index (esi, edi).
En mode 32 bits n'importe quel registre à usage général peut contenir une adresse offset. Les contraintes d'usage de ces registres se font ressentir dans le mode 16 bits où seuls les registres de base et d'index doivent dêtre utilisés pour stocker une adresse offset.
La formule BISO permet d'obtenir le format d'un adressage en mode 32 bits et la formule BIO en mode 16 bits :
| BISO | Base + Index * Scale + Offset |
| BIO | Base + Index + Offset |
| Base | Adresse offset de base d’une variable ou d'un label. |
| Index | Position de la donnée dans un ensemble. |
| Scale | Taille de la donnée manipulée. |
| Offset | Décalage. Cet offset se traduit par une valeur multiple de la taille d’une donnée traitée. |
Voici quelques exemples d' adressage :
mode protégé 32 bits :
N'importe quel registre à usage général peut servir de base ou d'index.
mov al, [ebx + edi] ;[base + index]
mov al, [ebx + esi * 1] ;[base + Index * taille de la donnée traitée]
mov al, [ebx + edi * 1 + 6] [base + Index * taille de la donnée traitée + Offset]
mov al, [ebp + esi] ;[base + index]
mov al, [eax + edx * 2 + 8] ;[Base + Index * taille de la donnée traitée + offset]
Dans cet autre exemple, nous manipulons six nombres codés sur deux octets faisant partie d'un ensemble (tableau). On copie la troisième valeur, 789, dans le registre ax.
Valeurs DW 345, 458, 789, 2048, 15248, 45000
mov ebx, offset Valeurs
mov ax, [ebx + 2 * 2] ; ax = 789
L’index est 2, puisque tout tableau débute à l’index 0.
Scale est 2, puisque l’on manipule des données de deux octets (word)La technique BISO ou celle du BIO (16 bits) sera utilisée à chaque fois que l'on traite un ensemble de données de même taille.
Retenez les bien !
mode réél 16 bits :
Seuls les registres basés (bx, bp) peuvent servir de base et les registres (si, di) comme index.
mov al, [bx + di] ;[base + index]
mov al, [bx + si + 1] ;[base + Index + offset]
mov al, [bx + di * 1 + 6] ;Illégal (di ne peut pas être utilisé comme opérande de multiplication à ce niveaux)
mov al, [bx + 2 * 2 + 6] ;Légal mov al, [bp + si] . Si dans cet exemple la mise à l'échelle est autorisée c'est parce que
mov al, [ax + dx * 2 + 8] ;prohibé;le compilateur fera de lui même la multiplication et mettra le produit à la place de l'opération.
;Ce qu'il ne peut pas faire dans l'instruction précédente.
mov al, [ax + di] ;prohibé, ax n'est pas un registre basé.
;Le code ci-dessous copie le caractère pointé par le second operande dans le ;registre al. ;Plusieurs syntaxes existent mais au final lorsque le lieur à fait son boulot les syntaxes qui en ;résultent peuvent être les suivantes : ;[registre basé] ;[registre basé + registre d'index] ;[registre basé + registre d'index + déplacement] ;[registre basé+déplacement] ;[registre d'index] ;[registre d'index + déplacement] ;Variable[registre basé] ;Variable[registre basé + registre d'index] ;Variable[registre basé + registre d'index + déplacement] ;Variable[registre basé+déplacement] ;Variable[registre d'index] ;Variable[registre d'index + déplacement] ;Les syntaxes que je préfère sont les 6 premières. Il n'y a aucune ambiguïté. Le registre basé ;contiendra toujours l'adresse offset de la variable. Alors que dans les 6 dernières syntaxes ;la compréhension n'est pas évidente mais reste quand même compréhensible. ;Dans la définition d'une adresse offset on ne peut pas utiliser plusieurs registres d'index, ;cela est prohibé. Si les registres basé ne figurent pas à la bonne place par exemple : [di+bx] ;cela n'est pas considéré comme une erreur. Le compilateur réagencera la syntaxe et produira : [bx+di]. ;Les crochets utilisés agissent comme l'opérateur d'addition (+). .8086 .MODEL TINY MonCode SEGMENT 'CODE' ASSUME ds:SEG Msg ORG 100h Main: mov bx, 2 mov di, 4 mov si, di ;Syntaxe en utilisant la variable Msg (pour pouvoir l'utiliser, n'oublier de faire une déclaration ASSUME). ;1) Msg sera considéré comme un déplacement correspondant à l'adresse offset de la variable Msg. ;2) A ce déplacement est ajouté le déplacement supplémentaire représenté par la valeur entière 1 et ; constitura le déplacement final. ;3) Un registre basé (bx ou bp) contenant une valeur multiple du type de donné traité. ;4) Un registre d'index (di ou si) contenant une valeur qui doit être portée à l'échelle. Cette échelle est ; une valeur multiple du type de donnée traitée. ;5) Un déplacement supplémentaire représenté par une valeur constante et qui n'est pas un registre. ; ce déplacement est lui aussi une valeur multiple du type de donnée traitée. ;La variable Msg à la valeur offset 016B. Cette valeur reste la même pour l'ensemble du programme. mov al, Msg[1] ;"H[e]llo World !" -> Msg[1] = [016C] mov al, Msg[bx] ;"He[l]lo World !" -> ;Msg[bx] = [bx+016B] mov al, Msg[bx+1] ;"Hel[l]o World !" -> Msg[bx+1] = [bx + 016C] mov al, Msg[bx+di] ;"Hello [W]orld !" -> Msg[bx+di] = [bx+di+016B] mov al, Msg[bx+di+1] ;"Hello W[o]rld !" -> Msg[bx+di+1] = [bx+di+016C] mov al, Msg[bx+si] ;"Hello [W]orld !" -> Msg[bx+si] = [bx+si+016B] mov al, Msg[bx+si+1] ;"Hello W[o]rld !" -> Msg[bx+si+1] = [bx+si+016C] ;Le code suivant est le même que le précédent. mov al, Msg[bx][1] ;"Hel[l]o World !" -> Msg[bx][1] = [bx+016C] mov al, Msg[bx][di] ;"Hello [W]orld !" -> Msg[bx][di] = [bx+di+016B] mov al, Msg[bx][di][1] ;"Hello W[o]rld !" -> Msg[bx][di][1] = [bx+di+016C] mov al, Msg[bx][si] ;"Hello [W]orld !" -> Msg[bx][si] = [bx+si+016B] mov al, Msg[bx][si][1] ;"Hello W[o]rld !" -> Msg[bx][si][1] = [bx+si+016C] mov al, Msg[di+1] ;"Hello[ ]World !" -> Msg[di+1] = [di+016C] mov al, Msg[si+1] ;"Hello[ ]World !" -> Msg[si+1] = [si+016C] mov al, Msg[di][1] ;"Hello[ ]World !" -> Msg[di][1] = [di+016C] mov al, Msg[si][1] ;"Hello[ ]World !" -> Msg[si][1] = [si+016C] ;Initialise le registre bx avec l'offset de la variable Msg. mov bx, OFFSET Msg ;bx = 016B ;Le code qui suit est identique à celui du dessus à la différence que le ;registre bx contient une adresse offset et qui correspond à la variable Msg. mov al, [bx] ;"[H]ello World !" mov al, [bx+1] ;"H[e]llo World !" mov al, [bx+di] ;"Hell[o] World !" mov al, [bx+di+1] ;"Hello[ ]World !" mov al, [bx+si] ;"Hell[o] World !" mov al, [bx+si+1] ;"Hello[ ]World !" mov al, [bx][1] ;"H[e]llo World !" -> [bx][1] = [bx+1] mov al, [bx][di] ;"Hell[o] World !" -> [bx][di] = [bx+di] mov al, [bx][di][1] ;"Hello[ ]World !" -> [bx][di][1] = [bx+di+1] mov al, [bx][si] ;"Hell[o] World !" -> [bx][si] = [bx+si] mov al, [bx][si][1] ;"Hello[ ]World !" -> [bx][si][1] = [bx+si+1] ;Fin du programme mov ax, 4C00h int 21h ;Ici, commence la déclaration des variables globales. Msg DB "Hello World !$" MonCode ENDS END Main |
Adresse physique
C’est l’adresse réélle de la donnée en mémoire.
Adresse Logique
C’est une adresse segmentée utilisée par le couple : segment :offset. Toute adresse est logique.
Adresse segmentée
C’est une adresse en deux partie représentée par la valeur d’un segment, qui correspond à l’adresse physique de base du segment en mémoire ajoutée une valeur offset. Elle prend la forme suivante : Segment :Offset ->CS :[00402000]
Adresse virtuelle
C’est une adresse segmentée dont la valeur de segment représente un sélecteur de segment.
Dans tout programme 32 bits le programmeur manipulera des adresses virtuelles qui sont des adresses segmentées et logiques.
Toute adresse qui n'est pas une adresse physique est une adresse virtuelle.
Adresse linéaire
Adresse virtuelle dans l'espcae d'adressage Linéaire du microprocesseur. Si la pagination est désactivée, l'adresse linéaire est l'adresse physique. Au contraire, l'adresse linéaire subira un traitement supplémentaire afin de déterminer l'adresse physique.
Il existe sept modèles mémoire :
TINY, SMALL, COMPACT, MEDIUM, LARGE, HUDGE, FLAT
Un modèle mémoire apporte au programmeur un moyen efficace d'organiser le code et les données utilisés au sein de son application.
Les six premiers modèles sont exclusivement employés en programmation 16 bits MS-DOS et le dernier en programmation 32 bits Windows.Un seul segment d’une taille de 65536 octets et contient l’ensemble du code et des données du programme y compris la pile. Ce modèle est utilisé par les fichiers exécutables .COM.
Adressage proche : NEAR
SMALL
Ce modèle autorise l’usage d’un segment de données et d’un segment de code. Chaque segment a une taille de 65536 octets.
Adressage proche : NEAR
COMPACT
Ce modèle autorise l’usage de plusieurs segments de code et un seul segment de données. La taille de chaque segment est de 65536 octets.
Adressage éloigné : FAR pour le code et, proche : NEAR pour les données.
MEDIUM
Ce modèle autorise l’usage d'un seul segment de code et de plusieurs segments de données. La taille de chaque segment est de 65536 octets.
Adressage proche : NEAR pour le code et éloigné : FAR pour les données.
LARGE
Ce modèle permet l’usage de plusieurs segments de code et de données. Chaque segment a une taille de 65536 octets.
Adressage éloigné : FAR pour le code et les données.
HUDGE
Idem que le précédent.
FLAT
C’est le modèle mémoire 32 bits. Utilisé par Windows. Tout programme destiné au système d’exploitation Windows utilise ce modèle. Un seul segment, pour le code et un autre pour les données. La taille de chaque segment est dans la mesure de l’espace d’adressage disponible pour un système 32 bits, c’est-à-dire 4 giga octets. Adressage proche : NEAR pour le code et les données. En cela il ressemble au modèle TINY.
Les six premiers modèles mémoire sont utilisés en mode réel (16 bits), ce sont les programmes MS-DOS qui en sont les usagers.
Signification de NEAR et FAR :
NEAR : Effectue un déplacement intrasegment.
FAR : Effectue un déplacement intersegment.
Tableau récapitulatif des modèles mémoire :
| MODELE | SEGMENT DE CODE | SEGMENT DE DONNEES | TAILLE SEGMENT | CODE ET DONNEES COMBINES |
|---|---|---|---|---|
| TINY | NEAR | NEAR | 16 BITS (MS-DOS) | OUI |
| SMALL | NEAR | NEAR | 16 BITS (MS-DOS) | NON |
| COMPACT | NEAR | FAR | 16 BITS (MS-DOS) | NON |
| MEDIUM | FAR | NEAR | 16 BITS (MS-DOS) | NON |
| LARGE | FAR | FAR | 16 BITS (MS-DOS) | NON |
| HUGE | FAR | FAR | 16 BITS (MS-DOS) | NON |
| FLAT | NEAR | NEAR | 32 BITS (WINDOWS) | OUI |
Il existe trois types de BUS. Le bus permet la communication entre le processeur et la mémoire. Toute donnée chargée en mémoire et traitée par le processeur doit obligatoirement passer par le bus.
LE BUS D’ADRESSE
Comme son nom l’indique c’est sur ce bus que le processeur place l’adresse physique. L'adresse en mode 16 bits devra être multiple de 2 et en mode 32 bits elle sera multiple de 4. Si tel n'est pas le cas, le processeur effectuera autant d'adressage que nécessaire afin de retrouver la donnée demandée. Sur un 8088 le bus de donnée est de 8 bits, donc l'adresse déposée sur le bus sera un multiple de 1. Sur un 8086 et jusqu'au 386sx le bus de donnée est de 16 bits, donc l'adresse déposée sur le bus sera un multiple de 2. Sur une 386dx et supérieurs le bus de donnée est de 32 bits donc, l'adresse déposée sur le bus sera un multiple de 4.
LE BUS DE DONNEES
Les données (donnée code / donnée informative) sont placées sur ce bus. La taille de ce bus dépend du microprocesseur utilisé. Les tailles sont de 8, 16, 32, 64 bits.
L'alignement des données en mémoire sur une frontière de 8, 16, 32 bits est fondamentale.
Le microprocesseur dépose sur le bus d'adresse une adresse qui est toujours multiple de la taille de son bus de données.
De cela dépend les performances d'exécution de l'application.
Si une donnée n'est pas correctement alignée, le microprocesseur effectuera autant d'adressages que nécessaire afin de trouver la valeur demandée.
Bien sûr si la donnée est correctement alignée une seule opération d'adressage sera effectuée par le CPU et l'application sera plus performante.
Une donnée de pile sera toujours alignée sur une frontière de 4 octets (0,4,8,12,16, etc...) pour le mode 32 bits et sur une frontière de 2 octets (0,2,4,6,8,10,12,14,16, etc...) pour le mode 16 bits.
Microprocesseur 4004
Son bus de donnée est de 4 bits (nibble). Ce type de microprocesseur est très très lent. En effet, si nous devons rapatrier de la mémoire 256 octets, le microprocesseur effectuera 512 adressages (accés mémoire).
Microprocesseur 8008
Son bus de donnée est de 8 bits (1 octet). Le problème de l'alignement ne se pose pas. Ce type de microprocesseur est très lent. En effet, si nous devons rapatrier de la mémoire 256 octets, le microprocesseur effectuera 256 adressages (accés mémoire).
Microprocesseur 8086
C'est à partir du CPU 8086 et supérieur qu'il faudra apporter une attention accrue sur l'alignement des données afin de ne pas compromettre les performances d'exécution de l'application.
Son bus de donnée est de 16 bits (2 octets). Donc, ce type de CPU est deux fois plus rapide que le 8008. Les données sont rapatriées deux par deux. L'adresse déposée sur le bus d'adresse sera toujours multiple de 2.
Sur ce type de microprocesseur, copier 256 octets de la mémoire vers un registre nécessite 128 adressages. Le traitement des chaînes ASCII est très lent. Il faut faire très attention lorsque l'on manipule ce type de donnée. Ce type d'accés mémoire doit être finement contrôlé. L'alignement pour le traitement de chaînes ASCII n'est, donc, pas important.
Par contre, si une donnée de type WORD (2 octets) est placée à l'adresse 5, le CPU effectuera deux adressages. Il placera sur son bus d'adresse l'adresse 4 puis lira le second octet situé à l'adresse 5 après cela il déposera une autre adresse afin d'atteindre le deuxième octet de notre donnée qui est située à l'adresse 6. Si le programmeur avait effectué correctement l'alignement de la donnée à partir de l'adresse 6, le CPU aurait effectué seulement 1 adressage. Puisque il aurait déposé sur son bus d'adresse, l'adresse numéro 6 et transférer les deux octets sur le bus de donnée.
Le traitement de données de type DWORD s'effectue, donc, avec deux adressages. Non alignée, ce seront trois adressages.
Exemple :
| Adresse | Donnée | Commentaire |
| 0 | ValeurBYTE DB 120 | |
| ALIGN 2 | ||
| 2 | ValeurWORD DW 120 | ;Donnée alignée sur une adresse multiple de 2 |
| ALIGN 4 | ||
| 4 | ValeurDWORD DD 120 | ;Donnée alignée sur une adresse multiple de 4 |
Microprocesseur 386 et supérieurs
Son bus de donnée est de 32 bits (4 octets). Pour rapatrier une donnée de type DWORD ce sera un seul adressage et non alignée ce seront deux adressages.
N'oubliez pas que l'alignement d'une donnée en mémoire est primordial afin de garantir les performances d'exécution optimales de l'application.

Ci-dessous une autre vue de l'alignement des données en mémoire, comme la voit le microprocesseur :

Vous remarquerez, que chaque processeur
organise la mémoire en fonction de son bus de donnée. La
largeur du bus de donnée conditionne
l'adresse déposée par le processeur.
Chaque information débute toujours à une adresse multiple
de la taille du bus de donnée dudit microprocesseur.
LE BUS DE CONTROLE
C'est lui qui permet la communication entre le CPU et le système. Il contient deux lignes électriques. Une ligne pour la lecture et une autre pour l'écriture. Lorsque ces deux lignes sont définies avec la valeur de 1, le CPU et la mémoire ne communiquent pas. Lorsque la ligne de lecture est positionnée à 0 le CPU lit la mémoire et à ce moment là, la ligne d'écriture est positionnée à 1. Lorsque le CPU écrit dans la mémoire la ligne d'écriture est définie à 0 et la ligne de lecture à 1.
Lecture en mémoire
Dans ce contexte la ligne de lecture du bus de contrôle est définie à 0 et la ligne d'écriture à 1.
Un tableau de 4 valeurs codées chacune sur quatre octets (DWORD). On va lire la quatrième valeur et la placer dans le registre eax :
Tableau DD 14, 15, 165, 145896
mov ebx, offset Tableau
mov eax, DS:[ebx + 3 * 4] ;eax = 145896
Ecriture en mémoire
Dans ce contexte la ligne d'écriture du bus de contrôle est définie à 0 et la ligne de lecture à 1.
Un tableau de 4 valeurs codées chacune sur quatre octets (DWORD). On va écrire à la place de la quatrième valeur, la valeur contenue dans le registre eax et qui vaut 100h (256) :
Tableau DD 14, 15, 165, 145896
mov ebx, offset Tableau
mov eax, 100h
mov DS:[ebx + 3 * 4] , eax ; Tableau[3] = 100h

Le mode réél est le mode dans lequel s'initialise l'ordinateur. Dès l'allumage, le BIOS (Basic Input Output System) teste l'ensemble des matériels installés dans l'ordinateur.
Cette initialisation s'appelle le POST (Power On Self Test). A l'issue de cette opération, le BIOS va tenter de lancer le système d'exploitation via un média de boot (Disquette, Disque dur, CD-Rom, etc...)
La mémoire disponible dans ce mode est de 1 048 576 octets (1 MO). L'accès s'opère via des registres de segment. Chaque registre de segment a une taille de 16 bits. Son contenu est une adresse mémoire réélle comprise dans l'espace d'adressage de 1 MO.
La question évidente que vous vous posez est : comment le microprocesseur peut-il accéder à 1 MO de mémoire alors que la valeur logée dans celui-ci ne peut excéder 65536 octets ?
En fait, le microprocesseur décale de 4 bits vers la gauche le contenu du registre de segment. Cette opération permet d'obtenir une valeur de segment égale à 20 bits. Ensuite, il ajoute la fameuse adresse offset pour toucher la case mémoire désirée. L'opération qui consiste à transformer l'adresse de segment 16 bits en une adresse 20 bits est totalement transparente pour le programmeur.
Cette particularité implique que le dernier chiffre est toujours égal à 0. En d'autres termes, on trouve une adresse de segment tous les 16 octets. Si on multiplie 65536 par 16 cela donne un produit de : 1 048 576 qui est la quantité totale de mémoire disponible.
Du fait de la taille des registres de segment et de l'opération de transformation de son contenu en une adresse 20 bits réalisée par le microprocesseur, la quantité de mémoire gérée dans ce mode est de 1 MO. Pour pouvoir accéder aux 4 GO de mémoire que peut posséder l'ordinateur actuel il faut basculer le microprocesseur en mode natif qui est le mode protégé. La gestion de la mémoire dans ce mode est extrémement complexe et je l'expliquerai plus tard dans un chapitre dédié au mode protégé.
La gestion de cette zone mémoire repose entièrement sur la responsabilité du programmeur. Chaque adresse de segment qu'il manipule est une adresse physique et aucun mécanisme de protection de la mémoire n'est apporté par le microprocesseur dans ce mode. Développer un programme dans ce mode requiert une attention toute particulière afin que le programme développé n'écrase pas, une fois chargé et exécuté, des données appartenant à d'autres programmes si non, c'est le CRASH !!!
La mémoire ne contient à ce niveau que les données vitales (Données systèmes, données vidéo et la table des vecteurs d'interruption). La quantité de mémoire disponible pour y loger un système d'exploitation et y faire tourner des programmes n'est que de 640 KO (655 360), soit très peu. Cette mémoire débute à l'adresse de segment : 00500h jusqu'à A0000h et en fait ce ne sont que 654080 octets de disponibles pour le système d'exploitation et les programmes.
Cartographie de la mémoire en mode réél avant le chargement d'un système d'exploitation 16 bits tel que MS-DOS (MicroSoft - Disk Operating System)
![]() |
MS-DOS est un système d'exploitation 16 bits fonctionnant en mode réél. Ce système apporte un ensemble de fonctionnalités permettant le développement de programmes. Ces derniers spécialement conçus pour ce système ne fonctionneront qu' avec celui-ci.
Comme je l'ai indiqué dans la section précédente, la quantité de mémoire disponible pour le système d'exploiation et les programmes est de 640 KO, et cette zone mémoire débute à l'adresse de segment 00500h et se termine à l'adresse de segment A0000h.
Cela est très peu et c'est la raison pour laquelle chaque octet est précieux. Lorsque vous developperez de tels programmes, traquez le moindre octet inutile.
Cartographie de la mémoire
![]() |
Le mode protégé est le mode natif des microprocesseurs actuels mais pas le mode par défaut. Le mode par défaut est le mode réél.
Le mode protégé ou mode 32 bits permet l'utilisation de toute la puissance de gestion de la mémoire qu'offre le microprocesseur. Dans ce mode, le microprocesseur apporte un mécanisme de la gestion de la mémoire que l'on appelle : la mémoire virtuelle.
La mémoire virtuelle est une zone du disque dur utilisée pour stocker les pages code/données inutilisées afin de libérer de la mémoire vive dans le but d'y faire tourner le programme en cours d'exécution.
La quantité de mémoire adressable est de : 4 GO.![]() |
![]() |