ARCHITECTURE DES ORDINATEURS IG3 2012-2013 TD 4 : Introduction au langage MIPS Clément Jonquet {[email protected]} Environnement de travail Nous allons utiliser un simulateur pour le langage MIPS. Étant donné la grande utilisation de MIPS, plusieurs simulateurs sont disponibles (e.g., SPIM, MARS, MIPSter, etc.). Si vous êtes sur votre machine personnelle vous pouvez utiliser SPIM ou MARS ; si vous êtes sur une machine Polytech, il vous faut utiliser MARS, qui est une application Java et qui ne nécessite pas d'installation. SPIM (http://pages.cs.wisc.edu/~larus/spim.html) de James R. Larus (Microsoft, Univ. of Wisconsin) ; MARS (http://courses.missouristate.edu/KenVollmar/MARS/index.htm) de Ken Vollmar (Missouri State Univ.). Les 2 environnements de programmation proposent un mode "step-by-step" qui permet d'exécuter les instructions les unes après les autres. Ce mode va nous intéresser pour comprendre les mécanismes de la programmation assembleur. Quel que soit l'environnement choisi, les généralités de MIPS nécessaires au TD sont rappelés ci-après. Généralités MIPS Syntaxe Les commentaires commencent par le symbole # et se terminent à la n de la ligne. Un identicateur est une séquence de caractères alphanumériques, de soulignés (_) et de points (.), qui ne commence pas par un chire. Les codes opération d'instruction sont des mots réservés qui ne peuvent pas être utilisés comme identicateurs. Les étiquettes sont déclarées en les plaçant au début d'une ligne et en les faisant suivre du symbole :. Les nombres sont en base 10 par défaut. S'ils sont précédés de 0x ils sont interprétés comme hexadécimaux. Les chaînes de caractères sont encadrées par des doubles apostrophes ". Certains caractères spéciaux dans les chaînes de caractères suivent la convention C : retour-chariot : \n tabulation : \t guillemet : \" Directives .ascii str Enregistre en mémoire la chaîne de caractères str, mais ne la termine pas par un caractère nul. .asciiz str Enregistre en mémoire la chaîne de caractères str et la termine par un caractère nul. .data<@> Les éléments qui suivent sont enregistrés dans le segment de données. Si l'argument optionnel @ est présent, les éléments qui suivent sont enregistrés à partir de l'adresse @. .byte b1 , . . . ,bn Enregistre les n valeurs dans des octets consécutifs en mémoire. .word w1 , . . . ,wn Enregistre les n quantités 32 bits dans des mots consécutifs en mémoire. .float f1 , . . . ,fn Enregistre les n nombres ottants simples précision dans des emplacements mémoire consécutifs. .text <@> Les éléments qui suivent sont placés dans le segment de texte de l'utilisateur. Dans SPIM, ces éléments ne peuvent être que des instructions ou des mots. Si l'argument optionnel @ est présent, les éléments qui suivent sont enregistrés à partir de l'adresse @. .globl sym Déclare que le symbole sym est global et que l'on peut y faire référence à partir d'autres chiers. Les registres Il existe 32 registres de 32 bits numérotés $0, . . . , $31 ; les registres peuvent être accédé soit par leur numéro soit par leur nom. Nom $zero $at $v0,$v1 Numéro 0 1 2-3 $a0,. . .,$a3 $t0,. . .,$t7 $s0,. . .,$s7 $t8,$t9 $k0,$k1 4-7 8-15 16-23 24-25 26-27 $gp $sp $fp $ra 28 29 30 31 Description Constante 0 Réservé à l'assembleur Évaluation d'une expression et résultats d'une fonction Arguments de sous-programmes Valeurs temporaires (non préservées) Valeurs temporaires (préservées) Valeurs temporaires (non préservées) Réservé pour les interruptions (i.e., système d'exploitation) Pointeur global Pointeur de pile Pointeur de bloc Adresse de retour Appel de procédure - conventions Par convention, lors de l'appel de procédure, les registres $t0,. . .,$t9 sont sauvegardés par l'appelant et peuvent donc être utilisés sans problème par l'appelé. Les registres $s0,. . .,$s7 doivent quand à eux être sauvegardés et restitués exact par l'appelé. La pile croit des adresses hautes vers les adresses basses : on soustrait à $sp pour allouer de l'espace dans la pile, on ajoute à $sp pour rendre de l'espace dans la pile. Les déplacements dans la pile se font sur des mots mémoire entiers (multiples de quatre octets). Lors du passage de passage de paramètres : tout paramètre plus petit que 32 bits est automatiquement promu sur 32 bits. Les quatre premiers paramètres sont passés par les registres $a0,. . .,$a3. Les paramètres supplémentaires sont passés dans la pile. Toute valeur de format inférieur ou égal à 32 bits est retournée par le registre $v0 (sur 64 bits $v1 est utilisé avec $v0). 2 Gestion de la mémoire Les appels système Les simulateurs fournissent un ensemble de services par l'intermédiaire de l'instruction d'appel syscall. Pour demander un service on charge le code du service (voir tableau ci-dessous) dans le registre $v0 et ses arguments dans les registres $a0,...,$a3 (ou $f12 pour les valeurs ottantes). Les appels système qui retournent des valeurs placent leurs résultats dans le registre $v0 (ou $f0 pour les résultats ottants). service print_int print_oat print_double print_string read_int read_oat read_double read_string sbrk exit code 1 2 3 4 5 6 7 8 9 10 arguments $a0 = entier $f12 = ottant simple précision $f12 = ottant double précision $a0 = chaîne de caractères $a0 = tampon, $a1 = longueur $a0 = quantité résultat un entier dans $v0 un ottant simple dans $v0 un ottant double dans $v0 une adresse dans $v0 Par exemple, le code suivant imprime la réponse = 5 . str: main: .data .asciiz .text li la syscall li li syscall "la réponse = " $v0,4 $a0,str #code appel système print_str #adresse chaîne à imprimer #on imprime la chaîne #code appel système print_int #entier à imprimer #on l'imprime $v0,1 $a0,5 3 Questions Question 1 Enregistrer dans un chier et exécuter le code ci-dessus. (a) A quelle adresse est chargée le début de la chaîne de caractère str ? 0x10010000 (b) Quelle est la signication des instructions lui $1,4097 et ori $4,$1, 0, rajoutées par l'assembleur ? Le chargement d'une adresse 32 bits n'existe pas en MIPS (rappelez vous que les adresses sont sur 16 bits dans les instructions de type I). Il faut donc traduire la pseudo instruction la $a0,str en instructions MIPS. Ceci est réalisé en plaçant dans les bits de poids fort d'un registre (ici $1 = $at) réservé à l'assembleur l'adresse du début du segment de donnée (lui $1,4097) et dans les bits de poids faible, l'adresse relative dans le segment de donnée (ori $4,$1, 0). (c) Rajouter une autre chaîne de caractère avant str dans le code. Que devient l'instruction ori ? Elle change l'immédiat pour s'adapter à la nouvelle position de la chaîne de caractères dans le segment de données. Question 2 Traduire en MIPS les aectations suivantes en continuant le programme ci-dessous : .data .asciiz ", " .text li $t1, 10 li $t2, 50 li $t3, 15 add $a0, $t1, $t2 sub $a0, $a0, $t3 li $v0, 1 syscall li $v0, 4 la $a0, sep syscall (a) a0:= t1+4 (b) a0:= t1*8 (c) a0:= ((t1+t2)/4)+(t3 mod 2) sep: # a0:= t1+t2-t3 # a0=45 . data sep : . asciiz " , " . text li li li $t1 , 10 $t2 , 50 $t3 , 15 add sub jal $a0 , $t1 , $t2 $a0 , $a0 , $t3 print # a0 := t1 + t2 - t3 addi jal $a0 , $t1 , 4 print # a0 := t1 +4 li mul jal $t4 , 8 $a0 , $t1 , $t4 print 4 # a0 := t1 *8 exit : print : sll jal $a0 , $t1 , 3 print # a0 := t1 *8 add srl li div mfhi add jal j exit $a0 , $t1 , $t2 $a0 , $a0 , 2 $t4 , 2 $t3 , $t4 $t4 $a0 , $a0 , $t4 print # a0 := (( t1 + t2 )/4)+( t3 mod 2) li $v0 , 10 syscall li $v0 , 1 syscall li $v0 , 4 la $a0 , sep syscall jr $ra Remarque : l'instruction sll $a0, $t1, 3 peut être utilisé pour éviter d'utiliser le registre $t4. (d) Comment éviter de répéter systématiquement le code d'achage ? En utilisant jal print où print est une étiquette contenant le code a répéter : print: li syscall li la syscall jr $ra $v0, 1 $v0, 4 $a0, sep Question 3 Écrire le code ci-dessous en MIPS et exécuter le programme avec des valeurs e.g., i=2, j=2, g=7, h=3 if (i=j) then f := g+h; else f := g-h; endif print f i: main : . data . word 2 ,2 ,7 ,3 . text la lw lw lw lw bne $a1 , i $t0 ,0( $a1 ) $t1 ,4( $a1 ) $t2 ,8( $a1 ) $t3 ,12( $a1 ) $t0 , $t1 , else # $t0 ! i # $t1 ! j # $t2 ! g # $t3 ! h # if ( i = j ) then 5 else : endif : add $t4 , $t2 , $t3 j endif sub $t4 , $t2 , $t3 li $v0 ,1 move $a0 , $t4 syscall # $f = g + h # else # $f = g - h # endif # print Question 4 Considérer les tableaux T1 et T2 de taille 10 respectivement initialisés à {1,2,3,4,5,6,7,8,9,10} et {10,10,10,10,10,10,10,10,10,10}. (a) Coder en MIPS la boucle suivante et exécuter la sur les tableaux T1 et T2. c:=10 for i in 1..10 loop T2[i] := T1[i] + c; end loop; T1 : T2 : main : loop : end : . data . word 1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 . word 10 ,10 ,10 ,10 ,10 ,10 ,10 ,10 ,10 ,10 . text la la li li li $a1 , T1 $a2 , T2 $t1 ,0 $t2 ,9 $t3 ,10 # a1 # a2 # t1 # t2 # t3 = = = = = adresse de T1 adresse de T2 i = 0 nombre ditérations = 9 c = 10 bgt lw add sw addi addi addi j $t1 , $t2 , end $t4 ,0( $a1 ) $t4 , $t4 , $t3 $t4 ,0( $a2 ) $t1 , $t1 ,1 $a1 , $a1 ,4 $a2 , $a2 ,4 loop # for i in 1..10 loop # t4 = T1 [ i ] # t4 = T1 [ i ]+ c # T2 [ i ]= T1 [ i ]+ c # i = i +1 (b) Combien y-a-t-il d'instructions exécutées pendant l'exécution de ce code ? Combien de références (lw, sw) à des données en mémoire seront faites pendant l'exécution ? Nombre d'instructions exécutées : 7 avant le début de la boucle, 9 dans la boucle exécutée 10 fois. ⇒ 97. Nombre de références : 1 lecture (lw) de la valeur de T1[i] et une écriture (sw) de la nouvelle valeur de T2[i] par itération. ⇒ 20. Voir chier td4-q4-count.s Question 5 Écrire un programme MIPS qui calcule le carré d'un nombre saisi au clavier à l'aide d'un procédure square dénie à une étiquette donné. . data square_out_string : . asciiz " \ nSquare : " 6 main : exit : . text j test_square li $v0 , 10 syscall # code for exit = 10 # call operating system test_square : li $v0 , 5 syscall move $t0 , $v0 li $v0 , 4 la $a0 , square_out_string syscall move $a0 , $t0 jal square move $a0 , $v0 li $v0 , 1 syscall j exit square : mul $v0 , $a0 , $a0 jr $ra # code for reading integer # save input int # code for printing string # load address of string # pass input int as argument # save the result # code for printing integer # compute the multiplication Question 6 Écrivez un programme MIPS inversant l'ordre des octets d'un mot de 4 octets O1 O2 O3 O4 (e.g., 0A0B0C0D) chargé au début du programme dans le registre $a0. A la n du programme, le registre $v0 doit contenir le mot O4 O3 O2 O1 où les octets sont inversés (e.g., 0D0C0B0A). str : main : inv : . data . asciiz . text li lui or move jal move j addi sw li jal sll li jal sll or li jal srl or li jal srl or lw addi " Octet inversé : " $a0 ,0 x0C0D $t0 ,0 x0A0B $a0 , $a0 , $t0 $v0 , $0 inv $t0 , $v0 print $sp , $sp , -4 $ra ,0( $sp ) $a1 ,0 byte $v1 , $v0 ,24 $a1 ,8 byte $t0 , $v0 ,8 $v1 , $v1 , $t0 $a1 ,16 byte $t0 , $v0 ,8 $v1 , $v1 , $t0 $a1 ,24 byte $v0 , $v0 ,24 $v0 , $v0 , $v1 $ra ,0( $sp ) $sp , $sp ,4 # # # # load last 2 bytes in a0 load first 2 bytes in t0 load first 2 bytes in a0 initialize v0 # save result before printing # # # # # reserve space in stack save ra in stack shit amount argument for proc byte v0 =000( o4 ) v1 =( o4 )000 # v0 =000( o3 ) # v1 =( o4 )( o3 )00 # v1 =( o4 )( o3 )( o2 )0 # v0 =000( o1 ) # v0 =( o4 )( o3 )( o2 )( o1 ) # restore ra # restore sp 7 byte : print : jr li sllv and jr $ra $t0 ,0 xff $t0 , $t0 , $a1 $v0 , $t0 , $a0 $ra li la syscall li move syscall $v0 , 4 $a0 , str # used to copy the appropriate byte # shift left t0 # copy a0 in v0 $v0 , 1 $a0 , $t0 Question 7 Écrire la fonction factorielle et le code suivant en MIPS. main () { printf ("The factorial of 10 is ", fact (10)); } int fact (int n) { if (n < 1) return (1); else return (n * fact (n - 1)); } # Factorial . data out_str : . ascii " The factorial of 10 is " main : fact : . text subu sw sw addiu li jal move li la syscall li move syscall lw lw addiu li syscall subu sw sw addiu sw lw bgtz $sp , $sp ,32 $ra ,20( $sp ) $fp ,16( $sp ) $fp , $sp ,28 $a0 ,10 fact $t0 , $v0 $v0 , 4 $a0 , out_str # # # # # # # # # Reserve frame size Save return address Save old frame pointer Set up frame pointer Put argument (10) in $a0 Call factorial function Move fact result code for printing string Put format string in $a0 $v0 , 1 $a0 , $t0 $ra ,20( $sp ) $fp ,16( $sp ) $sp , $sp ,32 $v0 , 10 # Restore return address # Restore frame pointer # Pop stack frame $sp , $sp ,32 $ra ,20( $sp ) $fp ,16( $sp ) $fp , $sp ,28 $a0 ,0( $fp ) $v0 ,0( $fp ) $v0 , L2 # # # # # # # 8 Reserve frame size Save return address Save frame pointer Set up frame pointer Save argument ( n ) Load n Branch if n > 0 L2 : L1 : li j $v0 ,1 L1 # Return 1 # Jump to code to return lw subu move jal lw mul $v1 ,0( $fp ) $v0 , $v1 ,1 $a0 , $v0 fact $v1 ,0( $fp ) $v0 , $v0 , $v1 lw lw addiu jr $ra , 20( $sp ) $fp , 16( $sp ) $sp , $sp , 32 $ra # # # # # # # # # # # 9 Load n Compute n - 1 Move value to $a0 Call factorial Load n Compute fact (n -1) * n Result is in $v0 Restore $ra Restore $fp Pop stack Return to caller