Syntaxe assembleur

De RAD Studio
Aller à : navigation, rechercher

Remonter à Code assembleur inline - Index

Les rubriques suivantes décrivent les éléments de la syntaxe assembleur.

Instructions

Une instruction assembleur a la syntaxe suivante :

Label: Prefix Opcode Operand1, Operand2

Label est un libellé, Prefix est un opcode (code d'opération) de préfixe assembleur, Opcode est une directive ou un opcode d'instruction assembleur, et Operand est une expression assembleur. Label et Prefix sont facultatifs. Certains opcodes ne prennent qu'un seul opérande, d'autres n'en prennent aucun.

Les commentaires sont autorisés entre des instructions assembleur, mais pas à l'intérieur d'elles. Par exemple :

     MOV AX,1 {Initial value}  { OK }
     MOV CX,100 {Count}        { OK }

Libellés

Les libellés sont utilisés dans les instructions de l'assembleur intégré comme ils le sont en langage Delphi : en écrivant le libellé et un deux-points devant une instruction. Il n'y a pas de limite à la longueur d'un libellé. Comme dans Delphi, les libellés doivent être déclarés dans une partie déclaration label du bloc contenant l'instruction asm. Il y a une exception à cette règle : les libellés locaux.

Les libellés locaux sont des libellés qui commencent par un signe @. Ils sont composés d'un signe @ suivi d'une ou de plusieurs lettres, chiffres, caractères souligné ou signes @. L'utilisation des libellés locaux est restreinte aux instructions asm et la portée d'un libellé local s'étend du mot réservé asm à la fin de l'instruction asm qui le contient. Il n'est pas nécessaire de déclarer un libellé local.

Opcodes d'instruction

L'assembleur intégré supporte tous les opcodes documentés par Intel pour l'usage général dans les applications. Notez que des instructions privilégiées du système d'exploitation peuvent ne pas être supportées. En particulier, les familles d'instructions suivantes sont supportées :

  • IA-32
    • Famille Pentium
    • Pentium Pro et Pentium II
    • Pentium III
    • Pentium 4
  • Intel 64

De plus, l'assembleur intégré supporte les extensions de jeux d'instructions suivantes :

  • Intel SSE (notamment SSE4.2)
  • AMD 3DNow! (depuis AMD K6)
  • AMD Enhanced 3DNow! (depuis AMD Athlon)

Pour une description complète de chaque instruction, reportez-vous à la documentation de votre microprocesseur.

Dimensionnement de saut automatique

A moins qu'il ne soit orienté autrement, l'assembleur intégré optimise les instructions de saut en sélectionnant automatiquement la forme la plus courte, et par conséquent la plus efficace d'une instruction de saut. Ce dimensionnement de saut automatique s'applique à l'instruction de saut inconditionnel (JMP), et à toutes les instructions de saut conditionnel quand la cible est un libellé (et non une procédure ou une fonction).

Pour une instruction de saut inconditionnel (JMP), l'assembleur intégré génère un saut court (un opcode d'un octet suivi d'un déplacement sur un octet) si la distance au libellé cible est de -128 à 127 octets. Sinon, il génère un saut proche (un opcode d'un octet suivi d'un déplacement sur deux octets).

Pour une instruction de saut conditionnel, un saut court (un opcode d'un octet suivi d'un déplacement sur un octet) est généré si la distance au libellé cible est de -128 à 127 octets. Sinon, l'assembleur intégré génère un saut court jusqu'au libellé cible.

Les sauts sur les points d'entrée de procédures et fonctions sont toujours proches.

Directives

L'assembleur intégré supporte trois directives assembleur de définition : DB (définition d'octet), DW (définition de mot) et DD (définition de double mot). Chaque directive génère les données correspondant aux opérandes séparées par des virgules qui suivent la directive.

Directive Description

DB

Définition d'octet : génère une séquence d'octets. Chaque opérande peut être une expression constante avec une valeur comprise entre 128 et 255, ou une chaîne de caractères de n'importe quelle longueur. Les expressions constantes génèrent un octet de code et les chaînes génèrent une séquence d'octets avec des valeurs correspondant au code ASCII de chaque caractère.

DW

Définition de mot : génère une séquence de mots. Chaque opérande peut être une expression constante avec une valeur comprise entre 32 768 et 65 535, ou une expression d'adresse. Pour une expression d'adresse, l'assembleur intégré génère un pointeur near, c'est-à-dire un mot contenant la partie offset (déplacement) de l'adresse.

DD

Définition d'un double mot : génère une séquence de double mots. Chaque opérande peut être une expression constante avec une valeur comprise entre 2 147 483 648 et 4 294 967 295, ou une expression d'adresse. Pour une expression d'adresse, l'assembleur intégré génère un pointeur far, c'est-à-dire un mot contenant la partie offset (déplacement) de l'adresse, suivi d'un mot contenant la partie segment de l'adresse.

DQ

Définition d'un quadruple mot : définit un quadruple mot pour les valeurs Int64.

Les données générées par les directives DB, DW et DD sont toujours stockées dans le segment de code, tout comme le code généré par d'autres instructions de l'assembleur intégré. Pour générer des données non initialisées ou initialisées dans le segment de données, vous devez utiliser les déclarations Delphi var ou const.

Voici quelques exemples de directives DB, DW et DD :

     MOV {Initial value} AX,1; { Error! }
     MOV CX, {Count} 100       { Error! }

Quand un identificateur précède une directive DB, DW ou DD, il provoque la déclaration d'une variable octet, mot ou double mot à l'emplacement de la directive. Par exemple, l'assembleur autorise :

asm
  DB     FFH                           { One byte }
  DB     0.99                          { Two bytes }
  DB     'A'                           { Ord('A') }
  DB     'Hello world...',0DH,0AH      { String followed by CR/LF }
  DB     12,'string'                   { Delphi style string }
  DW     0FFFFH                        { One word }
  DW     0,9999                        { Two words }
  DW 	'A'                             { Same as DB  'A',0 }
  DW 	'BA'                            { Same as DB 'A','B' }
  DW 	MyVar                           { Offset of MyVar }
  DW 	MyProc                          { Offset of MyProc }
  DD 	0FFFFFFFFH                      { One double-word }
  DD 	0,999999999			{ Two double-words }
  DD 	'A'				{ Same as DB 'A',0,0,0 }
  DD 	'DCBA'				{ Same as DB 'A','B','C','D' }
  DD 	MyVar 				{ Pointer to MyVar }
  DD 	MyProc				{ Pointer to MyProc }
 end;

L'assembleur intégré ne supporte pas de telles déclarations de variable. Le seul type de symbole pouvant être défini dans une instruction de l'assembleur inline est un libellé. Toutes les variables doivent être déclarées en utilisant la syntaxe Delphi. La construction précédente peut être remplacée par :

     ByteVar 		DB	?
     WordVar 		DW 	?
     IntVar		DD 	?
	.
	.
	.
     			MOV 	AL,ByteVar
     			MOV 	BX,WordVar
     			MOV	ECX,IntVar

SMALL et LARGE peuvent être utilisés pour déterminer la largeur d'un déplacement :

var
  ByteVar: Byte;
  WordVar: Word;
  IntVar: Integer;
       .
       .
       .
asm
  MOV AL,ByteVar
  MOV BX,WordVar
  MOV ECX,IntVar
end;

Cette instruction génère un transfert 'normal' avec un déplacement sur 32 bits ($00001234) :

MOV EAX, [LARGE $1234]

La seconde instruction générera un transfert avec un préfixe de redéfinition de la taille d'adressage et un déplacement sur 16 bits ($1234).

SMALL peut être utilisé pour économiser de l'espace. L'exemple suivant génère une redéfinition de la taille d'adressage et une adresse sur 2 octets (au total trois octets) :

MOV EAX, [SMALL $1234]

à l'opposé de :

 MOV EAX, [SMALL 123]

qui ne générera pas de redéfinition de la taille d'adressage et une adresse sur 4 octets (au total quatre octets).

Deux directives supplémentaires permettent au code assembleur d'accéder aux méthodes virtuelles et dynamiques : VMTOFFSET et DMTINDEX.

VMTOFFSET récupère le décalage en octets de l'entrée de la table des pointeurs de méthodes virtuelles de l'argument méthode virtuelle à partir du début de la table des méthodes virtuelles (VMT). Cette directive nécessite un nom de classe entièrement spécifié avec un nom de méthode en paramètre (par exemple, TExample.VirtualMethod), ou un nom d'interface et un nom de méthode d'interface.

DMTINDEX récupère l'index dans la table des méthodes dynamiques de la méthode dynamique transmise. Cette directive nécessite aussi un nom de classe entièrement spécifié avec un nom de méthode en paramètre (par exemple, TExample.DynamicMethod). Pour invoquer la méthode dynamique, appelez System.@CallDynaInst avec le registre (E)SI contenant la valeur obtenue de DMTINDEX.

Remarque : Les méthodes avec la directive message sont implémentées comme des méthodes dynamiques et elles peuvent aussi être appelées à l'aide de la technique DMTINDEX. Par exemple :

 MOV EAX, [123]

L'exemple suivant utilise à la fois DMTINDEX et VMTOFFSET pour accéder aux méthodes virtuelles et dynamiques :

  TMyClass = class
    procedure x; message MYMESSAGE;
  end;

Opérandes

Les opérandes de l'assembleur inline sont des expressions composées de constantes, de registres, de symboles et d'opérateurs.

Parmi les opérandes, les mots réservés suivants ont une signification prédéfinie :

Mots réservés de l'assembleur intégré


Registres CPU

Catégorie

Identificateurs

Registres CPU 8 bits

AH, AL, BH, BL, CH, CL, DH, DL (registres à usage général) ;

Registres CPU 16 bits

AX, BX, CX, DX (registres à usage général) ; DI, SI, SP, BP (registres d'index) ; CS, DS, SS, ES (registres de segment) ; IP (pointeur d'instruction)

Registres CPU 32 bits

EAX, EBX, ECX, EDX (registres à usage général) ; EDI, ESI, ESP, EBP (registres d'index) ; FS, GS (registres de segment) ; EIP

FPU

ST(0), ..., ST(7)

Registres MMX FPU

mm0, ..., mm7

Registres XMM

xmm0, ..., xmm7 (..., xmm15 sur x64)

Registres Intel 64

RAX, RBX, ...


Données et opérateurs

Catégorie

Identificateurs

Données

BYTE, WORD, DWORD, QWORD, TBYTE

Opérateurs

NOT, AND, OR, XOR; SHL, SHR, MOD; LOW, HIGH; OFFSET, PTR, TYPE

VMTOFFSET, DMTINDEX

SMALL, LARGE


Les mots réservés ont toujours la priorité sur les identificateurs définis par l'utilisateur. Par exemple :

  program Project2;
  type
    TExample = class
      procedure DynamicMethod; dynamic;
      procedure VirtualMethod; virtual;
    end;
  procedure TExample.DynamicMethod;
  begin

charge 1 dans le registre CH, et non dans la variable Ch. Pour accéder à un symbole défini par l'utilisateur ayant le même nom qu'un mot réservé, vous devez utiliser l'opérateur de redéfinition "et commercial" (&).

  end;
  procedure TExample.VirtualMethod;
  begin

Il est préférable d'éviter les identificateurs définis par l'utilisateur ayant les mêmes noms que des mots réservés de l'assembleur intégré.

Voir aussi