Tutoriel 7 - Instructions asm et corps Programme FASM

Pour discuter de l'assembleur
Anonyme2
Messages : 3518
Inscription : jeu. 22/janv./2004 14:31
Localisation : Sourans

Tutoriel 7 - Instructions asm et corps Programme FASM

Message par Anonyme2 »

Code : Tout sélectionner

Tutorial 7 - les instructions asm les plus courantes et le corps d'un programme FASM pour les librairies.
--------------------------------------------------------------------------------------------------------

Par Denis



1) les instructions asm les plus courantes : 
------------------------------------------
  A) Instruction MOV
  ------------------
    (page 498 du manuel d'Intel: Jeu d'instructions volume 2A (A-M)) 

   C'est certainement l'instruction la plus utilisée ou une des plus utilisée.
   
   L'instruction MOV permet de déplacer, d'affecter des valeurs à un registre, une mémoire.
   MOV est composé d'une opérande source et d'une opérande de destination.
   
   L'opérande source est copiée dans l'opérante destination.
   La valeur copiée dépend du type de la source.
   Le format est le suivant
   
   MOV  Destination, source
   
  L'opérande destination de cette instruction peut être :
    - Un registre (8, 16 ou 32 bits)
    - Une mémoire (8,16 ou 32 bits)
    - Un registre de segment (16 bits)

  L'opérande source de cette instruction peut être :
    - Un registre (8, 16 ou 32 bits)
    - Une mémoire (8,16 ou 32 bits)
    - Un registre de segment (16 bits)
    - Une valeur immédiate (un nombre)

    Toutes les possibilités sont données dans la doc Intel.
    
   
   Voici quelques exemples
   -----------------------

   MOV  EAX, 10   --> charge le registre EAX avec la valeur décimale 10, format 32 bits
                      puisque EAX est un registre 32 bits
   
   MOV  10, EAX   --> Cette instruction est illégale car il n'est pas possible d'attribuer 
                      la valeur d'un registre à une valeur, ça n'a pas de sens.

   MOV  AX, 7     --> charge le registre AX avec la valeur décimale 7, format 16 bits
                      puisque AX est un registre 16 bits
                      
   MOV  BL, 3     --> charge le registre BL avec la valeur décimale 3, format 8 bits
                      puisque BL est un registre 8 bits

   MOV  EDX, EAX  --> charge le registre EDX avec la valeur contenue dans le registre EAX
                      format 32 bits puisque EDX et EAX sont des registres 32 bits.
                      
   MOV  EDX, AX   --> Cette instruction est illégale car la destination et la source n'ont
                      pas la même taille (EDX = 32 bits et AX = 16 bits)

   MOV  DX, SI    --> charge le registre DX avec la valeur contenue dans le registre SI
                      format 16 bits puisque DX et SI sont des registres 16 bits.
                      
   MOV  dword [10010], EAX   --> Copie le contenu du registre EAX à l'adresse 10010.
                       La valeur de EAX est une valeur 32 bits
                       On peut très bien écrire cette instruction comme ceci
                       MOV  [10010], EAX   car la source (EAX) est une valeur 32 bits.
                       FASM va interpréter cette 2ème écriture sans problème.
                       Ce sont les crochets qui indiquent que c'est une adresse.
                       Par contre, MOV  byte [10010], EAX  est une instruction illégale.

   MOV  byte [10010], DL   --> Copie le contenu du registre DL à l'adresse 10010.
                       La valeur de DL est une valeur 8 bits
                       On peut très bien écrire cette instruction comme ceci
                       MOV  [10010], DL   car la source (DL) est une valeur 8 bits.
                       FASM va interpréter cette 2ème écriture sans problème.
                       Ce sont les crochets qui indiquent que c'est une adresse.

   MOV  dword [10010], [2048]   --> Cette instruction est illégale car la destination et
                                    la source sont 2 emplacements mémoire de 32 bits
                                    Il n'est pas possible de faire cette opération directement.
                                    Il faut passer par un registre.
                                    Voici comment 
                                    PUSH EAX                 ; on sauvegarde EAX (si besoin)
                                    MOV  EAX, [2048]         ; on charge EAX
                                    MOV  dword [10010], EAX  ; on copie EAX à l'adresse 10010
                                    POP  EAX                 ; on récupère la valeur initiale de EAX (si besoin)
                                    
                                    voici une autre variante avec les instructions PUSH et POP
                                    PUSH dword [2048]        ; on met sur la pile le contenu de l'adresse
                                    POP  dword [10010]       ; on copie le dernier élément empilé à l'adresse 10010
                                    ici pas besoin de sauvegarder le registre EAX
                                    
   L'instruction MOV ne peut pas être utilisée pour charger le registre de segment CS.
   La doc Intel indique aussi pour les registres de segments : Si l'opérande de destination
   est un registre de segment (DS, ES, FS, GS ou SS), l'opérande source doit être un sélecteur
   de segment valide.
   
    A noter que L'instruction MOV telle que décrite ci-dessus ne modifie aucun drapeau (registre EFLAGS).
    ---------------------------------------------------------------------------------------------------- 
    
    
    L'instruction MOV permet aussi de charger un registre de contrôle dans un registre général 32 bits et réciproquement.
    L'instruction MOV permet aussi de charger un registre de debbugage dans un registre général 32 bits et réciproquement.

    Cette utilisation se fait dans un cadre particulier et modifie certains drapeaux.    

  
  B) Instruction MOVSX
  --------------------
    (page 559 du manuel d'Intel: Jeu d'instructions volume 2A (A-M)) 


   L'instruction MOVSX copie l'opérande source (mémoire ou registre) dans l'opérante destination (registre).
   La valeur source est soit une opérande 8 bits soit 16 bits.
   MOVSX va charger l'opérante 8 bits dans un registre 16 ou 32 bits en conservant le signe ou
   va charger l'opérande 16 bits dans un registre 32 bits en conservant le signe.

   Les formats sont les suivants (r pour registre et m pour mémoire, le nombre qui suit indique le nombre de bits)
   
   MOVSX  r16, r/m8  charge un registre 16 bits avec extension de signe à partir d'un registre/mémoire 8 bits
   MOVSX  r32, r/m8  charge un registre 32 bits avec extension de signe à partir d'un registre/mémoire 8 bits
   MOVSX  r32, r/m16  charge un registre 32 bits avec extension de signe à partir d'un registre/mémoire 16 bits

   Par exemple si on a le registre 8 bits BL vaut -5, l'instruction 
   MOVSX   EAX, BL
   va charger eax avec la valeur -5 mais sur 32 bits.

   Voici quelques exemples
   
   MOVSX  EAX, BL             ; charge EAX avec la valeur du reg 8 bits BL
   
   MOVSX  EAX, byte [10010]   ; charge EAX avec la valeur 8 bits située à l'adresse mémoire 10010

   MOVSX  EAX, word [10010]   ; charge EAX avec la valeur 16 bits située à l'adresse mémoire 10010


    A noter que L'instruction MOVSX ne modifie aucun drapeau (registre EFLAGS).
    --------------------------------------------------------------------------- 


  C) Instruction MOVZX
  --------------------
    (page 564 du manuel d'Intel: Jeu d'instructions volume 2A (A-M)) 


   L'instruction MOVZX copie l'opérande source (mémoire ou registre) dans l'opérante destination (registre),
   et les bits de l'opérande destination non affectés par l'opération seront mis à 0.
   Le bit de signe signe est mis à 0.
   
   Les formats pour MOVZX sont les suivants:
   
   MOVZX  r16, r/m8  charge un registre 16 bits avec mise à zéro des bits b8 à b15 à partir d'un registre/mémoire 8 bits
   MOVZX  r32, r/m8  charge un registre 32 bits avec mise à zéro des bits b8 à b31 à partir d'un registre/mémoire 8 bits
   MOVZX  r32, r/m16  charge un registre 32 bits avec mise à zéro des bits b16 à b31 à partir d'un registre/mémoire 16 bits

   Par exemple si on a le registre 8 bit BL valant 5, l'instruction 
   MOVZX  EAX, BL
   va charger EAX avec la valeur 5 mais sur 32 bits.

   Par contre si BL vaut -5, l'instruction 
   MOVZX  EAX, BL
   va charger EAX avec la valeur 251 sur 32 bits. Pourquoi ?
   
   Voici -5 sur 8 bit
   11111011
   
   Maintenant on prend cette valeur et on ajoute 24 zéro sur la gauche pour avoir une représentation sur 32 bits
   00000000000000000000000011111011
   
   On voit tout de suite que le bit de signe est à 0, c'est donc un nombre positif!
   
   Ce nombre vaut 251.

   Il faut bien faire attention à son utilisation.


   Voici quelques exemples
   
   MOVZX  EAX, BL             ; charge EAX avec la valeur du reg 8 bits BL
   MOVZX  EAX, byte [10010]   ; charge EAX avec la valeur 8 bits située à l'adresse mémoire 10010
   MOVZX  EAX, word [10010]   ; charge EAX avec la valeur 16 bits située à l'adresse mémoire 10010


    A noter que L'instruction MOVZX ne modifie aucun drapeau (registre EFLAGS).
    --------------------------------------------------------------------------- 


  Il existe aussi les instructions MOVS, MOVSB, MOVSW et MOVSD qui permettent de déplacer des octets, des mots de 16
  bits et de 32 bits en utilisant les registres d'index SI et DI. Ces déplacements s'effectuent entre mémoires.
 (page 552 du manuel d'Intel: Jeu d'instructions volume 2A (A-M)) 


  D) Instructions DEC et INC
  --------------------------
    (page 232 du manuel d'Intel: Jeu d'instructions volume 2A (A-M)) 
    (page 394 du manuel d'Intel: Jeu d'instructions volume 2A (A-M)) 

   DEC décrémente de 1 (soustrait 1) à l'opérande de destination
   INC incrémente de 1 (ajoute 1) à l'opérande de destination
   
   L'opérante peut être un registre de 8, 16 ou 32 bits ou une mémoire 8, 16 ou 32 bits.
   
   Exemple 
   MOV  EAX, 10  ; EAX vaut 10
   DEC  EAX      ; EAX est décrémenté de 1, --> EAX vaut 9
   INC  AX       ; AX est incrément de 1  , --> EAX vaut 10
   
   DEC  byte [ECX]   ; la valeur (octet) située en mémoire dont l'adresse est contenue dans ECX est décrémenté de 1

   INC  dword [ESI]  ; la valeur (32 bits) située en mémoire dont l'adresse est contenue dans ESI est incrémenté de 1

   Les instructions INC et DEC modifient les drapeaux suivants (registre EFLAGS).
   ---------------------------------------------------------------------------
   OF, SF, ZF, AF, PF en fonction du résultat. On peut donc utiliser les instructions de tests après ces 
   opérations.
   
   CF n'est pas affecté. Pour que CF soit affecté, il faut utiliser les instructions suivantes à la place,
   par exemple avec le registre eax
   
   ADD eax, 1   --> INC  eax
   SUB eax, 1   --> DEC  eax


  E) Instructions ADD et SUB
  --------------------------
    (page 62 du manuel d'Intel: Jeu d'instructions volume 2A (A-M)) 
    (page 233 du manuel d'Intel: Jeu d'instructions volume 2B (N-Z)) 

   ADD additionne l'opérande source à l'opérande de destination et stocke le résultat dans
   l'opérande de destination.
   SUB soustrait l'opérande source à l'opérande de destination et stocke le résultat dans
   l'opérande de destination.

   ADD destination, source
   SUB destination, source

   L'opérande source peut être un registre, une mémoire ou une valeur immédiate (un nombre).
   L'opérande de destination peut être un registre, une mémoire.
   Les opérandes peuvent être au format 8, 16 ou 32 bits.
   
   Deux mémoires ne peuvent être utilisées dans la même instruction.
   Lorsqu'une valeur immédiate est utilisée, l'instruction utilise ce nombre au format de
   l'opérande de destination si besoin (16 ou 32 bits) avec extension du signe pour ce nouveau
   format (c'est transparent pour nous).
   

   Les instructions ADD et SUB modifient les drapeaux suivant (registre EFLAGS).
   -----------------------------------------------------------------------------
   OF, SF, ZF, AF, PF et CF en fonction du résultat. On peut donc utiliser les instructions de tests après ces 
   opérations.
   
   
**********************************************************************   

  Le corps d'un programme FASM pour les librairies PureBasic

**********************************************************************   

  Le corps d'un programme FASM pour les librairies PureBasic se présente comme ceci.
  
  Tout d'abord, tout comme PB, les commentaires sont supportés et commencent par un point virgule.
  Tout ce qui suit le ; est considéré comme un commentaire.
  
  1) On déclare le format objet utilisé - Microsoft coff 32 bits - (se reporter à la Doc FASM pour de plus amples renseignements)
     on écrira la ligne suivante telle quelle  (page 69 de la doc FASM)
     
         format MS COFF
  

  2) On déclare ensuite le nom de notre commande, par exemple si on crée une commande en Purebasic :
  
     InsertElement(a, b)
     
     les paramètres n'ont pas d'importance dans la déclaration, ils seront définis dans le fichier descripteur
     obligatoire qu'il faudra créer (fichier .desc)
     
     Le nom de la commande devra être précédé par PB_
     
     La déclaration de cette commande devient (on utilise la directive Public de FASM pour pouvoir appeler cette
     commande depuis PB ainsi que dans la librairie)
     
     
         Public  PB_InsertElement


  3) si on veut utiliser des commandes Pure (par exemple la commande NewList) on utilisera la directive extrn suivi
     du nom de la commande PB; la commande PB devra être obligatoirement précédée des 3 caractères suivant :
     PB_
     La commande NewList déclarée devient
     
         extrn PB_NewList


     Il en est de même pour les API Windows prédéclarées par PureBasic, par exemple pour l'API InitCommonControls, sauf que
     la commande commence par _ au lieu de PB_, cette API devra être suivi du symbole @ puis d'une valeur indiquant en nombre
     d'octets le total des paramètres. Par exemple l'API InitCommonControls n'a aucun paramètre, sa déclaration devient
     
         extrn _InitCommonControls@0
     

     Par exemple l'API SendMessage() est définie comme ceci, avec 4 paramètres de type long
     SendMessage_(hwnd, uMsg, wParam, lParam), sa déclaration dans le fichier asm sera 


         extrn _SendMessage@16  
     
     
     le 16 indiquant qu'il y a 4 paramètres de type long (4 octets chacun)
     
     C'est également la même chose si on veut utiliser une commande d'une de ses propres librairies ou d'une librairie
     utilisateur.
     
     Par exemple pour utiliser la commande SetListIconGadgetColor() de ma librairie MoreListiconGadgetColor, on
     déclarera la commande comme ceci
     
     
         extrn PB_SetListIconGadgetColor



  4) Les équivalences et égalité
     Dans la plupart des langages assembleur, on définit des équivalences qui permettent de rendre la lecture
     plus compréhensible car l'assembleur devient vite fastidieux à comprendre en tant que programme.
     
     Imaginons que nous utilisions le registre esi comme variable (temps d'accès très rapide) et que la variable se
     nomme Valeur
     
     la déclaration d'équivalence utilise la directive equ
     
     
         Valeur equ esi
     
     Attention, FASM considère Valeur comme différent de valeur, je n'ai pas encore cherché si on peut éviter
     la différentiation des majuscules/minuscules.
     
     FASM, lorsqu'il va convertir le fichier en exe, remplacera partout le mot Valeur par esi (transparent pour nous)
     
          
     On peut aussi écrire par exemple
     
         Valeur2 equ 3
     
     idem, FASM remplacera Valeur2 par 3
     
     On peut aussi écrire 
     
     
         Valeur = 2
   

     La directive equ est très intéressante lorsque l'on utilise des mémoires etc.
     Imaginons que le registre ebp pointe une adresse et à cette adresse on a une valeur que l'on utilisera
     On peut écrire 
     
     
         Valeur  equ dword [ebp]
     
     puis utiliser par exemple ceci
     
     
         MOV  eax, Valeur
         
         
     ce qui revient à écrire 
     
         MOV  eax, dword [ebp]
         
         
     mais  MOV  eax, Valeur   est plus parlant.
     
     
     Je l'utilise souvent pour déclarer les constantes, par exemple la constante Windows LVM_SETBKCOLOR vaut $1001
     On la déclare comme ceci (idem qu'en Pure pour le format hexa et binaire).
     
     
         LVM_SETBKCOLOR   equ  $1001


     
  5) Les variables globales utilisables par toutes les commandes de la librairie.
     Si on veut pouvoir utiliser des variables globales dans d'autres commandes de la librairie (j'ai pas dit 
     qu'on pouvait les utiliser depuis PureBasic directement !) il faut les déclarer avec la directive Public
     
     Imaginons que l'on veuille utiliser la variable globale 'Compteur' dans toute la librairie
     Sa déclaration publique devient
     
         Public Compteur
     
     
     Attention, la déclaration Public ne réserve pas de place pour la variable globale, on verra ça dans quelques lignes.
     
     
  6) La section programme (les instructions)
     On déclare les instructions par la ligne Toute faite suivante
     
         section '.text' code readable executable
 
        
     qui indique à FASM que ce qui va suivre est du code en lecture et exécutable.
     Ensuite on met notre étiquette (label) qui permettra au programme de connaître exactement ou se situe le début du code.
     Cette étiquette n'est rien d'autre que le nom de la fonction précédé de PB_ et terminé par :
     Pour notre exemple de commande qui se nomme InsertElement(a, b), on a :
     
     
         Public  PB_InsertElement:
         
         
     et puis ensuite on met les instructions asm. Les étiquettes doivent se terminer par :

  
  7) La section des données (chaines utilisables).
     Si on utilise des chaînes de caractères prédéfinies ou des Datas à inclure, c'est dans cette section
     qu'il faut les déclarer. Je n'ai pas testé mais je pense que l'on peut mettre les data à la suite immédiate des 
     Chaînes sans avoir à déclarer une nouvelle section. Dans les programmes PureBasic, elles sont séparées pour 
     pouvoir aligner les données des chaînes (pas celles des Datas). Nous verrons dans le prochain tutorial comment
     aligner les données avec FASM pour Purebasic.


     On déclare la section avec la ligne suivante

         section '.data' Data readable writeable 
         
         
     Il s'agit d'une section de données que l'on peut lire et écrire (on pourrait essayer de ne la mettre que 
     Readable car avec PB, on ne réutilise pas cet espace pour modifier les chaînes de caractères.
     La syntaxe de déclaration d'une chaîne est la suivante :
     
     
     Chaine1: db "Création de librairies asm pour PureBasic",0
     
     on commence par une étiquette (ici Chaine1:) qui permettra l'accès à l'adresse de la chaîne, puis on écrit 
     db qui est une directive FASM qui indique que la donnée est sur un octet (b comme byte), le d de db indique
     que la valeur est connue lors de la compilation, on aura bien la chaîne qui sera inscrite dans le fichier
     exécutable. Ensuite on commence par ouvrir les guillemets et on écrit la chaîne puis, on referme les guillemets
     qui indiquent que la chaîne est terminée. Ensuite, une virgule pour séparer les données et enfin le 0 de fin
     de chaîne toujours sur 1 octet puisqu'on utilise la directive db.
     
     Si on veut mettre plusieurs chaînes, on met en suivant les chaînes (une chaîne par ligne)
     
     Chaine1: db "Création de librairies asm pour PureBasic",0
     Chaine2: db "avec PureBasic V 3.92",0
     Chaine3: db "et FASM...",0


    Les directives utilisables avec des valeurs connues à la compilation:
    
       db      --> 1 octet
       dw      --> 2 octets  = 1 word  = 16 bits
       dd      --> 4 octets  = 1 dword = 1 Long = 32 bits
       dp, df  --> 6 octets  = 48 bits 
       dq      --> 8 octets  = 64 bits 
       dt      --> 10 octets = 80 bits 



  8) La section des variables globales.
     Même principe que pour les chaînes sauf que pour les variables, les valeurs ne sont pas connues à la compilation.
     La première de la directive devient r au lieu de d (r comme réservé)
     
     La section des variables globales est déclarées comme ceci:
     
         section '.bss' readable writeable

     Le nom des variables globales Purebasic (sauf les listes chaînées) commencent par v_. Pour nos libs, on
     est pas obligé de faire pareil.
     On commence par écrire le nom de la variable puis la directive en fonction de la longueur puis ensuite un 1.

     Exemple :
     

         section '.bss' readable writeable
     
         BoutonDemarrerIcone rd 1
         BoutonRecommencerIcone rd 1
         Brush rd 1



  9) La section des Data 
     C'est le même principe qu'au paragraphe 7
     La déclaration
     
         section '.data' Data readable writeable

     et ensuite les valeurs séparables par une virgule (on peut mettre une étiquette ou on veut en début de ligne
     pour pouvoir récupérer les Datas)
     
     Exemple pour des valeurs numériques
     
     
     Debut:
         dd     65536,269484038,65536,57147416,6684672,538968064
         dd     65536,212336664,63832064,808452096,65536,480772120
     Suivant:
         db     36,26,6,0,66,53

     et pour des Datas alphanumériques
     
     db  "MOV",0,"AAA",0,"AAD",0,"AAM",0,"AAS",0,"ADC",0,"ADD",0,"AND",0
     db  "CALL",0,"CBW",0,"CLC",0,"CLD",0,"CLI",0,"CMC",0,"CMP",0,"CMPSB",0


     Pour pouvoir utiliser ces Datas, il faut connaître le type (byte, long, string) qui détermine
     la manière dont on va lire ces Datas.
     Pour les chaines, il suffit d'ajouter à l'adresse de début, la longueur de la chaine + 1 (0 de fin de chaine sur 1 octet)

      
     Voilà, on a vu le corps d'un programme FASM pour Pure.
     Nous verrons dans un prochain tut l'alignement des données pour les sections de données et variables et la macro
     correspondante et une ou 2 macros FASM simples.
      
     Voici de 3 exemples pour récapituler (extrait de mes libraires).
      
      
      
format MS COFF
public PB_StatusBarFieldWidth
 

; StatusBarFieldWidth(StatusBarID, Champ)
; retourne la largeur du champ de la statusBar
; identifiée par StatusBarID
; Field identifie le champ concerné
 
; --------------------------------------;
;        Libs externes/Extern Libs      ;
; --------------------------------------;

extrn  _SendMessageA@16
extrn  PB_StatusBarBorderSize

; --------------------------------------;
;        équivalence                    ;
; --------------------------------------;
 
Decalage   = (Fin_Decalage-DebutFin_Decalage)*4
LocalVar   = 16     ; nombres d'octets réservés sur la pile

; --------------------------------------;
;        Paramètres d'entrée            ;
; --------------------------------------;
 
Champ          equ   esp+Decalage+LocalVar+8
StatusBarID    equ   esp+Decalage+LocalVar+4
 
; --------------------------------------;
;        Variables locales              ;
; --------------------------------------;
 
; Value.RECT
Value.bottom          equ     esp + 12
Value.right           equ     esp + 8
Value.top             equ     esp + 4
Value.left            equ     esp 

; --------------------------------------;
;        Section code                   ;
; --------------------------------------;
 
section '.text' code readable executable
 
PB_StatusBarFieldWidth:
 
DebutFin_Decalage:
  PUSH   ebx
  PUSH   ecx
Fin_Decalage:
  ADD    esp, -LocalVar

  LEA    edx, [Value.left]
  PUSH   edx
  PUSH   dword [Champ+4]          ; Champ
  PUSH   dword 1034               ; #SB_GETRECT
  PUSH   dword [StatusBarID+12]   ; StatusBarID
  CALL   _SendMessageA@16
  TEST   eax, eax
  JZ     _Erreur

; Récupération de la largeur du champ  Value\right - Value\left
  MOV    eax, dword [Value.right]
  SUB    eax, dword [Value.left]
  JMP    _Retour
  
_Erreur: 
  DEC    eax

_Retour:
  ADD    esp, LocalVar
  POP    ecx
  POP    ebx
  RET    8
      

  Autre exemple :
  ------------

format MS COFF
public PB_StatusBarHeight
 
 
; --------------------------------------;
;        Libs externes/Extern Libs      ;
; --------------------------------------;

extrn  _SendMessageA@16
extrn  PB_StatusBarBorderSize    ; commande de la même librairie

; --------------------------------------;
;        équivalence                    ;
; --------------------------------------;
 
Decalage   = (Fin_Decalage-DebutFin_Decalage)*4
LocalVar   = 28             ; nombres d'octets réservés sur la pile

PB_BorderV   equ dword 1

; --------------------------------------;
;        Paramètres d'entrée            ;
; --------------------------------------;
 
StatusBarID    equ   dword  [esp+Decalage+LocalVar+4]
 
; --------------------------------------;
;        Variables locales              ;
; --------------------------------------;
 
; Value.RECT
Value.bottom             equ     esp + 24
Value.right              equ     esp + 20
Value.top                equ     esp + 16
Value.left               equ     esp + 12

BordureInterRectangle    equ     esp + 8
BordureVerticale         equ     esp + 4
BordureHorizontale       equ     esp
 
; --------------------------------------;
;        Section code                   ;
; --------------------------------------;
 
section '.text' code readable executable
 
PB_StatusBarHeight:
 
DebutFin_Decalage:
  PUSH   ebx
  PUSH   ecx
Fin_Decalage:
  ADD    esp, -LocalVar

  MOV    edx, StatusBarID

  LEA    eax,[Value.left]
  PUSH   eax          ; adresse de BordureHorizontale
  PUSH   dword 0
  PUSH   dword 1034   ; SB_GETRECT
  PUSH   edx          ; StatusBarID
  CALL   _SendMessageA@16
  TEST   eax, eax
  JZ     _Erreur

; Récupération de la hauteur et de la largeur de la StatusBar
  MOV    edx, StatusBarID
  PUSH   PB_BorderV
  PUSH   edx          ; StatusBarID
  CALL   PB_StatusBarBorderSize
  CMP    eax, -1
  JZ     _Erreur
; retourne Value\bottom - Value\top + BordureVerticale
; ici eax =   BordureVerticale
  MOV    edx, [Value.bottom]
  SUB    edx, [Value.top]
  ADD    eax, edx
  JMP    _Retour
  
_Erreur: 
  MOV    eax, -1   ; retourne -1 en cas d'erreur

_Retour:
  ADD    esp, LocalVar
  POP    ecx
  POP    ebx
  RET    4  
      

  Autre exemple :
  ------------

; Version 2.0  (September, 2003)

format MS COFF

    ; --------------------------------------;
    ;        Fonctions externes             ;
    ; --------------------------------------;

extrn PB_NewList
extrn _InitCommonControls@0

    ; --------------------------------------;
    ;        Variables globales             ;
    ; --------------------------------------;

Public t_ElementHeader
Public e_ElementHeader
Public t_ColorieHeader
Public e_ColorieHeader
Public t_ColonneVerrouiller
Public e_ColonneVerrouiller
Public WindProc_Origine_

Public _v_WindProc_Origine
Public t_CouleurItem
Public e_CouleurItem

    ; --------------------------------------;
    ;        Macro alignement               ;
    ; --------------------------------------;

macro bssalign value { rb (value-1) - ($-_VarSection  + value-1) mod value }
include 'Macros.inc'


Public PB_InitListIconGadget

section '.text' code readable executable


PB_InitListIconGadget:

  PUSH   ebp
  PUSH   ebx
  PUSH   ecx
  
  CALL  _InitCommonControls@0

;========================================================================================
; Initialisation pour SetHeaderColor()
;========================================================================================
  XOR    ecx, ecx
  MOV    [WindProc_Origine_], ecx              ; initialise la variable à 0
  MOV    [_v_WindProc_Origine], ecx            ; initialise la variable à 0
  MOV    ebp, 12                               ; taille de la structure
  MOV    ebx, t_ColorieHeader
  CALL   PB_NewList
;========================================================================================
; Initialisation pour LockcolumnSize()
;========================================================================================
  XOR    ecx, ecx
  MOV    ebp,12                                ; taille de la structure
  MOV    ebx,t_ColonneVerrouiller
  CALL   PB_NewList
;========================================================================================
; Initialisation pour les couleurs des items
;========================================================================================
  XOR    ecx, ecx
  MOV    ebp, 24                               ; taille de la structure
  MOV    ebx, t_CouleurItem 
  CALL   PB_NewList
;========================================================================================
; Initialisation pour les éléments du header
;========================================================================================
  XOR    ecx, ecx
  MOV    ebp,16                                ; taille de la structure
  MOV    ebx,t_ElementHeader
  CALL   PB_NewList
;========================================================================================
  POP    ecx
  POP    ebx
  POP    ebp
  RET

section '.bss' readable writeable
_VarSection:
bssalign 4

t_ElementHeader      rd 1
e_ElementHeader      rd 1

t_ColorieHeader      rd 1
e_ColorieHeader      rd 1

t_ColonneVerrouiller rd 1
e_ColonneVerrouiller rd 1

t_CouleurItem        rd 1
e_CouleurItem        rd 1

WindProc_Origine_    rd 1
_v_WindProc_Origine  rd 1

      
      
  10) Valeur retournée par une commandes en asm
      Nous verrons cela plus en détail dans le prochain tut, mais pour retourner une valeur, cela se fait par le
      registre eax sauf pour les chaînes et les nombres flottants (STO pour les flottants)
     
     

Prochain tut : L'alignement des données pour les sections de données, approche d'une macro simple.
               Comment retourner une valeur pour Pure, le fichier descripteur de PureBasic.


A+
Denis
Dernière modification par Anonyme2 le mer. 27/avr./2005 16:41, modifié 7 fois.
comtois
Messages : 5172
Inscription : mer. 21/janv./2004 17:48
Contact :

Message par comtois »

C'est excellent , merci .

C'est très bien détaillé. Il faut que je le relise à tête reposée pour que ça rentre bien , mais c'est abordable :)
Anonyme2
Messages : 3518
Inscription : jeu. 22/janv./2004 14:31
Localisation : Sourans

Message par Anonyme2 »

En fait c'est pas très compliqué mais ça fait beaucoup de choses à la fois.
Gillouz
Messages : 17
Inscription : mar. 26/oct./2004 9:40

Message par Gillouz »

Merci Merci , vraiment trés sympa à lire et trés complet.

Bon courage pour la suite, je l'attend avec impatience.
@+
Anonyme2
Messages : 3518
Inscription : jeu. 22/janv./2004 14:31
Localisation : Sourans

Message par Anonyme2 »

Gillouz a écrit :Merci Merci , vraiment trés sympa à lire et trés complet.

Bon courage pour la suite, je l'attend avec impatience.
@+

Merci,

faut pas être trop pressé :oops:

Bon, on a déjà fait 70% du trajet, reste des bricoles et quelques instructions asm souvent utilisées (MUL, DIV, IMUL, IDIV, LEA, RET (le complément) mais je vais pas passer tout le jeu d'instruction en revue, la doc Intel est très bien faite mais en anglais.
Il faut être curieux et faire des expériences...
Il est difficile de passer tous les cas possible d'une instructions, ça depend aussi des modes d'adressage (tiens si quelqu'un a le temps - si si j'en connais qui pourraient faire un tut- de faire les modes d'adressage les plus courants, ça serait sympa)
gansta93
Messages : 1448
Inscription : jeu. 26/févr./2004 11:17
Localisation : Le Village
Contact :

Message par gansta93 »

ça m'a ausi l'air abordable... ça sert pas que pour les lib... depuis le temps que j'attendais un tuto clair comme ça. :-D
Anonyme2
Messages : 3518
Inscription : jeu. 22/janv./2004 14:31
Localisation : Sourans

Message par Anonyme2 »

gansta93 a écrit :ça m'a ausi l'air abordable... ça sert pas que pour les lib... depuis le temps que j'attendais un tuto clair comme ça. :-D
Dans quelques temps, on essayera de faire une petite lib avec ceux que ça interesse (pas une usine à gaz, une lib avec quelques commandes et on fera le desc et les asm correspondant et on compile. C'est pour mettre le pied à l'étrier.
Oliv
Messages : 2117
Inscription : mer. 21/janv./2004 18:39

Message par Oliv »

Denis a écrit :
gansta93 a écrit :ça m'a ausi l'air abordable... ça sert pas que pour les lib... depuis le temps que j'attendais un tuto clair comme ça. :-D
Dans quelques temps, on essayera de faire une petite lib avec ceux que ça interesse (pas une usine à gaz, une lib avec quelques commandes et on fera le desc et les asm correspondant et on compile. C'est pour mettre le pied à l'étrier.
ça serait génial :D , merci Denis
Anonyme2
Messages : 3518
Inscription : jeu. 22/janv./2004 14:31
Localisation : Sourans

Message par Anonyme2 »

Je viens de corriger une petite erreur qui pourrait vous empêcher de dormir :0:

Malgré tous mes efforts de relecture, il y a encore des coquilles...

j'ai écrit ça pour la directive d'équivalence

Code : Tout sélectionner

        Variable equ dword [ebp]
     
     puis utiliser par exemple ceci
     
     
         MOV  eax, Valeur
         
         
     ce qui revient à écrire 
     
         MOV  eax, dword [ebp]
         
         
     mais  MOV  eax, Valeur   est plus parlant.
Mais en fait la première ligne doit être

Code : Tout sélectionner

     Valeur equ dword [ebp]
et pas

Code : Tout sélectionner

       Variable equ dword [ebp]
J'ai corrigé directement dans le tut.
Dr. Dri
Messages : 2527
Inscription : ven. 23/janv./2004 18:10

Message par Dr. Dri »

Super Denis, grâce à ton tut et à l'aide de Karlkox j'ai réussi à créer trois fonctions qui se compilent sans probleme en userlib...

Par contre je n'arrive pas à gérer l'arrivée d'un réel en première position... j'espérais un peu d'aide ^^

Dri
Anonyme2
Messages : 3518
Inscription : jeu. 22/janv./2004 14:31
Localisation : Sourans

Message par Anonyme2 »

Dr. Dri a écrit :Super Denis, grâce à ton tut et à l'aide de Karlkox j'ai réussi à créer trois fonctions qui se compilent sans probleme en userlib...

Par contre je n'arrive pas à gérer l'arrivée d'un réel en première position... j'espérais un peu d'aide ^^

Dri
Heu je ne comprend pas la question :roll:
Dr. Dri
Messages : 2527
Inscription : ven. 23/janv./2004 18:10

Message par Dr. Dri »

bah en asm j'arrive pa sà faire l'équivalent de

Code : Tout sélectionner

procedure truc(reel.f)
et de manipuler ma variable reel

Dri
Anonyme2
Messages : 3518
Inscription : jeu. 22/janv./2004 14:31
Localisation : Sourans

Message par Anonyme2 »

Comme tu donnes peu d'infos, je ne sait pas ou est le problème

Dans ton fichier descripteur, par exemple tu dois avoir un truc qui ressemble à ça

Code : Tout sélectionner

; Langage utilisé pour coder la librairie ASM ou C
ASM

; Nombre de Dll windows utilisées par la lib
0

; Type de librairie
LIB

; Aucune librairies PureBasic & utilisateur utilisé par la librairie
0

; Nom du fichier d'aide de notre libraire, OBLIGATOIRE !
truc1.chm

; Enumération des nouvelles fonctions crées
truc1, Float, ()
Float | StdCall
avec ce fichier desc, tu retournes un flottant et le passage de paramètres se fait sur la pile, donc le 1er paramètre en esp+4, le second en esp+8, le 3eme en esp+12 etc

si tu omets le StdCall dans le descripteur, le premier paramètre se fera par eax, le 2eme en esp + 4, le 3eme en esp+8 etc

pour chaque fonction, si tu utilises le mode standard call , tu devras mettre StdCall même si tu ne retournes pas de valeur

Dans le cas des flottants, PB utilises ST0 du FPU pour récupérer la valeur retournée, tu dois donc charger ST0 avec le résultat de ton calcul avec une instruction telle que FLD ou FILD etc, PB dépilera automatiquement la FPU lors du retour (ST0)
Dr. Dri
Messages : 2527
Inscription : ven. 23/janv./2004 18:10

Message par Dr. Dri »

D'accord, c'est parce que j'ai tout confondu que je n'arrivais pas à faire de fonction avec un réel en parametre.

L'histoire de EAX pour la valeur de retour je l'avais bien compris, de même avec ST0 pour les réels. J'avais aussi compris que le premier parametre se transmettais par EAX.

C'est là que j'ai tout confondu. Je croyais que je recevais aussi le premier parametre par ST0 si c'est un réel au lieu de EAX. Du coup je ne devrais plus avoir de probleme.

Merci beaucoup :D

Dri
Avatar de l’utilisateur
majikeyric
Messages : 602
Inscription : dim. 08/déc./2013 23:19
Contact :

Re: Tutoriel 7 - Instructions asm et corps Programme FASM

Message par majikeyric »

il y a des personnes que ce sujet intérèsse ?
(je me suis remis à la prog en ASM)

Si oui, je peux y apporter un complément d'information qui simplifie la création d'une userlib en ASM avec FASM.
Répondre