Décisions et répétitions

Décisions et répétitions

Par défaut, le CPU exécute vos instructions dans l'ordre où elles apparaissent, de haut en bas. À elle seule, cet ordre ne peut jamais faire un choix ni répéter un travail. Une branche est la façon dont un programme quitte ce chemin rectiligne : elle teste une condition et, lorsque la condition est vraie, dirige le CPU vers un autre endroit au lieu de la ligne suivante. Chaque décision et chaque boucle proviennent de ce seul mécanisme.

Il n'y a ni if ni for. Vous les construisez à partir d'étiquettes (positions nommées) et de branches (saut vers une position lorsqu'une condition est vraie). Une étiquette est un nom suivi de :.

Pour comprendre comment une branche s'exécute, vous devez connaître le compteur de programme (PC). Le PC est un registre spécial, caché à votre code, qui contient l'adresse mémoire de l'instruction en cours d'exécution. Une instruction de branche écrit une nouvelle valeur dans le PC, de sorte que le CPU récupère son instruction suivante à l'étiquette cible plutôt qu'à la ligne de code suivante.

La famille des branchements

Chaque branche compare deux registres et saute à l'étiquette uniquement lorsque le test est vrai. Lorsque le test échoue, l'exécution se poursuit à la ligne suivante. Six tests forment le cœur :

Les six branchements réels
beq a, b, label # branche si a == b
bne a, b, label # branche si a != b
blt a, b, label # branche si a < b (signé)
bge a, b, label # branche si a >= b (signé)
bltu a, b, label # branche si a < b (non signé)
bgeu a, b, label # branche si a >= b (non signé)

et testent l'égalité et ignorent le signe. et forment la paire d'ordre, inférieur à et supérieur ou égal, et comparent les registres comme des nombres signés. et effectuent les mêmes comparaisons mais traitent les valeurs comme non signées, ce qui convient aux adresses ou aux compteurs qui ne sont jamais négatifs.

Que signifient signé et non signé ? Un registre contient 32 bits, et les mêmes bits peuvent être lus de deux façons. Lus comme non signés, ils forment un nombre entier ordinaire allant de 0 à environ 4 milliards. Lus comme signés, le bit le plus haut compte comme un signe moins, de sorte que la moitié des motifs de bits représentent des nombres négatifs. Les bits eux-mêmes ne changent pas. Seule la façon dont vous les lisez change. C'est pourquoi il existe deux ensembles de comparaisons : utilisez les signées (, ) pour les nombres ordinaires qui peuvent être négatifs, et les non signées (, ) pour les valeurs qui ne sont jamais négatives, comme les adresses mémoire.

Il n'y a pas de « branche si supérieur » ni de « branche si inférieur ou égal » directes. Vous les obtenez en échangeant les deux registres, et l'assembleur fournit et comme pseudo-branches qui effectuent l'échange pour vous, de sorte que devient . Deux autres raccourcis : et testent un registre par rapport à zéro, et saute sans aucune condition.

Sommer les nombres de 1 à 5 avec une boucle
li t0, 0 # somme = 0
li t1, 1 # i = 1
li t2, 6 # limite = 6 (boucle tant que i < 6)
loop:
bge t1, t2, done # quitte la boucle si i >= 6
add t0, t0, t1 # somme += i
addi t1, t1, 1 # i++
j loop # revient vérifier
done:
mv a0, t0 # affiche la somme (doit être 15)
li a7, 1
ecall
li a7, 10
ecall

Tracez-le. t0 contient la somme courante (commence à 0), t1 contient le compteur i (commence à 1), et t2 contient la limite 6. À chaque passage dans loop : quitte la boucle dès que i atteint 6. Sinon ajoute i à la somme, augmente i de un, et saute en arrière pour tester à nouveau. La boucle ajoute 1, 2, 3, 4 et 5, et done affiche le total, 15.

Boucle avec une branche
loop:
bge t1, t2, done
add t0, t0, t1
addi t1, t1, 1
j loop
i (t1)1
moi (t0)0
ouinot1 < 6?sum += t1t1 += 1terminé

bge compare t1 avec t2 pour décider si la boucle continue.

La figure ci-dessous marque l'instruction qui met à jour le compteur et celle qui met à jour la somme courante, les distinguant du reste de la boucle. fait avancer le compteur, et le qui suit ajoute la nouvelle valeur du compteur à la somme. La dernière image montre le test qui termine la boucle. Lorsque t1 devient 6, transfère le contrôle à done plutôt que de laisser le corps s'exécuter à nouveau. Ce test est ce qui sépare un passage qui se répète du passage qui s'arrête.
💡
Avancez dans le programme avec Step plutôt que Run. Au fur et à mesure, t1 augmente et le compteur de programme (pc) saute en arrière vers loop. Chaque fois que pc revient à loop, la condition de branche était fausse, donc la boucle s'exécute à nouveau.
⚠
Si la condition de sortie est erronée, la boucle ne se termine jamais. Le simulateur arrête une boucle emballée, mais la logique de branche est la vôtre à maîtriser.