Télécharger le document (10 pages) - Philippe J. Giabbanelli

Philippe Giabbanelli Notes sur l’assembleur MIPS
1
Low Level Programming
Notes du cours de Lin Jensen, septembre-décembre 2006
I. Quelques rappels de calcul
Les nombres en hexadécimal sont précédés de 0x pour éviter une confusion dans les bases. Quand on veut
multiplier 0x3FCB par 2, il suffit de le convertir en binaire et d’opérer un décalage :
0111 1111 1001 0110, soit le résultat 0x7F96. On rappelle que chaque nombre s’écrit sur 4 bits.
Pour soustraite lorsqu’on a des nombres signés, on boucle. Ainsi 0000 moins 1 nous donne 1111.
La règle du complément à deux consiste à inverser les bits et à rajouter 1 :
0101 = 5 1010 (inversion) 1011 (ajout du 1) : -5
0110 = 6 1001 1010 = - 6
Il y a donc un seul circuit pour toute l’arithmétique, puisque les nombres négatifs sont convertis grâce à la
règle du complément à deux. De même, il n’y a pas de circuit soustracteur mais juste un additionneur ; on
utilise la règle du complément à deux en cas de soustraction. Le résultat doit être complémenté à nouveau
pour l’exprimer en notation décimale. Par exemple si l’on obtient 111110, en le complémentant on obtient
000010 soit +2 ; donc le nombre obtenu était -2.
II. Eléments d’architecture d’un ordinateur MIPS
Data bus
Address bus
Control
Unit
instruction
Instruction Adress Decode (IAD)
ADD
MIPS OLD MIPS
1
5
6
$1
$2
$3
MEMORY
1 – Fetch instruction
2 - Decode
3 – Increment program count
4 – Execute instructions
Par exemple un 0 dans l’IAD va faire un ADD.
Philippe Giabbanelli Notes sur l’assembleur MIPS
2
Le Program Count (PC) nous donne l’instruction à exécuter. Si on passe d’une instruction à l’autre
linéairement, alors on passe de la 0 à la 4, i.e. on avance de 4 bytes.
On notera qu’il y a différents langages d’assembleur. Par exemple, il y a différentes syntaxes pour les
processeurs Intel. En plus des syntaxes, les noms des commandes et des registres ne sont pas les mêmes.
En assembleur x86 on trouve par exemple les registres eax et ebx, tandis qu’en MIPS on a des $t0, etc.
Les opinions sur la syntaxe sont également divisés dans le sens de lecture des commandes : de gauche à
droite, ou de droite à gauche. Est-il mieux d’écrire ADD $3 $2 $1 pour $3 $2+$1 ou $3+$2 $1.
III. Opérations arithmétiques et premiers programmes MIPS
We will use SPIM or XSPIM to show which line of the program code is going to be executed. We will
also use bank of test data, i.e. tests units, with MIPS MARK (command mipsmark file.a). At the end, we use
submit116 file.a.
A simple operating system is simulator system calls, called syscall. It tells the O/S that we need help.
Register $v0 has to contain the service number, i.e. what we want syscall to do for us. Another registers
will be use by the service. $a0 stands for argument 0 for any kind of a call..
Service Call code Arguments (input) Results
print integer 1 $a0 = integer signed decimal integer printed in console window
print string 4 $a0 = address of string Prints every character until the terminating ‘\0’
read integer 5 (none) $v0 holds integer that was entered
read string 8 $a0=address to store
$a1= length limit characters are stored
exit 10 (none) Ends the program
Processeur 6502, Atari
Quand on dépasse des bornes lors d’un calcul, on parle d’arithmetic
overflow, cela entraîne des erreurs. Certains processeurs le détecte et font un
trap ou interruption request, d’autres ne réagissent pas ; ils utilisent en
général un overflow flag, chargé dans les processeurs x86 par les instructions
arithmétiques et de comparaison (équivalent à une soustraction sans stockage
de résultat), et non utilisé par les opérations logiques. Ne pas confondre : la
division par 0 n’est pas un arithmetic overflow ; la division par 0 est
mathématique indéfinie : ce n’est pas que sa valeur est trop large, mais plutôt
qu’elle n’a pas de valeur !
EDIT ASSEMBLE LINK TO THE LIBRARIES EXECUTE
SYNTAX ERROR
## Text segment
.text
.globl __start
__start: # execution starts here
lw $t1, mynum # load word mynum into temporary register 1
li $t2, 5 # load immediate (for a constant) 5
add $t3, $t1, $t2 # add the numbers and put them into t3
sw $t3, mynum # store word from register to memory (location of mysum)
.data
mynum: .word 1
mysum: .word 0 # will be changed to 6 at the end
In MIPS, the
results always go
back to register.
If we want to
move something
from a register to
a data, we use
instruction store.
If we want to
take something,
then we use load.
Philippe Giabbanelli Notes sur l’assembleur MIPS
3
# the goal of the program is to add 2 + 3 and print “5 oranges”
.text
.globl __start
__start:
lw $t0, mynum # t0 2 (mynum)
li $t1, 3 # t1 3
add $a0, $t0, $t1 # a0 mynum + 3
li $v0, 1 # ready to print an int
syscall # ask the OS to run service 1
la $a0, str # load the address of str
li $v0, 4 # ready to print a string
syscall # print the string
li $v0, 10 # ready to end
syscall # exit
.data
mynum: .word 2 # a word is 32 bits. mynum is a label
str: .asciiz “oranges\n” # asciiz ends the string with \0
Avec ‘mul Rdest, Rsrc1, src2’, on fait une multiplication de nombres signés et on stocke le résultat sur 32
bits. Or, multiplier 32 bits par 32 bits peut donner un résultat sur 64 bits, et alors ça ne loge pas, il y a une
arithmetic overflow. La pseudo-instruction mulo regarde s’il y a overflow, et arrête proprement la chose ;
c’est implémenté par plusieurs instructions, pour disposer de la vérification.
L’instruction div marche de même manière que mul :
li $t0, 20
div $a0, $t0, 3
li $v0, 1
syscall # affiche 6, à savoir le résultat de la division entière
Avec rem Rdest, Rsrc1, src2 on obtient le reste (remainder) de la division. Il y a aussi une version non-
signée de la multiplication et de la division, en mettant un ‘u’ à la fin (exemple : mulou).
L’instruction réelle de la machine pour la multiplication est mult Rsrc1, Rsrc2. Comme le résultat fait 64
bits, il est stocké dans deux registres :
En contrôlant l’allure de hi et de lo, on sait s’il y a eu overflow ou non. C’est le même principe avec la
division, où on met le quotient en lo et le reste en hi.
Optimisation
(compilateur)
expansion
S’ajoutent aux
32 registres de
l’ordinateur
Register lo Register hi
Il y a des pseudo-instructions qui
sont ensuite expansées, comme
neg $t3, $t0 sub $t0, $0, $t0
Où $0 contient toujours 0.
La forme normale est :
<add|sub> Rdest, Rsrc1, src2
Où src2 est un registre ou une
constante. Parfois, addi est utilisé
pour la forme à 2 registres et une
constante. Il y aussi un raccourci :
add $t0, 1 add $t0, $t0, 1
Cela marche de la même manière
avec la soustraction, mais il n’y a
pas de subi. C’est plutôt :
addi $t5, $t0, -5
sub $t5, $t5, 4 addi $t5, $t5, -4
lw $t0, apples
div $t1, $t0, 8 # apples / student
rem $t2, $t0, 8 # remainder
li $1, 8
div $t0, $1
mflo $t1
li $1, 8
div $t0, $1
mfhi $t2
li $1, 8
div $t1, $1
mflo $t2
mflo $t3
Philippe Giabbanelli Notes sur l’assembleur MIPS
4
# The problem is to find a point on a line, given by the equation: y = slope*x + intercept for some value
# x, calculate and print the corresponding y value that is (nearly) on the line. Since we are doing integer
# arithmetic, slope will be given as two numbers, dy and dx, see figure. You will need to multiply before
# dividing, and the answer will be only approximate, simply ignore any remainder.
.text
.globl __start
__start: # execution starts here
la $a0, hi # we print a welcome message `cause we are polite
li $v0, 4 # be ready to print the message
syscall # ok the message has been print
lw $t0,x # let us have the variable we want to compute... x
lw $t1,dy # and here we get dy
mult $t0,$t1 # we multiply the number, it goes in Lo
mflo $t2 # the result is now in register t2 `cause we know it is a little number
lw $t1,dx
div $t2,$t1 # and now we have (dy.x)/dx
mflo $t1 # we move the result from Low because we assume it is the quotient
lw $t0, intercept
add $t3,$t0,$t1 # we make the final computing for additionning every part of the expression
sw $t3, y # we store the final result in the variable y
li $v0, 4 # be ready to print
la $a0,ans # "answer ="
syscall
lw $a0,y # print the answer
li $v0, 1
syscall
li $v0,4 # print "\n"
la $a0,endl
syscall
li $v0,10 # exit program
syscall # ciao !
## ------------------------------------------------------
.data
x: .word 9 # find y for this value
dy: .word 10 # vertical rise
dx: .word 6 # horizontal run
intercept: .word -2 #line crosses y-axis here
y: .word -99 #optionally use this memory...
ans: .asciiz "answer = "
endl: .asciiz "\n"
hi: .asciiz "hi there we are gonna make some calculus\n"
Philippe Giabbanelli Notes sur l’assembleur MIPS
5
IV. Control Flow Instructions
On discerne trois patterns : appel de functions (call functions), sauter une partie (skip), boucle (loop).
?
Instruction Branch to
label if Unsigned
beq Rsrc1,
Src2, label Rsrc1 = Src2
bne Rsrc1,
Src2, label Rsrc1 <> Src2
blt Rsrc1,
Src2, label Rsrc1 < Src2 bltu
bgt Rsrc1,
Src2, label Rsrc1 > Src2 bgtu
ble Rsrc1,
Src2, label Rsrc1 <= Src2 bleu
bge Rsrc1,
Src2, label Rsrc1 >= Src2 bgeu
Keep going in
normal way
F
T
Branch to label
On utilisera principalement l’instruction ‘j’ qui permet d’aller à un
label. Par exemple, j backthere saute à au label (étiquette) backthere.
# affiche un message
# nombre de fois :
.text
.globl –start
la $a0, mess
li $v0, 4
again :
syscall
j again
done:
li $v0, 10
syscall
.data
mess: .asciiz “hey\n”
# affichons un message 5 fois
.text
.globl –start
li $t0, 5 # initialization of loop counter
la $a0, mess
li $v0, 4
again :
beqz $t0, done # while t0 != 0…
syscall # loop body
sub $t0, 1 # decrement t0
j again # go to the test
done:
li $v0, 10
syscall
.data
mess: .asciiz “hello again\n”
On utilise aussi les pseudo-instructions
avec un z à la fin pour signifier « zéro ».
beqz $t0, done beq $t0, $0, done
.text
.globl –start
--start
li $v0, 4
la $a0, what # ‘what is the temperature ?’
syscall
li $v0, 5 # read int, store result in $v0
syscall
move $t0, $v0
blt $t0, 5, chilly # s’il fait moins de 5°C
la $a0, nice # ‘it’s hot !’
li $v0, 4
syscall
j endif
chilly :
la $a0, cold # ‘brr !’
li $v0, 4
syscall
endif:
li $v0, 10
syscall
{sum non-negative numbers}
sum := 0
num := 0
WHILE num >= 0 DO
sum := sum + num
read (num)
ENDWHILE
{write 10 lines}
FOR countdown := 10 downto 1 DO
print ("Hello again...")
ENDFOR
# add $t0, $0, $0 really:
move $t0, $0 #sum=0
move $v0, $0 #num=0
while5:
bltz $v0, endwhile5
add $t0, $v0
# ---- $v0 switches role here
li $v0, 5 #read int call
syscall
j while5
endwhile5:
# set up print arguments before loop
la $a0, hello_again
li $v0, 4 #print string call
li $t0, 10 # countdown
for10:
beqz $t0, endfor10
syscall #print another line
sub $t0, 1 #decr. countdown
j for10
endfor10:
Ces instructions de branchement sont parmi les soucis
qu’engendre l’écriture d’un code assembleur à la main.
En effet, si le programme devient gros et qu’il y a
plusieurs boucles, on risque de leur attribuer les
mêmes labels… un compilateur utilise un générateur
de label, qui asure qu’il n’a pas déjà été utilisé.
1 / 10 100%
La catégorie de ce document est-elle correcte?
Merci pour votre participation!

Faire une suggestion

Avez-vous trouvé des erreurs dans linterface ou les textes ? Ou savez-vous comment améliorer linterface utilisateur de StudyLib ? Nhésitez pas à envoyer vos suggestions. Cest très important pour nous !