Aller au contenu

EX05 - Assembleur des processeurs ARM – avec solutions

Exercice 1 : Utilisation des registres

Décrivez succinctement le rôle des 4 registres spéciaux (SP, LR, PC et APSR).

Solution

Ces registres requièrent une attention particulière:

  • R13/SP : le “stack pointer” permet de gérer la pile. Il pointe sur le sommet de la pile (Full Descending Stack). La pile implémente le principe LIFO (Last In / First Out).
  • R14/LR : le “link register” contient l’adresse de retour. Il est utilisé lors d’appel de fonction avec les instructions BL et BLX. En cas d’appel successif, son contenu doit impérativement être sauvé sur la pile.
  • R15/PC : le “program counter” pointe sur la prochaine instruction à exécuter.
  • APSR : le “application program status register” contient les fanions de conditions (Z, C, V et N) résultants d’opérations arithmétiques et logiques. Il sert aux branchements conditionnels.

Exercice 2 : Développement croisé vs natif

Quelles différences existent-ils entre un développement croisé et un développement natif ?

Solution

Dans un développement croisé, l’édition du code source, l’assemblage/compilation et l’édition de liens s’effectuent sur machine différente (machine hôte - host) que l’exécution de l’application (machine cible - Target).

Dans un développement natif, l’ensemble des opérations s’effectue sur la même machine.

Exercice 3 : Structure d’un module d’assemblage

Quelles sont les sections principales d’un module en langage assembleur ?
Que contiennent ces sections ?
Quelle est l’utilité de la directive .align ?

Solution

Les principales sections:

  • .section .text: section pour le code
  • .section .rodata: section pour les constantes
  • .section .data: section pour les données avec une valeur initiale non nulle
  • .section .bss: section pour les données avec une valeur initiale nulle

La directive .align <x> permet d’aligner le début d’une section afin que son adresse soit un multiple de \(2^x\).
Il est impératif d’utiliser cette directive au début d’une section afin d’éviter que du code, des constantes ou des données soient mal alignés et dégradent les performances du µP.

Exercice 4 : Fonction assembleur et interface C/C++

Implémentez en assembleur la fonction C “factorial”. Implémentez également le fichier d’en-tête permettant d’appeler la fonction depuis du code C/C++. La récursivité doit impérativement être conservée et le fichier doit être compilable pour la cible des TPs.

unsigned long factorial (unsigned n) {
    if (n == 0) {
        return 1;
    } 
    return n * factorial (n-1);
}
Solution

Interface C permettant son utilisation par des fonctions et méthodes C/C++.

#pragma once

#ifdef __cplusplus
extern "C" {
#endif

extern unsigned long factorial(unsigned n);

#ifdef __cplusplus
}
#endif

Code assembleur implémentant la fonction.

    .text
    .thumb
    .syntax unified
    .align 2

    .global factorial
    .type factorial, %function
factorial:
    cmp     r0, #0
    itt     eq
    moveq   r0, #1
    bxeq    lr
    push    {r4, lr}
    movs    r4, r0
    subs    r0, #1
    bl      factorial
    muls    r0, r4
    pop     {r4, pc}
    .size factorial, .-factorial

Exercice 5 : Boucle “for”

Implémentez en assembleur la fonction C ci-dessous.

struct CheckSum {
    unsigned cks1;
    unsigned cks2;
}; 

struct CheckSum checksum (const char* msg, unsigned n) {
    struct CheckSum cks = {0, 0};

    for (unsigned i=0; i<n; i++) {
        cks.cks1 += msg[i];
        cks.cks2 += msg[i] * (i+1);
    }

    return cks;
}

Trouvez l’algorithme le plus performant et réalisez-le.

Solution

Version original:

    .text
    .thumb
    .syntax unified
    .align 2

    .global checksum
    .type checksum, %function
checksum:
    push    {r4-r6}
    movs    r3, #0  // cks.cks1 = 0
    movs    r4, #0  // cks.cks2 = 0
    movs    r5, #0  // i = 0
    b       2f
1:  ldrb    r6, [r1, r5]  // r6 = msg[i]
    adds    r5, #1        // i++
    adds    r3, r6        // cks.cks1 += msg[i]
    muls    r6, r5        // r6 = msg[i] * (i+1)
    adds    r4, r6        // cks.cks2 += r6
2:  cmp     r5, r2        // i < n
    blt     1b            // si i plus petit que -> entre dans la boucle 
    str     r3, [r0]      // sinon met cks.cks1 dans la structure passée par r0
    str     r4, [r0, #4]  // et cks.cks2 dans cette même structure (avec offset 
                          // correspondant - cks1 et cks2 étant de type unsigned)
    pop     {r4-r6}
    bx      lr
    .size checksum, .-checksum

Version optimisée:

struct CheckSum checksum (const char* msg, unsigned n) {
    struct CheckSum cks = {0, 0};

    for (int i=n-1; i>=0; i--) {
        cks.cks1 += msg[i];
        cks.cks2 += cks.cks1;
    }

    return cks;
}

    .text
    .thumb
    .syntax unified
    .align 2

    .global checksum
    .type checksum, %function
checksum:
    push    {r4-r5}
    movs    r3, #0
    movs    r4, #0
    b       2f
1:  ldrb    r5, [r1, r2]
    adds    r3, r5
    adds    r4, r3
2:  subs    r2, #1
    bhs     1b
    str     r3, [r0]
    str     r4, [r0, #4]
    pop     {r4-r5}
    bx      lr
    .size checksum, .-checksum

Exercice 6 : Boucle “while”

Implémentez en assembleur la fonction C ci-dessous.

char* strcpy (char* dest, const char* src) {
    char* d = dest;
    while (*dest++ = *src++);
    return d;
}
Solution
    .text
    .thumb
    .syntax unified
    .align 2

    .global strcpy
    .type strcpy, %function
strcpy:
    movs    r2, r0
1:  ldrb    r3, [r1]
    strb    r3, [r2]
    adds    r2, #1
    adds    r1, #1
    cmp     r3, #0
    bne     1b
    bx      lr
    .size strcpy, .-strcpy

Exercice 7 : Calcul de parité verticale

Implémentez en assembleur la fonction C ci-dessous.

uint8_t parity (const uint8_t* msg, unsigned n) {
    char parity = 0;
    while (n>0) {
        n--;
        parity ^=msg[n];
    }
    return parity;
}
Solution
     .text
     .thumb
     .syntax unified
     .align 2

     .global parity
     .type parity, %function
 parity:
     movs    r2, #0
     b       2f
 1:  ldrb    r3, [r0, r1]
     eor     r2, r3
 2:  subs    r1, #1
     bhs     1b
     movs    r0, r2
     bx      lr
     .size parity, .-parity