Introduction simplifiée #
pdf : cette fiche imprimable
ARMLite est machine disposant d’une architecture de Von Neumann qui exécute du code saisi en mnémoniques.
- Elle dispose de 13 registres généraux :
R0 à R12
. On les noteraRx, Ry
etc. - Les nombres littéraux sont notés
#val
, par exemple#20
désigne le nombre 20, vingt. - Sa mémoire est initialisée avec le code du programme.
- Les autres registres sont utilisés pour le fonctionnement interne, quelques bits indiquent l’état de la dernière opération
Instructions #
Les instructions les plus couramment employées sont les suivantes :
-
Déplacement de contenu
MOV Rx, val
: déplaceval
dans le registreRx
.val
peut-être un registre ou un littéral.LDR Rx, adresse
: charge dans le registreRx
le contenu de l’adresse mémoire indiquée,STR Rx, adresse
: écrit le contenu du registreRx
à l’adresse mémoire indiquée,
Attention, seules les adresses multiples de 4 sont valides. En effet, ARMLite fonctionne avec des mots de 4 octets et chaque octet dispose d’une adresse.
Donc
LDR R0, 40
est valide maisLDR R0, 41
est invalide, car 41 n’est pas multiple de 4. -
Opérations arithmétiques et logiques
ADD Rx, Ry, val
: effectue l’additionRy + val
et écrit le résultat dansRx
.val
peut être un registre ou un littéral.- Les instructions
SUB
,AND
,ORR
,EOR
(respectivement soustraire, ET logique, OU logique, OU EXCLUSIF) ont un fonctionnement similaire.
-
Rupture de séquence
-
CMP Rx, Ry
: effectue la comparaison entreRx
etRy
-
BGT label
: saute vers l’instruction précédée du nomlabel
SEULEMENT si la précédente comparaison était “plus grand que.”
Par exemple :- si on a comparé 4 et 2, alors
BGT label
va sauter enlabel
, - si on a comparé 4 et 10, alors
BGT label
va passer à l’instruction suivante.
- si on a comparé 4 et 2, alors
-
BEQ label
: saute vers l’instruction précédée du nomlabel
SEULEMENT si les valeurs comparées étaient égales, -
BLT label
: saute seulement si la dernière comparaison était “plus petit que”, -
B label
: saute, sans condition verslabel
, -
HALT
: arrête la machine.
-
Exemple de programme #
1| MOV R1, #10 // R1 contient 10
2| MOV R2, #5 // R2 contient 5
3| ADD R3, R1, R2 // R3 contient 5 + 10 = 15
4| CMP R3, R1 // Comparaison de 15 et 5
5| BGT more // 15 > 5 donc on va en "more", ligne 08.
6| HALT // Fin du programme (ne sera pas exécuté dans l'exemple)
7| // Rien, on va à la ligne
8| more: // pas de code après l'étiquette, on ne fait rien
9| ADD R3, R1, R3 // R3 contient 15 + 10 = 25
10| HALT // Fin du programme
Ci-dessous une documentation plus complète
ARMLite: Manuel de référence de programmation #
- Le simulateur ARMLite
- documentation complète
Fenêtre #
Code #
Grande fenêtre de gauche.
- On l’edit, puis submit.
- on peut l’exporter (save) vers
myprog.txt
ou le charger (load) depuis un fichier texte.
Une fois soumis, on peut ajouter un breakpoint (point d’arrêt) en cliquant sur une ligne. Si la soumission engendre une erreur, elle est affichée en rose.
Lorsque l’exécution est terminée (ou interrompue), la ligne courante est en orange.
Processeur #
La colonne centrale remplie de boutons
Ses registres. #
Ceux dans lesquels on va écrire sont R0, ..., R12
PC
indique l’instruction courante (le nombre est multiplié par 4)
Boutons #
- play (plein) pour exécuter tout d’un coup,
- pause, stop : je vous laisser deviner
- step & slow : exécuter une instruction ou pas à pas.
- la molette tourne lors de l’exécution
Le reste #
-
count : incrémenté à chaque instruction exécutée
-
current instruction : le numéro de l’instruction courante,
-
status bits : 0 ou 1 selon le dernier calcul ou la dernière comparaison.
- N (Negative) : 1 si la dernière comparaison est négative,
- Z (Zero) : 1 si la dernière comparaison est égale
- C (Carry) : 1 si la dernière instruction a entrainé une retenue,
- V (oVerflow) : 1 si la dernière instruction a engendré un dépassement de capacité (oubliez pour l’instant)
Mémoire #
La mémoire est rangée dans un tableau ligne x colonne
.
Chaque adresse est notée en hexadécimal en concaténant les nombres :
ligne 0x0006 | colonne 0x4 -> 0x00064
Il y a 1 Mo de mémoire (environ 8 millions de bits).
Chaque mot (case visible) peut être présenté en binaire, hexa ou décimal (signé ou non).
Le décimal signé utilise le complément à deux sur 32 bits.
Lorsqu’on soumet un code :
- les instructions sont traduites en langage machine,
- elles sont stockées en mémoire,
- s’il y a des données, elles sont stockées à la fin.
Depuis le programme (ou à la main en cliquant) on peut écrire dans la mémoire.
Formatage du code #
Le code (fenêtre de gauche) est formaté lorsqu’il est envoyé à ARMLite. Cela ajoute des numéros de ligne à gauche.
Lorsqu’on édite le code, on peut ignorer ces numéros et il n’est pas nécessaire de se soucier de l’ordre des lignes. ARMLite va les recalculer.
Commentaires #
Les commentaires sont précédés d’une double barre oblique //
ou ;
ou /
.
Étiquettes - Labels #
Une etiquette est un mot faisant référence à un emplacement mémoire.
Elles servent à deux occasions :
- branchement.
B loop
va “brancher” vers la ligneloop:
- données : on peut enregistrer des données, un peu comme des variables, en les nommant
Mémoire #
ARMlite dispose d’un total de 1 Mo de mémoire soit 1048567 (8 bits)
octets dans la plage 0x00000
à 0xfffff
. Un mot mémoire est composé de
quatre octets successifs soit 32 bits.
Toutes les adresses spécifiées dans le code ARMlite sont des adresses
d’octets. Lorsqu’un mot mémoire doit être chargé ou enregistré (LDR
ou
STR
), l’adresse doit correspondre à une limite de mot, c’est-à-dire qu’elle
doit être divisible par 4 - sinon vous obtiendrez une erreur “accès non
aligné…”.
Registres #
ARMlite a seize registres 32 bits. Il existe treize registres à
usage général - R0
à R12
- et trois registres qui ont également
des buts spéciaux SP (Stack Pointer), LR (Link Register) et PC
(Compteur de programme).
Le PC
est initialisé à 0 et contient l’adresse de la prochaine instruction à
exécuter. Il est incrémenté de 4 à chaque nouvelle instruction extraite de la
mémoire passer à l’instruction suivante.
En première nous n’utiliserons pas les autres registres spéciaux.
Instructions et directives #
ARMlite implémente ces instructions (classées par ordre alphabétique ici):
ADD, ADDS, AND, B, BCS, BEQ, BGT, BL, BLT, BMI, BNE, BVS, CMP, EOR, HALT, LDR,
LDRB, LSL, LSR, MOV, MVN, ORR, POP, PUSH, RET, RFE, STR, STRB, SUB, SUBS, SVC
Les mots-clés suivants ne sont pas des ‘instructions’ (ils ne sont pas traduits en opérations de processeur) mais sont des ‘directives’ à l’assembleur (par ordre alphabétique) :
.ALIGN, .ASCII, .ASCIZ, .BLOCK, .BYTE, .DATA, .WORD
Pour faciliter la programmation et pour s’aligner sur les mnémoniques largement utilisés ailleurs, les synonymes suivants sont acceptés :
BIS (=ORR), XOR (=EOR), OR (=ORR), JMS (=BL), HLT (=HALT)
HALT
#
Interrompt l’exécution de la machine. Tous les programmes doivent se terminer par HALT
.
HALT
n’est pas forcément en bas du code, généralement ce n’est pas le cas.
Cette instruction empêche le processeur de continuer à lire et exécuter le code.
MOV
#
MOV
(‘Move’) est utilisé pour placer une valeur spécifique (une ‘valeur immédiate’) dans un registre spécifié, ou pour
copier une valeur d’un registre à un autre. MOV
ne peut pas référencer les adresses mémoire. Le format est :
MOV Rd, <opérande2>
Où Rd spécifie le registre de destination, et <opérande2>
peut être un registre source ou une valeur immédiate
MOV R0, #25
déplace la valeur décimale “immédiate” 25 dans R0MOV R5, R3
copie la valeur de R3 dans R5MOV R4, SP
copie la valeur du registre SP dans R4
Les valeurs immédiates peuvent être spécifiées au format décimal, hexadécimal ou binaire (par exemple #0x3f
, #0b1101
) .
Avec MOV sur ARMlite les valeurs immédiates vont de $-2^{25}$ à $2^{35}$ ($\pm 33554432$). C’est assez vaste pour que n’importe quelle valeur ou étiquette d’E/S (ou couleur HTML) puisse être donné à un MOV.
LDR, STR
#
LDR
(‘Load Register’) copie une valeur d’un emplacement mémoire dans un registre.
STR
(‘Store Register) fait le contraire : il copie une valeur d’un registre dans un emplacement mémoire.
LDR
et STR
ne peuvent être utilisés avec des valeurs immédiates, seulement des registres.
Pour LDR
et STR
, la valeur copiée est un mot de 32 bits. Pour cette raison, l’adresse mémoire spécifie le premier des quatre octets
successifs où le mot est stocké - l’adresse doit donc être divisible par quatre.
Les deux instructions prennent deux opérandes, le premier spécifie le registre et le second spécifie l’adresse mémoire. Dans sa forme la plus simple, appelée “adressage direct”, la référence d’adresse mémoire prend la forme d’une étiquette ou d’un nombre (spécifié en décimal, hexadécimal ou binaire). Exemples d’adressage direct :
LDR R0,0xfc
charge le contenu d’un mot débutant à l’adresse mémoire 0x000fc
into R0.
STR R1,120
charge le contenu de R1
dans le mot débutat à l’adresse mémoire (décimale) 120.
LDR R3,0x81
est une instruction invalide car l’adresse 0x00081
n’est pas une adresse de mot valide (elle n’est
pas divisible par quatre).
STR R3,myLabel
stocke le contenu de R3
dans le mot défini par l’étiquette myLabel
.
LDR R4,#0xfc
est une instruction invalide car LDR
ne fonctionne pas avec une valeur immédiate.
Nous ne devrions pas souvent utiliser LDRB et STRB,
Nous n’utiliserons pas l’adressage indirect.
Opérations arithmétiques #
Deux opérations sont implantées : l’addition et la soustration. Les autres doivent être programmées.
ADD
& SUB
#
ADD R0, R1, R2
: ajoute R1
et R2
et enregistre dans R0
: En python, on écrirait : R0 = R1 + R2
SUB R0, R1, R2
: soustraie R2
à R1
et enregistre dans R0
. En python, on écrirait : R0 = R1 - R2
Si on veut ajouter un nombre littéral, comme le nombre 5 (cinq), il faut le précéder d’un dièze: #
et il doit être spécifié en dernier.
ADD R0, R1, #5
: R0 = R1 + 5
Il n’est pas possible de spécifier deux nombres littéraux. Le premier opérande doit être un registre.
Opérations bits à bits #
Elles suivent les mêmes règles que les opérations arithmétiques
AND, ORR (OR), EOR (XOR), LSL, LSR
#
AND
: et logique bit à bit,ORR
ouOR
: ou logique bit à bit,EOR
ouXOR
: ou exclusif bit à bit,LSL
etLSR
: décalages à gauche / à droite
Il existe aussi MVN
pour Move Not
, déplace et inverse tous les bits.
MOV R0 #0b11111000
MOV R1 #0b10001100
AND R2, R0, R1
// R2 0b10001000
Comparaisons et branchements #
Branchement inconditionnel #
L’instruction B
, suivie d’une adresse, généralement donnée sous la forme d’un label, saute à cette ligne.
MOV R0, #42
B la_bas
MOV R0, #14
HALT
la_bas:
HALT
// R0 contient #42, pas 14.
Comparaison #
L’instruction CMP Rx, RY
fait deux choses :
-
la soustraction des opérandes :
Rx - Ry
-
affecte aux bits d’état
N
etZ
(“Status bits”) les valeurs.-
N
pour Negative. SiRx - Ry < 0
alorsN
vaut 1. -
Z
pour Zero. Il vaut 1 siRx - Ry = 0
-
Les comparaisons doivent précédent les branchement conditionnels.
Une seule comparaison peut servir à plusieurs branchements.
Branchement conditionnels #
Ils fonctionennt tous de la même manière et correspondent à un if condition, alors
BEQ
: branche si “égaux”,BNE
: branche is “pas égaux”,BGT
: branche si “plus grand”,BLT
: branche si “plus petit”, etc.
Il en existe d’autres, en particulier permettant de travailler avec des “subroutines”, l’équivalent de fonctions, que nous n’utiliserons pas.
MOV R0, #25
MOV R1, #33
CMP R0, R1
BEQ equal
BLT plus_petit
BGT plus_grand
equal:
MOV R0, #10
HALT
plus_petit:
MOV R0, #20
HALT
plus_grand:
MOV R0, #30
HALT
// 25 < 33 donc R0 contient 20
Entrées / Sorties : I/O #
Sous le processeur vous avez 3 fenêtres blanches :
- la console d’affichage (message d’erreur et sorties)
- la fenêtre de saisie,
- la fenêtre graphique.
Nous allons simplement écrire du texte (châines, caractères, nombres) et lire du texte. Nous ne dessinerons pas sur dans la fenêtre graphique.
Adressage mémoire #
ARMlite utilise un principe assez courant : Memory-mapped I/O
Cela signifie que certaines adresses mémoires sont reservées aux entrées et sorties.
On utilise donc les instructions LDR
et STR
pour lire et écrire avec les périphériques (clavier ecran etc.)
L’autre approche courante est appelée Port-mapped I/O et nécessite d’avoir des instructions spécifiques pour lire et écrire.
Quelques exemples simples :
Écrire un nombre #
MOV R0, #42
STR R0, .WriteUnsignedNum
Va charger le nombre 42 (quarante deux) dans R0
et l’écrire dans la console, comme un nombre décimal positif. On verra 42
.
On peut utiliser .WriteSignedNum
pour écrire des nombres négatifs.
Écrire une chaîne de caractères #
MOV R1, #msg1
STR R1, .WriteString
msg1: .ASCIZ "Hello World\n"
msg1
contient une chaîne ASCIZ (Ascii terminée par l’octet0
) avec le texteHello World
suivi d’un retour à la ligne (\n
)- on charge dans le registre
R1
cette chaîne, - on l’écrit comme du texte en précisant l’adresse
.WriteString
Écrire un seul caractère #
Ceci n’a pas d’analogue en Python. Python n’a pas de type particulier pour les caractères ASCII.
On peut afficher un seul caractère ASCII avec l’adresse WriteChar
Par exemple le A
est encodé par le nombre 65 dans la table ASCII et donc :
MOV R0, #65
STRB R0, .WriteChar
HALT
va :
- stocker dans R0 la valeur 65,
- l’afficher comme un caractère ASCII (attention, l’instruction est
STRB
)
Lire un nombre #
LDR R2, .InputNum
Va demander à l’utilisateur de saisir un nombre décimal et le charger dans R2
.
Lire une chaîne ASCII #
LDR R0, #myString
STR R0, .ReadString
HALT
myString:
myString:
réserve un emplacement mémoire pour la chaîne à venir,LDR R0, #myString
: on prépare un registre pour la chaîne à venir,STR R0, .InputNum
: on lit une chaîne ASCII et on l’enregistre dans l’emplacement réservé.
Autre exemple :
lire et afficher une chaîne tapée par l’utilisateur :
MOV R0, #myWord
STR R0, .ReadString
MOV R1, #myWord
STR R1, .WriteString
HALT
myWord:
myWord:
réserve un emplacement mémoire avec l’étiquettemyWord
MOV R0, #myWord
est indispensable… cela prépare simplement le registre.STR R0, .ReadString
, lit le contenu à l’adresse.ReadString
, cette adresse correspond à du texte tapé dans la fenêtre de saisie, etmyWord
va contenir ce mot.MOV R1, #myWord
, déplace le contenu demyWord
dans le registre 1,STR R1, .WriteString
, écrit le contenu deR1
comme une chaîne ASCII à l’écran,
Exemples de programmes #
Opérations basiques #
a = 14
b = 3
c = a - b
d = a + b
e = a & b # ET bit à bit entre a et b
f = a | b # OU bit à bit entre a et b
g = a ^ b # XOR bit à bit entre a et b
MOV R0, #14
MOV R1, #3
ADD R2, R0, R1
SUB R3, R0, R1
AND R4, R0, R1
ORR R5, R0, R1
XOR R6, R0, R1
HALT
if else
#
a = 5
b = 7
if a == b:
c = 4
else:
c = 5
# donc c = 5
MOV R0, #5
MOV R1, #7
CMP R0, R1
BEQ equal
MOV R2, #5
HALT
equal:
MOV R2, #4
HALT
Boucle #
a = 0
while a < 8:
print("bonjour")
a = a + 1
MOV R0, #0
loop:
CMP R0, #8
BGT end
MOV R1, #msg
STR R1, .WriteString
ADD R0, R0, #1
B loop
end:
HALT
msg: .ASCIZ "bonjour\n"
Multiplier #
a = 3
b = 6
c = a * b
# à la main sans le produit
# a * b = a + a + a + ... + a
p = 0
c = 0
while c < b:
p = p + a
c = c + 1
# p = 18
// R0: a
// R1: b
// R2: a * b
// R3: compteur de boucle
MOV R0, #3
MOV R1, #6
MOV R2, #0
MOV R3, #0
loop:
CMP R3, R1
BEQ end
ADD R2, R2, R0
ADD R3, R3, #1
B loop
end:
HALT
Division entière #
Pas non plus de division dans ARMLite mais on peut utiliser les soustractions successives :
diviser 21 par 4
- $q = 0, r = 21$. $21 > 4$ donc on continue
- $q = 1, r = 21 - 4 = 15$. $15 \ge 4$ donc on continue
- $q = 2, r = 15 - 4 = 9$. $9 \ge 4$ donc on continue
- $q = 3, r = 9 - 4 = 5$. $5 \ge 4$ donc on continue
- $q = 4, r = 5 - 4 = 1$. $1 < 4$ donc on a fini.
- $21 = 4 \times 5 + 1$
a = 21
b = 4
# a = 5 * b + 1
c = a // b # 5
d = a % b # 1
# soustractions successives
q = 0
r = a
while r >= b:
q += 1
r = r - b
print(q)
print(r)
# q = 5
# r = 1
// R0: a
// R1: b
// R2: a // b
// R3: a % b
MOV R0, #21
MOV R1, #4
MOV R2, #0
MOV R3, R0
loop:
CMP R3, R1
BLT end
ADD R2, R2, #1
SUB R3, R3, R1
B loop
end:
STR R2, .WriteUnsignedNum
STR R3, .WriteUnsignedNum
HALT
Il existe de nombreuses autres instructions présentées dans la documentation (lien tout en haut).