Initiation au langage machine (assembleur)
Principes
Revenons sur ces instructions aussi appelées "instructions machines" exécutées par l'unité de commande. Un ordinateur exécute des programmes qui sont des suites d'instructions. Le CPU est incapable d'exécuter directement des programmes écrits, par exemple, en Python. En effet, comme tous les autres constituants d'un ordinateur, le CPU gère uniquement 2 états (toujours symbolisés par un « 1 » et un « 0 »), les instructions exécutées au niveau du CPU sont donc codées en binaire. L'ensemble des instructions exécutables directement par le microprocesseur constitue ce que l'on appelle le « langage machine ».
Une instruction machine est une chaîne binaire composée principalement de 2 parties :
le champ « code opération » qui indique au processeur le type de traitement à réaliser. Par exemple le code "00100110" donne l'ordre au CPU d'effectuer une multiplication.
le champ « opérandes » indique la nature des données sur lesquelles l'opération désignée par le « code opération » doit être effectuée.
Voici donc une instruction machine :
champ code opération | champ opérandes |
Les instructions machines sont relativement basiques (on parle d'instructions de bas niveau), voici quelques exemples :
les instructions arithmétiques (addition, soustraction, multiplication...)
les instructions de transfert de données qui permettent de transférer une donnée d'un registre du CPU vers la mémoire vive et vice versa.
les instructions de rupture de séquence : les instructions machines sont situées en mémoire vive, si, par exemple, l'instruction n°1 est située à l'adresse mémoire 343, l'instruction n°2 sera située à l'adresse mémoire 344, l'instruction n°3 sera située à l'adresse mémoire 345...
Au cours de l'exécution d'un programme, le CPU passe d'une instruction à une autre en passant d'une adresse mémoire à l'adresse mémoire immédiatement supérieure : après avoir exécuté l'instruction n°2 (situé à l'adresse mémoire 344), le CPU "va chercher" l'instruction suivante à l'adresse mémoire 344+1=345. Les instructions de rupture de séquence d'exécution encore appelées instructions de saut ou de branchement permettent d'interrompre l'ordre initial sous certaines conditions en passant à une instruction située une adresse mémoire donnée.
Par exemple, nous pouvons avoir une instruction qui ressemble à cela :
Imaginons qu'à l'adresse mémoire 354 nous avons l'instruction "si la valeur contenue dans le registre R1 est strictement supérieure à 0 alors exécuter l'instruction située à l'adresse mémoire 4521".
Si la valeur contenue dans le registre R1 est strictement supérieure à 0 alors la prochaine instruction à exécuter est l'adresse mémoire 4521, dans le contraire, la prochaine instruction à exécuter est à l'adresse mémoire 355.
Comme déjà dit, les opérandes désignent les données sur lesquelles le code opération de l'instruction doit être réalisée. Un opérande peut être de 3 natures différentes :
l'opérande est une valeur immédiate : l'opération est effectuée directement sur la valeur donnée dans l'opérande,
l'opérande est un registre du CPU : l'opération est effectuée sur la valeur située dans un des registres (R0,R1, R2,...), l'opérande indique de quel registre il s'agit,
l'opérande est une donnée située en mémoire vive : l'opération est effectuée sur la valeur située en mémoire vive à l'adresse XXXXX. Cette adresse est indiquée dans l'opérande.
Un programme en langage machine est donc une suite très très longue de « 1 » et de « 0 », ce qui vous en conviendrez est quelque peu rébarbatif à programmer : sur les dizaines de milliers de « 1 » et de « 0 » qui composent un programme en langage machine de taille modeste, une seule erreur, et votre programme ne fonctionne pas...imaginer la difficulté pour retrouver l'erreur !
Bref, programmer en langage machine est extrêmement difficile, pour pallier cette difficulté, les informaticiens ont remplacé les codes binaires abscons par des symboles mnémoniques (plus facile à retenir qu'une suite de « 1 » et de « 0 »).
Nous avons toujours des instructions machines du genre « additionne le nombre 125 et la valeur située dans le registre R2 , range le résultat dans le registre R1 », mais au lieu d'écrire « 11100010100000100001000001111101 », nous pourrons écrire « ADD R1,R2,#125 ». Dans les 2 cas, la signification est identique : « additionne le nombre 125 et la valeur située dans le registre R2 , range le résultat dans le registre R1 ».
Le processeur est uniquement capable d'interpréter le langage machine, un programme appelé « assembleur » assure donc le passage de « ADD R1,R2,#125 » à « 11100010100000100001000001111101 ». Par extension, on dit que l'on programme en assembleur quand on écrit des programmes avec ces symboles mnémoniques à la place de suites de « 1 » et de « 0 » . Aujourd'hui, plus personne n'écrit de programme directement en langage machine ;en revanche l'écriture de programme en assembleur est encore chose relativement courante.
Exemple : Quelques exemples d'instructions
Il n'est pas question d'apprendre à programmer en assembleur dans ce cours, mais voici tout de même quelques exemples d'instructions en assembleur :
Place la valeur stockée à l'adresse mémoire 78 dans le registre R1 (par souci de simplification, nous continuons à utiliser des adresses mémoire codées en base 10)
Place la valeur stockée dans le registre R3 en mémoire vive à l'adresse 125
Additionne le nombre 128 (une valeur immédiate est identifiée grâce au symbole #) et la valeur stockée dans le registre R0, place le résultat dans le registre R1
Additionne la valeur stockée dans le registre R1 et la valeur stockée dans le registre R2, place le résultat dans le registre R0
Soustrait le nombre 128 de la valeur stockée dans le registre R0, place le résultat dans le registre R1
Soustrait la valeur stockée dans le registre R2 de la valeur stockée dans le registre R1, place le résultat dans le registre R0
Place le nombre 23 dans le registre R1
Place la valeur stockée dans le registre R3 dans le registre R0
Nous avons une structure de rupture de séquence, la prochaine instruction à exécuter se situe en mémoire vive à l'adresse 45
Compare la valeur stockée dans le registre R0 et le nombre 23. Cette instruction CMP doit précéder une instruction de branchement conditionnel BEQ, BNE, BGT, BLT (voir ci-dessous)
Compare la valeur stockée dans le registre R0 et la valeur stockée dans le registre R1.
La prochaine instruction à exécuter se situe à l'adresse mémoire 78 si la valeur stockée dans le registre R0 est égale à 23
La prochaine instruction à exécuter se situe à l'adresse mémoire 78 si la valeur stockée dans le registre R0 n'est pas égale à 23
La prochaine instruction à exécuter se situe à l'adresse mémoire 78 si la valeur stockée dans le registre R0 est plus grand que 23
La prochaine instruction à exécuter se situe à l'adresse mémoire 78 si la valeur stockée dans le registre R0 est plus petit que 23
Arrête l'exécution du programme
Attention :
Il n'est pas question dans ce cours d'apprendre à programmer en assembleur. Le programme de 1NSI parle de :
« Dérouler l’exécution d’une séquence d’instructions simples du type langage machine. » en capacités attendues.
Utilisation des labels
Les instructions assembleur B, BEQ, BNE, BGT et BLT n'utilisent pas directement l'adresse mémoire de la prochaine instruction à exécuter, mais des « labels ». Un label correspond à une adresse en mémoire vive (c'est l'assembleur qui fera la traduction « label »-> « adresse mémoire »). L'utilisation d'un label évite donc d'avoir à manipuler des adresses mémoires en binaire ou en hexadécimale. Voici un exemple qui montre comment utiliser un label :
Dans l'exemple ci-dessus, nous avons choisi monLabel
comme nom de label. La ligne MOV R0,#18
a pour label monLabel
car elle est située juste après la ligne monLabel:
.
Concrètement, voici ce qui se passe avec ce programme :
Si la valeur stockée dans le registre R4 est supérieure à 18 on place le nombre 18 dans le registre R0 sinon on place le nombre 14 dans le registre R0.
ATTENTION : la présence du HALT
juste après la ligne MOV R0,#14
est indispensable, car sinon, la ligne MOV R0,#18
sera aussi exécutée (même si la valeur stockée dans le registre R4 est inférieure à 18 )
Du langage de haut niveau à l'assembleur
Comme dit plus haut, un CPU est uniquement capable d'exécuter des instructions machines. Les instructions des langages de haut niveau comme Python doivent être « transformées » en langage machine (compilation ou interprétation).
Voici ce que pourrait donner la « transformation » d'un programme Python tout simple en langage machine (plus précisément ici en assembleur).
Voici le programme Python :
et voici maintenant voici son équivalent en assembleur :
MOV R0, #4
STR R0,30
MOV R0, #8
STR R0,75
LDR R0,30
CMP R0, #10
BNE else
MOV R0, #9
STR R0,75
B endif
else:
LDR R0,30
ADD R0, R0, #1
STR R0,30
endif:
MOV R0, #6
STR R0,23
HALT
MOV R0, #4 STR R0,30 MOV R0, #8 STR R0,75 LDR R0,30 CMP R0, #10 BNE else MOV R0, #9 STR R0,75 B endif else: LDR R0,30 ADD R0, R0, #1 STR R0,30 endif: MOV R0, #6 STR R0,23 HALT
Comme vous pouvez le constater le moindre programme Python donne un programme assembleur relativement complexe.