Aller au contenu principal

Le jeu d'instructions

Ce que ce chapitre apporte

  • Reconnaître dans n'importe quelle instruction RISC-V l'une des trois formes d'écriture du jeu.
  • Lire la notation décalage(registre) d'un accès mémoire, et dire quelle adresse elle désigne.
  • Expliquer ce qu'est un immédiat, pourquoi il tient sur douze bits, et ce que l'assembleur fait d'une constante plus grande.
  • Démasquer une pseudo-instruction en comparant la ligne écrite et l'instruction réelle affichée à côté d'elle.
  • Retrouver dans le mémento la forme d'écriture et l'effet d'une instruction rencontrée dans un programme.

Le chapitre précédent a montré trois instructions en action et affirmé qu'il en existait une quarantaine. Cette quarantaine est le sujet de ce chapitre. Elle ne s'apprend pas par cœur : elle se consulte. Une fois connues les trois formes d'écriture qui couvrent presque tout, plus aucune instruction du module n'est une surprise, et il ne reste qu'à savoir où retrouver celle dont le nom échappe. C'est la fonction de cette page, qui est la seule du module conçue pour être relue plus tard : un mémento par familles, chacune accompagnée d'une figure qui la fait tourner. Les figures se modifient, et c'est la meilleure façon de s'assurer d'avoir compris une ligne du tableau.

Trois formes d'écriture, et presque rien d'autre

Une instruction RISC-V s'écrit toujours de la même façon : un nom, puis des opérandes séparés par des virgules. Ce qui change d'une famille à l'autre, c'est la nature de ces opérandes, et il n'y a que trois combinaisons à retenir.

formeécriturece qu'elle dit
deux registres vers un registreadd t0, a0, a1prendre le contenu de a0 et de a1, et ranger le résultat dans t0
un registre et une constante vers un registreaddi t1, a0, 100prendre le contenu de a0, la constante 100, et ranger le résultat dans t1
accès mémoirelw t3, 0(t2)lire en mémoire à l'adresse contenue dans t2, plus 0, et ranger dans t3

Dans les deux premières formes, le premier nom est la destination, et c'est le seul registre que l'instruction modifie. La constante de la deuxième forme porte un nom, immédiat, parce qu'elle est écrite dans l'instruction elle-même au lieu d'être cherchée ailleurs. Le i final de addi, andi, slli ne veut rien dire d'autre que cela.

La troisième forme est la seule qui parle à la mémoire, et sa notation mérite d'être lue lentement. 0(t2) ne désigne ni le registre t2, ni le nombre zéro : il désigne la case dont l'adresse est le contenu de t2 augmenté de zéro. Le nombre devant la parenthèse est un décalage, ajouté à l'adresse au moment de l'accès.

RISC-V RV32Ipas 0 sur 6pc = 0x0

programme

  1. 0x0add t0, a0, a1
  2. 0x4addi t1, a0, 100
  3. 0x8la t2, mesurelui t2, 1
  4. 0xcaddi zero, zero, 0
  5. 0x10lw t3, 0(t2)
  6. 0x14sw t0, 0(t2)

registres

sp0x2000
t00
t10
t20
a012
a15
t30

mémoire

0x1000mesure40

Rien n'a encore été exécuté. Le compteur ordinal pointe la première instruction, en 0x0.

Les trois formes à la suite. Exécuter pas à pas : t0 reçoit 17 de deux registres, t1 reçoit 112 d'un registre et d'une constante, puis la dernière paire de lignes travaille sur l'adresse 0x1000, d'abord pour en lire 40, ensuite pour y écrire 17. La ligne du bas signale les deux accès mémoire.

Une quatrième forme existe, plus courte, et le mémento la donne plus bas : les branchements, comme beq a0, a1, fin, nomment deux registres à comparer et une étiquette où aller. Ils ne rangent rien nulle part, et n'ont donc pas de destination. C'est la seule exception à la règle du premier nom.

Le rangement en mémoire inverse la règle

sw t0, 0(t2) ne range rien dans t0 : le registre nommé en premier est ici la source, et la destination est la case mémoire. Les trois instructions de rangement, sw, sh et sb, sont les seules du jeu où le premier nom n'est pas celui qui reçoit. Le chapitre suivant y revient longuement, parce que c'est la confusion la plus fréquente du module.

L'immédiat, et sa limite de douze bits

Une instruction occupe trente-deux bits, et pas un de plus. Ces trente-deux bits doivent loger l'opération à faire, le registre destination, le registre source, et la constante. Le compte est vite fait : il reste douze bits pour la constante, ce qui donne les valeurs de -2048 à 2047.

RISC-V RV32Ipas 0 sur 6pc = 0x0

programme

  1. 0x0addi t0, zero, 2047
  2. 0x4addi t1, zero, -2048
  3. 0x8li t2, 100000lui t2, 24
  4. 0xcaddi t2, t2, 1696
  5. 0x10li t3, 2048lui t3, 1
  6. 0x14addi t3, t3, -2048

registres

t00
t10
t20
t30

Rien n'a encore été exécuté. Le compteur ordinal pointe la première instruction, en 0x0.

Les deux premières lignes tiennent à la limite exacte de ce qu'un immédiat accepte. Les deux dernières la dépassent, et la colonne des instructions réelles montre que chacune se paie en deux instructions : lui pose les bits de poids fort, addi ajoute le reste. Regarder t2 passer par 98 304, et t3 par 4096.

Au-delà de cette limite, il n'y a pas d'instruction unique possible, et l'assembleur en fabrique deux. lui pose les vingt bits de poids fort de la valeur, puis addi ajoute les douze bits du bas. Le détour se voit dans la figure : t3 vaut 4096 avant de valoir 2048, ce qui est un chemin curieux pour atteindre un nombre aussi petit, et dit bien que 2048 est de l'autre côté de la frontière.

Une constante trop grande dans une instruction ordinaire est refusée

li s'occupe seul des grands nombres, mais addi non. Écrire addi t0, zero, 3000 fait échouer l'assemblage, sur un message qui nomme la ligne fautive et la limite :

l'immédiat ne tient pas sur douze bits signés : 3000 n'est pas entre -2048 et 2047

La marche à suivre est alors de charger la constante avec li dans un registre, puis d'employer la forme à deux registres, add au lieu de addi.

Les pseudo-instructions, et la colonne qui les démasque

Une pseudo-instruction est un nom que l'assembleur accepte à l'écriture et remplace par une ou deux instructions réelles avant de produire le programme. Elle n'existe pas dans le matériel. Les figures de ce module affichent toujours, à droite de la ligne écrite, l'instruction réellement exécutée, ce qui rend le remplacement visible au lieu d'avoir à le croire.

RISC-V RV32Ipas 0 sur 6pc = 0x0

programme

  1. 0x0mv t0, t1addi t0, t1, 0
  2. 0x4neg t2, t1sub t2, zero, t1
  3. 0x8not t3, t1xori t3, t1, -1
  4. 0xcseqz t4, t1sltiu t4, t1, 1
  5. 0x10snez t5, t1sltu t5, zero, t1
  6. 0x14nopaddi zero, zero, 0

registres

t00
t17
t20
t30
t40
t50

Rien n'a encore été exécuté. Le compteur ordinal pointe la première instruction, en 0x0.

Six lignes, et pas une seule instruction réelle du même nom. Lire la colonne de droite : mv devient une addition de zéro, neg une soustraction depuis zero, not un ou exclusif avec -1, seqz et snez des comparaisons à zéro, nop une addition sans effet. Le résultat final reste celui qu'annoncent les noms écrits.

Ce remplacement n'est pas une facilité gratuite. Il économise des instructions réelles, donc du silicium : copier un registre, poser une constante ou tester la nullité d'une valeur ne demandent aucune instruction dédiée dès lors que zero existe. Le jeu reste petit, et l'écriture reste lisible.

Voici la table complète des développements, telle que le moteur des figures les produit.

nop        -> addi zero, zero, 0
mv   t0,t1 -> addi t0, t1, 0
not  t0,t1 -> xori t0, t1, -1
neg  t0,t1 -> sub t0, zero, t1
seqz t0,t1 -> sltiu t0, t1, 1
snez t0,t1 -> sltu t0, zero, t1
sltz t0,t1 -> slt t0, t1, zero
sgtz t0,t1 -> slt t0, zero, t1
beqz t0,L  -> beq t0, zero, L
bnez t0,L  -> bne t0, zero, L
blez t0,L  -> bge zero, t0, L
bgez t0,L  -> bge t0, zero, L
bltz t0,L  -> blt t0, zero, L
bgtz t0,L  -> blt zero, t0, L
bgt  a,b,L -> blt b, a, L
ble  a,b,L -> bge b, a, L
bgtu a,b,L -> bltu b, a, L
bleu a,b,L -> bgeu b, a, L
j    L     -> jal zero, L
jr   t0    -> jalr zero, t0, 0
ret        -> jalr zero, ra, 0
call L     -> jal ra, L
li t0, 5   -> addi t0, zero, 5
li t0, 100000 -> lui t0, 24  puis  addi t0, t0, 1696

Deux lignes de cette table méritent un commentaire. Les quatre branchements bgt, ble, bgtu et bleu se contentent d'échanger leurs deux registres : le matériel ne sait tester que « plus petit » et « plus grand ou égal », et « plus grand que » s'obtient en posant la question à l'envers. Quant à li et à son voisin la, qui charge l'adresse d'une donnée, ils occupent deux instructions, la place étant réservée d'avance : la t2, mesure produit lui t2, 1 suivi d'un addi zero, zero, 0 qui ne sert qu'à occuper la place. Ce nop apparaît dans les figures, et n'est pas une erreur.

Compter les lignes ne donne pas le nombre d'instructions

Un programme de dix lignes peut produire quatorze instructions. C'est ce qui explique qu'un débogueur s'arrête parfois à un endroit qui ne correspond à aucune ligne écrite, et qu'une étiquette posée après une pseudo-instruction ne désigne pas toujours l'adresse attendue. En cas de doute, la colonne de droite d'une figure tranche.

Calculer

La première famille fait l'arithmétique et la logique. Chaque opération existe en deux versions : une entre deux registres, une entre un registre et un immédiat, reconnaissable à son i final.

écriturece qu'elle fait
add rd, rs1, rs2range dans rd la somme des deux registres sources
sub rd, rs1, rs2range dans rd le premier registre source moins le second
and rd, rs1, rs2range dans rd le et bit à bit des deux sources
or rd, rs1, rs2range dans rd le ou bit à bit des deux sources
xor rd, rs1, rs2range dans rd le ou exclusif bit à bit des deux sources
sll rd, rs1, rs2décale rs1 vers la gauche du nombre de bits contenu dans rs2
srl rd, rs1, rs2décale rs1 vers la droite, en remplissant de zéros
sra rd, rs1, rs2décale rs1 vers la droite, en recopiant le bit de signe
addi rd, rs1, immajoute la constante au registre source
andi rd, rs1, immapplique le et bit à bit avec la constante
ori rd, rs1, immapplique le ou bit à bit avec la constante
xori rd, rs1, immapplique le ou exclusif bit à bit avec la constante
slli rd, rs1, immdécale vers la gauche d'un nombre fixe de bits
srli rd, rs1, immdécale vers la droite d'un nombre fixe de bits, en remplissant de zéros
srai rd, rs1, immdécale vers la droite d'un nombre fixe de bits, en recopiant le bit de signe
RISC-V RV32Ipas 0 sur 15pc = 0x0

programme

  1. 0x0add t0, a0, a1
  2. 0x4sub t1, a1, a0
  3. 0x8and t2, a0, a1
  4. 0xcor t3, a0, a1
  5. 0x10xor t4, a0, a1
  6. 0x14sll t5, a0, a1
  7. 0x18srl t6, t5, a1
  8. 0x1csra s0, t1, a1
  9. 0x20addi s1, a0, -6
  10. 0x24andi s2, a0, 6
  11. 0x28ori s3, a0, 6
  12. 0x2cxori s4, a0, 6
  13. 0x30slli s5, a0, 1
  14. 0x34srli s6, a0, 2
  15. 0x38srai s7, t1, 1

registres

t00
t10
t20
s00
s10
a020
a16
s20
s30
s40
s50
s60
s70
t30
t40
t50
t60

Rien n'a encore été exécuté. Le compteur ordinal pointe la première instruction, en 0x0.

Toute la famille sur les mêmes deux valeurs, 20 et 6. Aller à la fin et relever les résultats : 26 pour l'addition, -14 pour la soustraction, 4, 22 et 18 pour les trois opérations logiques, 1280 pour le décalage à gauche. Les deux dernières lignes travaillent sur un nombre négatif : srai en retire -7, et sra en retire -1, parce que le bit de signe se recopie au lieu de laisser entrer des zéros.

Il n'y a ni multiplication ni division dans RV32I. Doubler une valeur se fait en l'ajoutant à elle-même, ou en la décalant d'un bit vers la gauche, slli t0, t0, 1. Multiplier par une autre valeur demande une boucle, ou l'extension du jeu d'instructions que ce parcours n'emploie pas.

Comparer

Cette famille ne décide de rien : elle pose un 0 ou un 1 dans un registre, et laisse le programme en faire ce qu'il veut. Le s de slt est celui de set, « poser ».

écriturece qu'elle fait
slt rd, rs1, rs2pose 1 dans rd si rs1 est plus petit que rs2, en lecture signée, et 0 sinon
sltu rd, rs1, rs2même chose, en lisant les deux valeurs comme des nombres non signés
slti rd, rs1, immcompare le registre à une constante, en lecture signée
sltiu rd, rs1, immcompare le registre à une constante, en lecture non signée
seqz rd, rspose 1 si rs vaut zéro, et 0 sinon
snez rd, rspose 1 si rs ne vaut pas zéro, et 0 sinon
sltz rd, rspose 1 si rs est négatif
sgtz rd, rspose 1 si rs est strictement positif
RISC-V RV32Ipas 0 sur 8pc = 0x0

programme

  1. 0x0slt t0, a0, a1
  2. 0x4slt t1, a1, a0
  3. 0x8slt t2, a2, a0
  4. 0xcsltu t3, a2, a0
  5. 0x10slti t4, a0, 9
  6. 0x14sltiu t5, a2, 9
  7. 0x18seqz t6, a0sltiu t6, a0, 1
  8. 0x1csnez s0, a0sltu s0, zero, a0

registres

t00
t10
t20
s00
a05
a19
a2-1
t30
t40
t50
t60

Rien n'a encore été exécuté. Le compteur ordinal pointe la première instruction, en 0x0.

Comparer les lignes t2 et t3 du tableau des registres à la fin. Les deux posent la même question sur les mêmes valeurs, -1 et 5, et répondent l'inverse l'une de l'autre : slt lit -1 comme un négatif et pose 1, sltu lit les mêmes bits comme un très grand nombre et pose 0.

Cette différence entre slt et sltu n'est pas un détail d'écriture : c'est la même suite de bits, lue de deux façons. Le chapitre sur les décisions revient dessus, et le module sur les masques en donne les conséquences.

Déplacer et charger une constante

Aucune de ces instructions ne calcule quoi que ce soit d'intéressant. Elles remplissent des registres, et ce sont les plus employées de tout le module.

écriturece qu'elle fait
mv rd, rsrecopie le contenu de rs dans rd
li rd, valeurpose une constante dans rd, quelle que soit sa taille
la rd, étiquettepose dans rd l'adresse de la donnée nommée, sans rien lire en mémoire
lui rd, immpose la constante dans les vingt bits de poids fort de rd, et des zéros en dessous
neg rd, rspose dans rd l'opposé de rs
not rd, rspose dans rd le contraire bit à bit de rs
nopne fait rien, et occupe quatre octets à le faire
RISC-V RV32Ipas 0 sur 8pc = 0x0

programme

  1. 0x0mv t0, a0addi t0, a0, 0
  2. 0x4li t1, 100addi t1, zero, 100
  3. 0x8li t2, 100000lui t2, 24
  4. 0xcaddi t2, t2, 1696
  5. 0x10la t3, mesurelui t3, 1
  6. 0x14addi zero, zero, 0
  7. 0x18lui t4, 2
  8. 0x1cnopaddi zero, zero, 0

registres

t00
t10
t20
a042
t30
t40

mémoire

0x1000mesure7

Rien n'a encore été exécuté. Le compteur ordinal pointe la première instruction, en 0x0.

Cinq façons de remplir un registre. Noter que la ligne la range 4096 dans t3, c'est-à-dire l'adresse 0x1000 de la donnée, et non la valeur 7 qui s'y trouve. Le nop qui la suit dans la colonne de droite n'a pas été écrit : il vient du développement de la pseudo-instruction.
Adresse et contenu

la t3, mesure donne se trouve la donnée. Lire ce qu'elle contient demande une seconde instruction, lw t4, 0(t3). Confondre les deux est l'erreur la plus coûteuse de la suite du module, et la figure ci-dessus permet de la voir en une fois : t3 contient 4096, la zone mémoire contient 7.

Lire et écrire la mémoire

Cette famille est celle du chapitre suivant, qui l'installe geste par geste. Le mémento la donne ici pour mémoire, et la figure la fait tourner en entier.

écriturece qu'elle fait
lw rd, déc(rs)charge dans rd les quatre octets lus à l'adresse rs + déc
lh rd, déc(rs)charge deux octets, en les lisant comme une valeur signée
lhu rd, déc(rs)charge deux octets, en les lisant comme une valeur non signée
lb rd, déc(rs)charge un octet, en le lisant comme une valeur signée
lbu rd, déc(rs)charge un octet, en le lisant comme une valeur non signée
sw rs2, déc(rs1)range les quatre octets de rs2 à l'adresse rs1 + déc
sh rs2, déc(rs1)range les deux octets de poids faible de rs2
sb rs2, déc(rs1)range l'octet de poids faible de rs2
RISC-V RV32Ipas 0 sur 12pc = 0x0

programme

  1. 0x0la t0, mesureslui t0, 1
  2. 0x4addi zero, zero, 0
  3. 0x8lw t1, 0(t0)
  4. 0xclw t2, 4(t0)
  5. 0x10sub t3, t2, t1
  6. 0x14sw t3, 8(t0)
  7. 0x18lb t4, 8(t0)
  8. 0x1clbu t5, 8(t0)
  9. 0x20lh t6, 8(t0)
  10. 0x24lhu s0, 8(t0)
  11. 0x28sh t1, 8(t0)
  12. 0x2csb t2, 8(t0)

registres

sp0x2000
t00
t10
t20
s00
t30
t40
t50
t60

mémoire

0x1000mesures12
0x10045
0x10080

Rien n'a encore été exécuté. Le compteur ordinal pointe la première instruction, en 0x0.

Un chargement, un calcul, un rangement, puis quatre relectures de la même case 0x1008 avec quatre largeurs différentes. La valeur rangée est -7, et les relectures donnent -7, 249, -7 et 65 529 : les mêmes bits, lus sur un ou deux octets, avec ou sans signe. Les deux dernières lignes réécrivent cette case par moitié puis par octet, et la zone mémoire de la figure le montre.

Retenir pour l'instant les deux règles qui ne changeront plus : le décalage s'ajoute à l'adresse contenue dans le registre, et le processeur ne calcule jamais directement sur la mémoire. Tout passe par l'un des trente-deux registres. Ranger et reprendre reprend cette famille en détail, avec l'hexadécimal, l'ordre des octets et l'alignement.

Décider

Les branchements sont les seules instructions qui modifient le compteur ordinal autrement qu'en l'avançant de quatre. Ils comparent deux registres et sautent à une étiquette si la comparaison est vraie ; sinon l'exécution continue à la ligne suivante.

écriturece qu'elle fait
beq rs1, rs2, Lsaute à L si les deux registres sont égaux
bne rs1, rs2, Lsaute à L s'ils sont différents
blt rs1, rs2, Lsaute si rs1 est plus petit que rs2, en lecture signée
bge rs1, rs2, Lsaute si rs1 est plus grand ou égal à rs2, en lecture signée
bltu rs1, rs2, Lsaute si rs1 est plus petit, en lecture non signée
bgeu rs1, rs2, Lsaute si rs1 est plus grand ou égal, en lecture non signée
bgt, ble, bgtu, bleules mêmes questions posées dans l'autre sens, par échange des deux registres
beqz rs, Lsaute si rs vaut zéro
bnez rs, Lsaute si rs ne vaut pas zéro
blez, bgez, bltz, bgtzcomparaisons à zéro, sur le même modèle
RISC-V RV32Ipas 0 sur 14pc = 0x0

programme

  1. 0x0li t0, 0addi t0, zero, 0
  2. 0x4beq a0, a1, un
  3. 0x8addi t0, t0, 1
  4. 0xcbne a0, a1, deux
  5. 0x10addi t0, t0, 2
  6. 0x14blt a0, a1, trois
  7. 0x18addi t0, t0, 4
  8. 0x1cbge a0, a1, quatre
  9. 0x20addi t0, t0, 8
  10. 0x24bltu a0, a1, cinq
  11. 0x28addi t0, t0, 16
  12. 0x2cbgeu a0, a1, six
  13. 0x30addi t0, t0, 32
  14. 0x34beqz a0, septbeq a0, zero, sept
  15. 0x38addi t0, t0, 64
  16. 0x3cbnez a0, huitbne a0, zero, huit
  17. 0x40addi t0, t0, 128
  18. 0x44j finjal zero, fin
  19. 0x48addi t0, t0, 256

registres

t00
a04
a19

Rien n'a encore été exécuté. Le compteur ordinal pointe la première instruction, en 0x0.

Huit branchements posés sur les mêmes valeurs, 4 et 9. Chacun qui ne saute pas laisse exécuter l'addition qui le suit. Aller à la fin : t0 vaut 105, soit 1 + 8 + 32 + 64, et désigne exactement les quatre branchements non pris, beq, bge, bgeu et beqz.

j n'est pas un branchement mais un saut sans condition, et la colonne de droite le montre déjà : c'est jal zero, fin, une instruction de la famille suivante. Décider et Répéter traitent cette famille en détail, avec l'inversion de condition qu'un si … sinon demande et la forme d'une boucle.

Sauter et appeler

Un saut change le compteur ordinal. Un appel fait la même chose en gardant de quoi revenir, et c'est toute la différence.

écriturece qu'elle fait
j Lsaute à L, sans rien garder
jal rd, Lsaute à L en rangeant dans rd l'adresse de l'instruction suivante
call Lsaute à L en rangeant l'adresse de retour dans ra
jalr rd, rs, décsaute à l'adresse contenue dans un registre, et range l'adresse de retour dans rd
jr rssaute à l'adresse contenue dans rs, sans rien garder
retrevient à l'adresse rangée dans ra
RISC-V RV32Ipas 0 sur 6pc = 0x0

programme

  1. 0x0call doublerjal ra, doubler
  2. 0x4li t0, 1addi t0, zero, 1
  3. 0x8j finjal zero, fin
  4. 0xcadd a0, a0, a0
  5. 0x10retjalr zero, ra, 0
  6. 0x14li t1, 2addi t1, zero, 2

registres

ra0
t00
t10
a07

Rien n'a encore été exécuté. Le compteur ordinal pointe la première instruction, en 0x0.

Suivre le compteur ordinal dans l'en-tête de la figure plutôt que les registres. Le call saute en 0xc et range 4 dans ra, le ret y revient, et l'exécution reprend là où elle s'était arrêtée. Sans ce 4 rangé dans ra, aucun retour ne serait possible.

Le registre ra ne peut retenir qu'une adresse à la fois, ce qui suffit pour un appel et pas pour deux. Appeler résout ce problème avec la pile, et donne la convention qui rend un code appelable par un autre.

Ce qui n'est pas dans ce mémento

Ces tableaux couvrent les instructions que ce parcours emploie. RV32I en compte quelques autres, que le module ne rencontre pas : auipc, qui calcule une adresse relative au compteur ordinal, fence, qui ordonne les accès mémoire entre processeurs, ebreak, qui rend la main à un débogueur, et les instructions d'accès aux registres de contrôle. Une dernière, ecall, sert à demander un service au système et fait l'objet de Agir sur le monde.

Essayer plutôt que croire

Chaque figure de ce module porte un bouton « Modifier le programme ». Il ouvre le texte du programme, qui se réassemble et se réexécute à chaque frappe ; une erreur d'assemblage s'affiche à la place de la machine, avec son message, et un bouton ramène au programme d'origine. Un mémento cesse alors d'être une liste à lire pour devenir une liste à essayer, et c'est la seule façon de vérifier qu'une ligne de tableau a bien été comprise.

RISC-V RV32Ipas 0 sur 2pc = 0x0

programme

  1. 0x0add t0, a0, a1
  2. 0x4slli t1, t0, 2

registres

t00
t10
a020
a16

Rien n'a encore été exécuté. Le compteur ordinal pointe la première instruction, en 0x0.

Deux instructions seulement, pour laisser de la place. t0 vaut 26, t1 vaut 104. Ouvrir « Modifier le programme » et procéder aux deux essais décrits en dessous.

Deux modifications à tenter sur cette figure, dans l'ordre.

  1. Remplacer add par sub, puis prévoir la nouvelle valeur de t1 avant de regarder. Recommencer en échangeant a0 et a1 dans la même ligne : la soustraction n'est pas commutative, et le résultat change de signe.
  2. Écrire addi t2, zero, 3000 en troisième ligne. L'assemblage est refusé, et le message nomme la limite des douze bits. Remplacer ensuite par li t2, 3000, qui passe, et lire la colonne des instructions réelles pour voir les deux instructions que l'assembleur a fabriquées.

Une troisième vaut pour toutes les figures de cette page : y ajouter la ligne d'une instruction du tableau qui reste obscure, avec des valeurs choisies exprès, et regarder ce qu'elle produit.

Vérification rapideon peut se reprendre

1.Dans addi t1, a0, 100, que désigne le nombre 100 ?

2.Que désigne 8(t0) dans lw t1, 8(t0) ?

3.Pourquoi un immédiat ne peut-il pas dépasser 2047 ?

4.Quelle instruction réelle l'assembleur produit-il pour mv t0, t1 ?

5.slt t2, a2, a0 et sltu t3, a2, a0 s'exécutent sur a2 valant -1 et a0 valant 5. Que donnent-elles ?

6.Quelle est la différence entre j doubler et call doubler ?

La méthode

  1. Reconnaître la forme avant de chercher le nom : deux registres et une destination, un registre, une constante et une destination, ou un accès mémoire en décalage(registre). La forme dit déjà d'où viennent les valeurs et où va le résultat.
  2. Lire le premier nom comme la destination, sauf pour les trois rangements sw, sh, sb et pour les branchements, qui n'en ont pas.
  3. Vérifier qu'une constante tient sur douze bits avant de l'écrire dans une instruction ordinaire, et passer par li dans un registre au-delà de 2047.
  4. Comparer la ligne écrite et l'instruction réelle chaque fois qu'un compte d'instructions ou une adresse d'étiquette est en jeu.
  5. Chercher l'instruction dans la famille plutôt que dans une liste alphabétique : calculer, comparer, déplacer, lire et écrire, décider, sauter et appeler. Une instruction inconnue appartient forcément à l'une des six.
  6. Essayer la ligne au lieu de la relire : ouvrir « Modifier le programme » sur la figure de la famille, ajouter l'instruction avec des valeurs choisies, et regarder le registre changer.

Synthèse

  • Presque toute instruction RISC-V prend l'une de trois formes : deux registres vers un registre, un registre et une constante vers un registre, ou un accès mémoire noté décalage(registre). Les branchements forment une quatrième famille, sans destination.
  • La notation 8(t0) désigne la case dont l'adresse est le contenu de t0 augmenté de 8, et jamais autre chose.
  • Un immédiat est une constante écrite dans l'instruction. Il tient sur douze bits signés, de -2048 à 2047 ; au-delà, l'assembleur fabrique deux instructions, lui puis addi.
  • Une pseudo-instruction est un nom remplacé à l'assemblage. mv, li, la, j, ret, call, beqz et les autres n'existent pas dans le matériel, et la colonne de droite des figures affiche ce qui s'exécute réellement.
  • Le mémento range les instructions du module en six familles : calculer, comparer, déplacer et charger une constante, lire et écrire la mémoire, décider, sauter et appeler. Trois d'entre elles ont leur chapitre.
  • Il n'y a ni multiplication ni division dans RV32I, et les trois rangements en mémoire sont les seules instructions dont le premier registre nommé est une source.
  • Chaque figure se modifie, et se réassemble à chaque frappe : une instruction dont l'effet reste flou se vérifie en l'ajoutant à un programme plutôt qu'en relisant sa ligne de tableau.

Ces six familles suffisent à lire n'importe quel programme du module. Reste la question que les registres seuls ne peuvent pas régler : où vivent les données qui ne tiennent pas dans trente-deux cases. Ranger et reprendre ouvre la mémoire, et avec elle la famille des chargements et des rangements.