Expressions assembleur (Win32 uniquement)

De RAD Studio (Français)

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

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.

Voir aussi

Autres langues