PureBasic

Forums PureBasic
Nous sommes le Jeu 22/Aoû/2019 0:08

Heures au format UTC + 1 heure




Poster un nouveau sujet Répondre au sujet  [ 19 messages ]  Aller à la page Précédente  1, 2
Auteur Message
 Sujet du message: Re: Tutoriel 7 - Instructions asm et corps Programme FASM
MessagePosté: Lun 14/Avr/2014 14:51 
Hors ligne

Inscription: Mer 14/Sep/2011 16:59
Messages: 903
Moi, ça m’intéresse :D

Mesa.


Haut
 Profil  
Répondre en citant le message  
 Sujet du message: Re: Tutoriel 7 - Instructions asm et corps Programme FASM
MessagePosté: Lun 14/Avr/2014 18:23 
Hors ligne
Avatar de l’utilisateur

Inscription: Dim 08/Déc/2013 23:19
Messages: 603
Localisation: Hérault
Cool quelqu'un d'intéressé ! :D

Bon, je ne vais sans doute rien apprendre à ceux qui codent déjà en ASM mais cela peut intéresser les personnes qui ont du mal à
l'appréhender.

(Les exemples sont pour du code 32 bit)
pour générer du code 64 bit, mettre en début de code:
Code:
format MS64 COFF
include "Win64a.inc"

ET ADAPTER les tailles des registres si besoin.

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

Pour accéder aux paramètres et aux variables locales dans une fonction ASM, il n'y a pas besoin de s'embêter avec les choses
du style :

(pour accéder au paramètre Param1 de la fonction, sur la pile):

Code:
Param1    equ     esp + 8

Pour pouvoir ensuite faire

Code:
      mov   eax,[Param1]

au lieu de : (et qui est moins parlant)
Code:
      mov   eax,[esp+8]


FASM (comme d'autres assembleurs) propose des macros qui facilitent l'accès aux
paramètres et variables locales d'une fonction en assembleur (à son cadre de pile).
Ici ce sont les macros : proc , endp ou local (page 110 du manuel de FASM)

On n'a pas besoin de jongler avec le pointeur de pile (ESP(32 bit) ou RSP(64 bit)) et des déplacements
pour pouvoir accéder aux variables . C'est l'assembleur qui fait ces calculs pour nous.
D'autant plus que calculer le déplacement par rapport au pointeur de pile est fastidieux.

De plus utiliser le registre ESP (ou RSP) pour accéder au cadre de pile n'est pas une
vraiment bonne méthode, car si dans la fonction on modifie le pointeur de pile par empilement
d'une valeur par exemple pour la sauvegarder temporairement
tous nos accès ensuite aux paramètres et variables locales ne sont plus valides puisque ESP a changé.
(Mais c'est une manière de faire, c'est d'ailleurs la manière dont sont générées les fonctions PureBasic).
Le registre EBP (RBP en 64 bit) a été crée pour ça, c'est le "registre de base".

pour écrire une fonction ASM : 'MaSomme' qui accepte, par exemple, 2 paramètres (longs) en entrée et en retourne la somme
vous pouvez le faire directement comme cela:

Code:

proc PB_MaSomme param1:DWORD, param2:DWORD
         mov   eax,[param1]            ; eax=param1
         add   eax,[param2]            ; eax=eax+param2
         ret                        ; le resultat est dans eax
endp



L'assembleur lui générera ça :

Code:
param1 equ ebp+8
param2 equ ebp+12

push ebp      ; on sauve ebp
mov ebp,esp      ; ebp=esp   
sub esp,0      ; place réservée sur la pile pour les variables locales, ici on en a pas, donc : 0

; votre code
mov   eax,[param1]      ; eax=param1
add   eax,[param2]      ; eax=eax+param2
;-

mov esp,ebp      ; on restaure esp
pop ebp         ; on restaure ebp
ret 8         ; on sort de la fonction et libère la place réservée pour les paramètres sur la pile


Pour avoir accès aux macros proc et endp, il faut inclure un des "headers" proposés par FASM,
ici par exemple "win32a.inc" pour 32 bit Ascii.

Donc notre code complet ASM sera:

Code:

format MS COFF

include "Win32a.inc"

public PB_MaSomme

section '.text' code readable executable

proc PB_MaSomme param1:DWORD, param2:DWORD

      mov   eax,[param1]            ; eax=param1
      add   eax,[param2]            ; eax=eax+param2
      ret
      
endp



et c'est tout!
Vous le compilez avec FASM pour générer le fichier objet : .obj

Vous créer le fichier .desc associé :

La convention d'appel de la fonction est : StdCall,
(la manière dont les paramètres sont passés à la fonction puis libérés)
Donc ne pas oublier de le spécifier ! (à la fin en attribut de la valeur de retour)

Code:
; Langage utilisé pour coder la librairie ASM ou C
ASM
; Nombre de Dll windows utilisées par la lib
0
; Type de librairie (OBj,LIB)
OBJ
; Librairies PureBasic & utilisateur utilisées par la librairie
0
; Nom du fichier d'aide de notre librairie, OBLIGATOIRE !
test1.chm
; Enumération des nouvelles fonctions crées
MaSomme, Long, Long (param1, param2)
Long | StdCall


Et vous passez le tout à LibraryMaker dans le répertoire SDK de PureBasic,
dans le readme.txt de ce répertoire, il y aussi plein d'infos intéressantes.


Vous pouvez aussi déclarer des variables locales dans une fonction ASM (page 111 du manuel de FASM) :

Code:

proc PB_MaSomme param1:DWORD, param2:DWORD

   local var1:DWORD

      mov dword [var1],123
      mov   eax,[param1]            ; eax=param1
      add   eax,[param2]            ; eax=eax+param2
      add eax,[var1]               ; eax=eax+var1
      ret
      
endp



Vous pouvez aussi spécifier (à l'aide de "USES"), si votre fonction utilisent certains registres qui ne doivent pas être modifiés,
l'assembleur génère automatiquement les instructions de sauvegarde et restauration de ces registres
en début et fin de procédure.

Code:

proc PB_MaSomme uses ebx esi,param1:DWORD, param2:DWORD

   local var1:DWORD

      mov ebx,98
      mov esi,11212
      mov dword [var1],123
      mov   eax,[param1]            ; eax=param1
      add   eax,[param2]            ; eax=eax+param2
      add eax,[var1]               ; eax=eax+var1
      add   eax,ebx                  ; eax=eax+ebx
      add   eax,esi                  ; eax=eax+esi
      ret
      
endp



On peut aussi déclarer les variables locales à une fonction comme cela (pour des DWORDs on met DD (define Double), ?: sans valeur prédéfinie):

Code:
   locals
         var1 dd ?
         var2 dd ?
   endl


Voilà, j'ai "survolé" quelques pages du manuel de FASM, mais j'ai pensé que cela pouvait intéresser certains,

l'emploi de macros facilite grandement le développement en ASM en s'affranchissant de certaines taches fastidieuses.

_________________
Mon site dédié à ma passion pour la programmation :
http://majikeyric.free.fr


Haut
 Profil  
Répondre en citant le message  
 Sujet du message: Re: Tutoriel 7 - Instructions asm et corps Programme FASM
MessagePosté: Jeu 03/Mar/2016 16:56 
Hors ligne
Avatar de l’utilisateur

Inscription: Jeu 22/Jan/2004 14:31
Messages: 3518
Localisation: Sourans
La convention d'appel classique en 32/64 StdCall

Si on omet StdCall dans le fichier descripteur pour le code 32 bit, le 1er paramètre sera passé par eax.

J'utilise les macro FASM du fichier IF.INC qui permettent de créer des if/elseif/else/endif et des boucles .while/.endw et .repeat/.until.

J'utilisais ces Macros avec MASM il y a déjà quelques années.

Les tests se font sur les registres.

par exemple pour utiliser la fonction PB IsGadget en supposant que la fonction n'a qu'un paramètre en stardCall (passage du paramètre par la pile).
Voila en 32 bit, le test se fait sur eax ici je le fait en non signé car si eax vaut 0, la fonction a échoué, on teste sur la valeur 0 et c'est comme PB on utilise l'inversion avec ~

Pour faire le test en signé, il faut utiliser le mot signed avant l'expression.

A noter que j'ai déclaré le mot Resultat comme étant équivalent au registre eax.
FASM va remplacer Resultat par eax à la compilation.
Cela apporte un peu de clarté au code.

le code suivant veut dire :
si eax est différent de 0 alors saute à l'étiquette (label) _Ok
Code:
  .if Resultat
      jmp  _Ok
  .endif

que l'on pourrait écrire aussi
Code:
  .if Resultat <> 0
      jmp  _Ok
  .endif

pour le code qui suit c'est le contraire :
si eax = 0 alors saute à l'étiquette (label) Erreur
Code:
  .if ~Resultat
      jmp  _Erreur
  .endif

que l'on pourrait écrire aussi
Code:
  .if Resultat = 0
      jmp  _Erreur
  .endif


Code:
;----------------------------------------------
;       Commandes PureBasic
;----------------------------------------------
extrn _PB_IsGadget@4
PB_IsGadget  equ  _PB_IsGadget@4
;----------------------------------------------
;       équivalences
;----------------------------------------------
Resultat  equ  eax
GadgetID  equ  eax
;----------------------------------------------
;       Paramètre d'entrée
;----------------------------------------------
; Gadget (Integer)
Gadget  equ esp+4
; Appel de la fonction PB IsGadget()
  PUSH   dword [Gadget]
  CALL   PB_IsGadget
  .if ~Resultat
      jmp  _Erreur
  .endif


je n'utilise pas toujours les .if

exemple d'une boucle while extraite d'une de mes fonctions en 64 bit.
J'ai mis la fonction complète avec les déclarations.
A noter, la macro ne modifie pas les registres, il faut donc faire évoluer le/les registres qui permettent de sortir de la boucle.
Le test peut être une expression avec plusieurs registres.

En 64 bits, Il faut aligner chaque variable locales sur 8 bit ou sur un multiple de 8 sinon on crash.
Idem pour les variables globales que j'utilise dans cette fonction (sinon crash ?).
La marco alignvar est déclarée et utilisée entre chaque déclaration dans la section bss.
Je n'ai initialisé aucune variable à 0 ni locale ni globale.

Code:
; Fichier généré le 01/03/2016 à 11h44

format MS64 COFF
public PB_SwapRows_F

;**********************************************
;       X64 ASCII
;**********************************************
;----------------------------------------------
;       Include
;----------------------------------------------
include '..\IF.INC'
; macro d'alignement des variables globales sur 32 bits
macro alignvar value {rb (value-1) - ($-_SectionVariables  + value-1) mod value}
;----------------------------------------------
;       Constantes
;----------------------------------------------
Erreur                  equ  -1
False                   equ  0
True                    equ  1
HDM_GETITEMCOUNT        equ  4608
LVIF_COLFMT               equ  65536
LVIF_COLUMNS            equ  512
LVIF_DI_SETITEM         equ  4096
LVIF_GROUPID            equ  256
LVIF_IMAGE              equ  2
LVIF_INDENT             equ  16
LVIF_NORECOMPUTE        equ  2048
LVIF_PARAM              equ  4
LVIF_STATE              equ  8
LVIF_TEXT               equ  1
LVM_GETHEADER           equ  4127
LVM_GETITEM             equ  4101
LVM_GETITEMCOUNT        equ  4100
LVM_SETITEM             equ  4102
MaskLecture               equ  LVIF_COLFMT  or LVIF_COLUMNS  or LVIF_DI_SETITEM  or LVIF_GROUPID  or LVIF_IMAGE
MaskLecture               equ  MaskLecture  or LVIF_INDENT  or LVIF_NORECOMPUTE  or LVIF_PARAM  or LVIF_STATE  or LVIF_TEXT
StateLecture            equ  MaskLecture
MaskEcriture_Item       equ  MaskLecture
MaskEcriture_SubItem    equ  LVIF_TEXT  or LVIF_IMAGE
;----------------------------------------------
;       Commandes PureBasic
;----------------------------------------------
extrn PB_GadgetID
;----------------------------------------------
;       API Windows
;----------------------------------------------
extrn SendMessageA
SendMessage  equ  SendMessageA
extrn UpdateWindow
;----------------------------------------------
;       équivalences
;----------------------------------------------
Decalage  = (Fin_Decalage-Debut_Decalage)*8
; nombre d'octets réservés sur la pile
ShadowSpace = 40
LocalVar    = 176 + ShadowSpace
Colonne          equ  esi
Nb_Colonnes      equ  edi
Colonne_x64      equ  rsi
Nb_Colonnes_x64  equ  rdi
NB_Elements      equ  rax
HeaderID         equ  rax
;----------------------------------------------
;       Offset des structures
;----------------------------------------------
; Structure Lvitem  (Taille : 84 octets)
lvitem.mask             equ 0   ; long
lvitem.iItem            equ 4   ; long
lvitem.iSubItem         equ 8   ; long
lvitem.state            equ 12  ; long
lvitem.stateMask        equ 16  ; long
lvitem.PB_Alignment1_0  equ 20  ; byte x 4
lvitem.PB_Alignment1_1  equ 21  ; byte
lvitem.PB_Alignment1_2  equ 22  ; byte
lvitem.PB_Alignment1_3  equ 23  ; byte
lvitem.pszText          equ 24  ; pointeur
lvitem.cchTextMax       equ 32  ; long
lvitem.iImage           equ 36  ; long
lvitem.lParam           equ 40  ; integer
lvitem.iIndent          equ 48  ; long
lvitem.iGroupId         equ 52  ; long
lvitem.cColumns         equ 56  ; long
lvitem.PB_Alignment2_0  equ 60  ; byte x 4
lvitem.PB_Alignment2_1  equ 61  ; byte
lvitem.PB_Alignment2_2  equ 62  ; byte
lvitem.PB_Alignment2_3  equ 63  ; byte
lvitem.puColumns        equ 64  ; integer
lvitem.piColFmt         equ 72  ; integer
lvitem.iGroup           equ 80  ; long
;----------------------------------------------
;       Paramètres d'entrée
;----------------------------------------------
; GadgetId (Integer)
GadgetId  equ rsp+Decalage+LocalVar+8
; Item1 (Integer)
Item1  equ rsp+Decalage+LocalVar+16
; Item2 (Integer)
Item2  equ rsp+Decalage+LocalVar+24
;----------------------------------------------
;       Variables locales
;----------------------------------------------
; Element1.Lvitem  (taille de la structure : 84)
Element1  equ  qword [rsp+ShadowSpace] 
; Element2.Lvitem  (taille de la structure : 84)
Element2  equ  qword [rsp+ShadowSpace+88]
;----------------------------------------------
;       Variables globales
;----------------------------------------------
; Text1.Buffer (Structure Taille : 101)
Text1  equ  v_Text1
; Text2.Buffer (Structure Taille : 101)
Text2  equ  v_Text2
;----------------------------------------------
;       Section code
;----------------------------------------------
; les registres volatiles PB sont : rax, rcx, rdx, r8, r9, xmm0, xmm1, xmm2 et xmm3

section '.text' code readable executable
PB_SwapRows_F:
; sauvegarde des paramètres sur la pile
  MOV    qword [rsp+08], rcx
  MOV    qword [rsp+16], rdx
  MOV    qword [rsp+24], r8

Debut_Decalage:
  PUSH   rsi
  PUSH   rdi
Fin_Decalage:
; réserve la place sur la pile (cadre de pile)
  SUB    rsp, LocalVar
   
  XOR    r9, r9
  MOV    rdx, LVM_GETITEMCOUNT
  MOV    r8, r9
  CALL   SendMessage
  CMP    NB_Elements, 2
  jl     _Erreur
  MOV    rcx, qword [Item1]
  NOP
  TEST   rcx, rcx
  jl     _Erreur
  MOV    rdx, qword [Item2]
  NOP
  TEST   rdx, rdx
  jl     _Erreur
  CMP    rcx, NB_Elements
  JGE    _Erreur
  CMP    rdx, NB_Elements
  JGE    _Erreur
  CMP    rcx, rdx
  JE     _Erreur

  XOR    r9, r9
  MOV    rdx, LVM_GETHEADER
  MOV    r8, r9
  MOV    rcx, qword [GadgetId]
  CALL   SendMessage
  OR     rax, rax
  JZ     _Erreur
 
  XOR    r9, r9
  MOV    rdx, HDM_GETITEMCOUNT
  MOV    r8, r9
  MOV    rcx, HeaderID   
  CALL   SendMessage
  CMP    rax, -1
  JE     _Retour
  PUSH   rax
; initialisation compteur de boucle
  MOV    Colonne_x64, 0
  POP    Nb_Colonnes_x64
   
  .while  Colonne < Nb_Colonnes
      LEA    r9, Element1
      MOV    rcx, qword [Item1]
      ; Element1\mask = #maskLecture
      MOV    dword [r9+lvitem.mask], MaskLecture
      ; Element1\iItem = Item1
      NOP
      MOV    dword [r9+lvitem.iItem], ecx
      ; Element1\iSubItem = Colonne
      NOP
      MOV    dword [r9+lvitem.iSubItem], Colonne
      ; Element1\state = #StateLecture
      NOP
      MOV    dword [r9+lvitem.state], StateLecture
      ; Element1\stateMask = -1
      NOP
      MOV    dword [r9+lvitem.stateMask], -1
      ; Element1\pszText = *pszText1
      MOV    r8, Text1
      MOV    qword [r9+lvitem.pszText], r8
      ; Element1\cchTextMax = SizeOf(Text1)-SizeOf(character)
      NOP
      MOV    dword [r9+lvitem.cchTextMax], 101
      ; If SendMessage_(GadgetID, #LVM_GETITEM, 0, @Element1) = #False
      XOR    r8, r8
      MOV    rdx, LVM_GETITEM
      MOV    rcx, qword [GadgetId]
      CALL   SendMessage
      OR     rax, rax
      JZ     _Erreur

       ; Element2\mask = #maskLecture
      LEA    r9, Element2
      MOV    rcx, qword [Item2]
      MOV    dword [r9+lvitem.mask], MaskLecture
      ; Element2\iItem = Item2
      NOP
      MOV    dword [r9+lvitem.iItem], ecx
      ; Element2\iSubItem = Colonne
      NOP
      MOV    dword [r9+lvitem.iSubItem], Colonne
      ; Element2\state = #StateLecture
      NOP
      MOV    dword [r9+lvitem.state], StateLecture
      ; Element2\stateMask = -1
      NOP
      MOV    dword [r9+lvitem.stateMask], -1
      ; Element2\pszText = *pszText2
      MOV    r8, Text2
      MOV    qword [r9+lvitem.pszText], r8
      ; Element2\cchTextMax = SizeOf(Text1)-SizeOf(character)
      NOP
      MOV    dword [r9+lvitem.cchTextMax], 101
      ; If SendMessage_(GadgetID, #LVM_GETITEM, 0, @Element2) = #False
      XOR    r8, r8
      MOV    rdx, LVM_GETITEM
      MOV    rcx, qword [GadgetId]
      CALL   SendMessage
      OR     rax, rax
      JZ     _Erreur

      ;// on réécrit l'élément Item2 à l'emplacement Item1
      LEA    r9, Element2
      OR     Colonne, Colonne
      ; Element2\mask = #maskEcriture_SubItem
      MOV    dword [r9+lvitem.mask], MaskEcriture_SubItem
      JNZ    _Element2_iItem_Element2
     
      ; Element2\mask = #maskEcriture_Item
      MOV    dword [r9+lvitem.mask], MaskEcriture_Item
     
      _Element2_iItem_Element2:
      ; Element2\iItem = Item1
      PUSH   qword [Item1]
      POP    rcx
      NOP
      MOV    dword [r9+lvitem.iItem], ecx
      ; Element2\iSubItem = Colonne
      NOP
      MOV    dword [r9+lvitem.iSubItem], Colonne
      ; If SendMessage_(GadgetID, #LVM_SETITEM, 0, @Element2) = #False
      XOR    r8, r8
      MOV    rdx, LVM_SETITEM
      MOV    rcx, qword [GadgetId]
      CALL   SendMessage
      OR     rax, rax
      JZ     _Erreur

       ;// on réécrit l'élément Item1 à l'emplacement Item2
      LEA    r9, Element1
      OR     Colonne, Colonne
      ; Element1\mask = #maskEcriture_SubItem
      MOV    dword [r9+lvitem.mask], MaskEcriture_SubItem
      JNZ    _Element2_iItem_Element1

      _MaskEcriture_Item_Element1:
      ; Element1\mask = #maskEcriture_Item
      MOV    dword [r9+lvitem.mask], MaskEcriture_Item

      _Element2_iItem_Element1:
      ; Element1\iItem = Item2
      PUSH   qword [Item2]
      POP    rcx
      NOP
      MOV    dword [r9+lvitem.iItem], ecx
      ; Element1\iSubItem = Colonne
      NOP
      MOV    dword [r9+lvitem.iSubItem], Colonne
      ; If SendMessage_(GadgetID, #LVM_SETITEM, 0, @Element1) = #False
      XOR    r8, r8
      MOV    rdx, LVM_SETITEM
      MOV    rcx, qword [GadgetId]
      CALL   SendMessage
      OR     rax, rax
      JZ     _Erreur
      INC    Colonne
  .endw
 
  MOV    rcx, qword [GadgetId]     ; rcx = GadgetId(Gadget)
  CALL   UpdateWindow
  .if rax
      MOV    rax, True          ; c'est Ok, on retourne #True
_Retour:
      ADD     rsp, LocalVar
      POP    rdi
      POP    rsi
      RET 
  .endif
 
_Erreur:
  MOV     rax, Erreur
  JMP     _Retour
;----------------------------------------------
;       Déclaration des variables globales
;----------------------------------------------
section '.bss' readable writeable
_SectionVariables:
; Text1.Buffer (Structure Taille : 101)
alignvar 8
v_Text1 rb 101
; Text2.Buffer (Structure Taille : 101)
alignvar 8
v_Text2 rb 101


Haut
 Profil  
Répondre en citant le message  
 Sujet du message: Re: Tutoriel 7 - Instructions asm et corps Programme FASM
MessagePosté: Jeu 03/Mar/2016 18:16 
Hors ligne
Avatar de l’utilisateur

Inscription: Jeu 22/Jan/2004 14:31
Messages: 3518
Localisation: Sourans
En fait, il faudrait réécrire un compilateur :mrgreen:


Haut
 Profil  
Répondre en citant le message  
Afficher les messages postés depuis:  Trier par  
Poster un nouveau sujet Répondre au sujet  [ 19 messages ]  Aller à la page Précédente  1, 2

Heures au format UTC + 1 heure


Qui est en ligne

Utilisateurs parcourant ce forum: Aucun utilisateur enregistré et 1 invité


Vous ne pouvez pas poster de nouveaux sujets
Vous ne pouvez pas répondre aux sujets
Vous ne pouvez pas éditer vos messages
Vous ne pouvez pas supprimer vos messages

Rechercher:
Aller à:  

 


Powered by phpBB © 2008 phpBB Group | Traduction par: phpBB-fr.com
subSilver+ theme by Canver Software, sponsor Sanal Modifiye