Utilisation des fonctions de chaine

Voir le sujet précédent Voir le sujet suivant Aller en bas

Utilisation des fonctions de chaine

Message par Admin le Mar 21 Mar - 15:10

Un programme simple pour montrer l'utilisation de chaines de caractères en assembleur avec les fonctions de l'API Windows.
Ce programme affiche dans la console une invite de saisie de mots (fonctions GetStdHandle, lstrlen et writeFile) Remarque : la fonction lstrlen qui calcule la longueur d' une chaine ne fonctionne pas si la chaine est vide !!!)
Il récupère la chaine saisie par l'utilisateur (fonctions GetStdHandle,ReadConsoleA). Ensuite il découpe la chaine en mots (le séparateur étant quelconque car il utilise la fonction IsCharAlphaNumericA) et hélas, je n'ai pas trouvé de fonctions de l'API qui le fasse.
Pour le découpage, nous enregistrons dans une table l'adresse du début de chaque mot contenu dans le buffer et nous écrasons le séparateur en mettant un zéro ainsi chaque mot forme une chaine correcte. La procèdure retourne le nombre de mots trouvés.
Ensuite, le programme va afficher chaque mot trouvé puis le stocker dans une structure d'arbre binaire, histoire de manipuler des pointeurs.
Chaque mot est recherché dans l'arbre en le comparant à ceux déjà stockés avec la fonction lstrcmp, s'il est trouvé un compteur est incrémenté pour comptage sinon il est inséré dans le tas sous forme d'une structure qui contient le pointeur gauche, le pointeur droit, le compteur puis le mot complet. Celui ci est copié du buffer dans la structure en utilisant la fonction lstrcpy. Si la casse ne doit pas jouer, il est possible d'utiliser la fonction de comparaison lstrcmpi. Après stockage, il est nécessaire de mettre à jour le pointeur du tas pour stocker le mot suivant.
Quand la saisie est terminée (test de la chaine #fin mais il est possible aussi de rester une chaine vide), l'arbre crée est balayé de manière récursive pour afficher tous les mots et le compteur par ordre alphabétique.
Pour l'affichage, j'aurais pu me contenter d'utiliser la fonction wsprintfa pour formater le message complet mais j'ai utilisé les fonctions lstrcpy et lstrcat pour montrer la concaténation de chaines.
Pour des explications sur l'utilisation des arbres binaires, voir un bon manuel d’algorithmes ( Livres de Knuth par exemple).
Voici donc quelques fonctions possibles mais hélas, je n'ai pas trouvé de fonction pour chercher une sous chaine dans une chaine, ni rempacer une sous-chaine par une autre etc. !!!
Code:

;---programme 64 bits
;  fonctions de chaines de caractères
; affichage dans la console

%include "../windowsinc64.inc"

;=======================================
; segment des données initialisées
;=======================================
segment .data
;code page 850 é=82h  à=85h è=8Ah ê=88h 
szChForm db "%u ",0  ; format décimal pour la fonction wsprintfA
szMessInvite db "Veuillez saisir une liste de mots (saisir #fin pour terminer):",10,13,0 ; les chaines
szMessFin  db 10,13,"Fin du programme.",0                  ;doivent se terminer par 0
szRetourLigne db 10,13,0                                    ; les caractères 10, 13 entainent un saut de ligne
szChaineFin db "#fin",0        ; pour comparaison et arrêter la saisie
szTablePetite db "Table trop petite !!",10,13,0
szTasPetit  db "Le tas est trop petit !!",10,13,0
szChInter  db " compteur = ",0  ; chaine intermédiaire
;
;structure d'un arbre
struc ARBRE
 .gauche resq 1    ; pointeur branche gauche
 .droit resq 1      ; pointeur branche droite
 .cpt  resq 1      ; données compteur
 .debutcle resb 0  ; la chaine sera stockée à partir de cette adresse (et c'est bien zéro)
 endstruc
iarbre istruc ARBRE  ; instance de l'arbre mais ne sert en fait que pour calculer la longueur
iend                  ; de la structure
ILGARBRE equ $ - iarbre
 
ptArbre dq sArbre    ; pointeur donnant la première position libre de l'arbre
;=======================================
; segment des données non initialisées
;=======================================
segment .bss
hMain  resq 1
INBMAXI equ 100
ptTableMots resq INBMAXI
szResult  resb 100
ILGBUFFER equ 200
szBuffer  resb ILGBUFFER
ILGTASARBRE equ 50000
sArbre  resb ILGTASARBRE        ; tas contenant l'arbre
 
;=======================================
; segment de code
;=======================================
segment .text
    global Main
   extern afferreur
Main:
    sub rsp, 8h    ; alignement de la pile avant tout appel de procédure
   sub rsp,20h
    ;recup handle de l'instance du programme
   mov  rcx,NULL
   call GetModuleHandleA
   mov r9,__LINE__ - 1
   cmp  eax,NULL
   je  .gestionerreurs
   ;init debut arbre
   mov rcx,sArbre    ; adresse du début de l'arbre
   mov rdx,ptArbre    ; adresse du pointeur de la première position libre de l'arbre
   call initarbre
.boucle:   
   ;ecrire le message d'invite dans la console
   mov rcx,szMessInvite
   call affconsolemess
   ;lire la chaine de caractères saisie
   mov rcx,szBuffer  ; adresse du buffer pour récuperer la saisie
   mov rdx,ILGBUFFER  ; longueur  de ce buffer
   call saisieClavier
   ;push  __LINE__
   ;call afftoutregistreP
   ;affichage de la chaine saisie
   mov rcx,szBuffer
   call affconsolemess
   mov rcx,szRetourLigne  ; mais il faut aussi afficher un retour ligne
   call affconsolemess
   ;si egale à #fin terminer le programme
   mov rcx,szBuffer
   mov rdx,szChaineFin
   call lstrcmp
   cmp eax,0
   je .fin1
   ;push  __LINE__
   ;call afftoutregistreP
   ;eclater en mots distincts
   mov rcx,szBuffer    ; adresse du buffer (attention il sera modifié)
   mov rdx,ptTableMots ; adresse de la table des pointeurs de chaque mot
   mov r8,INBMAXI      ; taille de la table
   call eclatement
   ;push  __LINE__
   ;call afftoutregistreP
   ;controle eclatement
   mov r12,rax
   mov rbx,0
.boucleV:
    cmp rbx,r12
   jge .finboucleV
    mov  rcx,[ptTableMots + rbx*8]  ; recuperation dans la table du pointeur vers le début d'un mot
    call affconsolemess
   mov rcx,szRetourLigne  ; mais il faut aussi afficher un retour ligne
   call affconsolemess   
   ;stocker les mots dans un arbre pour comptage
   mov  rcx,[ptTableMots + rbx*8]  ; adresse du mot à stocker
   mov rdx,sArbre    ; adresse début de l'arbre
   mov r8,ptArbre    ; adresse du pointeur de la position libre du tas
   call stockage
   inc rbx
   jmp .boucleV
.finboucleV:
    jmp  .boucle  ; boucle principale

.fin1:   ; faut afficher les résultats dans la console
    mov rcx,sArbre        ; debut de l'arbre
    call balayagealpha    ; par ordre alphabetique

   mov rax,0
   jmp .main_fin
.gestionerreurs:
   call afferreur
   mov  rax,1
   jmp .fin
.main_fin:   
    mov rcx,szMessFin    ; message de fin
   call affconsolemess
   mov rcx,szBuffer    ; Pause
   mov rdx,ILGBUFFER
   call saisieClavier
.fin:   
   mov  rcx,rax          ; code retour
   call ExitProcess  ; fin du programme

;========================================   
;affichage message dans console
;========================================
;rcx contient le message à afficher
affconsolemess:
%define .pMess  [rbp-8]
%define .hCons  [rbp-16]
    enter 16,0
   cmp byte [rcx],0  ; si le message est vide on ne fait rien
   je  .fin_aff   
    mov .pMess,rcx  ; save message
   sub rsp, 20h        ; ajuste RSP pour la sauvegarde des paramètres  20h
   mov rcx,STD_OUTPUT_HANDLE  ; code pour la sortie standard
    call    GetStdHandle      ; acquisition du handle de la sortie standard
   mov r9,__LINE__ - 1
   cmp    eax,NULL
   je       .erreur
    mov    .hCons, rax    ; stockage du handle de la console
 
   ;calcul de la longueur du message
   mov rcx,.pMess; recup de l'adresse du message à  afficher
   call  lstrlen  ; cette fonction ne fonctionne pas si le message est vide
   mov r9,__LINE__ - 1
   cmp    eax,NULL
   je       .erreur
   add rsp,20h  ; on rend la place des parametres précédent
   sub rsp, 8h  ; donc on enleve déjà 8 octets pour preparer la pile
   push    0    ; avant le push du parametre qui enleve 8 octets sur la pile
   sub rsp, 20h  ; puis on enleve les 32 octets pour la place des 4 parametres
   
   mov    rcx,.hCons  ;handle console
   mov    rdx,.pMess   
   mov    r8,rax  ; longueur calculee avant   
   mov    r9,NULL
    call    WriteFile
   add  rsp, 10h ; dépile le paramètre pushe et son alignement
   mov r9,__LINE__ - 2
   cmp    eax,NULL
   je       .erreur   
    mov rax,TRUE   
   jmp  .fin_aff
.erreur:
   call afferreur
   mov rax,FALSE
.fin_aff:   
    leave    ; pendant du enter, remet la pile en l'état
   ret 
;=========================================================
;lecture clavier
;=========================================================
;rcx contient l'adresse du buffer
;rdx contient la longueur maxi du buffer
;
saisieClavier:
%define .pBuff  [rbp-8]
%define .iLong  [rbp-16]
%define .iByteLus  [rbp-24]
   enter 32,0  ; reserve 4 fois 8 octets
   mov .pBuff,rcx
   mov .iLong,rdx
    sub rsp,20h   
   mov rcx,STD_INPUT_HANDLE
    call    GetStdHandle  ; recup de handle ( STD_INPUT_HANDLE)
   mov r9,__LINE__
   cmp eax,NULL
   je .erreur
    add rsp,20h
    sub rsp,8h  ; car un seul push   
   push  0            ;  parametre de fin de saisie (ici standard)
   sub rsp,20h
   mov rcx,rax  ; handle fonction précedente
   mov rdx,.pBuff    ; buffer
   mov r8,.iLong
   lea r9, .iByteLus      ; nb octets lus
   call  ReadConsoleA
   add rsp,10h  ; le push et l'alignement
    mov r9,__LINE__
   cmp eax,NULL
   je .erreur
   mov rcx,.pBuff
   mov rax,.iByteLus
   sub rax,2
   mov byte [rcx+rax],0  ; forçage du 00 à la place du 0D final
   ; rax contient la longueur de la chaine hors 0 final

   jmp .fin
.erreur:
   call afferreur
   mov rax,-1        ; si erreur  retour - 1
.fin:
    leave
   ret 
;==============================================
;Eclatement d'une chaine en mot
;le separateur est tout caractère non alpha numerique
; ATTENTION, le buffer d'entrée est modifié !!
;==============================================   
; rcx contient l'adresse du buffer à eclater
; rdx contient le pointeur de début de table
;utilise rsi,rbx,r12,r13,r14
;retourne le nombre de mots trouves dans rax ou -1 si erreur
eclatement:
    sub rsp,28h
    mov r13,rdx  ; pointeur debut de table
    mov rbx,0    ; nombre de mots
   mov rsi,rcx  ; pointeur d'un caractere du buffer
   mov r12,0    ; compteur de caracteres
   xor r14,r14  ; top pour signaler un mot
   xor rcx,rcx  ; raz du registre
.boucle:
    cmp byte[rsi+r12],0  ; c'est la fin du buffer on termine
    je .fin1
    mov cl,[rsi+r12]    ; sinon on recupere le caractères
   call  IsCharAlphaNumericA  ; est-il valide ?
   cmp rax,0
   je .suiteb        ; non
   cmp r14,1          ; as-ton commencé un mot
   je  .suiteboucle  ; oui donc on continue
   add rsi,r12        ; on ajoute le compteur precedent
   xor r12,r12      ; et on le remet à zero
   mov [r13+rbx*8],rsi    ;  stockage du debut du mot dans la table
   mov r14,1            ; top pour indiquer qu'un mot est en cours
   jmp .suiteboucle    ; et on boucle
.suiteb:    ;le carctere n'est pas alphanumerique
    cmp r14,1    ;un mot est-il en cours ?
    jne .suiteboucle
    mov  byte[rsi+r12],0  ; on écrase le caractère par 0
   mov r14,0  ; on met le top à zero
   inc rbx     ; on incremente le compteur de mots
   cmp rbx,r8
   jge .erreur
   jmp .suiteboucle
.suiteboucle:
    inc r12
   jmp .boucle   
.fin1:
    cmp r14,0          ; as-ton commencé un mot
   je  .fin2  ; non
   mov [r13+rbx*8],rsi    ;  stockage du debut du mot dans la table
   inc rbx
.fin2:   
   mov rax,rbx
   jmp .fin
.erreur:
    mov rcx,szTablePetite
    call affconsolemess
    mov rax,-1
.fin:
    add rsp,28h
   ret
;===============================================   
;initialisation d'un arbre
;==============================================
;rcx contient l'adresse du debut de l'arbre
;rdx contient l'adresse du pointeur du tas
initarbre:
    mov rax,rcx
   mov [rax+ARBRE.gauche],rcx  ; pointeurs initialisés avec le début de l'arbre
   mov [rax+ARBRE.droit],rcx
   mov qword [rax+ARBRE.cpt],0  ; raz du compteur
   add qword[rdx],ILGARBRE    ; on déplace le pointeur de fin de la longueur structure arbre
    ret   
;===================================================================
; stockage des mots trouvés dans un arbre
;===================================================================
;rcx pointeur du mot a chercher
;rdx contient l'adresse du début de l'arbre
;r8 contient l'adresse du pointeur de la première place libre du tas
;utilise r8,rsi,r13,r14,r15
stockage:
%define .pDebutArbre  [rbp-8]
%define .Sens  [rbp-16] 
%define .pTasArbre [rbp-24]
   enter 32,0
   sub rsp,20h
   mov r14,rdx  ; adresse du père initialisée avec le debut arbre
   mov .pDebutArbre,rdx  ; adresse debut arbre sert de pointeur d'arrêt
   mov .pTasArbre,r8
   mov r15,rcx    ; adresse du mot à inserer
   mov rsi,[r14+ARBRE.droit]  ; pointeur de balayage de l'arbre
   mov qword .Sens,0  ; on part à droite
.boucle:
    cmp rsi,.pDebutArbre  ; le pointeur est arrivé sur un noeud vide
   je .insert            ; on insere le mot
   mov r14,rsi          ; sinon on va balayer et on garde le pointeur comme nouveau père
   ;comparaison des clés
   mov rcx,r15          ; mot en entree
   lea rdx,[rsi+ARBRE.debutcle]  ; mot déjà stocké
   call lstrcmp
   cmp eax,0            ; attention il faut seulement comparer eax
   je .egalite
   jl .gauche
    mov rsi,[rsi+ARBRE.droit]  ; plus grand on suit le pointeur de droite
   mov qword .Sens,0  ; on part à droite
   jmp .boucle
.gauche:     ; plus petit on suit le pointeur de gauche
    mov rsi,[rsi+ARBRE.gauche]
   mov qword .Sens,1  ; on part à gauche
   jmp .boucle   
;   
;le balayage est fini   
.egalite:    ; trouvé  on le compte
    inc qword[rsi+ARBRE.cpt]   
   jmp .fin
.insert:    ; on n'a pas trouvé le mot donc on le stocke en fin du tas
    mov rax,.pTasArbre ; récuperation de l'adresse du pointeur de place vide
    mov r13,[rax]   ; pointeur de place vide
   mov rax,.pDebutArbre
    mov [r13+ARBRE.droit],rax  ; on initialise les pointeurs avec l'adresse de debut
    mov [r13+ARBRE.gauche],rax
   mov qword[r13+ARBRE.cpt],1          ; on met le compteur à 1   
   lea rcx,[r13+ARBRE.debutcle]      ; et on copie le mot ensuite
   mov rdx,r15
   call lstrcpy
   mov rcx,r15        ; hélas cette fonction ne rend pas la longueur copiée (A VERIFIER )
   call lstrlen      ; calcul longueur du mot stocké
   inc rax            ; pour le caractère de fin de chaine
   add rax,ILGARBRE  ; pour les zones de l'arbre
   mov r8,.pTasArbre  ; utilisation temporaire du registre r8
    add [r8],rax    ; on augmente le pointeur de tas de la longueur stockée
   mov rax,[r8]    ; verification si on dépasse pas la taille allouée
   sub rax,.pDebutArbre
   cmp rax,ILGTASARBRE
   jge .erreurTas
   ;
   ;mise à jour du pointeur du père avec le début du nouveau noeud crée
   cmp qword .Sens,0
   jne .gauche1
   mov [r14+ARBRE.droit],r13
   jmp .fin
.gauche1:   
   mov [r14+ARBRE.gauche],r13
   mov rax,0
   jmp .fin
.erreurTas:
    mov rcx,szTasPetit   
   call affconsolemess
   mov rax,-1
.fin:
    leave
    ret   

;================================================================
;balayage arbre par ordre alphabetique
;================================================================
balayagealpha:   
;rcx contient le début de l'arbre
   sub rsp,8h
   mov rdx,[rcx+ARBRE.droit]  ; on initialise avec le pointeur droit du début de l'arbre
   call balayagearbre          ; et on laisse dans rcx l'adresse du début de l'arbre
   add rsp,8h
   ret

;================================================================
;balayage arbre par ordre alphabetique
;================================================================
;rcx contient le début de l'arbre
;rdx le pointeur (initialisé avec le pointeur droit)
balayagearbre:
%define .pCourant  [rbp-8]  ; necessaire car procèdure récursive
%define .pDebut  [rbp-16]  ; donc on garde les variables locales
    enter 16,0
   cmp rdx,rcx  ; le pointeur est arrivée sur un noeud final
   je .fin      ; donc on retourne
   mov  .pDebut,rcx  ; sinon on balaye la partie gauche récursivement
   mov .pCourant,rdx
   mov rdx,[rdx+ARBRE.gauche]
   call balayagearbre
   mov rcx,.pCourant  ;puis on affiche le contenu du noeud
   call affichage
   mov rcx,.pDebut    ; puis on balaye la partie droite recursivement
   mov rax,.pCourant
   mov rdx,[rax+ARBRE.droit]
   call balayagearbre
.fin:
    leave
   ret
;=====================================
; affichage du contenu de chaque noeud
;=====================================
affichage:
    sub rsp,28h
   ;conversion du compteur avec wsprinfa
    mov rbx,rcx    ; on utilise rbx comme sauvegarde de l'adresse
    mov rcx,szResult  ; buffer du resultat de la conversion
   mov rdx,szChForm  ; chaine de formatage
   mov r8,[rbx+ARBRE.cpt]  ; contenu du compteur
   call wsprintfA
   mov r9,__LINE__ - 1
   cmp  eax,NULL
   je  .erreur
   ;concatene chaine
   mov rcx,szBuffer    ; zone de reception
   lea rdx,[rbx+ARBRE.debutcle]  ; mot à afficher
   call lstrcpy    ; copie
   mov r9,__LINE__ - 1
   cmp  eax,NULL
   je  .erreur
   mov rcx,szBuffer    ; zone de reception
   mov rdx,szChInter  ; chaine intermédiaire
   call lstrcat        ; on concatene
   mov r9,__LINE__ - 1
   cmp  eax,NULL
   je  .erreur
   mov rcx,szBuffer    ; zone de reception
   mov rdx,szResult    ; resultat de la conversion du compteur
   call lstrcat          ; on concatene
   mov r9,__LINE__ - 1
   cmp  eax,NULL
   je  .erreur
   ;affichage de la chaine ainsi creée
   mov rcx,szBuffer
   call affconsolemess
   mov rcx,szRetourLigne  ; mais il faut aussi afficher un retour ligne
   call affconsolemess
   jmp .fin
.erreur:
    call afferreur   
.fin:
    add rsp,28h
    ret   
avatar
Admin
Admin

Messages : 38
Date d'inscription : 28/11/2016

Voir le profil de l'utilisateur http://assembleur64.forumactif.com

Revenir en haut Aller en bas

Voir le sujet précédent Voir le sujet suivant Revenir en haut

- Sujets similaires

 
Permission de ce forum:
Vous ne pouvez pas répondre aux sujets dans ce forum