UNIVERSITE Joseph FOURIER, Grenoble U.F.R. d’ Informatique et Maths. Appliquées Licence Sciences et Technologie Parcours INM, INF, IAG, BIN AFONSO Nicolas VIVIEN Jonathan MIN L2 10/03/2010 UE INF241 : Introduction aux Architectures Logicielles et Matérielles Feuilles de compte-rendu pour le travail pratique des séances 5 et 6: codage des structures de contrôle Introduction: dans la continuation de notre apprentissage en langage d'assembleur, nous allons aujourd'hui apprendre à coder et résoudre des algorithmes en utilisant des structures de contrôle. Vous trouverez en fin de ce compte-rendu le listing de nos programmes. 1/ Accès à un tableau Nous commençons ce TP en travaillant sur les tableaux. On complète le fichier tableau.s afin de réaliser les dernières affectations. Nous rencontrons déjà un message d'erreur lorsque nous essayons de le compiler: tableau.s: Assembler messages: tableau.s:16: Error: invalid constant (14d) after fixup tableau.s:24: Error: invalid constant (22b) after fixup Ce souci vient du fait qu’on ne peut passer toutes les valeurs immédiates que l’on veut dans nos instructions d’affectation. On pourra consulter la page 9 de la doc. Technique pour plus d’informations. Après un changement mineur dans notre énoncé (on veut désormais affecter dans notre tableau les valeurs 11, 22, 33,44, 55), la compilation se passe sans problèmes. Attention tout de même à ne pas oublier de s’aligner sur une adresse multiple de 4 (commande .balign 4) avant la déclaration de réservation de mémoire .skip ! Nous avons fait cet oubli, repéré grâce au débogueur (que nous allons voir juste après) ; en résultait une non exécution du programme jusqu’au bout. Nous allons maintenant apprendre à nous servir du déboguer gdb, dans le but de suivre l’exécution de nos programmes plus en détail, pas à pas. Suivons l’exécution de notre programme tableau.s grâce à cet outil. La commande info reg nous permet de connaître à tout moment les valeurs stockées dans tous les registres. Regardons ce qu’on obtient en tout début de programme (aucune instruction n’a été exécutée). info reg r0 r1 r2 r3 r4 0x1 1 0x1ffff8 2097144 0x0 0 0xffffffff 4294967295 0x1 1 r5 r6 r7 r8 r9 r10 r11 r12 sp lr pc fps cpsr 0x1ffff8 2097144 0x0 0 0x0 0 0x0 0 0x0 0 0x200100 2097408 0x0 0 0x1fffe8 2097128 0x1ffff8 0x1ffff8 0x81fc 33276 0x8218 0x8218 <main> 0x0 0 0x40000013 1073741843 Aucune valeur particulière, nous sommes d’accord. Si maintenant nous réutilisons cette commande après avoir exécuté les trois premières instructions du programme (on les rappelle) : ldr r0, ptr_debutTAB @ on charge dans r0 l'adresse de la première case mov r1, #11 @ r1 <- 11 str r1, [r0] @ on sauvegarde dans la case courante du tableau la valeur de r1 info reg r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 sp lr pc fps cpsr 0x12ec0 77504 0xb 11 0x0 0 0xffffffff 4294967295 0x1 1 0x1ffff8 2097144 0x0 0 0x0 0 0x0 0 0x0 0 0x200100 2097408 0x0 0 0x1fffe8 2097128 0x1ffff8 0x1ffff8 0x81fc 33276 0x8224 0x8224 <main+12> 0x0 0 0x40000013 1073741843 On constate que notre programme fonctionne bien : on a chargé dans r0 l’adresse de la première case de notre tableau et la valeur 11 dans r1. On peut donc pour finir l’analyse de ce premier programme noter les valeurs successives de r0 : 77504, 77508, 77512, 77516, 77520. De même pour le contenu de la mémoire à partir de l’adresse debutTAB en début de programme et après l’exécution de toutes les instructions (on utilise la commande x/5w &debutTAB) : x/5w &debutTAB 0x12ec0 <debutTAB>: 0x0000000b 0x00000016 0x12ed0 <debutTAB+16>: 0x00000037 0x00000021 0x0000002c On retrouve l’adresse de la première case du tableau (débutTAB) : 0x12ec0, 0x0000000b en hexadécimal est bien égal à 11 en décimal, 0x00000016 = 22, 0x00000021 = 33, 0x0000002c = 4. La dernière case du tableau quand à elle contient bien la valeur 55 (codé en hexadécimal par 0x00000037) et est bien à l’adresse 0x12ed0 (soit debutTAB +16). 2/ Codage d’une itération Regardons maintenant comment procéder pour coder une itération. On veut créer un programme ayant le même résultat que précédemment, mais cette fois-ci nous voulons utiliser une itération pour être bien plus efficace (imaginez donc devoir affecter 500 cases de tableau, à 3 instructions par affectation, il nous faudrait taper au minimum 1 500 lignes de code !). Ce programme est déjà réalisé, c’est le fichier iteration.s. Grâce à gdb, regardons quelles sont les valeurs contenues à chaque itération dans les registres r0 et r2: r0: ce registre doit contenir, d'après l'algorithme donné, l'adresse courante de la case de notre tableau que l’on souhaite affecter. r0 prend pour valeurs successives : 77484 (debut_TAB+0*4), 77488 (debut_TAB +1*4), 77492 debut_TAB + 2*4), 77496 (debut_TAB + 3*4), 77500 ( debut_TAB + 4*4) r2: ce registre doit contenir, d'après l'algorithme donné, la variable d’incrémentation. r2 prend pour valeurs successives : 0, 1, 2, 3, 4, 5 La valeur contenue dans r2 à la fin de l’itération, c’est-à-dire lorsque le contrôle est à l’étiquette fintq est donc 5 (normal car on veut réaliser 5 fois notre affectation). Supposons maintenant que l’algorithme soit écrit avec tant que i <= 4 au lieu de tant que i <> 5. Le tableau contiendra les mêmes valeurs à la fin de l’itération ! En effet dans cette deuxième version on va bien réaliser 5 fois notre affectation : pour i=0, 1, 2, 3, 4. Exactement comme dans notre premier algorithme. On décide de coder avec le nouvel algorithme jusqu’à la fin de cette partie. Supposons que le tableau soit maintenant un tableau de mots de 16 bits. On modifie le programme qui devient iteration2.s (voir partie listing). Il nous suffit juste de prendre en compte que chaque case de notre tableau ne fait plus que 2octets (car 16bits = 2octets) : on réserve donc moins de place en mémoire en début de programme, on ne multiplie i que par 2 à chaque itération. Même chose pour un tableau d’octets. Dans ce cas, chaque case ne fait plus qu’un seul octet. Voir le programme iteration3.s 3/ Calcul de la suite de “Syracuse” On désire désormais dans cette troisième partie créer un programme en assembleur qui nous calcule les n premiers termes de la suite de Syracuse. Donnons pour commencer les valeurs de la suite de Syracuse pour U0 = 15, calculées à la main: U0=15, U1= 46, U2=23, U3= 70, U4=35, U5= 106, U6= 53,U7= 160, U8= 80, U9= 40, U10= 20, U11= 10, U12=5 U13= 16, U14= 8, U15= 4, U16= 2, U17=1, U18=4, U19=2, etc... On remarque que cette suite converge vers 1 avec un cycle de 3. Pour coder ce programme il nous a fallu tout simplement suivre l’algorithme et les indications fournies dans le poly (trop facile). On se limitera à calculer les 50 premiers termes de cette suite. On vérifie sous gdb le bon déroulement de notre programme : r4 : ce registre va nous servir de compteur du rang de la suite. r4 prend pour valeur successives : 0, 1, 2, 3, 4, 5, ….. 17. On a bien retrouvé que notre suite converge vers 1 à partir du 17ème terme. r0 : ce registre contient à l’initialisation du programme la valeur qu’on attribue au premier terme (U0) de la suite (ici 15). r0 est ensuite modifié selon l’algorithme de notre programme. C’est donc la valeur courante de la suite. r0 prend bien pour valeurs successives les mêmes nombres que l’on a calculé à la main un peu plus haut : 15, 46, 23, 70, …..,1 r3 : ce registre va nous servir de comparaison. On continue notre boucle tant que la valeur courante de la suite (contenue dans r0) est différente de 1. r3 contient la valeur 1 et n’est pas modifié durant l’exécution du programme. La vérification sous gdb nous à permis de vérifier le bon fonctionnement de notre programme. Nous réaliserons dans la dernière partie de ce TP un autre programme qui calculera des valeurs de suite, mais nous utiliserons les fonctions d’affichage, ce qui nous permettra de vérifier l’exactitude de notre programme sans utiliser gdb et taper à maintes et maintes reprises -s (la commande sous gdb pour passer à l’instruction suivante). Passons maintenant au gros programme de ce TP… 4/ Tables de multiplication On souhaite désormais réaliser un programme qui affiche un tableau avec les tables de multiplication de 1 à 10 suivant l'exemple donné dans le polycopié. Pour réaliser celui-ci il nous faudra utiliser deux boucles itératives imbriquées ( une pour le parcours des lignes et une autre pour celui des colonnes ). En s'inspirant de l'algorithme donné en page 5 du polycopié et en travaillant avec méthode ( utilisation de nombreuses étiquettes, de schémas pour s'y retrouver ), la réalisation ne nous à pas trop donné de problèmes. Nous avons essayé de détailler aux maximum, par des commentaires, notre programme ( voir partie listing ). Pour l'affichage nous avons dû jouer entre les EcrChn et EcrChaine (problème relaté avec plus de précisions au programme suivant). A l'exécution de celui-ci on obtient bien le résultat recherché: arm-elf-run tabmult | 1| 2| 3| 4| 5| 6| 7| 8| 9| 10| |---|---|---|---|---|---|---|---|---|---| | 2| 4| 6| 8| 10| 12| 14| 16| 18| 20| |---|---|---|---|---|---|---|---|---|---| | 3| 6| 9| 12| 15| 18| 21| 24| 27| 30| |---|---|---|---|---|---|---|---|---|---| | 4| 8| 12| 16| 20| 24| 28| 32| 36| 40| |---|---|---|---|---|---|---|---|---|---| | 5| 10| 15| 20| 25| 30| 35| 40| 45| 50| |---|---|---|---|---|---|---|---|---|---| | 6| 12| 18| 24| 30| 36| 42| 48| 54| 60| |---|---|---|---|---|---|---|---|---|---| | 7| 14| 21| 28| 35| 42| 49| 56| 63| 70| |---|---|---|---|---|---|---|---|---|---| | 8| 16| 24| 32| 40| 48| 56| 64| 72| 80| |---|---|---|---|---|---|---|---|---|---| | 9| 18| 27| 36| 45| 54| 63| 72| 81| 90| |---|---|---|---|---|---|---|---|---|---| | 10| 20| 30| 40| 50| 60| 70| 80| 90|100| |---|---|---|---|---|---|---|---|---|---| Victoire ! 5/ Calcul des nombres de la suite de Fibonacci Pour finir ce TP, nous allons créer un programme qui va nous permettre de calculer les n premiers termes de la suite de Fibonacci. Pour nous guider, on nous fournit un programme fibonacci.c (donc en langage c) sur lequel nous devons nous appuyer pour réaliser notre programme homologue en langage d'assemblage ARM. Le principe de réalisation va être d'utiliser des itérations, et de conserver les valeurs déjà calculées dans un tableau. Vérifions tout d'abord que le programme « modèle » fibonacci.c fonctionne bien. L'exécution de celui-ci nous donne: ./fib indice = 0, valeur = 0 indice = 1, valeur = 1 indice = 2, valeur = 1 indice = 3, valeur = 2 indice = 4, valeur = 3 indice = 5, valeur = 5 indice = 6, valeur = 8 indice = 7, valeur = 13 indice = 8, valeur = 21 indice = 9, valeur = 34 indice = 10, valeur = 55 indice = 11, valeur = 89 indice = 12, valeur = 144 indice = 13, valeur = 233 indice = 14, valeur = 377 indice = 15, valeur = 610 indice = 16, valeur = 987 indice = 17, valeur = 1597 indice = 18, valeur = 2584 indice = 19, valeur = 4181 indice = 20, valeur = 6765 indice = 21, valeur = 10946 indice = 22, valeur = 17711 indice = 23, valeur = 28657 indice = 24, valeur = 46368 indice = 25, valeur = 75025 indice = 26, valeur = 121393 indice = 27, valeur = 196418 indice = 28, valeur = 317811 indice = 29, valeur = 514229 indice = 30, valeur = 832040 indice = 31, valeur = 1346269 indice = 32, valeur = 2178309 indice = 33, valeur = 3524578 indice = 34, valeur = 5702887 indice = 35, valeur = 9227465 indice = 36, valeur = 14930352 indice = 37, valeur = 24157817 indice = 38, valeur = 39088169 indice = 39, valeur = 63245986 indice = 40, valeur = 102334155 indice = 41, valeur = 165580141 indice = 42, valeur = 267914296 indice = 43, valeur = 433494437 indice = 44, valeur = 701408733 indice = 45, valeur = 1134903170 indice = 46, valeur = 1836311903 indice = 47, valeur = 2971215073 indice = 48, valeur = 512559680 indice = 49, valeur = 3483774753 Après quelques calculs à la main, nous confirmons que ce programme calcule les valeurs correctes des 50 premiers termes de la suite de Fibonacci. On réalise notre programme (voir partie listing) et vérifions à son tour qu'il nous retourne des valeurs correctes: arm-elf-run fibonacci indice = 0, valeur = 0 indice = 1, valeur = 1 indice = 2, valeur = 1 indice = 3, valeur = 2 indice = 4, valeur = 3 indice = 5, valeur = 5 indice = 6, valeur = 8 indice = 7, valeur = 13 indice = 8, valeur = 21 indice = 9, valeur = 34 indice = 10, valeur = 55 indice = 11, valeur = 89 indice = 12, valeur = 144 indice = 13, valeur = 233 indice = 14, valeur = 377 indice = 15, valeur = 610 indice = 16, valeur = 987 indice = 17, valeur = 1597 indice = 18, valeur = 2584 indice = 19, valeur = 4181 indice = 20, valeur = 6765 indice = 21, valeur = 10946 indice = 22, valeur = 17711 indice = 23, valeur = 28657 indice = 24, valeur = 46368 indice = 25, valeur = 75025 indice = 26, valeur = 121393 indice = 27, valeur = 196418 indice = 28, valeur = 317811 indice = 29, valeur = 514229 indice = 30, valeur = 832040 indice = 31, valeur = 1346269 indice = 32, valeur = 2178309 indice = 33, valeur = 3524578 indice = 34, valeur = 5702887 indice = 35, valeur = 9227465 indice = 36, valeur = 14930352 indice = 37, valeur = 24157817 indice = 38, valeur = 39088169 indice = 39, valeur = 63245986 indice = 40, valeur = 102334155 indice = 41, valeur = 165580141 indice = 42, valeur = 267914296 indice = 43, valeur = 433494437 indice = 44, valeur = 701408733 indice = 45, valeur = 1134903170 indice = 46, valeur = 1836311903 indice = 47, valeur = 2971215073 indice = 48, valeur = 512559680 indice = 49, valeur = 3483774753 Nous avons donc bien réalisé ce programme. Il nous a fallu cependant faire attention à bien jongler dans l'utilisation de EcrNdecim32, EcrNdecimal32, EcrChn et EcrChaine si l'on veux obtenir exactement la même présentation qu'avec le programme en langage c ( EcrNdecim32 et EcrChn ne font pas un retour à la ligne après l'affichage alors que les deux autres instructions oui ). Pour conclure avec ce programme on aurait pu l'optimiser quelque peu en ne réalisant qu'une seule boucle où calcul des valeurs de la suite et affichage de ceux-ci sont mêlés ( un peu comme dans notre programme des tables de multiplication précédant ). Conclusion: Nous avons manipulé aujourd'hui bon nombre d'itérations (voir même d'itérations imbriquées), ainsi que plus généralement des structures de contrôle, ce qui nous a permis de maîtriser cet outil important de la programmation. Ce TP nous à également permis de remanier tous nos acquis en langage d'assemblage ARM accumulés jusqu'ici. Nous attendons maintenant impatiemment le prochain TP où nous pourrons enfin apprendre à coder des fonctions...