Annexe 2. Syntaxe MASM FAQ

Contenu:

1. ADDR et OFFSET

2. Les Crochets

3. L'opérateur PTR

4. Structures

5. Constructions de haut niveau en MASM

6. Opérateurs Relationnels

7. Préservation des registres.

 


ADDR et OFFSET

L'opérateur OFFSET renvoie l'adresse d'une variable. Il est utilisé pour spécifier l'emplacement plutôt que le contenu de la variable.

.data
MyVar     db        77h       ;  Une variable d'un octet appelée MyVar initialisée à 77h.

.code
mov eax, MyVar               ;  Copie 77h dans eax.
mov ebx, offset MyVar      ;  Copie l'adresse mémoire où 77h est stocké dans ebx.

L'opérateur OFFSET peut également transmettre l'adresse d'une variable à une fonction dans une instruction invoke. Cependant, cela ne fonctionnera que pour les variables globales déclarées dans les sections .data ou .data?. Il échouera avec les variables locales que vous déclarez à l'entrée de votre procédure en utilisant l'instruction LOCAL. Celles-ci n'ont pas d'offset car elles sont créées sur la pile à l'exécution.

L'opérateur ADDR résout ce problème. Il est utilisé exclusivement avec l'instruction invoke pour transmettre l'adresse d'une variable à une fonction. Pour les variables globales, il se traduit par une simple instruction push, tout comme si l'opérateur OFFSET avait été utilisé :

push GlobalVar


En revanche, pour les variables locales,l'opérateur ADDR se traduit par:


lea eax, LocalVar          ;  Cela charge l'adresse effective de la variable locale LocalVar dans le registre eax en utilisant l'instruction LEA (Load Effective Address). Ensuite, la valeur de eax est poussée sur la pile à l'aide de l'instruction PUSH.

(Il est important de se rappeler que lors de l'utilisation de "addr" avec des variables locales, le registre eax est utilisé plutôt que de le laisser libre pour d'autres utilisations à l'intérieur de la procédure.)

"lea eax, LocalVar" est généralement équivalent à "mov eax, offset LocalVar", mais il est 1 cycle d'horloge plus lent. Ainsi, "mov... offset" est préféré dans les cas autres que les variables locales.

Les Crochets

Les crochets indiquent généralement la valeur (contenu) d'une variable plutôt que son adresse. Cependant, la syntaxe de MASM diffère légèrement de celle des autres assembleurs à cet égard. Dans MASM, toutes ces notations génèrent la même instruction :

mov eax,1
mov eax,[1]
mov eax, DWORD PTR 1
mov eax, DWORD PTR [1]

MyVariable & "MyVariable" et "[MyVariable]" signifient tous deux la valeur de MyVariable.

De nombreux programmeurs utilisent régulièrement des crochets avec des variables pour indiquer le contenu, car cela rend le code source un peu plus lisible et facilite le portage du code vers d'autres assembleurs.

Comme expliqué précédemment, "offset MyVariable" et "addr MyVariable" Les deux notations sont équivalentes et renvoient l'adresse de la variable MyVariable.

Lorsqu'ils sont utilisés avec des registres, les crochets font la différence tendent à indiquer une adresse mémoire :

mov ebx,eax        ;  Copie la valeur de eax dans ebx.

mov ebx,[eax]      ;  copie la valeur située à l'adresse mémoire contenue dans eax dans ebx.

mov [ebx],eax      ;  copie la valeur contenue dans eax dans la mémoire à l'adresse indiquée par ebx.

L'opérateur PTR

L'opérateur PTR indique à l'assembleur la taille des données dans des situations où la taille ne peut pas être déduite automatiquement. Par exemple :



MOV [eax], 0

Ceci n'est pas une instruction valide car l'opérande mémoire [eax] n'indique pas la taille. Si vous souhaitez copier la valeur zéro dans un octet de la mémoire à [eax], vous devez utiliser la notation BYTE PTR pour que l'assembleur sache combien d'espace mémoire utiliser. Pour éclaircir la confusion, voici quelques exemples basiques :



mov bl, [eax]



Ici, le compilateur sait que 'bl' est un registre de 8 bits, et vous demandez au processeur de remplir 'bl' avec la valeur ([...]) trouvée à l'adresse contenue dans eax. Comme le compilateur sait que la limite est de 8 bits, indiquée par le registre 'bl' dans la destination, il n'y a aucun problème.



mov bl, byte ptr [eax]



EST EXACTEMENT LA MÊME CHOSE. C'est inutile mais peut-être plus lisible. Alors pourquoi voudriez-vous utiliser byte ptr ou word ptr avant une instruction "à l'adresse de" ([...]) ? La réponse est visible ici :



mov [ebx], 3



Voici un dilemme pour le compilateur. Il dit "déplacez la valeur 3 à l'emplacement mémoire à l'adresse de ebx". Le compilateur aime penser en termes de bits et a besoin de savoir si 3 est un BYTE, un WORD ou un DWORD, etc. Il ne sait pas combien de bits de mémoire utiliser pour stocker la valeur donnée !! Pour clarifier cela, BYTE PTR est utilisé pour stocker 3 dans un octet de mémoire à l'adresse spécifiée. De même, WORD PTR utiliserait 2 octets de mémoire pour stocker 3 en mémoire :



mov byte ptr [ebx], 3



Si vous pensez que vous devez utiliser l'opérateur PTR, demandez-vous si le processeur peut déterminer lui-même combien de bits déplacer, grâce à une indication DIRECTE (comme dans l'exemple 'bl' ci-dessus).

Structures

Déclarer une structure type:

Exemple STRUCT :
   field1
   field2
   field3
Exemple ENDS

Pour définir une instance de variable de structure non initialisée :

.data?
MyStruct1 Example <>

Ou une instance initialisée de la variable :

.data
MyStruct2 Example <5,67,89>

Pour accéder directement aux champs :

mov eax,Mystruct2.field2      ;  copie 67 dans eax

Pour accéder aux champs de manière indirecte :

mov ebx,offset MyStruct2       ;ebx contient le pointer de la structure MyStruct2

Syntax1:

mov eax,[ebx].Example.field2

Syntax2:

mov eax,[ebx.Example.field2]

Syntax3:

mov eax,[Example ptr eax].field2

Syntax4 - utilisation de la directive ASSUME:

assume eax:ptr Example
mov [eax].field2
assume eax:nothing          ;  N'oubliez jamais de désassumer lorsque vous avez terminé.

Consultez le chapitre sur Data Structures pour un exemple d'accès aux champs dans des structures imbriquées.

Constructions de haut niveau en MASM

MASM permet plusieurs constructions de haut niveau de type C pour rendre le code source plus lisible. L'assembleur les traduit en instructions asm régulières avant de les assembler en code objet. La structure .if - .elseif - .else - .endif a été utilisée de manière extensive dans nos procédures de dialogue jusqu'à présent.

1

Opérateurs Relationnels

Vous pouvez exprimer les conditions des directives .IF, .REPEAT et .WHILE en utilisant des opérateurs relationnels de la même manière qu'en C. MASM traduit cela en instructions de comparaison, de test et de saut conditionnel. Ils incluent :

1

Une condition sans opérateurs teste la non-nullité de la même manière qu'en C :
.WHILE (x) est identique à .WHILE (x != 0) - tant que x est non nul
.WHILE (!x) est identique à .WHILE (x == 0) - tant que x est nul

Vous pouvez également utiliser les noms des indicateurs (ZERO?, CARRY?, OVERFLOW?, SIGN?, et PARITY?) comme opérandes, par exemple dans .WHILE (CARRY?), la valeur de l'indicateur de retenue détermine le résultat de la condition.

Préservation des registres.

Une fonction Win32 que vous appelez préservera toujours les registres de segment ainsi que les registres EBX, EDI, ESI et EBP.
En revanche, les registres ECX et EDX sont considérés comme des registres temporaires et sont toujours indéfinis après le retour d'une fonction Win32.
EAX n'est jamais préservé non plus, et comme nous l'avons vu précédemment, il contient la plupart du temps une valeur de retour.
Sinon, il est vide. Par conséquent, si vous avez des valeurs importantes dans EAX, ECX ou EDX, vous devez les préserver (les sauvegarder sur la pile) avant d'appeler une fonction API.

De même, si vous utilisez EBX, EDI, ESI ou EBP dans votre fonction de rappel, n'oubliez jamais de les restaurer avant de renvoyer le contrôle à Windows. Cela ne signifie pas que vous ne pouvez pas utiliser ces quatre registres, vous le pouvez. Assurez-vous simplement de les restaurer avant de rendre la main à Windows.

 


Copyright (C)- xtx Team (2021)

XHTML valide 1.1 CSS Valide !