Expressions assembleur (Win32 uniquement)
Remonter à Code assembleur en ligne (Win32 uniquement) - Index
L'assembleur intégré évalue toutes les expressions en valeurs entières 32 bits. Il ne supporte pas la virgule flottante et les valeurs chaîne, à l'exception des constantes chaîne. L'assembleur en ligne est disponible uniquement dans le compilateur Delphi Win32.
Les expressions sont construites à partir d'éléments et d'opérateurs d'expression ; chaque expression a une classe d'expression et un type d'expression associés. Cette rubrique couvre les sujets suivants :
- Différences entre les expressions Delphi et assembleur
- Eléments d'expressions
- Classes d'expressions
- Types d'expressions
- Opérateurs d'expressions
Sommaire |
Différences entre les expressions Delphi et assembleur
La différence la plus importante entre les expressions Delphi et celles de l'assembleur intégré est que toutes les expressions de l'assembleur doivent se résoudre en une valeur constante. En d'autres termes, elles doivent se résoudre en une valeur qui peut être calculée lors de la compilation. Par exemple, soit ces déclarations :
const X = 10; Y = 20; var Z: Integer;
l'instruction suivante est correcte :
asm MOV Z,X+Y end;
Comme X et Y sont des constantes, l'expression X + Y est juste un moyen commode d'écrire la constante 30 et l'instruction résultante place simplement la valeur 30 dans la variable Z. Mais si X et Y sont des variables :
var X, Y: Integer;
l'assembleur intégré ne peut calculer la valeur de X + Y à la compilation. Dans ce cas, pour placer la somme de X et Y dans Z, vous devez utiliser :
asm MOV EAX,X ADD EAX,Y MOV Z,EAX end;
Dans une expression Delphi, la référence à une variable indique le contenu de cette variable. Mais, dans une expression assembleur, la référence à une variable indique l'adresse de cette variable. En Delphi, l'expression X + 4 (où X est une variable) correspond au contenu de X plus 4, alors que pour l'assembleur intégré cela désigne le contenu du mot situé à l'adresse située quatre octets après celle de X. Donc, même si vous avez le droit d'écrire :
asm MOV EAX,X+4 end;
ce code ne charge pas la valeur de X plus 4 dans AX : il charge la valeur d'un mot stocké quatre octets après X. La manière correcte d'ajouter 4 au contenu de X est :
asm MOV EAX,X ADD EAX,4 end;
Eléments d'expressions
Les éléments composant une expression sont les constantes, les registres et les symboles.
Constantes numériques
Les constantes numériques doivent être des entiers et leurs valeurs doivent être comprises entre 2 147 483 648 et 4 294 967 295.
Par défaut, les constantes numériques utilisent la notation décimale, mais l'assembleur intégré supporte également les notations binaires, octales et hexadécimales. La notation binaire est sélectionnée en écrivant un B après le nombre, la notation octale en écrivant une lettre O après le nombre et la notation hexadécimale en écrivant un H après le nombre ou un $ avant le nombre.
Les constantes numériques doivent commencer par un des chiffres compris entre 0 et 9 ou un caractère $. Par conséquent lorsque vous écrivez une constante hexadécimale en utilisant le suffixe H, un zéro supplémentaire devant le nombre est nécessaire si le premier chiffre significatif est un des chiffres allant de A à F. Par exemple, 0BAD4H et $BAD4 sont des constantes hexadécimales, mais BAD4H est un identificateur car il commence par une lettre.
Constantes chaînes
Les constantes chaînes doivent être entourées d'apostrophes ou de guillemets. Deux apostrophes (ou deux guillemets) consécutives de même type comptent pour un seul caractère. Voici des exemples de constantes chaînes :
'Z' 'Delphi' 'Windows' "That's all folks" '"Thats all folks," he said.' '100' '"' "'"
Les constantes chaînes de toute longueur sont autorisées dans les directives DB et provoquent l'allocation d'une séquence d'octets contenant les valeurs ASCII des caractères de la chaîne. Dans tous les autres cas, une constante chaîne ne peut pas dépasser quatre caractères, et donne une valeur numérique pouvant se trouver dans une expression. La valeur numérique d'une constante chaîne est calculée ainsi :
Ord(Ch1) + Ord(Ch2) shl 8 + Ord(Ch3) shl 16 + Ord(Ch4) shl 24
où Ch1 est le caractère le plus à droite (le dernier) et Ch4 le plus à gauche (le premier). Si la chaîne est inférieure à quatre caractères, les caractères les plus à gauche sont pris pour zéro. Le tableau suivant présente certains exemples de constantes chaînes et de leurs valeurs numériques.
Exemples de chaînes avec leurs valeurs :
| Chaîne | Valeur |
|---|---|
|
'a' |
00000061H |
|
'ba' |
00006261H |
|
'cba' |
00636261H |
|
'dcba' |
64636261H |
|
'a ' |
00006120H |
|
' a' |
20202061H |
|
'a' * 2 |
000000E2H |
|
'a'-'A' |
00000020H |
|
not 'a' |
FFFFFF9EH |
Registres
Les symboles réservés suivants indiquent des registres CPU dans l'assembleur en ligne :
Registres CPU :
|
Général 32 bits |
EAX EBX ECX EDX |
Pointeur ou index 32 bits |
ESP EBP ESI EDI |
|
Général 16 bits |
AX BX CX DX |
Pointeur ou index 16 bits |
SP BP SI DI |
|
Registres 8 bits poids faible |
AL BL CL DL |
Registres de segment 16 bits |
CS DS SS ES |
|
Registres de segment 32 bits |
FS GS | ||
|
Registres 8 bits poids fort |
AH BH CH DH |
Pile des registres du coprocesseur |
ST |
Lorsqu'un opérande consiste uniquement en un nom de registre, il est appelé opérande registre. Tous les registres peuvent être utilisés comme opérandes registre, certains registres peuvent être utilisés dans d'autres contextes.
Les registres de base (BX et BP) et les registres d'index (SI et DI) peuvent être écrits entre des crochets pour indiquer l'indexation. Les combinaisons de registre de base/d'index correctes sont [BX], [BP], [SI], [DI], [BX+SI], [BX+DI], [BP+SI] et [BP+DI]. Vous pouvez aussi indexer avec tous les registres 32 bits, par exemple [EAX+ECX], [ESP] et [ESP+EAX+5].
Les registres de segment (ES, CS, SS, DS, FS et GS) sont supportés, mais les segments ne sont pas utiles dans les applications 32 bits.
Le symbole ST indique le registre le plus au-dessus de la pile de registre en virgule flottante 8087. Chacun des huit registres en virgule flottante peut être désigné par ST(X), où X est une constante comprise entre 0 et 7 indiquant la distance depuis le haut de la pile des registres.
Symboles
L'assembleur intégré vous permet d'accéder à presque tous les symboles Delphi dans des expressions en langage assembleur, dont les constantes, les types, variables, les procédures et les fonctions. De plus, l'assembleur intégré implémente le symbole spécial @Result qui correspond à la variable Result à l'intérieur d'une fonction. Ainsi, la fonction suivante :
function Sum(X, Y: Integer): Integer; begin Result := X + Y; end;
peut s'écrire en langage assembleur de la manière suivante :
function Sum(X, Y: Integer): Integer; stdcall;
begin
asm
MOV EAX,X
ADD EAX,Y
MOV @Result,EAX
end;
end;
Il n'est pas permis d'utiliser les symboles suivants dans des instructions asm :
- Les procédures et fonctions standard (par exemple, WriteLn et Chr).
- Les constantes chaîne, virgule flottante et ensemble (excepté lors du chargement des registres).
- Les libellés non déclarés dans le bloc en cours.
- Le symbole @Result hors des fonctions.
Le tableau suivant résume les types de symbole utilisables dans les instructions asm.
Symboles reconnus par l'assembleur intégré :
| Symbole | Valeur | Classe | Type |
|---|---|---|---|
|
Libellé |
Adresse du libellé |
Référence mémoire |
Taille de type |
|
Constante |
Valeur de la constante |
Valeur immédiate |
0 |
|
Type |
0 |
Référence mémoire |
Taille de type |
|
Champ |
Déplacement de champ |
Mémoire |
Taille de type |
|
Variable |
Adresse de variable ou adresse d'un pointeur sur la variable |
Référence mémoire |
Taille de type |
|
Procédure |
Adresse de procédure |
Référence mémoire |
Taille de type |
|
Fonction |
Adresse de fonction |
Référence mémoire |
Taille de type |
|
Unité |
0 |
Valeur immédiate |
0 |
|
@Result |
Déplacement de la variable de résultat |
Référence mémoire |
Taille de type |
Si l'optimisation est désactivée, les variables locales (variables déclarées dans des procédures et fonctions) sont toujours allouées sur la pile et accessibles relativement à EBP et la valeur d'un symbole de variable locale est son déplacement signé depuis EBP. L'assembleur ajoute automatiquement [EBP] dans les références aux variables locales. Par exemple, soit ces déclarations :
var Count: Integer;
dans une fonction ou une procédure, l'instruction :
MOV EAX,Count
s'assemble en MOV EAX,[EBP4].
L'assembleur intégré traite les paramètres var comme des pointeurs 32 bits et la taille d'un paramètre var est toujours 4. La syntaxe pour accéder à un paramètre var est différente de celle utilisée pour accéder à un paramètre transmis par valeur. Pour accéder au contenu d'un paramètre var, vous devez d'abord charger le pointeur 32 bits puis accéder à l'emplacement qu'il désigne. Par exemple :
function Sum(var X, Y: Integer): Integer; stdcall;
begin
asm
MOV EAX,X
MOV EAX,[EAX]
MOV EDX,Y
ADD EAX,[EDX]
MOV @Result,EAX
end;
end;
Il est possible de qualifier les identificateurs dans les instructions asm. Par exemple, soit ces déclarations :
type
TPoint = record
X, Y: Integer;
end;
TRect = record
A, B: TPoint;
end;
var
P: TPoint;
R: TRect;
Il est possible d'utiliser les constructions suivantes dans une instruction asm pour accéder aux champs :
MOV EAX,P.X MOV EDX,P.Y MOV ECX,R.A.X MOV EBX,R.B.Y
Un identificateur de type peut être utilisé pour construire des variables à la volée. Chacune des instructions suivantes génère le même code machine, qui charge le contenu de [EDX] dans EAX.
MOV EAX,(TRect PTR [EDX]).B.X MOV EAX,TRect([EDX]).B.X MOV EAX,TRect[EDX].B.X MOV EAX,[EDX].TRect.B.X
Classes d'expressions
L'assembleur intégré divise les expressions en trois classes : registres, références mémoire et valeurs immédiates.
Une expression qui ne comprend qu'un nom de registre est une expression registre. Des exemples d'expressions registre sont AX, CL, DI et ES. Utilisées comme opérandes, les expressions registre dirigent l'assembleur pour générer des instructions qui fonctionnent sur les registres CPU.
Les expressions qui indiquent un emplacement en mémoire sont appelées références mémoire. Les libellés, variables, constantes typées, procédures et fonctions de Delphi appartiennent à cette catégorie.
Les expressions qui ne sont pas des registres et qui ne sont pas associées aux emplacements mémoire sont des valeurs immédiates. Ce groupe inclut les constantes non typées et les identificateurs de type de Delphi.
Les valeurs immédiates et les références mémoire provoquent la génération d'un code différent lorsqu'elles sont utilisées comme opérandes. Par exemple :
const
Start = 10;
var
Count: Integer;
.
.
.
asm
MOV EAX,Start { MOV EAX,xxxx }
MOV EBX,Count { MOV EBX,[xxxx] }
MOV ECX,[Start] { MOV ECX,[xxxx] }
MOV EDX,OFFSET Count { MOV EDX,xxxx }
end;
Comme Start est une valeur immédiate, le premier MOV est assemblé dans une instruction de déplacement immédiat. Le deuxième MOV, cependant, est traduit en une instruction mémoire de déplacement, comme Count est une référence mémoire. Dans le troisième MOV, l'opérateur crochet est utilisé pour convertir Start en référence mémoire (dans ce cas, le mot à l'offset 10 du segment de données). Dans le quatrième MOV, l'opérateur OFFSET est utilisé pour convertir Count en une valeur immédiate (l'offset de Count dans le segment de données).
Les crochets et l'opérateur OFFSET se complémentent l'un l'autre. L'instruction asm suivante est identique aux deux premières lignes de l'instruction asm précédente :
asm MOV EAX,OFFSET [Start] MOV EBX,[OFFSET Count] end;
Les références mémoire et les valeurs immédiates sont classées comme relogeables ou absolues. Le relogement est le processus par lequel le lieur affecte des adresses absolues aux symboles. Une expression relogeable désigne une valeur qui nécessite un relogement lors de la liaison alors qu'une expression absolue désigne une valeur qui n'a pas besoin de relogement. Habituellement, les expressions faisant référence à des libellés, des variables, des procédures ou des fonctions sont relogeables car l'adresse finale de ces symboles est inconnue lors de la compilation. Les expressions qui agissent uniquement sur des constantes sont absolues.
L'assembleur intégré vous permet d'exécuter toute opération sur une valeur absolue, mais restreint les opérations sur les valeurs relogeables pour l'addition et la soustraction des constantes.
Types d'expressions
Chaque expression de l'assembleur intégré a un type ou plus précisément une taille associée, car l'assembleur intégré considère le type d'une expression simplement comme la taille de son emplacement mémoire. Par exemple, le type d'une variable Integer est quatre car il occupe 4 octets. L'assembleur intégré effectue des vérifications de type à chaque fois que cela est possible, comme dans les instructions suivantes :
var QuitFlag: Boolean; OutBufPtr: Word; . . . asm MOV AL,QuitFlag MOV BX,OutBufPtr end;
où l'assembleur vérifie que la taille de QuitFlag est un (un octet) et que la taille de OutBufPtr est deux (un mot). L'instruction :
MOV DL,OutBufPtr
produit une erreur car DL est un registre de taille octet et OutBufPtr est un mot. Le type d'une référence mémoire peut être changé via un transtypage ; ainsi pour écrire correctement l'instruction précédente, vous pouvez utiliser :
MOV DL,BYTE PTR OutBufPtr MOV DL,Byte(OutBufPtr) MOV DL,OutBufPtr.Byte
Ces instructions MOV font toutes référence au premier octet (le moins significatif) de la variable OutBufPtr.
Dans certains cas, une référence mémoire est non typée ; c'est-à-dire qu'elle n'a pas de type associé. Un exemple est une valeur immédiate (Buffer) entourée de crochets :
procedure Example(var Buffer); asm MOV AL, [Buffer] MOV CX, [Buffer] MOV EDX, [Buffer] end;
L'assembleur intégré autorise ces instructions, car l'expression [Buffer] n'a pas de type. [Buffer] signifie "le contenu de l'emplacement indiqué par Buffer" et le type peut être déterminé à partir du premier opérande (octet pour AL, mot pour CX et double-mot pour EDX).
Dans les cas où le type ne peut pas être déterminé à partir d'un autre opérande, l'assembleur intégré requiert un transtypage explicite. Par exemple :
INC BYTE PTR [ECX] IMUL WORD PTR [EDX]
Le tableau suivant résume les symboles de type prédéfini que l'assembleur intégré fournit en plus de tout type Delphi actuellement déclaré.
Symboles de types prédéfinis :
| Symbole | Type |
|---|---|
|
BYTE |
1 |
|
WORD |
2 |
|
DWORD |
4 |
|
QWORD |
8 |
|
TBYTE |
10 |
Opérateurs d'expressions
L'assembleur intégré propose divers opérateurs. Les règles de priorité sont différentes de celles s'appliquant en langage Delphi. Par exemple, dans une instruction asm, AND a une priorité plus faible que les opérateurs d'addition et de soustraction. Le tableau suivant énumère les opérateurs d'expression de l'assembleur intégré en ordre décroissant de priorité.
Priorité des opérateurs d'expressions de l'assembleur intégré
| Opérateurs | Remarques | Priorité |
|---|---|---|
|
& |
Maximum | |
|
(... ), [... ],., HIGH, LOW |
||
|
+, - |
+ et - unaires |
|
|
: |
||
|
OFFSET, TYPE, PTR, *, /, MOD, SHL, SHR, +, - |
+ et - binaires |
|
|
NOT, AND, OR, XOR |
Minimum |
Le tableau suivant définit les opérateurs d'expressions de l'assembleur intégré :
Définitions des opérateurs d'expressions de l'assembleur intégré :
| Opérateur | Description |
|---|---|
|
& |
Redéfinition d'identificateur. L'identificateur suivant immédiatement cet opérateur est traité comme symbole défini par l'utilisateur, même si l'orthographe est identique à celle du symbole réservé de l'assembleur intégré. |
|
(... ) |
Sous-expression. Les expressions entre parenthèses sont évaluées complètement avant d'être traitées comme éléments d'expressions simples. Une autre expression peut parfois précéder l'expression entre parenthèses ; le résultat dans ce cas devient la somme des valeurs des deux expressions, avec le type de la première expression. |
|
[... ] |
Référence mémoire. L'expression entre crochets est évaluée complètement avant d'être traitée comme élément d'expression simple. Une autre expression peut parfois précéder l'expression entre crochets ; le résultat dans ce cas devient la somme des valeurs de deux expressions, avec le type de la première expression. Le résultat est toujours une référence mémoire. |
|
. |
Sélecteur de membre de structure. Le résultat est la somme de l'expression située avant le point avec celle située après le point, avec le type de l'expression située après le point. L'accès aux symboles appartenant à la portée identifiée par l'expression située avant le point peut s'effectuer dans l'expression située après le point. |
|
HIGH |
Renvoie les huit bits de poids fort de l'expression mot suivant l'opérateur. L'expression doit être une valeur immédiate absolue. |
|
LOW |
Renvoie les huit bits de poids faible de l'expression mot suivant l'opérateur. L'expression doit être une valeur immédiate absolue. |
|
+ |
Plus unaire. Renvoie l'expression suivant le plus sans changement. L'expression doit être une valeur immédiate absolue. |
|
- |
Moins unaire. Renvoie la valeur négative de l'expression suivant le moins. L'expression doit être une valeur immédiate absolue. |
|
+ |
Addition. Les expressions peuvent être des valeurs immédiates ou des références mémoire, mais une seule des expressions peut être une valeur relogeable. Si une des expressions est une valeur relogeable, le résultat est aussi une valeur relogeable. Si les deux expressions sont des références mémoire, le résultat est aussi une référence mémoire. |
|
- |
Soustraction. La première expression peut avoir n'importe quelle classe, mais la deuxième expression doit être une valeur immédiate absolue. Le résultat a la même classe que la première expression. |
|
: |
Redéfinition de segment. Instruit l'assembleur que l'expression située après les deux-points appartient au segment donné par le nom de registre segment (CS, DS, SS, FS, GS ou ES) situé avant les deux-points. Le résultat est une référence mémoire avec la valeur de l'expression située après les deux-points. Lorsqu'une redéfinition de segment est utilisée dans une opérande d'instruction, l'instruction aura comme préfixe une instruction préfixe de redéfinition de segment appropriée. Ceci pour s'assurer que le segment indiqué est sélectionné. |
|
OFFSET |
Renvoie la partie offset (double mot) de l'expression suivant l'opérateur. Le résultat est une valeur immédiate. |
|
TYPE |
Renvoie le type (taille en octets) de l'expression suivant l'opérateur. Le type d'une valeur immédiate est 0. |
|
PTR |
Opérateur de transtypage. Le résultat est une référence mémoire avec la valeur de l'expression suivant l'opérateur et le type de celle située devant l'opérateur. |
|
* |
Multiplication. Les deux expressions doivent être des valeurs immédiates absolues et le résultat est une valeur immédiate absolue. |
|
/ |
Division entière. Les deux expressions doivent être des valeurs immédiates absolues et le résultat est une valeur immédiate absolue. |
|
MOD |
Reste après la division entière. Les deux expressions doivent être des valeurs immédiates absolues et le résultat est une valeur immédiate absolue. |
|
SHL |
Décalage gauche logique. Les deux expressions doivent être des valeurs immédiates absolues et le résultat est une valeur immédiate absolue. |
|
SHR |
Décalage droite logique. Les deux expressions doivent être des valeurs immédiates absolues et le résultat est une valeur immédiate absolue. |
|
NOT |
Négation bit-à-bit. L'expression doit être une valeur immédiate absolue et le résultat est une valeur immédiate absolue. |
|
AND |
AND bit-à-bit. Les deux expressions doivent être des valeurs immédiates absolues et le résultat est une valeur immédiate absolue. |
|
OR |
OR bit-à-bit. Les deux expressions doivent être des valeurs immédiates absolues et le résultat est une valeur immédiate absolue. |
|
XOR |
OR exclusif bit-à-bit. Les deux expressions doivent être des valeurs immédiates absolues et le résultat est une valeur immédiate absolue. |