mardi 16 avril 2019

Les outils et la création d'un exécutable


Pour commencer, il faut créer un fichier source du langage d’assemblage avec un éditeur de texte soit directement sur le raspberry avec l’éditeur nano (ou un autre éditeur plus perfectionné) soit sur un autre ordinateur (par exemple avec notepad++ sur un PC) puis le transférer sur le raspberry.

Si vous utilisez notepad++ il faudra ajouter la coloration syntaxique pour l’assembleur ARM qui n’est pas disponible par défaut. Mais l’avantage de notepad++ est de pouvoir transférer directement le source sur le raspberry avec le menu NppFtp.

Le source crée est à sauvegarder avec l’extension de nom .s 

Pour la compilation sur le raspberry, il faut utiliser le compilateur as en tapant la commande :
as –o nomduprogramme.o nomduprogramme.s
Pour l’édition de lien, il faut utiliser ld avec la commande : 
ld –o  nomduprogramme nomduprogramme.o

Les 2 outils as et ld sont disponibles en standard dans la version raspbian du raspberry.
Il est préférable d’écrire un script qui effectue les 2 opérations à partir du seul nom de programme  par exemple :
      

#compilation assembleur
#echo $0,$1
echo "Compilation de "$1".s"
as -o $1".o" $1".s" -a >$1".txt"
ld -o $1 $1".o" -e main
ls -l $1*
echo "Fin de compilation."
 
 
Si ce script s’appelle compilasm, il suffira de le lancer par :
compilasm nomduprogramme
pour effectuer la compilation et l’édition de liens.
Et pour l’exécution (si aucune erreur n’a été détectée) il suffit de taper nomduprogramme.

Pour tester ces outils et scripts, il vous faudra saisir (ou copier-coller) le petit programme suivant qui affiche le « Bonjour le monde » classique. Évidement si vous êtes débutant, ce code vous paraitra obscur mais cela est le paradoxe de l'assembleur, car pour afficher un simple message, il est nécessaire d'avoir des connaissances qui ne seront acquises que petit à petit.
       

/* ARM assembleur Raspberry PI  32 bits */
/*  program pgm1.s   */

/* Données initialisées */
.data
szMessage:      .asciz "Pgm1 : Bonjour le monde. \n"       @ message

/* Code du programme */
.text
.global main 
main:
    ldr r0,iAdrszMessage         @ adresse du message 
    bl affichageMess             @ appel fonction d'affichage

 /* fin du programme */
    mov r0, #0                   @ code retour OK
    mov r7, #1                   @ code fin LINUX 
    svc 0                        @ appel système LINUX

iAdrszMessage: .int szMessage
/******************************************************************/
/*     affichage des messages   avec calcul longueur              */ 
/******************************************************************/
/* r0 contient l adresse du message */
affichageMess:
    push {r0,r1,r2,r7,lr}        @ save des registres
    mov r2,#0                    @ compteur longueur
1:                               @ calcul de la longueur
    ldrb r1,[r0,r2]              @ recup octet position debut + indice
    cmp r1,#0                    @ si 0 c est fini
    beq 2f
    add r2,r2,#1   @             @ sinon on ajoute 1
    b 1b
2:                               @ donc ici r2 contient la longueur du message
    mov r1,r0                    @ adresse du message en r1 
    mov r0,#1                    @ code pour écrire sur la sortie standard Linux */
    mov r7,#4                    @ code de l appel systeme 'write' 
    svc #0                       @ appel systeme Linux
    pop {r0,r1,r2,r7,lr}         @ restaur des registres
    bx lr                        @ retour procedure
 
 



Il faut sauvegarder ce programme sous le nom pgm1.s puis le compiler et linker avec les commandes ou le script précédents.
Après correction des erreurs de frappe éventuelles, l’exécution s'effectuera en tapant simplement pgm1
 Tant que vous n'avez pas obtenu un résultat, il faut vérifier et corriger chacune des étapes. Si tout est OK, je vous demande saisir (ou de copier coller) ce deuxième programme (pgm2.s) qui nous permettra d'afficher le contenu d'un registre et qui va nous servir dans la suite de ce cours :
       

/* ARM assembleur Raspberry PI  32 bits */
/*  program pgm1.s   */

/* Données initialisées */
.data
szMessDebut:    .asciz "Début programme. \n"       @ message debut
szMessFin   :   .asciz "Fin programme OK. \n"       @ message fin
sMessAffBin:    .ascii "Valeur du registre : "
sZonemess:      .space 36,' '
                .asciz "\n"
/* Code du programme */
.text
.global main 
main:
    ldr r0,iAdrszMessDebut         @ adresse du message 
    bl affichageMess              @ appel fonction d'affichage

    /***********DEBUT*********/
    mov r0,#1
    bl affichageReg2
    /***********FIN************/
    /* fin du programme */
    ldr r0,iAdrszMessFin         @ adresse du message 
    bl affichageMess             @ appel fonction d'affichage
    mov r0, #0                  @ code retour OK
    mov r7, #1                  @ code fin LINUX 
    svc 0                       @ appel système LINUX

iAdrszMessDebut: .int szMessDebut
iAdrszMessFin: .int szMessFin
/******************************************************************/
/*     affichage des messages   avec calcul longueur              */ 
/******************************************************************/
/* r0 contient l adresse du message */
affichageMess:
    push {r0,r1,r2,r7,lr}    @ save des registres
    mov r2,#0                @ compteur longueur
1:                           @ calcul de la longueur
    ldrb r1,[r0,r2]          @ recup octet position debut + indice 
    cmp r1,#0                @ si 0 c est fini
    beq 2f
    add r2,r2,#1             @ sinon on ajoute 1 
    b 1b
2:                           @ donc ici r2 contient la longueur du message
    mov r1,r0                @ adresse du message en r1
    mov r0,#1                @ code pour écrire sur la sortie standard Linux 
    mov r7,#4                @ code de l appel systeme 'write' 
    swi #0                   @ appel systeme 
    pop {r0,r1,r2,r7,lr}     @ restaur des registres
    bx lr                    @ retour procedure
/******************************************************************/
/*     affichage registre en binaire                              */ 
/******************************************************************/
/* r0 contient le registre */
affichageReg2:
    push {r0-r5,lr}          @ sauvegarde des registres
    mrs r5,cpsr              @ save du registre d'état  dans r5
    ldr r1,iAdrsZonemess     @ chargement adresse zone reception message
    mov r2,#0                @ compteur du nombre de bits traités
    mov r3,#0                @ compteur de position des caractères à afficher
1:
    lsls r0,#1               @ décalage à gauche des bits avec positionnement des flags
    movcc r4,#48             @ si le bit le plus à gauche (et passé dans le carry) = 0
    movcs r4,#49             @ s'il est egal à 1 
    strb r4,[r1,r3]          @ on stocke le caractère code 48 (0) ou 49 (1) dans le message
    add r2,r2,#1             @ passage au bit suivant
    add r3,r3,#1             @ position du caractère suivante
    cmp r2,#8                @ si 8 bits traités, on laisse un blanc 
    addeq r3,r3,#1
    cmp r2,#16               @ si 16 bits traités, on laisse un blanc 
    addeq r3,r3,#1
    cmp r2,#24               @ si 24 bits traités, on laisse un blanc 
    addeq r3,r3,#1
    cmp r2,#31               @ restent des bits ?
    ble 1b                   @ oui on boucle 

    ldr r0,iAdrsZonemessbin  @ et affichage du message
    bl affichageMess

100:
    msr cpsr,r5              @ restaur registre d'état
    pop {r0-r5,lr}           @ restaur des registres
    bx lr
iAdrsZonemess: .int sZonemess   
iAdrsZonemessbin: .int sMessAffBin




Par la suite, il vous suffira de modifier et/ou d'ajouter des instructions comprises entre 

les lignes :
/***********DEBUT*********/

Aucun commentaire:

Enregistrer un commentaire