Les programmes Win32 fonctionnent en mode protégé
depuis le 80286. Mais le 80286 c'est maintenant de l'histoire ancienne. Donc nous devons seulement nous
intéresser au 80386 et à ses descendants. Windows dirigent chaque programme Win32 séparément. Ceci
signifie que chaque programme Win32 aura à sa disposition ses propres espaces de mémoire. Cependant,
cela ne signifie pas que chaque programme win32 a 4GB de mémoire physique, mais seulement que le
programme peut adresser n'importe quelle adresse dans cette gamme. Windows fera tout le nécessaire pour
adresser les vraies références (physiques) du programme aux adresses (mémoires) valables 00401000… qu'il utilise.
Bien sûr, le programme doit respecter les règles de Windows, autrement il causera une Erreur de Protection
Générale tant redoutée. Chaque programme est seul dans son espace d'adresse. C'est la différence avec les
programmes Win16. Tous les programmes Win16 peuvent *malheureusement plus ou moins se chevaucher* les uns
les l'autres. Mais pas sous Win32. Ces gardes fous réduisent la chance d'une écriture du code d'un
programme par dessus les données d'un autre.
Le modèle de mémoire est aussi résolument différent des vieux jours du monde du 16 bits. Désormais,
sous Win32, nous ne sommes plus concernés par le modèle de mémoire en plusieurs segments!
(0001.xxxx puis 0002.xxxx puis 0003.xxxx ….)Il y a seulement un seul modèle de mémoire : le modèle de
mémoire uniforme (Flat). Il n'y a plus de segments de plus 64Ko (différence *.com / *.exe). La mémoire
est un seul espace continu. Ça veut dire aussi que vous n'avez plus besoin de jouer avec les différents
registres concernant les segments [ES et SS]. Vous pouvez employer n'importe quel registre de segment
pour adresser n'importe quel point dans l'espace de mémoire (ouf !). C'est une GRANDE aide pour les
programmeurs. C'est ce qui fait de l'assembleur Win32 une programmation aussi facile que le C.
Quand vous programmez sous Win32, vous devez respecter quelques règles importantes. Une règle{*Majeure*}
est que Windows emploie lui-même ESI, EDI,
EBP et EBX et les valeurs de ses registres changent, en même
temps qu'une autre application tourne en parallèle avec Windows. Rappelez-vous donc cette règle
{*Primordiale*} d'abord : si vous employez chacun de ces quatre registres à l'intérieur d'une procédure,
n'oubliez jamais de les reconstituer{*rétablir*} avant le contrôle de retour à Windows. L'exemple évident
sont les Call qui appellent une API de Windows. Cela ne signifie pas que vous ne pouvez pas employer ces
quatre registres, vous le pouvez. Mais seulement que vous devez rétablir ces registres après un call.
Voici le squelette de n'importe quel programme. Si vous ne comprenez pas quelques-uns des codes, ne paniquez pas. J'expliquerai chacun d'entre eux plus tard.
.386
.MODEL Flat, STDCALL
.DATA
<Vous initialisez les data>
......
.DATA?
<Vous initialisez les data>
......
.CONST
<Vous initialisez vos constantes>
......
.CODE
<label>
<Vous initialisez votre code(début du segment Code)>
.....
end <label>
C'est tout! On va analyser le squelette de ce programme.
.386
C'est une directive d'assembleur, disant à l'assembleur d'employer
le jeu d'instructions du 80386. Vous pouvez aussi employer .486 .586 mais l'important c'est d'utiliser au
minimum les fonctions à partir du .386. Il y a en réalité deux formes presque identiques pour chaque
modèle de directive. .386/.386p.486/.486p. Ces versions "p" sont nécessaires seulement quand votre programme
emploie des instructions privilégiées. Les instructions privilégiées sont des instructions réservées par
le système du CPU / FONCTIONNEMENT en mode protégé. Elles peuvent seulement être employés par le code
privilégié, comme les conducteurs de dispositif virtuels. La plupart du temps, votre programme
travaillera dans le mode non-privilégié donc c'est presque sûr que vous n'emploierez pas les versions -p.
.MODEL FLAT, STDCALL
.MODEL
est une directive d'assembleur qui spécifie le type de mémoire de votre programme. Sous Win32, il y a
seulement un type, le modèle FLAT (mémoire un en seul bloque).
STDCALL dit à MASM quel est l'ordre de passage des
paramètres de la pile vers un Call. De " gauche à droite " ou " le droit à gauche " et aussi ça
équilibrera l'encadrement de la pile après l'appel de la fonction.
Sous Win16, il y a deux types de convention d'appel, la convention
C et le PASCAL
La convention C passe les paramètres de la pile
de droit à gauche, c'est-à-dire le paramètre extrême droit est poussé d'abord. L'interlocuteur est
responsable du balancement de l'encadrement de pile après l'appel. Par exemple, pour appeler une fonction
nommée foo (int first_param, int second_param, int third_param) dans la 'convention d'appel C', les codes
d'asm ressembleront à cela :
push [3e_paramètre] ; Pousse le 3e paramètre sur la pilela convention d'appel PASCAL est le contraire de la convention C. elle passe les paramètres de la pile de gauche à droite et le Call est responsable de équilibre (de l'état) de la pile après l'appel.
push [2e_paramètre] ; Suivi par le second
push [1er_paramètre] ; Et du 1er
call Procédure_foo
add sp, 12 ; La fonction appelante équilibre le remplissage de la pile
.DATA
.DATA?
.CONST
.CODE
Ces quatre directives servent a appeler la section (00401000……).
Vous n'avez pas de segments (0001.xxxx, 0002.xxxx,…) dans Win32, vous vous souvenez ? Mais vous pouvez
diviser votre espace d'adresse (00401000 à 00620000 par exemple) en sections logiques, exemple : " le
code de 00401000 à 005F0000 " et "les DATA de 005F0000 à 00620000 ". Le début d'une section dénote la fin
de la section précédente. Il existe deux groupes de section : données et code. Les sections de données
sont divisées en 3 catégories :
Vous n'êtes pas forcément obligé d'employer ces trois
sections dans votre programme. Déclarez seulement la (ou les) section (s) que vous voulez utiliser.
Il n'y a seulement qu'une section pour le code : .CODE.
C'est celle où vos lignes de code résident.
<label>
end <label>
Où <label> représente n'importe quelle étiquette arbitraire, et est employé pour
indiquer le début et la fin de votre code. Les deux 'label' doivent être identiques. Toutes vos lignes
de code doivent se trouver entre <label>
et end <label>