0% ont trouvé ce document utile (0 vote)
53 vues65 pages

3 AssembleurARMv0

Le document présente les instructions de l'assembleur ARM, expliquant comment traduire des instructions en binaire et l'utilisation de variables et constantes pour simplifier la programmation. Il décrit également les types d'instructions disponibles, telles que le déplacement de données, l'accès à la mémoire, et les opérations arithmétiques et logiques. Enfin, il aborde la syntaxe pour déclarer des constantes, des variables et des tableaux, ainsi que des exemples de code illustrant ces concepts.

Transféré par

aymane.qaidi1
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
53 vues65 pages

3 AssembleurARMv0

Le document présente les instructions de l'assembleur ARM, expliquant comment traduire des instructions en binaire et l'utilisation de variables et constantes pour simplifier la programmation. Il décrit également les types d'instructions disponibles, telles que le déplacement de données, l'accès à la mémoire, et les opérations arithmétiques et logiques. Enfin, il aborde la syntaxe pour déclarer des constantes, des variables et des tableaux, ainsi que des exemples de code illustrant ces concepts.

Transféré par

aymane.qaidi1
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Assembleur ARM

Instructions

Rappel architecture K. Rhofir @2015/16


Instructions Assembleur
• Nous pouvons écrire un programme en binaire si on sait
comment représenter les instructions avec des “0” et des
“1”. Toutefois, le processus est très pénible: qui voudrait
écrire 0x23AB435F4EE7832FFA pour effectuer l’opération
“a = b + c”?

• Un assembleur est un programme qui traduit des mots, en


anglais, dans un fichier texte, en instructions.

• Exemple (a = b + c):

MOV R0, #0x00 ; Adresse de la variable b


LDR R0, [R0] ; Lire la variable b dans le registre R0
MOV R1, #0x01 ; Adresse de la variable c
LDR R1, [R1] ; Lire la variable c dans le registre R1
ADD R2, R0, R1 ; R2 = R0 + R1
MOV R0, #0x02 ; Adresse de la variable a
STR R2, [R0] ; Écrire le registre R2 dans la variable a

Rappel architecture K. Rhofir @2015/16


Revenons à notre exemple d’addition

MOV R0, #0x00 ; Adresse de la variable b


LDR R0, [R0] ; Lire la variable b dans le registre R0
MOV R1, #0x01 ; Adresse de la variable c
LDR R1, [R1] ; Lire la variable c dans le registre R1
ADD R2, R0, R1 ; R2 = R0 + R1
MOV R0, #0x02 ; Adresse de la variable a
STR R2, [R0] ; Écrire le registre R2 dans la variable a

• Pas pratique:
• d’avoir à connaître les adresses de a, b, et c

• d’avoir à utiliser deux instructions (MOV puis LDR) pour les charger

• Solution?
• L’assembleur nous permet de donner un nom à des adresses
mémoires: ce sont les constantes et les variables!
• constante = ne change pas
• variable = peut changer

Rappel architecture K. Rhofir @2015/16


Constantes — syntaxe

• Déclarer une constante (réserve de l’espace en ROM)

nom DCss valeur


• “nom” est le nom de la constante

• “DCss": C=constante, ss indique la taille. Par exemple:


• DC8: constante de 8 bits
• DC32: constante de 32 bits

• “valeur”: la valeur de la constante

• exemple:

a DC32 0xAB
b DC32 0xF2

Rappel architecture K. Rhofir @2015/16


Variables — syntaxe
• Déclarer une variable (réserve de l’espace en mémoire
RAM):

nom DSss nombre

• “nom” est le nom de la variable

• “DSss": S=variable, ss indique la taille. Par exemple:


• DS8: variable de 8 bits
• DS32: variable de 32 bits

• “nombre”: le nombre d’éléments à réserver

• exemple:
; dans notre exemple d’addition, la valeur initiale importe
peu,
; car nous allons la remplacer, mais en général on peut
initialiser
; les variables de la même façon que les constantes
c DS32 1
Rappel architecture K. Rhofir @2015/16
Tableaux (constantes et variables)

• Pour déclarer un tableau:


nom DCss el1 el2 el3 ... ; Constante
nom DSss nombreElements ; Variable
• “nom” est le nom de la variable/constante

• “D*ss”: ss indique la taille

• “el1 el2 el3”: la valeur des éléments du tableau s’il s’agit d’une
constante

• “nombreElements”: le nombre d’éléments dans le tableau s’il s’agit


d’une variable

• exemple:
a DS32 3 ; tableau de 3 mots de 32
bits chacun
b DC8 0x01 0x02 0x03 ; tableau de 3 octets

Rappel architecture K. Rhofir @2015/16


Tableaux

• Les tableaux peuvent être vus comme des chaînes de


variables.

• Une chaîne texte est un exemple de tableau d'octets,


chaque caractère est présenté comme un élément de code
ASCII (0 à 255).

• Par exemple:
strA DC8 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x00
strB DC8 'Hello', 0

• strA est la copie exacte de strB. Lorsque le compilateur


voit une chaîne entourée par des ' ', il la convertit
automatiquement en un ensemble d'octets.

Rappel architecture K. Rhofir @2015/16


Variables et constantes

• Une variable/constante est un nom donné à une adresse de


mémoire.

• Ce sont des créations du langage assembleur pour faciliter la


création d’un programme: il est plus facile de retenir un nom
qu’une adresse de mémoire!

• La directive DC sert à insérer des octets dans la mémoire


ROM du système. L’adresse de ces octets est l’adresse de la
constante.

• La directive DS sert à nommer des octets dans la mémoire


RAM du système. L’adresse de ces octets est l’adresse de la
variable.

• L’assembleur (le programme) s’occupera de remplacer tous


les noms des variables par les adresses correspondant à ces
noms.
Rappel architecture K. Rhofir @2015/16
Exemple d’addition—avec variables & constantes

• Comment représenter notre programme d’addition en utilisant des


variables et constantes?
; Définissons les constantes b et c
b DC32 0xAB
c DC32 0xF2

; Définissons la variable c (pour stocker le résultat)


a DS32 1

; Programme principal
LDR R0, b ; Lire la constante b dans le registre R0
LDR R1, c ; Lire la constante c dans le registre R1
ADD R2, R0, R1 ; Registre R2 = R0 + R1
LDR R3, =a ; Lecture de l’adresse de la variable a
STR R2, [R3] ; Écrire le registre R2 dans la variable a

Rappel architecture K. Rhofir @2015/16


Plus que de la “traduction” d’instructions

• En plus de traduire des mots/mnémoniques en binaire, l’assembleur


interprète aussi le texte de plusieurs façons. Il permet:

• d’associer des mots du programmeur à des adresses de mémoire.

• au programmeur de déclarer des variables et il gère l’adresse de ces


variables à travers les instructions du programme.

• au programmeur d’identifier des fonctions ou des sections de codes avec


des étiquettes (labels). Lorsque l’assembleur décode un appel de
fonction ou un branchement (saut) dans le programme, il remplace les
étiquettes par les adresses ou déplacements (offset) appropriées.

• L’assembleur supporte des directives qui lui disent comment placer le code
en mémoire, comment gérer plusieurs fichiers, comment précompiler le
code—modifier le code avant de le traduire en binaire—et plus. Les
directives sont des mots réservés qui ne génèrent pas de code en binaire,
mais qui dirigent la création du code exécuté.

• L’assembleur permet aussi d’insérer des commentaires dans le code!

Rappel architecture K. Rhofir @2015/16


Exemple de programme sur IAR Workbench

NAME main Nom du fichier/module

PUBLIC __iar_program_start Rend l’étiquette __iar_program_start disponible


pour d’autres fichiers
SECTION .text : CODE (2) Le texte qui suit doit être assemblé en mémoire FLASH
CODE32
Étiquette __iar_program_start: indique à
__iar_program_start
IAR de commencer le programme ici

; a = b + c
main
LDR R0, b
LDR R1, c
ADD R2, R0, R1 Code principal
LDR R3, =a
STR R2, [R3]
B main Saute à l’étiquette main: boucle infinie!

DATA Le texte qui suit doit être assemblé en mémoire FLASH


b DC32 0xAB
c DC32 0xF2

SECTION `.noinit`:DATA (2) Variables en mémoire RAM


a DS32 1

END Fin du fichier/module

Rappel architecture K. Rhofir @2015/16


6 types d’instructions en ARM

 Déplacement de données: Transfert de données ou de constante impliquant des


registres seulement.

 Accès à la mémoire: l’instruction, un load ou store, lit ou écrit la mémoire. La


valeur lue est mise dans un registre. La valeur écrite provient d’un registre.

 Opération arithmétique: addition, soustraction, multiplication, division et plus. Les


calculs s’effectuent sur des registres et peuvent changer les drapeaux de l’unité
d’arithmétique et de logique (ALU).

 Opération logique: ET, OU, NON-OU, OU EXCLUSIF et plus. Les calculs


s’effectuent sur des registres et peuvent changer les drapeaux de l’unité
d’arithmétique et de logique (ALU).

 Gestion de la séquence d’instructions: saut et branchements. Peuvent être


conditionnels ou inconditionnels, à des adresses directes ou indirectes. Comprend
les appels de fonctions.

 Contrôle du système: Comprend toutes les autres instructions contrôlant le


microprocesseur. Permet de gérer le temps, le format des instructions, l’exécution
en pipeline, les interruptions et plus.

Rappel architecture K. Rhofir @2015/16


Déplacement de Données: MOV

• L’instruction

MOV Rn Op1

met l’opérande de type 1 Op1 dans le registre Rn

• Opérande de type 1:
• Constante (valeur immédiate): toujours précédée du symbole #

• Registre

• Registre décalé
• Le décalage est fait avant l’opération. Cinq opérations sont possibles: LSL, LSR, ASR,
ROR et RRX. Ces opérations sont détaillées en Annexe A.

• Exemples:

MOV R0, #1234 ; R0 = 1234


MOV R0, R1 ; R0 = R1
MOV R0, R1, ASR #2 ; R0 = R1 / 4
Rappel architecture K. Rhofir @2015/16
Accès Mémoire: Load/Store

• Les accès à la mémoire se font avec deux instructions:


• LDR (LoaD Register) lit la mémoire et met la valeur lue dans un
registre.

• STR (STore Register) met la valeur d’un registre dans la mémoire.

• Ces instructions ont le format


LDR Rd, Op2
STR Rs, Op2

• Rd et Rs décrivent le registre de destination ou de source

• Op2 est une opérande de type 2

Rappel architecture K. Rhofir @2015/16


Opérande de type 2

• Symbolise tous les modes d’adressage du microprocesseur:


toutes les façons permises pour désigner une adresse de la
mémoire.

• Se découpe ainsi

LDR Rd, [Rb, Offset]

• Rb est le registre de base

• Offset est une opérande de type 1


LDR R0, [R2] ; R0 = Mémoire[R2]
LDR R0, [R2, #4] ; R0 = Mémoire[R2+4]
LDR R0, [R2, R3] ; R0 = Mémoire[R2+R3]
LDR R0, [R1, R2 LSL #2] ; R0 = Mémoire[R1 + (R2 * 4)]
• Pour calculer l’adresse, on additionne Rb et Offset

Rappel architecture K. Rhofir @2015/16


Opérande de type 2

• Pour faciliter les accès aux tableaux, on peut modifier Rb:

• avant le calcul d’accès mémoire (pre-indexing)

• symbole “!”

• après le calcul d’accès à la mémoire (post-indexing).

LDR R0, [R1, #4]! ; R1 = R1 + 4, suivi de R0 = Memoire[R1]

• en dehors des [ ].

LDR R0, [R1], #4 ; R0 = Memoire[R1], suivi de R1


= R1 + 4

Rappel architecture K. Rhofir @2015/16


Accès Mémoire: Load/Store Multiple

• Les instructions LDM (LoaD Multiple) et STM (Store Multiple) permettent


de lire des mots de mémoire contigus et de mettre les valeurs lues dans
plusieurs registres. Une seule instruction LDM peut remplacer plusieurs
instructions LDR si les adresses visées se suivent.

• Les instructions LDM et STM sont utilisées pour lire/écrire des données
de tableaux ou pour sauvegarder/récupérer de l’information sur la pile
(voir les prochains cours).

• Syntaxe:

LDMmm Ra{!}, {Liste de registres}

• mm = DB (Decrement Before), ou IA (Increment After)

• Exemples:

STMDB SP!, {R0, R1} ; R0 = Mem[SP-4], R1 = Mem[SP-8], SP = SP-8


LDMIA SP!, {R0, R1} ; R0 = Mem[SP], R1 = Mem[SP+4], SP = SP+8

Rappel architecture K. Rhofir @2015/16


Récapitulation: MOV vs LDR/STR

• MOV: déplacements entre des registres seulement

MOV R0, #0xFF ; R0 <- 0xFF


MOV R0, R1 ; R0 <- R1
MOV R0, R1 ASR #2 ; R0 <- (R1 / 4)

• LDR/STR: déplacements entre le CPU et la mémoire

LDR R0, [R1] ; R0 <- Memoire[R1]


LDR R0, [R1, #4] ; R0 <- Memoire[R1 + 4]
LDR R0, [R1, R2] ; R0 <- Memoire[R1 + R2]
LDR R0, [R1] #4 ; R0 <- Memoire[R1], R1 <- R1 + 4
STR R0, [R1] ; Memoire[R1] <- R0
STR R0, [R1, #4] ; Memoire[R1 + 4] <- R0
STR R0, [R1, R2] ; Memoire[R1 + R2] <- R0
STR R0, [R1] #4 ; Memoire[R1] <- 0, R1 <- R1 + 4

Rappel architecture K. Rhofir @2015/16


Accès mémoire avec variables

• Les instructions LDR et STR sont utilisées avec la syntaxe suivante pour
accéder aux variables:

• L’assembleur traduit les lignes en plusieurs instructions du processeur

LDR Rd, MaVariable ; Met la valeur de la


variable dans Rd
LDR Rd, =MaVariable ; Met l’adresse de la
variable dans Rd

Rappel architecture K. Rhofir @2015/16


Accès mémoire avec PC

• On peut aussi se servir de PC pour accéder à la mémoire

LDR Rd, [PC #16] ; Rd = Memoire[PC + 16]

• Particularité importante de ARM:

• PC contient l’adresse de l’instruction courante + 8

• PC est “en avance”: il pointe 2 instructions plus loin. Cela est dû à


une optimization de l’architecture ARM nommée “pre-fetching”.

• Donc, dans l’exemple ci-haut, si l’instruction courante est à l’adresse


0x80, nous aurons

0x80 LDR Rd, [PC #16] ; Rd = Memoire[(0x80+8) + 16]

Rappel architecture K. Rhofir @2015/16


Accès mémoire avec variables
LDR Rd, MaVariable ; Met la valeur de la
variable dans Rd
LDR Rd, =MaVariable ; Met l’adresse de la
variable dans Rd
• Toutes les adresses sont sur 32 bits et toutes les instructions sont
sur 32 bits. Une instruction 32 bits ne peut pas contenir un opcode,
un numéro de registre et une adresse de 32 bits…
• Lors d’un LDR R0, MaVariable, l’assembleur placera les items
suivants en mémoire:

• L’adresse de MaVariable sera en mémoire code, définie comme


une constante

• Une instruction LDR Rd, [PC+Offset] mettra l’adresse de


MaVariable dans Rd. Ici, Offset est une constante qui dépend de
la distance entre l’instruction LDR et l’adresse de MaVariable
(constante en code).
• Une autre instruction LDR R0, [Rd] mettra la valeur de
MaVariable dans R0
Rappel architecture K. Rhofir @2015/16
Exercices

Rappel architecture K. Rhofir @2015/16


Annexe 1: Décalage de bits

• LSL, Logical Shift Left, décale les bits vers la gauche et met des zéros à droite. Décaler un bit
vers la gauche équivaut à multiplier par 2. Carry devient égal au bit le plus significatif.
• LSR, Logical Shift Right, décale les bits vers la droite et met des 0 à gauche. Décaler un bit
vers la droite équivaut à diviser un nombre non-signé par 2. Carry devient égal au bit le moins
significatif.
• ASR, Arithmetical Shift Right, décale les bits vers la droite et copie le bit le plus significatif à
gauche. Décaler un bit vers la droite en conservant le bit de signe équivaut à diviser un nombre
signé par 2. Carry devient égal au bit le moins significatif.
• ROR, Rotate Right, décale les bits vers la droite et met le Carry à gauche. Carry devient égal
au bit le moins significatif.
• RRX, Rotate Right eXtended équivaut à ROR #1.

GIF-1001
Cours 8, p.18
Rappel architecture K. Rhofir @2015/16
Annexe 2: Qu'est-ce qu'une variable?

• Pour le microprocesseur, les variables n'existent pas: le microprocesseur


lit et exécute des instructions. Certaines instructions (LOAD et STORE)
lui demandent d'accéder à certaines adresses de la mémoire. La plupart
des instructions lui demandent de modifier ses registres internes.

Pour le programmeur et le mathématicien, une variable est un objet ayant


une certaine valeur qui peut changer dans le temps. Pour le
programmeur, une variable a un type, c'est-à-dire un format et une taille
(exemple: un entier sur 32 bits) et une portée (la variable peut être
utilisée dans la fonction seulement, dans le fichier seulement ou dans
tout le programme).

Le compilateur (ou l'assembleur) et l'éditeur de liens font la relation entre


les variables du programmeur et le monde du microprocesseur. Ces
programmes associent une adresse de mémoire (ou un registre) aux
variables que le programmeur déclare. Lorsque le programme du
programmeur lit ou écrit une variable, le compilateur transforme cette
lecture ou écriture en instructions qui accèderont aux adresses de
mémoires (ou aux registres) allouées aux variables...

Rappel architecture K. Rhofir @2015/16


Rappel: instructions

• De quoi une instruction est-elle constituée?

• “Opcode” (ou code d’opération): code identifiant quelle instruction est


effectuée (MOV, LDR, etc.)

• Paramètres: un ou plusieurs, dépendent de l’opcode.

Rappel architecture K. Rhofir @2015/16


Rappel: ARM

• Les instructions sont encodées sur combien de bits?

• 32!

• Quelle est la valeur de PC?

• L’adresse de l’instruction courante + 8

• Toujours 2 instructions “en avance”

• Quelle est la différence entre MOV et LDR/STR?

• MOV: entre les registres, à l’intérieur du microprocesseur

• LDR/STR: entre le microprocesseur et la mémoire

Rappel architecture K. Rhofir @2015/16


Instructions arithmétiques et logiques

• Les opérations mathématiques et logiques ont la forme

INSTRUCTION Rd, Rs, Op1

• Où
• Rd est le registre de destination

• Rs est un registre source

• Op1 est une opérande de type 1

• Le format de l’instruction ADD, par exemple, est:

ADD Rd, Rs, Op1 ; Rd = Rs + Op1

ADD R0, R0, #1 ; R0 = R0 + 1


ADD R0, R0, R1 ; R0 = R0 + R1
ADD R0, R0, R1, LSL #1 ; R0 = R0 + (R1 * 2)

Rappel architecture K. Rhofir @2015/16


Exemples

• Soustraction

SUB R0, R0, #1 ; R0 = R0 - 1


SUB R0, R0, R1 ; R0 = R0 - R1

• Décalage
LSL R0, R0, #1 ; R0 = R0 * 2
ASR R0, R0, #2 ; R0 = R0 / 4 (préserve le signe)

• “Et” logique

AND R0, R0, #1 ; R0 = R0 ET 1


AND R0, R0, R1 ; R0 = R0 ET R1

• Prendre le négatif

RSB R0, R0, #0 ; R0 = 0 - R0, donc R0 = -R0

Rappel architecture K. Rhofir @2015/16


Problème à résoudre

• But: comparer deux nombres placés dans R1 et R2


• Si R1 > R2, mettre R3 dans R0

• Si R2 >= R1, mettre R4 dans R0

• Comment faire?

• Nous allons avoir besoin de trois mécanismes:


• Une instruction pour comparer R1 et R2

• Un endroit pour stocker le résultat de la comparaison

• Des instructions pouvant être activées si la comparaison répond à


certains critères

Rappel architecture K. Rhofir @2015/16


Rappel: registre de statut (CPSR)

• Un registre de statut décrit l’état du processeur

Dépassement (overflow)

Retenue (carry/borrow )

Valeur nulle (zero)

Nombre négatif (negative )

Rappel architecture K. Rhofir @2015/16


Instructions avec drapeaux

• Les instructions arithmétiques et logiques changent les drapeaux de


l’ALU, lorsque l’option “S” est rajoutée après le nom de l’instruction

INSTRUCTIONS Rd, Rs, Op1 ; exécute l’instruction,


; et met à jour les drapeaux

• Exemple:
SUBS R0, R1, R2 ; R0 = R1 - R2
; et met à jour les drapeaux
Quels seront les drapeaux N et Z du CPSR?

Rappel architecture K. Rhofir @2015/16


CPSR: détection de conditions

• N: Détection de signe négatif


• 1 si résultat < 0, 0 autrement

• Z: Détection de zéro
• 1 si résultat = 0, 0 autrement

• Souvent utilisé pour détecter les égalités

• C: Détection de retenue (“carry)” ou d’emprunt (“borrow”)


• 1 si l’opération a impliqué une retenue, 0 autrement

• Ex. retenue d’addition de nombres positifs

• V: Détection de dépassements (overflow)


• 1 si l’opération a impliqué un dépassement, 0 autrement

• Ex. dépassement signé lors d’une addition

Rappel architecture K. Rhofir @2015/16


Instructions conditionnelles

• L’instruction

met l’opérande de type 1 Op1 dans le registre Rn, si la condition cc est


vraie

MOVcc Rn Op1

• Exemple:

MOVEQ R3, R1 ; R3 = R1 seulement si


le drapeau Z est 1
ADDNE R2, R2, R1 ; R2 = R2 + R1 seulement si
le drapeau Z est 0

Rappel architecture K. Rhofir @2015/16


Instructions conditionnelles

Code assembleur:
MOVEQ R3, R1 ; R3 = R1 seulement si
le drapeau Z est 1
Équivalent, en C, à:
if (Z == 1) {
R3 = R1;
}

Code assembleur:
ADDNE R2, R2, R1 ; R2 = R2 + R1 seulement si
le drapeau Z est 0
Équivalent, en C, à:
if (Z == 0) {
R2 = R2 + R1;
}

Rappel architecture K. Rhofir @2015/16


Codes de condition (CC)

• Plusieurs instructions s’exécutent si une condition est


rencontrée.
• Toutes les conditions sont évaluées à partir des
drapeaux de L’ALU et assument que ceux-ci ont été
déterminés auparavant.
• Par exemple, la condition EQ (equal) assume qu’une
soustraction ou comparaison a été faite avant: si le résultat de
l’opération est 0, le drapeau Z sera à 1 et la condition EQ sera
rencontrée.

• Les drapeaux N (Negative), Z (Zero), C (Carry) et V


(Overflow) servent à évaluer toutes les conditions.
• Les drapeaux et les conditions à évaluer changent si les
nombres comparés sont signés ou s’ils ne le sont pas.
Rappel architecture K. Rhofir @2015/16
Codes de condition
Manuel de référence ARM, p. A8-8

Rappel architecture K. Rhofir @2015/16


Problème à résoudre

• But: comparer deux nombres placés dans R1 et


R2
Code Symbole
• Si R1 > R2, mettre R3 dans R0
GT >
• Si R2 >= R1, mettre R4 dans R0 GE >=

LT <
• Comment faire?
LE <=

CMP R1, R2 ; calcule R1 - R2, change


les drapeaux
MOVGT R0, R3 ; si R1 > R2, R0 = R3
MOVLE R0, R4 ; si R2 >= R1, R0 = R4

Rappel architecture K. Rhofir @2015/16


Problème à résoudre

• But: Code Symbole


GT >
• R0 = abs(R1 - R2) ; valeur absolue
GE >=
• Comment faire? Indices:
LT <
• R0 = R1 - R2 si R1 > R2
LE <=
• R0 = R2 - R1 sinon

• l’instruction RSB peut être utilisée pour calculer le négatif d’un registre

RSB R0, R0, #0 ; R0 = -R0


• Solution (à 3 instructions):
CMP R1, R2 ; calcule R1 - R2, change les drapeaux
SUBGT R0, R1, R2 ; si R1 > R2, R0 = R1 - R2
SUBLE R0, R2, R1 ; si R1 <= R2, R0 = R2 - R1
• Solution (à 2 instructions):
SUBS R0, R1, R2 ; calcule R1 - R2, change les drapeaux
RSBLE R0, R0, #0 ; si R1 <= R2, R0 = -R0 (donc R0 = R2 -
R1)

Rappel architecture K. Rhofir @2015/16


Exemple: Addition de variables sur 64 bits

NAME Additionne64Bits ; Nom du fichier/module; Addition sur 64 bits en little endian

PUBLIC Additionne64Bits ; Rend l'étiquette/fonction Additionne64Bits disponible pour d'autres


fichiers

SECTION .intvec : CODE (2) ; Le texte qui suit doit être assemblé dans la mémoire FLASH
CODE32 ; Les instructions qui suivent sont sur 32 bits

Additionne64Bits
LDR R0, =Op1 ; Met l'adresse de Op1 dans R0
LDR R1, [R0] ; Met les 4 octets les moins significatifs d'Op1 dans R1
LDR R2, [R0,#4] ; Met les 4 octets les plus significatifs d'Op1 dans R2
LDR R0, =Op2 ; Met l'adresse de Op2 dans R0
LDR R3, [R0] ; Met les 4 octets les moins significatifs d'Op2 dans R3
LDR R4, [R0,#4] ; Met les 4 octets les plus significatifs d'Op2 dans R4
ADDS R5, R1,R3 ; R5 = R1+R3, additionne les octets les moins significatifs, met à jour Carry
ADC R6, R2,R4 ; Additionne les octets les plus significatifs avec la retenue
LDR R0, =Res ; Met l'adresse du résultat dans R0
STR R5, [R0] ; Sauvegarde le résultat: bit les moins significatifs
STR R6, [R0,#4] ; Sauvegarde le résultat: bit les plus significatifs
Fin
B Fin ; Terminé

DATA ; Le texte qui suit doit être assemblé dans la mémoire FLASH, constantes

Op1 DC32 0x11111111, 0x22222222 ; Déclaration de l'opérande 1 sur 2 fois 32bits


Op2 DC32 0x33333333, 0x44444444 ; Déclaration de l'opérande 2 sur 2 fois 32bits

SECTION `no_init`: DATA (2) ; Le texte qui suit doit être assemblé dans la mémoire RAM
Res DS32 2 ; Déclaration du résultat sur 2 fois 32 bits

END ; Fin du fichier/module

Rappel architecture K. Rhofir @2015/16


Annexe 1: Décalage de bits

• LSL, Logical Shift Left, décale les bits vers la gauche et met des zéros à droite. Décaler un bit
vers la gauche équivaut à multiplier par 2. Carry devient égal au bit le plus significatif.
• LSR, Logical Shift Right, décale les bits vers la droite et met des 0 à gauche. Décaler un bit
vers la droite équivaut à diviser un nombre non-signé par 2. Carry devient égal au bit le moins
significatif.
• ASR, Arithmetical Shift Right, décale les bits vers la droite et copie le bit le plus significatif à
gauche. Décaler un bit vers la droite en conservant le bit de signe équivaut à diviser un nombre
signé par 2. Carry devient égal au bit le moins significatif.
• ROR, Rotate Right, décale les bits vers la droite et met le Carry à gauche. Carry devient égal
au bit le moins significatif.
• RRX, Rotate Right eXtended équivaut à ROR #1.

GIF-1001
Cours 8, p.18

Rappel architecture K. Rhofir @2015/16


Annexe 2: Qu'est-ce qu'une variable?

• Pour le microprocesseur, les variables n'existent pas: le microprocesseur


lit et exécute des instructions. Certaines instructions (LOAD et STORE)
lui demandent d'accéder à certaines adresses de la mémoire. La plupart
des instructions lui demandent de modifier ses registres internes.

Pour le programmeur et le mathématicien, une variable est un objet ayant


une certaine valeur qui peut changer dans le temps. Pour le
programmeur, une variable a un type, c'est-à-dire un format et une taille
(exemple: un entier sur 32 bits) et une portée (la variable peut être
utilisée dans la fonction seulement, dans le fichier seulement ou dans
tout le programme).

Le compilateur (ou l'assembleur) et l'éditeur de liens font la relation entre


les variables du programmeur et le monde du microprocesseur. Ces
programmes associent une adresse de mémoire (ou un registre) aux
variables que le programmeur déclare. Lorsque le programme du
programmeur lit ou écrit une variable, le compilateur transforme cette
lecture ou écriture en instructions qui accèderont aux adresses de
mémoires (ou aux registres) allouées aux variables...

Rappel architecture K. Rhofir @2015/16


Modification de la séquence d’exécution

• Par défaut, les instructions s’exécutent séquentiellement et PC


est incrémenté automatiquement par le microprocesseur entre
chaque instruction
• PC = PC + 4 (taille d’une instruction)

• Dans nos programme, il arrive que l’on veuille exécuter autre


chose que la prochaine instruction:
• Saut direct à une instruction

• Énoncé conditionnel “si”

• Boucle: “répète N fois” ou “répète tant que”

• Appel de fonction

• Il est possible de contrôler la séquence d’exécution, en


assembleur, avec des instructions qui modifient PC.

Rappel architecture K. Rhofir @2015/16


Sauts Absolus

• PC est un registre et peut être modifié comme les autres registres, avec
la plupart des instructions

• Modifier la valeur de PC correspond à effectuer un saut absolu à une


adresse.

• Exemples:

MOV PC, 0x80 ; PC = 0x80


MOV PC, R0 ; PC = R0
LDR PC, [R0] ; PC = Memoire[R0]
ADD PC, R0, #0 ; PC = R0 + 0

Rappel architecture K. Rhofir @2015/16


Sauts relatifs

• Dans plusieurs cas, on ne veut pas exécuter l’instruction à l’adresse X, mais


exécuter l’instruction qui se trouve à N octets de l’instruction courante: on
veut « déplacer » PC par rapport à sa valeur actuelle

• L’instruction B (Branch) modifie PC relativement à sa valeur actuelle:

B Offset

• Offset est la valeur du déplacement, signé. L’adresse de la prochaine instruction


est calculée comme suit
• PC = Adresse de l’instruction B + Offset + 8*
• Rappelez-vous: PC contient l’adresse de la prochaine instruction + 8

• Comme le programmeur ne veut pas être obligé de compter l’offset, on peut


remplacer « Offset » par une étiquette: l’assembleur calculera la valeur pour
nous.

B #12 ; Saute à l’adresse de


l’instruction + 20
MonEtiquette B MonEtiquette ; Boucle infinie!

Rappel architecture K. Rhofir @2015/16


Branchements conditionnels

• Un énoncé conditionnel se code habituellement avec au moins deux instructions:


• une pour faire le test ou la comparaison

• une pour effectuer (ou pas) le branchement en fonction de la comparaison.

• Le résultat du test ou de la comparaison est entreposé dans les drapeaux de l’ALU


et l’instruction de branchement est conditionnelle. Si la condition est rencontrée, on
effectue le branchement. Sinon, on exécute la ligne suivante… Pour cette raison,
les conditions de branchement sont souvent inversées.
• Exemple: Si MaVar vaut 4, exécute une tâche:
if (MaVar == 4) {
// exécute une tâche…
exemple en C
}
// le programme continue…
LDR R0, MaVar ; Met la valeur de MaVar dans R0
CMP R0, 4 ; Change les drapeaux comme R0-4

BNE PasEgal
; execute une tâche…

PasEgal
; le programme continue… assembleur
Rappel architecture K. Rhofir @2015/16
Branchements conditionnels

• Autre exemple: if/else

if (MaVar == 4) { exemple en C
// exécute une tâche…
} else {
// exécute une autre tâche…
}
// le programme continue…

LDR R0, MaVar ; Met la valeur de MaVar dans R0 assembleur


CMP R0, 4 ; Change les drapeaux comme R0-4
BNE PasEgal
; execute une tâche…

B Continue
PasEgal
; exécute une autre tâche…

Continue
; le programme continue...

Rappel architecture K. Rhofir @2015/16


Branchements conditionnels

• Afin d’éviter de changer les drapeaux et pour faire un énoncé


conditionnel avec une seule instruction:

• CBZ (Conditional Branch Zero)

• CBNZ (Conditional Branch Non-Zero).

CBZ Rn, Offset ; Branchement à l’Offset si


Rn est égal à 0
CBNZ Rn, Offset ; Branchement à l’Offset si
Rn n’est pas égal à 0

Rappel architecture K. Rhofir @2015/16


Boucles
• Une boucle (répète N fois ou tant que) est constituée de trois opérations:
• initialiser la boucle
• condition d’arrêt
• opération mathématique qui mènera à la réalisation de la condition d’arrêt.
• En assembleur, le début de toutes les boucles est identifié par une étiquette qui
permet de revenir au début de la boucle
• Voici un exemple de boucle qui se répète cinq fois:

for (int i = 0; i < 5; ++i) { exemple en C


// tâche à l’intérieur de la boucle
}
// le programme continue…

InitDeBoucle assembleur
MOV R4,#5

DebutDeBoucle
; tâche à l’intérieur de la boucle
SUBS R4, R4, #1 ; R4 = R4 - 1, change les
drapeaux
BNE DebutDeBoucle ; Condition d’arrêt

; le programme continue…
Rappel architecture K. Rhofir @2015/16
Appel de Fonction et Adresse de Retour

• Appeler une fonction signifie mettre PC au début de la fonction pour exécuter


les instructions constituant la fonction.
• Retourner d’une fonction signifie reprendre l’exécution d’où l’appel s’est fait. Il
faut revenir à l’endroit où nous étions: c’est l’adresse de retour.
• Une fonction est un ensemble d’instructions à une adresse donnée. Cette
adresse est identifiée par une étiquette.
• Mauvais exemple d’appel de fonction:

Main Fonction

B MaFonction
; Tâche dans la fonction
AdresseDeRetour
B AdresseDeRetour
MOV R0, 0

• Pourquoi est-ce un mauvais exemple?


• Il est impossible d’appeler la fonction de deux endroits différents…

Rappel architecture K. Rhofir @2015/16


Appel de Fonction et Adresse de Retour

• Pour pouvoir revenir à plus d’un endroit, il faut sauvegarder l’adresse de


retour avant de faire le branchement.
• Instruction BL (Branch and Link):

BLcc Adresse ; met l’adresse de retour dans LR

• Après l’exécution de la fonction, on place PC = LR pour continuer


l’exécution après l’endroit où la fonction a été appelée, avec l’instruction
BX (Branch and eXchange):

BX Rm ; PC = Rm (Rm peut être n’importe


quel registre)

Main Fonction

BL MaFonction ; LR = PC-4
MOV R0, 0 ; Tâche dans la fonction
BL MaFonction ; LR = PC-4 BX LR ; PC = LR
ADD R0, R0, 1

Rappel architecture K. Rhofir @2015/16


Appel de Fonction et Adresse de Retour

• Que ce passe-t-il si une fonction appelle une autre fonction?

Main Fonction1 Fonction2

; Debut Fonction1
BL Fonction2
; Debut Main ; Code Fonction 2
; Suite 1 Fonction1
BL Fonction1 BX LR
BL Fonction2
; Suite Main
; Suite 2 Fonction1
BX LR

• La séquence de gestion des adresses de retour devient:


• LR = “Suite Main”
• LR = “Suite 1 Fonction1”
• PC = LR, donc PC = “Suite 1 Fonction1”
• LR = Suite 2 Fonction1
• PC = LR, donc PC = “Suite 2 Fonction1”
• PC = LR, donc PC = “Suite 2 Fonction1”… Le BX LR de Fonction1 “retourne" au
mauvais endroit!

Rappel architecture K. Rhofir @2015/16


La pile

• La pile est une structure de données qui permet d’empiler et de dépiler


des données. Le pile est un espace de la mémoire qui est géré comme
une pile de feuilles: mettre ou retirer une donnée se fait toujours à partir
du dessus de la pile.

• Le registre SP (Stack Pointer) devrait indiquer le dessus de la pile en tout


temps.

• PUSH
PUSH {Rs} ; Place le contenu de Rs sur la pile, SP = SP - 4
PUSH {R0, R1, R2} ; Place le contenu de R0, R1, et R2 sur la pile,
; SP = SP - 12

• Permet de mettre une (ou plusieurs) donnée(s) sur la pile.

• POP
POP {Rd} ; Place le contenu sur la pile dans Rd, SP = SP + 4
POP {R0, R1, R2} ; Place le contenu sur la pile dans R2, R1, et R0
; (dans l’ordre), SP = SP + 12

• Permet de récupérer une (ou plusieurs) donnée(s) de la pile.


Rappel architecture K. Rhofir @2015/16
Préparation d’une pile

• La pile est habituellement:


• descendante: lors d’un PUSH, SP diminue de 4 octets
(pourquoi 4?)

• placée après les données en RAM, donc à une adresse


supérieure

• Exemple:
SECTION .text : CODE (2)
CODE32

main
LDR SP, =MaPile ; Préparons une pile (de 64 octets)
ADD SP, SP, #64 ; La pile descend, donc il faut commencer à la
fin

SECTION `.noinit`:DATA(2)

MaVar DS32 1 ; Variable


MaPile DS32 16 ; Pile de 64 octets (16*4 = 64)

Rappel architecture K. Rhofir @2015/16


Appel de fonction et adresse de retour, avec pile

• Que ce passe-t-il si une fonction appelle une autre fonction?

Main Fonction1 Fonction2

; Debut Fonction1
BL Fonction2
; Debut Main ; Code Fonction 2
; Suite 1 Fonction1
BL Fonction1 BX LR
BL Fonction2
; Suite Main
; Suite 2 Fonction1
BX LR

Main Fonction1 Fonction2

PUSH {LR}
; Debut Fonction1
BL Fonction2 PUSH {LR}
; Debut Main
; Suite 1 Fonction1 ; Code Fonction 2
BL Fonction1
BL Fonction2 POP {LR}
; Suite Main
; Suite 2 Fonction1 BX LR
POP {LR}
BX LR

Rappel architecture K. Rhofir @2015/16


Exemple d’appel de fonction

• L’instruction BL commande un branchement

• Le nom de la fonction suit l’instruction BL

• L’éditeur de lien convertira le nom de la fonction en adresse

Code C
Retour = FonctionSansParametre();

BL FonctionSansParametre Code Assembleur

Rappel architecture K. Rhofir @2015/16


Appel de fonctions — conventions

• Paramètres:

• On se sert de R0 à R3 lorsqu’il y en a 4 ou moins

• S’il y en a plus?

• On utilise la pile… et/ou la mémoire

• Valeur de retour:

• On se sert de R0 lorsqu’il y en a 1 ou moins

• S’il y en a plus?

• On utilise la pile… et/ou la mémoire

Rappel architecture K. Rhofir @2015/16


Appel de fonction: 1 paramètre et 1 retour

• On place la valeur du paramètre dans R0 juste avant l’appel

• L’instruction BL commande un branchement

• R0 contient la valeur de retour une fois la fonction exécutée

Retour = FonctionAUnParametre(0x12);
Code C

Code Assembleur
MOV R0, #0x12 ; R0 contient le paramètre de la fonction
BL FonctionAUnParametre ; Appel de la fonction
MOV R3, R0 ; Récupère le résultat de la fonction

int FonctionAUnParametre (int param) Code C


{ return param + 1; }

Code Assembleur
FonctionAUnParametre
ADD R0, R0, #1 ; R0 contient le paramètre de la fonction
BX LR ; R0 contient le résultat de la fonction

Rappel architecture K. Rhofir @2015/16


Exemple

• Exemple de fonction
MOV R0, #8 ; Nous voulons que R5 = Fonction(8)
BL MaFonction
MOV R5, R0

MaFonction ; R0 = paramètre, R0 = valeur de retour

PUSH {LR} ; Préserve LR

ADD R4, R0, R0 ; Calcule le double du paramètre passé en entrée


MOV R0, R4 ; R0 = valeur de retour

POP {LR} ; Restaure R4 à sa valeur initiale


BX LR

• Quel est le problème?


• Qu’arrive-t-il à R4?

Rappel architecture K. Rhofir @2015/16


Préservation de l’environnement

• Le nombre de registres étant limité, on ne veut pas qu’une fonction


remplace le contenu des registres
• Problème:
• La fonction ne connait pas le nom des registres qui sont utilisés par le code
qui fait l’appel de la fonction

• Le code qui fait appel à la fonction ne connait pas le nom des registres qui
sont utilisés par la fonction

• Solution:
• La fonction “protège” le contenu des registres qu’elle utilise

• Méthode utilisée:
• On protège le contenu d’un registre en le sauvegardant sur la pile avec
PUSH

• On récupère ce contenu avec POP

Rappel architecture K. Rhofir @2015/16


Retour sur l’exemple

• Le code qui fait l’appel préserve le contenu de R0 pour ne pas le


perdre lors du retour de la fonction
• La fonction préserve le contenu de R4 pour ne pas le corrompre
en faisant ses calculs
• La valeur de retour est placée dans R5
PUSH {R0}
MOV R0, #8 ; Nous voulons appeler FonctionQuiPreserve(8)
BL FonctionQuiPreserve
MOV R5, R0
POP {R0}

FonctionQuiPreserve ; R0 = paramètre, R0 = valeur de retour

PUSH {R4, LR} ; Préserve LR et R4 -- nous allons nous en servir

ADD R4, R0, R0 ; Calcule le double du paramètre passé en entrée


MOV R0, R4 ; R0 = valeur de retour

POP {R4, LR} ; Restaure R4 et LR à leur valeur initiale


BX LR

Rappel architecture K. Rhofir @2015/16


Appel de fonction: 1—4 paramètres et 1 retour

• R0, R1, R2 et R3 servent à passer les paramètres

• R0 sert à retourner la valeur de retour


Retour = FonctionAMoinsDe5Parametres(0x12, 0x23, 0x34, 0x45); Code C

MOV R0, #0x12 ; Paramètre 1 Code Assembleur


MOV R1, #0x23 ; Paramètre 2
MOV R2, #0x34 ; Paramètre 3
MOV R3, #0x45 ; Paramètre 4
BL FonctionAUnParametre ; Appel de fonction
MOV R4, R0 ; Valeur de sortie

int FonctionAMoinsDe5Parametres(int P0, int P1, int P2, int P3) Code C
{ return P0 + P1 + P2 + P3; }

FonctionAMoinsDe5Parametres Code Assembleur


ADD R0, R0, R1 ; Calcul de la somme
ADD R0, R0, R2
ADD R0, R0, R3
BX LR ; Fonction terminée

Rappel architecture K. Rhofir @2015/16


Cas à plus de 4 paramètres: par la pile

• On utilise R0 à R3

• Si on en veut plus, on utilise la pile

• Il faut tenir compte des registres qu’on préserve en début de fonction avant
de lire des valeurs dans la pile
MOV R0, #1
MOV R1, #2
MOV R2, #3
MOV R3, #4
MOV R5, #5 ; Plaçons le 5e paramètre dans R5
PUSH {R5}
BL FonctionA5Parametres
MOV R8, R0
POP {R5}

FonctionsA5Parametres
PUSH {R9, LR} ; Sauvegarde LR et R9 (nous l’utiliserons)

LDR R9, [SP, #8] ; R9 = paramètre 5


ADD R0, R0, R9 ; retour = paramètre 0 + paramètre 5

POP {R9, LR} ; Restaure LR et R9


BX LR

Rappel architecture K. Rhofir @2015/16


Autres cas à plus de 4 paramètres

• Les paramètres sont indépendants les uns des autres:


• On les place un par un sur la pile et ils sont lus un par un

• Les paramètres font partie d’une chaîne ou d’un vecteur:


• On place l’adresse du début des valeurs sur la pile

• On place le nombre de valeurs sur la pile

• La fonction peut lire en boucle

• Les paramètres font partie d’une structure dont les éléments n’occupent pas tous
le même nombre d’octets:
• On place l’adresse du début de la structure sur la pile

• On place le nombre de mots utilisés pour mémoriser la structure

• La fonction doit lire la pile en tenant compte des longueurs variées des valeurs de la
structure

• Ça correspond à passer un pointeur ou une référence en langage plus évolué que


l’assembleur (natif ou évolué)

Rappel architecture K. Rhofir @2015/16


Cas à plusieurs retours

• R0 peut quand même servir à retourner une valeur

• Le code qui fait appel à la fonction passe des valeurs


d’adresse en paramètre (par R0 jusqu’à R3 et/ou par la pile)

• Les adresses passées pointent sur des espaces mémoires où


la fonction pourra écrire sans causer de problème au code qui
lui fait appel

• La fonction fait ses calculs et elle utilise les valeurs d’adresses


pour savoir où sauvegarder les résultats à retourner

• Le code qui a fait l’appel retrouve les valeurs retournées aux


adresses qu’il a passé en paramètre

• Le principe fonctionne tant pour des variables indépendantes


que pour des chaînes, des vecteurs, des structures, etc.

Rappel architecture K. Rhofir @2015/16


Annexe: La pile, plus que pour les retours

• La pile sert à entreposer les adresses de retours comme vu


précédemment.

• La pile sert aussi à passer des paramètres

• La pile sert à sauvegarder les registres utilisés dans une fonction.

• Souvent, les variables locales (dont la portée se limite à une fonction),


sont de l’espace mémoire sur la pile allouée dynamiquement (le
compilateur modifie SP au début de la fonction et à la fin pour réserver
de l’espace mémoire). Sinon les variables locales sont des registres.

• La pile sert à effectuer des calculs mathématiques comme réaliser une


chaînes d’additions et de multiplications

• La pile sert à sauvegarder le contexte d’une tâche lors d’interruptions


(voir prochain cours!)

• La pile est une structure de donnée fondamentale pour les ordinateurs.


Tous les processeurs ont un pointeur de pile…

Rappel architecture K. Rhofir @2015/16

Vous aimerez peut-être aussi