Utilisation de l'assembleur en ligne avec Delphi

La page est créée Thierry Martins
 
CONTINUER À LIRE
Utilisation de l'assembleur en ligne avec
                   Delphi

                                  par Nono40

                           Date de publication : 11/09/2003

                           Dernière mise à jour : 11/09/2003

  Ce document présente l'utilisation de l'assembleur en ligne intégré dans
  Delphi. Il décrit en détail l'utilisation en assembleur des principaux type de
  données utilisés en pascal. La version "en ligne" de ce document est ici :
Utilisation de l'assembleur en ligne avec Delphi par Nono40

Introduction
I - Programmer en assembleur sous Delphi
    I-A - Inclure du code assembleur
    I-B - Quelles sont les instructions utilisables ?
    I-C - Quels sont les registres utilisables ?
    I-D - Sur quoi pointent les registres segments ?
    I-E - Ecrire le code
        I-E-1 - Opérandes
        I-E-2 - Labels
        I-E-3 - Directives
        I-E-4 - Opérateurs spéciaux
        I-E-5 - Commentaires
II - Accès aux différents types de données
    II-A - Généralités
    II-B - Types ordinaux
    II-C - Types ensembles
    II-D - Types réels
    II-E - Type Int64
    II-F - Tableaux statiques
    II-G - Chaînes courtes
    II-H - Enregistrements
III - Ecrire une fonction en assembleur
    III-A - Généralités
    III-B - Code d'entrée et de sortie
    III-C - Empilage des paramètres
    III-D - Conventions d'appel
        III-D-1 - Généralités
        III-D-2 - Convention de type Pascal.
        III-D-3 - Convention de type StdCall
        III-D-4 - Convention de type C.
        III-D-5 - Convention de type Register.
    III-E - Paramètres et résultat de la fonction
        III-E-1 - Types ordinaux
        III-E-2 - Types ensembles
        III-E-3 - Types réels
        III-E-4 - Type Int64
        III-E-5 - Tableaux statiques
        III-E-6 - Chaînes courtes
        III-E-7 - Enregistrements
IV - Accéder aux objets
    IV-A - Appel d'une méthode statique
    IV-B - Appel d'une méthode virtuelle

                                                             -2-
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

   IV-C - Appel d'une méthode dynamique
   IV-D - Appel d'une méthode de classe
   IV-E - Accès aux propriétés
V - Création d'un objet.
   V-A - Le constructeur
   V-B - Le destructeur
   V-C - Ecriture des méthodes.
   V-D - Ecriture d'une méthode de classe.
VI - Problèmes spécifiques à l'assembleur
   VI-A - Fonction SizeOf()
   VI-B - Erreur de codage due à OFFSET
   VI-C - Erreur due à un EBP implicite
   VI-D - Erreur d'adresse dans des sous-procédures
Conclusion

                                                             -3-
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

Introduction

Ce document est destiné à tous ceux qui souhaitent utiliser de l'assembleur dans Delphi. Ceci peut
dans certains cas être plus rapide que l'écriture en langage évolué. Ce document n'est pas un cours
d'initiation à l'assembleur, il suppose que vous connaissiez la structure des registres et le langage des
processeurs x86 et compatibles. De même il est supposé que vous avez une bonne connaissance du
pascal. Il n'est pas nécessaire d'avoir des notions précises des modes Réel, Protégé et Virtuel du 386,
ni de la structure segmentée de la mémoire propre aux processeurs x86.

Ce document ne présente pas non plus de méthode d'optimisation grâce à l'assembleur. Mais plutôt
une description détaillée de l'interfaçage de l'assembleur avec le pascal.

Vu l'époque actuelle, et le peu de systèmes 16 bits encore en service, tout ce document ne porte que
sur Delphi 2 et suivant. C'est-à-dire sur l'adressage 32 bits des processeurs. L'utilisation de fonctions
récentes (SSE2) requiert Delphi 6.

                                                             -4-
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

I - Programmer en assembleur sous Delphi

I-A - Inclure du code assembleur

Pour intégrer de l'assembleur dans le code Pascal de Delphi, il suffit de l'entourer des mots clefs Asm
et End. Toutes les instructions situées entre ces mots doivent être des instructions assembleur. Elles
sont insérées tel que dans le code exécutable.

Exemple simple :

    procedure TForm1.Button2Click(Sender: TObject);
    Var a,b:Integer;
    begin
     A:=1;
     B:=2;
     Asm
      Mov EAX,A
      Add B,EAX
     End;
     Label1.Caption:=IntToStr(b);
    end;

I-B - Quelles sont les instructions utilisables ?

Delphi7 intègre dans l'assembleur en ligne tout le jeu d'instructions défini par Intel. Que ce soient les
instructions de base, FPU, MMX, SSE et SSE2. (Bien sûr toutes ne sont pas utilisables suivant les
processeurs !) Delphi est en mode d'adressage 32bits, c'est-à-dire qu'il vaut mieux utiliser les registres
32 bits par défaut. Il est toutefois possible de travailler avec les registres 8 et 16 bits, Delphi ajoutant
les préfixes nécessaires pour les opérations. De même l'adressage étendu est utilisé par défaut,
c'est-à-dire que les adressages suivants sont autorisés :

    Mov EAX,BaseTableau[ECX+EDX*4]
    Mov EAX,Dword ptr [ESI+EDI+20]

I-C - Quels sont les registres utilisables ?

Tous les registres définis dans la structure IA32 des processeurs Intel sont utilisables :

•     Registres communs : EAX,EBX,ECX,EDX,ESI,EDI,EBP,ESP ainsi que les registres
      AX,BX,CX,DX,SI,DI,BP,SP,AL,AH,BL,BH,CL,CH,DL,DH
•     Registre d'état EFLAGS
•     Registres de segment : DS ES FS GS CS SS, bien sur il est rare d'avoir à utiliser ses registres
      dans Delphi. Ce n'est recommandé qu'aux utilisateurs avancés, et inutile dans la plupart des
      cas. ( voir plus bas )
•     Registres FPU : ST0 à ST7
•     Registres MMX : MM0 à MM7
•     Registres SSE/SSE2 : XMM0 à XMM7 et MXCSR

                                                             -5-
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

Tous les registres sont utilisables dans le code assembleur, par contre vous ne devez pas modifier le
contenu des registres suivants : EBX, ESI, EDI, EBP, ESP, DS, ES, CS et SS. En cas de besoin il faut
en sauvegarder leur valeur en début de code et la restituer en fin :

    PUSH EBX
    PUSH EDI
    // Code asm utilisant EBX et EDI
    POP EDI
    POP EBX

I-D - Sur quoi pointent les registres segments ?

Les registres de segments sont chargés par Delphi. Il n'est pas utile de modifier leur valeur. Delphi
fonctionne en mode protégé en adressage 32 bits. Toute la mémoire de l'application est donc visible
sans avoir à changer la valeur des segments.

DS et ES pointent sur la zone données de l'application, que ce soient des variables statiques ou des
objets. Les instructions de chaînes utilisant ces segments par défaut, il n'est pas utile de surcharger
avec les préfixes ES : ou DS : vu que les deux pointent sur la même plage d'adresses

SS pointe sur la mémoire de la pile. Dans une application Delphi SS contient le même descripteur que
ES/DS.

CS pointe sur le code, sa valeur doit être utilisée pour l'accès au code.

Notez que dans 99% des cas, la gestion des segments effectuée par Delphi est suffisante et il n'est
pas utile de se poser la question du contenu de ces registres.

I-E - Ecrire le code

La syntaxe d'une instruction assembleur est la suivante :

    [Label :] Instruction Opérande1[,Opérande2[,Opérande3]

Le nombre d'opérande doit correspondre avec l'instruction utilisée. Quand différents nombres
d'opérandes sont utilisables pour une même instruction, Delphi encode l'instruction correspondant au
nombre d'opérandes utilisées. C'est le cas notamment pour l'instruction IMUL.

I-E-1 - Opérandes

Les opérandes possibles sont les suivantes. Les combinaisons autorisées dépendent de l'instruction
utilisée.

•     Valeur immédiate
•     Registre commun ou spécifique
•     Variables Delphi
•     Adresse mémoire directe ou indexée

                                                             -6-
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

Pour les valeurs immédiates, les règles d'évaluation à la compilation sont les mêmes que pour le
pascal. Les valeurs suivantes sont autorisées :

   Const Valeur=123 ;
   ...
   MOV EAX,4
   MOV ECX,Valeur
   MOV ECX,(Valeur+2)*3

Une valeur immédiate sera toujours de la même taille que le registre/mémoire de destination dans la
mesure où Delphi est capable d'en évaluer la taille. Dans le cas contraire il faut forcer la taille de
l'opération en utilisant l'une des directives BYTE PTR, WORD PTR etc.

   MOV BYTE PTR [EDI],3

La taille des opérandes doit correspondre à l'instruction utilisée et les tailles des opérandes doivent
correspondre entre elles. Delphi ne vérifie que la taille en octets des opérandes et non pas son type
exact. Pas exemple les types Integer, Dword et Array[0..3] of byte sont identiques au niveau de
l'assembleur. Exemples de concordance de taille d'opérande :

   Var a:Integer;
     b:Word;
     C:Array[0..3]Of Byte;
     R8:Array[0..7]Of Byte;
     R16:Array[0..15]Of Byte;
     R:Record
        rI:Integer;
        rD:Integer;
      End;
   begin
    Asm
     MOV EAX,EBX
     MOV EAX,A
     MOV CX,B
     MOV EAX,C
     MOV EAX,R.rD
     MOVQ MM1,R8
     MOVDQU XMM1,R16
    end;
   end;

Attention c'est le type de variable directement qui est utilisé pour la concordance. Dans le cas d'un
tableau, c'est la taille du type complet même si on accède à un indice, car l'indice n'est pas interprété
comme indice du tableau, mais comme un Offset supplémentaire ajouté à l'adresse de base du
tableau.

   Var
     T4:Array[0..10]Of Integer;
   Begin
    Asm
     MOV EAX,T4[2] // Incorrect
    End;
   End;

                                                             -7-
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

Dans le cas d'un adressage indirect, ou dans le cas d'un transtypage il est possible de forcer la taille
de l'opérande à l'aide d'une des directives suivantes :

   Var Buff:Array[0..511]Of Byte;
   begin
    Asm
     MOV AL,Byte ptr Buff        // 1 octet
     MOV AX,Word ptr Buff          // 2 octets
     MOV EAX,DWord ptr Buff // 4 octets
     MOVQ MM0,QWord ptr Buff // 8 octets
     MOVDQU XMM1,DQWord ptr Buff // 16 octets

     FLD    QWord ptr Buff       // 8 octets
     FLD    TByte ptr Buff     // 10 octets
    end;
   end;

I-E-2 - Labels

Les labels sont définis comme en pascal à l'aide du mot clef label :

   Label Retour ;
   #
   Asm
     Retour: ADD EAX,Tableau[ECX*4]
         LOOP Retour
   End;

Il est possible d'utiliser un label commençant par @, dans ce cas il n'est pas nécessaire de le définir
dans une clause label. Mais il n'est alors utilisable que dans le même bloc ASM.

   Asm
     JMP @LAB1 // Correct
     JMP @LAB2 // Interdit
     #
   @LAB1: ADD EAX,EDX
   End
   Inc(UneVariable) ;
   Asm
   @LAB2: ADD EAX,ECX
   End;

I-E-3 - Directives

Les directives suivantes sont utilisables dans le code assembleur : DB,DW,DD,DQ : Insertion en ligne
de données de type byte, word, dword ou quadword

Exemples :

   DB 1
   DB 'A'
   DD 12345678
   DQ $ABCDEFGH12345678

                                                             -8-
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

Ces directives intègrent directement les valeurs dans le code à l'endroit où elles sont données. Il est
préférable d'utiliser des constantes dans une zone const pour définir ce genre de valeur. La directive
DB permet aussi d'inclure du code machine " inline ", cette méthode est déconseillée mais peut servir
dans certains cas particuliers.

Les directives VMTOFFSET et DMTINDEX permettent d'accéder aux méthodes virtuelles et
dynamiques des objets. ( voir le chapitre sur les objets )

I-E-4 - Opérateurs spéciaux

Les opérateurs suivants peuvent être utilisés sur les opérandes :

•    Low : retourne l'octet de poids faible de l'opérande. Ne pas confondre avec la fonction Low() du
     pascal.
•    High : retourne l'octet de poids fort de l'opérande. Ne pas confondre avec la fonction High() du
     pascal.
•    Type : Retourne la taille en octet de l'opérande. C'est l'équivalent de la fonction SizeOf() du
     pascal.
•    : : Opérateur de surcharge du segment par défaut d'une instruction.
•    [ ] : Offset ajouté à l'adressage en cours, il peut contenir des registre, un identificateur ou une
     valeur immédiate. L'offset ajouté est toujours calculé en octets.

I-E-5 - Commentaires

L'ajout de commentaires dans le code assembleur est effectué par les mêmes balises qu'en pascal.
De plus ceux-ci ne peuvent être placés en milieu de ligne. Le commentaire assembleur de fin de ligne
';' n'est pas reconnu, il doit être remplacé par //.

Remarque : l'assembleur étant moins lisible que le pascal, il est fortement conseillé de bien
commenter le code.

                                                             -9-
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

II - Accès aux différents types de données

II-A - Généralités

Tous les types de données peuvent être utilisés en assembleur. Cependant tous les types statiques
sont beaucoup plus simples à utiliser. Les sections suivantes donnent en détail l'utilisation de ces
principaux types.

Les types dynamiques comme String ou Array Of -type-, sont d'un usage plus compliqué et
nécessitent l'usage de fonctions internes à Delphi non documentées. Ils ne seront donc pas décrits ici.

Dans la plupart des cas l'utilisation des types de données peut être fait sans utilisation de la directive
xxx PTR. L'erreur de compilation " Non concordance de taille d'opérande " signale le plus souvent une
erreur dans le choix d'un registre ou la taille d'une mémoire.

II-B - Types ordinaux

Les ordinaux sont tous les types équivalents à une valeur entière. C'est-à-dire les types entiers,
énumérés, booléens, caractères ou pointeurs.

L'utilisation de ces types est simple, l'assembleur travaille toujours sur leur représentation entière.

   Type
    TMonEnum=(Lundi,Mardi,Mercredi,Jeudi,Vendredi,Samedi,Dimanche);
   Var
    A,F:Integer;
    B:Byte;
    C:Char;
    D:Word;
    Enum:TMonEnum;
   begin
    Asm
     MOV EAX,A
     ADD EAX,F
     MOV AL,B
     MOV CL,C
     MOV AL,Enum;
     CMP AL,Vendredi
    end;
   end;

Attention le type énuméré est équivalent à Byte, Word ou Dword suivant la valeur la plus grande qu'il
contient.

   Type
    TEnumByte=(Lundi,Mardi,Mercredi,Jeudi,Vendredi,Samedi,Dimanche);
    TEnumWord=(Zero,Mille=1000,DeuxMille=2000,TroisMille=3000);
    TEnumDWord=(A,B=100000,C=200000);
   Var
    EnumB:TEnumByte;
    EnumW:TEnumWord;
    EnumD:TEnumDWord;

                                                            - 10 -
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

   begin
    Asm
     MOV     AL,EnumB; // byte
     CMP     AL,Vendredi
     MOV     AX,EnumW; // word
     CMP     AX,DeuxMille
     MOV     EAX,EnumD; // DWord
     CMP     EAX,C
    end;
   end;

II-C - Types ensembles

Les ensembles sont représentés en mémoire par une suite de bits donc chaque bit donne
l'appartenance de l'élément à l'ensemble. Le nombre d'octets utilisés dépend du nombre maximum
d'éléments de l'ensemble. Ce nombre peut aller de 1 à 32 octets (de 1 à 256 éléments). Les petits
ensembles (moins de 32 valeurs ) peuvent être manipulés directement. En exemple un ensemble de 7
valeurs (stocké sur un type byte, car moins de 8 valeurs )

   Type
    TJour =(Lundi,Mardi,Mercredi,Jeudi,Vendredi,Samedi,Dimanche);
    TEnsCourt=Set Of TJour;
   Var
    LesJours1:TEnsCourt;
    LesJours2:TEnsCourt;
   begin
    Asm
     MOV AL,LesJours1
     OR AL,LesJours2
    end;
   End;

De même les ensembles de 9 à 16 valeurs sont utilisables comme un type Word, les ensembles de 17
à 32 valeurs sont utilisables comme un type DWord.

Pour 33 valeurs et plus, il n'y a plus de correspondance directe. Leur manipulation doit être effectuée
en plusieurs étapes avec un transtypage :

   Type
    TListe = 0..63;
    TEnsLong =Set Of TListe;// Ensemble de 64 valeurs
   Var
    EnsLong1:TEnsLong;
    EnsLong2:TEnsLong;
   begin
    Asm
     MOV EAX,DWord ptr EnsLong1
     OR EAX,DWord ptr EnsLong2
     MOV EAX,DWord ptr EnsLong1[4]
     OR EAX,DWord ptr EnsLong2[4]
    end;
   end;

II-D - Types réels

                                                            - 11 -
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

Les types réels sont Single, Double, Extended, Comp et Currency. Ces types sont utilisés de façon
native par l'unité de calcul flottant intégrée aux processeurs. Il est fortement conseillé de manipuler
ces données avec le FPU. L'échange de ces types avec les registres FPU est direct :

   Var
    S1,S2:Single;
    D1,D2:Double;
    E1,E2:Extended;
    C1,C2:Currency;
   begin
    Asm
     FLD S1 // Single
     FADD S2
     WAIT
     FST S1

     FLD D1 // Double
     FADD D2
     WAIT
     FST D1

     FLD E1 // Extended
     FLD E2
     FADD
     WAIT
     FSTP E1

     FLD C1 // Currency
     FLD C2
     FADD C2
     WAIT
     FST C1
    end;
   end;

Note : le type Real48 n'est pas décrit ici. C'est un type réel non natif présent simplement pour la
compatibilité avec les programmes antérieurs à Delphi.

II-E - Type Int64

Le type Int64 ne peut être traité directement par les registres communs ou les opérations mémoires
classiques. Dans la plupart des cas le transtypage en deux Dword est utilisé.

   Var
    A:Int64;
    B:Int64;
   begin
    Asm // A:=A+B
     MOV EAX,DWord ptr B
     ADD DWord ptr A,EAX
     MOV EAX,DWord ptr B[4]
     ADC DWord ptr A[4],EAX
    end;
   end;

Certaines instructions FPU utilisent des valeurs entières sur 64 bits, dans ce cas le type Int64 est
utilisé directement :

                                                            - 12 -
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

   Var
    A:Int64;
    D:Double;
   begin
    Asm // D:=D+A
     FLD D // Valeur flottante
     FILD A // Valeur entière
     FADDP
     WAIT
     FST D
    end;
   end;

Le type Int64 est aussi compatible avec les instructions MMX ou SSE portant sur des entiers 64 bits :

   Var
    A:Int64;
    B:Int64;
   begin
    Asm // A:=A+B
     MOVQ MM0,A
     PADDQ MM0,B
     MOVQ A,MM0
    end;
   end;

II-F - Tableaux statiques

Les tableaux sont considérés comme un type dont la longueur est la taille totale du tableau. Pour
accéder à un seul élément du tableau, le transtypage est systématique. Le premier élément du
tableau est toujours avec un décalage nul par rapport à l'adresse de base du tableau. Par exemple les
deux tableaux suivants sont stockés de la même manière en mémoire :

   Var
    Tab1 :Array[0..3]Of Integer ;
    Tab2 :Array[2..5]Of Integer ;

Attention, l'opérateur [ ] ne signifie pas indice du tableau mais Offset supplémentaire. Pour accéder au
deuxième élément de Tab1 il faut écrire Tab1[4]. Dans le cas d'un parcourt du tableau à l'aide d'un
index, il faut augmenter l'index à chaque boucle de la taille de l'élément de base du tableau. Il est
conseillé d'utiliser l'opérateur Type.

   Var
    Tab1 :Array[1..20]Of Integer;
    Somme:Integer;
   begin
    Asm
     XOR ECX,ECX
     XOR EAX,EAX
   @L1:ADD EAX,DWord Ptr Tab1[ECX]
     ADD ECX,Type Integer
     CMP ECX,Type Tab1
     JB @L1
     MOV Somme,EAX
    end;
   end;

                                                            - 13 -
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

Il faut penser aussi aux multiplicateurs lors de l'adressage. Ceci peut simplifier les opérations sur des
tableaux dont le type de base est de taille 2,4 ou 8 octets.

Exemple d'optimisation de la boucle, en utilisant un coefficient multiplicateur d'index :

   Var
    Tab1 :Array[1..20]Of Integer;
    Somme:Integer;
   begin
    Asm
     // Nombre d'élément du tableau
     MOV ECX,(Type Tab1)/(Type Integer)
     XOR EAX,EAX
     // -4 Car ECX va Balayer de 20 à 1 au lieu de
     // 19 à 0
   @L1:ADD EAX,DWord Ptr Tab1[ECX*4-Type Integer]
     LOOP @L1
     MOV Somme,EAX
    end;
   end;

Les types tableaux peuvent aussi servir d'opérande dans les opérations MMX/SSE dans le cas
d'opérations groupées. Par exemple dans le cas d'instructions MMX sur des groupes les types
suivants peuvent être utilisés directement :

   Var
    TabB1,TabB2 :Array[0..7]Of Byte;
    TabW1,TabW2 :Array[0..3]Of Word;
    TabD1,TabD2 :Array[0..1]Of Integer;
   begin
    Asm
     // addition des éléments de TabB2 à TabB1
     MOVQ MM0,TabB1
     PADDB MM0,TabB2
     MOVQ TabB1,MM0
     // addition des éléments de TabW2 à TabW1
     MOVQ MM0,TabW1
     PADDW MM0,TabW2
     MOVQ TabW1,MM0
     // addition des éléments de TabD2 à TabD1
     MOVQ MM0,TabD1
     PADDD MM0,TabD2
     MOVQ TabD1,MM0
    end;
   end;

II-G - Chaînes courtes

Les chaînes courtes sont un tableau d'octets dont l'indice 0 donne la taille de la chaîne. Leur utilisation
est identique à celle des tableaux, avec en plus la gestion de l'indice 0 comme longueur. Les
instructions de chaîne sont adaptées au traitement de ce type de donnée.

   Var
    Chaine:ShortString;
   begin
    Asm // Chaine:='AAAAAAAA'

                                                            - 14 -
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

     PUSH EDI
     // Obtention de l'adresse du premier caractère
     LEA EDI,CHAINE[1]
     MOV ECX,8
     MOV AL,'A'
     // Stockage des huit 'A'
     REP STOSB
     // Mise à jour de la longueur de la chaîne
     MOV Byte Ptr Chaine[0],8
     POP EDI
    end;
   end;

II-H - Enregistrements

L'assembleur intégré inclus aussi l'opérateur . identique à celui du pascal pour obtenir un membre
d'un enregistrement. La concordance de taille est alors effectuée sur le membre en question et non le
type global. Cela simplifie l'utilisation des enregistrements en assembleur l'offset de chaque membre
étant ajouté par le compilateur.

   Var
    Rect:TPoint;
   begin
    Asm
     MOV EAX,RECT.X
     MOV ECX,RECT.Y
    end;
   end;

Dans l'exemple ci-dessus, le compilateur va ajouter les offsets nécessaires pour atteindre les
membres X et Y ( +0 pour X et +4 pour Y dans ce cas )

Le transtypage d'une adresse en variable de type enregistrement est aussi possible. C'est très utile
dans le cas d'utilisation d'un index seul ou d'un tableau d'enregistrements.

   Var
    TabRect:Array[0..9]Of TPoint;
    SommeX:Integer;
    SommeY:Integer;
   begin
    Asm
     XOR ECX,ECX
     XOR EAX,EAX
     XOR EDX,EDX
   @L1:ADD EAX,TabRect[ECX].TPoint.X
     ADD EDX,TabRect[ECX].TPoint.Y
     ADD ECX,Type TPoint
     CMP ECX,Type TabRect
     JB @L1
     MOV SommeX,EAX
     MOV SommeY,EDX
    end;
   end;

                                                            - 15 -
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

III - Ecrire une fonction en assembleur

III-A - Généralités

L'écriture d'une fonction ou d'une procédure en assembleur est effectuée en supprimant le begin de
début de code et en le remplaçant par asm. Tout le code de la fonction doit être écrit en assembleur.

   Function Somme(A,B :Integer) :Integer;
   Asm
    ADD EAX,ADX
   End;

Le fait de supprimer le begin, supprime toutes les copies de paramètres passés par valeur dont la
taille est supérieure à 4 octets. Il faut considérer que le passage de tels paramètres est implicitement
fait avec la directive var. ( voir III-E pour plus de détail en fonction des types de paramètres )

Note : le mot-clef Assembler n'existe que par soucis de compatibilité et ne doit pas être utilisé. De
même le mot-clef Interrupt permettant l'écriture d'une procédure d'interruption n'existe plus depuis
Delphi2.

III-B - Code d'entrée et de sortie

Lors de l'écriture d'une fonction en assembleur, Delphi gère automatiquement le cadre de pile pour
l'accès aux paramètres et la gestion des variables locales. Ce cadre est optimisé en fonction des
besoins, seules les instructions indispensables sont ajoutées au code.

L'entrée de la procédure ( Asm ) est codée par :

   PUSH EBP // seulement si la pile contient des paramètres#
   MOV EBP,ESP // # ou s'il existe des variables locales
   SUB ESP,XXX // Seulement dans le cas de variables locales

La sortie de la procédure ( End ) est codée par :

   POP EBP // seulement si la pile contient des paramètres#
       // # ou s'il existe des variables locales
   RET YYY // Codé dans tous les cas

Dans le cas où il n'y a pas de paramètres dans la pile ou dans le cas de la convention Cdecl, une
simple instruction RET est générée.

Dans tous les cas le RET généré est un RET Near.

Du code supplémentaire est ajouté éventuellement est début et fin de fonction dans le cas d'utilisation
de variables dynamiques comme les types String et Array Of -type-, ainsi que dans les
constructeurs/destructeurs de classes.

                                                            - 16 -
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

III-C - Empilage des paramètres

L'empilage des paramètres est toujours effectué par mot entier (4 octets).

Si le paramètre à empiler est plus petit qu'un entier, il faut tout de même empiler 4 octets. Par
exemple pour empiler la variable B de type byte il faut faire comme suit :

   MOV AL,B // Chargement de B
   PUSH EAX // Empilage d'un entier dans tous les cas

Dans le cas de paramètres empilés par valeur dans la pile mais dont la taille est supérieure à quatre
octets, il faut commencer par empiler le poids fort. En effet, la pile étant construite en descendant, le
poids fort sera en adresse la plus haute et le poids faible en adresse la plus basse.

Exemple pour V de type Int64 :

   PUSH DWord ptr V[4]
   PUSH DWord ptr V

III-D - Conventions d'appel

Il me semble utile de rappeler la signification des conventions de passage des paramètres. C'est très
utile pour l'écriture de fonctions, mais surtout pour l'appel de fonctions, écrites en assembleur ou non.

III-D-1 - Généralités

Toutes les fonctions utilisent la pile processeur pour le passage des paramètres. Cette pile est aussi
utilisée pour la réservation de l'espace des variables locales à la fonction. L'ordre de passage dans la
pile dépend de la convention utilisée. A la fin de la fonction, les paramètres et les variables locales
sont supprimées de la pile et l'exécution du programme reprend à l'instruction suivant l'appel. La
méthode de suppression des paramètres dépend aussi de la convention utilisée.

L'accès aux paramètres est effectué par le registre EBP. ESP n'est pas utilisé car il est
automatiquement modifié par les instruction POP et PUSH, rendant inaccessibles les paramètres et
variables locales. EBP est donc un registre très particulier dans les langages évolués. Il doit être
sauvegardé dès le début de la fonction. Cette sauvegarde est automatiquement effectuée par Delphi à
l'entrée de la fonction. L'instruction Begin ou Asm de début d'une fonction ajoute les lignes PUSH EBP
et MOV ESP,EBP au code.

   Procedure UneFonction(Param1,Param2 :Integer) ;Pascal ;
   Var
    Entier1 :Integer;
    Real1 : Double;
   Asm
    // code
   End;

                                                            - 17 -
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

Au début du code utile de la fonction, l'aspect de la pile est le suivant :

       XXXXXX
   EBP+12 : Param1 : Paramètre de la fonction
   EBP+8 : Param2 : Paramètre de la fonction
   EBP+4 : [EIP] : Adresse de retour de la fonction
   EBP> : [EBP] : Sauvegarde de EBP
   EBP-4 : Entier1 : Variable locale de type integer
   EBP-12 : Real1 : Variable locale de type double.

Le paramètre 1 sera alors accessible avec [EBP+12] La variable locale Entier1 sera accessible avec
[EBP-4]

Important : il conseillé d'utiliser les identificateurs des paramètres au lieu de leur équivalent utilisant
EBP, ceci rend la modification de la taille des variables et de leur place automatiques. Par exemple
MOV EAX,Param1 est préférable à MOV EAX,[EBP+12]

Les paragraphes suivants décrivent toutes les conventions, en prenant pour exemple une fonction
simple :

   Function Diff(A,B :Integer) :Integer ;
   Begin
    Result :=A-B;
   End;

Le codage correspondant en assembleur sera donné pour chaque cas

III-D-2 - Convention de type Pascal.

C'est la convention par défaut dans Delphi hors optimisations. Les paramètres sont empilés dans
l'ordre de la fonction. La suppression des paramètres est à la charge de la fonction.

   Function Diff(A,B :Integer) :Integer ;Pascal ;
   Asm
    Mov EAX,A // A est ici [EBP+12]
    Sub EAX,B // B est ici [EBP+ 8]
   End;

Avant l'appel :

   ESP > : XXXXXX

Après l'appel (juste avant l'exécution de la ligne Result :=)

       : XXXXXX
   EBP+12 : A    : Paramètre de la fonction
   EBP+8 : B    : Paramètre de la fonction
   EBP+4 : [EIP] : Adresse de retour de la fonction
   ESP/EBP>: [EBP] : Sauvegarde de EBP

                                                            - 18 -
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

Après l'exécution du End de la fonction

   ESP > : XXXXXX

Le code généré par le Begin (ou Asm ) est le suivant :

   PUSH EBP
   MOV EBP,ESP

Le code généré par le End est le suivant :

   POP EBP
   RET 8

On remarque que le RET 8 permet de supprimer les paramètres de la pile à la fin de la fonction.

III-D-3 - Convention de type StdCall

C'est la convention par défaut des fonctions Windows et des dlls. Les paramètres sont empilés dans
l'ordre inverse de la fonction. La suppression des paramètres est à la charge de la fonction.

   Function Diff(A,B :Integer):Integer;StdCall;
   Asm
    Mov EAX,A // A est ici [EBP+ 8]
    Sub EAX,B // B est ici [EBP+12]
   End;

Avant l'appel :

   ESP > : XXXXXX

Après l'appel (juste avant l'exécution de la ligne Result :=)

   ESP > : XXXXXX :
   EBP+12 : B    : Paramètre de la fonction
   EBP+8 : A    : Paramètre de la fonction
   EBP+4 : [EIP] : Adresse de retour de la fonction
   ESP/EBP>: [EBP] : Sauvegarde de EBP

Après l'exécution du End de la fonction

   ESP > : XXXXXX

Le code généré par le Begin (ou Asm ) est le suivant :

   PUSH EBP
   MOV EBP,ESP

                                                            - 19 -
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

Le code généré par le End est le suivant :

   POP EBP
   RET 8

On remarque que le RET 8 permet de supprimer les paramètres de la pile à la fin de la fonction.

III-D-4 - Convention de type C.

C'est la convention par défaut des fonctions écrites en C. Les paramètres sont empilés dans l'ordre
inverse de la fonction. La suppression des paramètres est à la charge du programme appelant.

   Function Diff(A,B :Integer):Integer;Cdecl;
   Asm
    Mov EAX,A // A est ici [EBP+ 8]
    Sub EAX,B // B est ici [EBP+12]
   End;

Avant l'appel :

   ESP > : XXXXXX

Après l'appel (juste avant l'exécution de la ligne Result :=)

       : XXXXXX :
   EBP+12 : B    : Paramètre de la fonction
   EBP+8 : A    : Paramètre de la fonction
   EBP+4 : [EIP] : Adresse de retour de la fonction
   ESP/EBP>: [EBP] : Sauvegarde de EBP

Après l'exécution du End de la fonction

       : XXXXXX :
       :B    : Paramètre non dépilé
   ESP > : A    : Paramètre non dépilé

Le code généré par le Begin (ou Asm ) est le suivant :

   PUSH EBP
   MOV EBP,ESP

Le code généré par le End est le suivant :

   POP EBP
   RET

Attention, dans ce cas c'est un simple RET qui est effectué, il faut penser à libérer les paramètres
soi-même (ici avec un ADD ESP,8) après l'appel de la fonction.

                                                            - 20 -
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

III-D-5 - Convention de type Register.

C'est la convention par défaut dans Delphi avec les optimisations. Les paramètres sont stockés dans
la mesure du possible dans les registres. Les registres utilisés sont dans l'ordre EAX EDX et ECX. Si
la fonction dispose de plus de trois paramètres, ils sont placés dans la pile comme pour la convention
de type Pascal. De même les types réels ou int64 sont placés dans la pile. Les chaînes, les types
structurés et les ensembles longs peuvent être en paramètre registre car ce sont des pointeurs vers
un bloc du type correspondant.

Si il n'y a pas de paramètres dans la pile et pas de variables locales, EBP n'est pas sauvegardé.

   Function Diff(A,B :Integer):Integer;Register;
   Asm
    SUB EAX,EDX // A est ici EAX et B est EDX
   End;

Avant l'appel :

   ESP > : XXXXXX :

Après l'appel (juste avant l'exécution de la ligne Result :=)

       : XXXXXX :
   ESP > :[EIP] : Adresse de retour

Après l'exécution du End de la fonction

   ESP > : XXXXXX :

Dans ce cas il n'y a pas de code généré par Begin, car les deux paramètres sont placés dans EAX et
EDX

Le code généré par le End est le suivant :

   RET

III-E - Paramètres et résultat de la fonction

Quelle que soit la convention d'appel, le résultat d'une fonction est retourné de la même manière. Mais
suivant le type de paramètre ou de résultat le codage est différent. Dans la suite nous allons décrire le
codage d'une fonction " Somme " d'un certain nombre de types de paramètres, en retournant le
résultat correspondant.

De plus un appel de cette fonction en assembleur sera également effectué afin de bien montrer dans
chaque cas le code préparatoire à l'appel.

                                                            - 21 -
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

La convention utilisée ici est Register, Dans le cas des autres conventions la transformation des
paramètres est la même. La différence est que les valeurs ordinales, ou assimilables à des valeurs
ordinales, sont passés dans la pile.

Il est conseillé de mettre en tête de fonction comment sont passés les paramètres et comment le
résultat est retourné. Cela facilite la maintenance des fonctions en assembleur.

Bien sûr il est possible de passer les paramètres sans respecter les normes. Mais ceci implique que la
fonction et l'appel de la fonction soient écrits en assembleur. Delphi n'interprète jamais le passage des
paramètres en fonction du code. Je conseille donc vivement de conserver le passage normal des
paramètres.

III-E-1 - Types ordinaux

Tous les types ordinaux, ainsi que les ensembles courts et les pointeurs. Le résultat est retourné dans
AL pour tous les types occupant un octet. Dans AX pour les types occupant deux octets et dans EAX
pour les types occupant 4 octets.

Fonction :

   Function SommeInteger(A,B:Integer):Integer;
   // A est dans EAX
   // B est dans EDX
   // Resultat dans EAX
   Asm
     ADD EAX,EDX
   end;

Appel :

   Procedure AppelSomme;
   Var v:Integer;
   Begin
    Asm // V:=V+4
     MOV EAX,V
     MOV EDX,4
     CALL SommeInteger
     MOV V,EAX
    End;
   End;

III-E-2 - Types ensembles

Pour les ensembles courts (de 1 à 32 valeurs) le résultat est retourné dans AL, AX ou EAX suivant la
taille de l'ensemble.

Pour les ensembles longs (33 à 256 valeurs), c'est le programme appelant qui doit réserver une zone
mémoire pour le résultat de la fonction puis passer l'adresse de cette zone en tant que paramètre
pointeur.

                                                            - 22 -
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

Fonction :

   Type
     TListe = 0..63;
     // Définition d'un type ensemble plus grand qu'un entier (sur 64 bits ici)
     TEnsLong=Set Of TListe;
   Function SommeSetLong(Ens1,Ens2:TEnsLong):TEnsLong;Register;
   // EAX : Adresse de Ens1
   // EDX : Adresse de Ens2
   // Resultat dans [ECX]
   Asm
     PUSH EBX
     MOV EBX,[EAX]
     OR EBX,[EDX]
     MOV [ECX],EBX
     MOV EBX,[EAX+4]
     OR EBX,[EDX+4]
     MOV [ECX+4],EBX
     POP EBX
   End;

Important : l'emplacement du résultat de la fonction est réservé par l'appelant, celui-ci donnant un
pointeur vers la zone réservée en tant que paramètre supplémentaire. ECX va donc contenir ici
l'adresse de stockage du résultat.

Appel :

   Procedure AppelSommeSetLongAsm;
   // Les constantes Typées pour les paramètres constants
   // sont le plus simple
   Const
       CE1:TEnsLong=[0,2,4,6];
       CE2:TEnsLong=[4,20,63];
   Var E2:TEnsLong;
   Asm
     LEA EAX,CE1 // Premier ensemble
     LEA EDX,CE2 // Deuxième ensemble
     LEA ECX,E2 // Adresse ou stocker le résultat
     CALL SommeSetLong
   End;

Dans le code ci-dessus, remarquez l'utilisation des constantes typées pour les enregistrements
constants. De même, le fait de déclarer une variable locale E2 permet de réserver la place pour le
résultat de la fonction.

III-E-3 - Types réels

Les paramètres réels sont passés par valeur dans la pile, bien que leur taille soit supérieure à 4
octets.

Les résultats réels sont retournés dans le registre du haut de la pile du coprocesseur. Attention, c'est
au code appelant de dépiler le résultat :

Fonction :

                                                            - 23 -
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

   Function SommeDouble(A,B:Double):Double;
   // [EBP+$10] contient A ( sur 8 octets )
   // [EBP+$08] contient B ( sur 8 octets )
   // ST0 contiendra le résultat
   Asm
     FLD A
     FADD B
     WAIT
   End;

Appel :

   Procedure AppelSommeDouble;
   Var A,R:Double;
   Const B:Double=2.3;
   Begin
    A:=1;
    Asm
     // R :=A+B
     PUSH DWORD PTR A[4] // Un double est sur 8 octets
     PUSH DWORD PTR A
     PUSH DWORD PTR B[4] // Un double est sur 8 octets
     PUSH DWORD PTR B
     CALL SommeDouble
     FSTP R         // Dépilage + Stockage du résultat

     // R :=A+2.3
     PUSH DWORD PTR A[4] // Un double est sur 8 octets
     PUSH DWORD PTR A
     PUSH $40026666    // Ici la constante réelle ( 2.3 ) est passée
     PUSH $66666666    // directement, mais il faut connaitre la valeur...
     CALL SommeDouble
     FSTP R       // Dépilage + Stockage du résultat
    End;
   End;

III-E-4 - Type Int64

Les paramètres de type Int64 sont passés par valeur dans la pile dans tous les cas.

Le résultat de type Int64 est retourné dans EDX:EAX. EDX contient les 32 bits de poids fort et EAX
contient les 32 bits de poids faible.

Fonction :

   Function SommeInt64(A,B:Int64):Int64;Register;
   // [EBP+$10] Valeur de A
   // [EBP+$08] Valeur de B
   // Résultat : contenu dans EDX:EAX
   Asm
     MOV EAX,DWORD PTR A
     ADD EAX,DWORD PTR B
     MOV EDX,DWORD PTR A[4]
     ADC EDX,DWORD PTR B[4]
   End;

                                                            - 24 -
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

Appel :

   Procedure AppelSommeInt64;
   Var A:Int64;
      R:Int64;
   Asm
    // Exemple ajout de $1234567890 à A et stockage dans R
    PUSH DWORD PTR A[4] // Il faut commencer par le point fort
    PUSH DWORD PTR A
    PUSH $00000012
    PUSH $34567890
    CALL SommeInt64
    MOV DWORD PTR R,EAX
    MOV DWORD PTR R[4],EDX
   End;

III-E-5 - Tableaux statiques

Les tableaux sont passés par adresse même dans le cas d'écriture de fonction par valeur. Il convient
à la fonction appelante de faire une copie du tableau si la fonction est susceptible d'en modifier le
contenu. L'emplacement du résultat doit être réservé par le programme appelant et il doit passer
l'adresse de cette zone en tant que paramètre pointeur supplémentaire.

Fonction :

   Type TTabInteger=Array[0..3]Of Integer;
   Function SommeTableau(T1,T2:TTabInteger):TTabInteger;
   // EAX adresse de T1
   // EDX adresse de T2
   // ECX adresse de stockage du résultat
   Asm
     PUSH EDI
     PUSH EBX
     MOV EDI,ECX
     XOR ECX,ECX
   @@L1:
     MOV EBX,DWord Ptr T1[ECX*4]
     ADD EBX,DWord Ptr T2[ECX*4]
     MOV DWord Ptr [EDI+ECX*4],EBX
     INC ECX
     CMP ECX,4
     JB @@L1
     POP EBX
     POP EDI
   End;

Appel :

   Procedure AppelSommeTableau;
   Const
    Tab1:TTabInteger=(1,2,3,4);
    Tab2:TTabInteger=(4,3,2,1);
   Var
    Res :TTabInteger;
   Begin
    Asm
     LEA EAX,Tab1

                                                            - 25 -
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

     LEA EDX,Tab2
     LEA ECX,Res
     Call SommeTableau
    End;
   End;

III-E-6 - Chaînes courtes

Les chaînes courtes sont traitées comme les tableaux, c'est-à-dire que les paramètres sont toujours
passés par adresse, et que l'emplacement du résultat doit être réservé par le programme appelant.

Fonction : ( concaténation ici )

   Function SommeChaine(A,B:ShortString):ShortString;
   // EAX contient l'adresse de A
   // EDX contient l'adresse de B
   // ECX contient l'adresse de stockage du résultat
   // Rem : aucun contrôle de longueur n'est effectué !!!
   Asm
     PUSH EDI
     PUSH ESI
     MOV EDI,ECX             // DI pointe sur le résultat
     MOV CL,Byte ptr [EAX] // longeur de A
     ADD CL,Byte ptr [EDX] // longeur de B
     MOV Byte ptr[EDI],CL // longeur totale
     INC EDI            // Prise en compte longueur
     MOV ESI,EAX             // ESI pointe sur A
     INC ESI
     XOR ECX,ECX              // ECX = longueur de A
     MOV CL,Byte Ptr [EAX]
     REP MOVSB               // Copie de A
     MOV ESI,EDX             // ESI pointe sur B
     INC ESI
     MOV CL,Byte Ptr [EDX] // ECX = longueur de B
     REP MOVSB               // Copie de B
     POP EDI
     POP ESI
   End;

Appel :

   Procedure AppelSommeChaine;
   Const
    ST1:ShortString='1234';
    ST2:ShortString='5678';
   Var
    Res:ShortString;
   Begin
    Asm
     LEA EAX,St1
     LEA EDX,St2
     LEA ECX,Res
     Call SommeChaine
    End;
    ShowMessage(Res);
   End;

                                                            - 26 -
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

III-E-7 - Enregistrements

Les enregistrements sont traités comme les tableaux, c'est-à-dire que les paramètres sont toujours
passés par adresse, et que l'emplacement du résultat doit être réservé par le programme appelant.

Fonction :

   Type TRec=Record
        rEntier1 : Integer;
        rEntier2 : Integer;
       End;
   Function SommeRecord1(R1,R2:TRec):TRec;Register;
   // EAX va contenir l'adresse de R1
   // EDX va contenir l'adresse de R2
   // ECX contient l'adresse de stockage du résultat
   Const RecCST:TRec=(rEntier1:3;rEntier2:4);
   Asm
     PUSH EBX
     MOV EBX,TRec([EAX]).rEntier1
     ADD EBX,TRec([EDX]).rEntier1
     ADD EBX,RecCST.rEntier1
     MOV TRec([ECX]).rEntier1,EBX
     MOV EBX,TRec([EAX]).rEntier2
     ADD EBX,TRec([EDX]).rEntier2
     ADD EBX,RecCST.rEntier2
     MOV TRec([ECX]).rEntier2,EBX
     POP EBX
   End;

Appel :

   Procedure AppelSommeRecord;
   Var A:TRec;
     R:TRec;
   Const B:TRec=(rEntier1:1230;rEntier2:5670);
   Asm
    LEA EAX,A
    LEA EDX,B
    LEA ECX,R
    CALL SommeRecord1
   End;

                                                            - 27 -
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Utilisation de l'assembleur en ligne avec Delphi par Nono40

IV - Accéder aux objets

C'est la partie la plus délicate et dont l'application n'est pas possible dans tous les cas. Le programme
doit réaliser le raisonnement du compilateur pour l'accès aux objets. La portée des membres d'un
objet fait qu'il n'est pas toujours possible d'y accéder.

Nous supposerons dans la suite qu'il n'y a pas de problème de portée dans l'utilisation des membres
de l'objet.

Dans ce chapitre, l'objet suivant sert d'exemple :

   Type
    TMonObjet=Class
    Private
     FValeur : Integer;
     Fentier : Integer;
    Protected
     Constructor Create;
    Public
     Function GetValeur :Integer;
     Function GetValeur2:Integer;Virtual;
     Function GetValeur3:Integer;Dynamic;
     Procedure SetValeur(V:Integer);
     Class Function GetInfo:Integer;
    Published
     Property Valeur:Integer read GetValeur Write SetValeur;
     Property Entier:Integer read FEntier Write FEntier;
    End;

IV-A - Appel d'une méthode statique

L'appel d'une méthode statique d'un objet est identique à l'appel d'une fonction. Par contre toutes les
méthodes d'objet ont un premier paramètre implicite qui est l'adresse de l'instance. Par défaut la
convention register s'applique aux méthodes d'objet.

La fonction GetValeur est ici une méthode statique et retourne la valeur de la propriété Valeur :

   MOV EAX,MonObjet      // Paramètre implicite
   CALL TMonObjet.GetValeur

IV-B - Appel d'une méthode virtuelle

L'appel d'une méthode virtuelle passe par une étape supplémentaire pour obtenir la " table des
méthode virtuelles ".

La fonction GetValeur2 est ici une méthode virtuelle et retourne la valeur de la propriété Valeur :

   MOV EAX,MonObjet
   MOV EDX,[EAX] // Récupération de la VMT
   CALL DWORD PTR [EDX + VMTOFFSET TMonObjet.GetValeur2]

                                                            - 28 -
Copyright © 2003Bruno Guérangé . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son
 contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3
                                ans de prison et jusqu'à 300 000 E de domages et intérets.
                                    http://nono40.developpez.com/tutoriel/delphi/asm/
Vous pouvez aussi lire