Академический Документы
Профессиональный Документы
Культура Документы
Estructura de Computadores
Captulo 3b: Programacin en
ensamblador del MIPS.
Jos Daniel Muoz Fras
Universidad Ponticia Comillas. ETSI ICAI.
Departamento de Electrnica y Automtica
Estructura de Computadores Captulo 3b: Programacin en ensamblador del MIPS. p. 1
ICAIdea
ndice
1. Introduccin.
2. Registros disponibles.
3. Operaciones aritmticas.
4. Acceso a memoria.
5. Codicacin en lenguaje mquina.
6. Toma de decisiones.
7. Llamadas a funciones.
8. Manejo de caracteres.
9. Aritmtica.
Estructura de Computadores Captulo 3b: Programacin en ensamblador del MIPS. p. 2
ICAIdea
Introduccin
En este tema se va a estudiar en detalle la
arquitectura MIPS. Se estudiar:
El juego de instrucciones.
Aunque cada arquitectura tiene un juego de instrucciones propio, todas las arquitectu-
ras incorporan una serie de instrucciones bsicas con funcionalidades muy parecidas.
Por ejemplo todas las arquitecturas incluyen la instruccin ADD para realizar una su-
ma. Las diferencias estarn en el nmero de operandos que admiten o en el nemnico.
Por tanto, una vez que se ha aprendido a programar en ensamblador con una arquitectura,
programar otra es cuestin del poco tiempo que lleva familiarizarse con ella.
Este tema se basa en el captulo 3 de (Patterson y Hennessy, 2000).
En el laboratorio se usar el simulador SPIM,
1
disponible en:
http://www.cs.wisc.edu/larus/spim.html
1
El nombre SPIM es simplemente MIPS escrito al revs.
ICAIdea
Registros disponibles
MIPS dispone de:
Los registros v0 y v1 se utilizan para evaluar expresiones y para que las funciones
puedan devolver sus resultados.
Los registros t0 t7 se utilizan para almacenar valores temporales. Por tanto cual-
quier funcin puede usarlos sin preocuparse de guardar su valor anterior. Ahora bien,
si una funcin necesita algn valor almacenado en estos registros ha de guardarlo en
otro sitio (memoria, registros) antes de llamar a otra funcin (la invocada), pues sta
supondr que dichos registros slo contienen valores intiles para quien la ha llamado
(la invocadora).
ICAIdea
Registros disponibles
16 s0 Salvados por la invocada
La invocada debe guardarlos
23 s7 antes de usarlos
24 t8 Valores temporales
25 t9 Idem ant.
26 k0 Reservados para el
27 k1 sistema operativo
28 gp Puntero al rea global
29 sp Puntero de pila (stack pointer)
30 fp Puntero al bloque de activacin (frame pointer)
31 ra Direccin de retorno (return address)
Estructura de Computadores Captulo 3b: Programacin en ensamblador del MIPS. p. 6
Los registros s0 s7 se utilizan para almacenar variables que deben ser preservadas
entre llamadas a funciones. Por tanto, si alguna funcin necesita usar alguno de estos
registros, ha de guardar antes su valor (por ejemplo en la pila). De esta forma, la
funcin que usa estos registros no ha de preocuparse de guardar sus valores antes de
llamar a otra funcin.
El registro gp se utiliza para apuntar al rea de memoria donde estn los datos del
programa. Su utilidad se ver ms adelante.
Por ltimo, el registro ra contiene la direccin a la que debe retornar la funcin in-
vocada cuando nalice su labor. El funcionamiento detallado de estos tres ltimos
registros se ver cuando se estudie el mecanismo de llamada a funciones de la arqui-
tectura MIPS.
ICAIdea
Operaciones aritmticas
En las siguientes transparencias se muestran varios
ejemplos en ensamblador MIPS para realizar
operaciones aritmticas.
Se partir de la expresin en C para estudiar cmo se
implanta dicha expresin en ensamblador.
Caractersticas de las operaciones aritmticas MIPS:
Operaciones de 3 direcciones.
Un operando puede ser una constante de 16 bits. En estos casos se realiza la operacin
entre dicha constante y un registro para almacenar el resultado en otro registro.
ICAIdea
Operaciones aritmticas
La expresin en C:
int a, b, c;
c = a + b;
Se traduce en ensamblador (suponiendo la asignacin
de registros: a->s0, b->s1 y c->s2) como:
add $s2, $s0, $s1
Estructura de Computadores Captulo 3b: Programacin en ensamblador del MIPS. p. 8
En este ejemplo, suponemos que las tres variables enteras a, b y c han sido situadas por
el compilador en los registros s0, s1 y s2 respectivamente (en el compilador de C del MIPS
el tipo int es de 32 bits).
La instruccin de suma en el MIPS se representa con el nemnico add. Adems, tal como
se ha dicho anteriormente, las instrucciones aritmticas del MIPS son de tres direcciones.
Esto implica que siempre realizan la operacin entre dos registros, o entre un registro y una
constante de 16 bits, y almacenan su resultado en un registro. En el ensamblador del MIPS se
escribe en primer lugar el registro destino del resultado y a continuacin los dos operandos.
La restriccin de que todas las operaciones aritmticas sean de tres direcciones puede
parecer caprichosa. Sin embargo dicha restriccin est motivada por el deseo de simplicar la
circuitera, ya que si se quisieran implantar instrucciones con distintos nmeros de operandos
se necesitara circuitera especca para cada uno de los casos. Esto se puede resumir en uno
de los principios de diseo hardware:
Principio de diseo 1: la uniformidad simplica el hardware.
Cabe preguntarse cmo se puede realizar la suma de ms de dos variables. En la siguiente
transparencia se ilustra con otro ejemplo.
ICAIdea
Operaciones aritmticas
Cmo se traduce en ensamblador la expresin en C?
int a, b, c, d, e;
a = b + c + d + e;
Suponiendo la asignacin de registros: a->s0, b->s1,
c->s2, d->s3 y e->s4, se necesitan ahora tres
instrucciones:
add $s0, $s1, $s2 # a contiene b+c
add $s0, $s0, $s3 # ahora a vale b+c+d
add $s0, $s0, $s4 # y por fin a es b+c+d+e
Estructura de Computadores Captulo 3b: Programacin en ensamblador del MIPS. p. 9
Como se puede apreciar, ahora es necesario sumar 4 variables, pero las instrucciones
aritmticas del MIPS slo permiten sumar dos. Para resolver este problema, se realizan tres
sumas parciales:
1. b+c
2. b+c+d
3. b+c+d+e
Dichas sumas parciales se han ido almacenando en el registro asignado a la variable a (s0).
En este ejemplo se ilustran dos cosas nuevas:
Un mismo registro puede ser a la vez fuente y destino. As, en la segunda instruccin
se est sumando s0 (que contiene ya b+c) con d y el resultado se vuelve a guardar en
s0.
Vectores y matrices.
Estructuras de datos.
beq reg1, reg2, L1. Esta instruccin compara los contenidos de los registros
reg1 y reg2. Si son iguales, el programa salta a la instruccin etiquetada con L1. Si
son distintos, el programa contina en la instruccin siguiente (a beq). El nemnico
beq viene de branch if equal.
bne reg1, reg2, L1. Al igual que la instruccin anterior, esta instruccin compara
los contenidos de los registros reg1 y reg2. Si son distintos, el programa salta a la
instruccin etiquetada con L1. Si son iguales, el programa contina en la instruccin
siguiente. El nemnico bne viene de branch if not equal.
Debido a su modo de funcionamiento, este tipo de instrucciones se conocen como saltos
condicionales.
El equivalente en C de ambas instrucciones es, respectivamente:
Constan de un interfaz bien denido con el resto del programa. Reciben una serie de
argumentos con los que realiza su tarea y devuelven el resultado de sta a quien la
ha llamado.
Son capaces de realizar su tarea sin interferir con el resto del programa. Esto se con-
sigue mediante un espacio de trabajo propio aislado del resto del programa. As, las
variables que utiliza son locales a la funcin y no son accesibles desde fuera de ella.
De la misma forma, la funcin no puede acceder a las variables locales de las dems
funciones.
ICAIdea
Llamadas a funciones
Para poder ejecutar una funcin es necesario:
1. Situar los parmetros donde la funcin llamada
pueda leerlos. a0-a3, pila
2. Transferir el control a la funcin llamada.
3. Reservar espacio para las variables locales.
t0-t9, pila.
4. Realizar la tarea necesaria.
5. Situar el resultado donde la funcin llamante
pueda leerlos. v0-v1, pila
6. Devolver el control a la funcin llamante.
Estructura de Computadores Captulo 3b: Programacin en ensamblador del MIPS. p. 29
En la transparencia se muestran los distintos pasos que es necesario realizar en la llamada
a una funcin.
Tanto el envo de argumentos (paso 1) como la reserva de espacio para las variables
locales (paso 3) y la recepcin de los resultados (paso 5) son tarea del programador (o del
compilador si se programa en un lenguaje de alto nivel). Ahora bien, todo ello se realiza
siguiendo una convencin software para que todas las funciones puedan interactuar sin pro-
blemas, independientemente de su autor y del lenguaje en que hayan sido escritas. En el caso
del MIPS, con el objetivo de conseguir mejores prestaciones, se reservan 4 registros (a0-a3)
para enviar argumentos a la funcin y 2 registros (v0-v1) para que la funcin devuelva su
resultado. Si se necesita enviar a la funcin un nmero mayor de argumentos o datos que no
puedan ser almacenados en registros, entonces es necesario recurrir a la pila. De la misma
forma, si la funcin ha de devolver algn dato que no quepa en los registros (v0-v1), como
por ejemplo una estructura de datos, ser necesario recurrir a la pila. Las variables locales se
pueden almacenar en los registros reservados para valores temporales (t0-t9) o en la pila. En
caso necesario se pueden usar tambin los registros (s0-s7), aunque antes de escribir nada
en ellos ser necesario guardar su valor en la pila y restaurarlos justo antes de devolver el
control a la funcin llamante, pues dicha funcin supone que la funcin llamada dejar estos
registros intactos. En otras arquitecturas (p. ej. IA-32) debido al escaso nmero de registros
disponible (8) no se reservan registros para el paso de argumentos ni para valores temporales,
siendo entonces necesario un uso intensivo de la pila.
El ltimo aspecto de la llamada a una funcin (paso 6) es la continuacin de la ejecucin
de la funcin llamante justo en la instruccin siguiente a la usada para realizar la llamada a la
funcin (paso 2). Para ello es necesario guardar la direccin de dicha instruccin siguiente en
algn sitio, lo cual necesita que el procesador incluya hardware especco para dicha tarea.
En la siguiente transparencia se discute la solucin adoptada por los arquitectos del MIPS.
2
9
-
2
ICAIdea
Llamadas a funciones. Instruccin jal
Funcionamiento:
Formato: jr registro
Se reserva el registro sp (del ingls stack pointer, puntero de pila) para apuntar al
tope de la pila. Es responsabilidad del programador el manejo de este registro, es
decir, si se introduce o se retira un dato en la pila, habr que modicar el sp para que
contine apuntando al tope de la pila.
La pila crece desde posiciones altas de memoria hacia posiciones bajas. No hay
ninguna razn para hacerlo as salvo por los precedentes histricos. En consecuencia
el tope de la pila ser la direccin ms baja de sta.
En la gura de la transparencia se ilustra la evolucin de la pila cuando se llama a una
funcin que necesita guardar dos registros (ra y a0) en la pila. Como se puede apreciar en
primer lugar (gura izquierda) el registro sp estar apuntando al ltimo dato introducido en
la pila (no mostrado por simplicar). Cuando se llama a la funcin, sta necesita guardar
en la pila dos registros, por lo que el sp se decrementa en dos palabras (8 bytes) y en el
espacio creado se copiar el contenido de los dos registros (ra y a0) (gura central). Una vez
nalizada la funcin, la pila ha de quedar como estaba, tal como se muestra en la gura de la
derecha.
ICAIdea
Llamadas a funciones. Anidamientos
En el ejemplo anterior se mostr una funcin hoja.
Lamentablemente hay funciones que llaman a otras
funciones.
En estos casos es necesario:
Si la funcin llamante necesita usar algn valor almacenado en los registros tempo-
rales t0-t9 despus de la llamada a la funcin, tambin tendr que guardarlos en la
pila antes de llamarla.
El ejemplo ms complicado de funcin no hoja es una funcin recursiva. En la siguiente
transparencia se muestra una funcin para calcular el factorial de forma recursiva. Aunque
este es el peor ejemplo de uso de la recursividad, pues es mucho ms eciente resolverlo con
un bucle, su simplicidad justica su uso en este caso.
ICAIdea
Llamadas a funciones. Ejemplo
int fact(int n)
{
if (n < 1){
return 1;
}else{
return n * fact(n-1);
}
}
Se traduce en (n se pasa en el registro a0):
Estructura de Computadores Captulo 3b: Programacin en ensamblador del MIPS. p. 35
ICAIdea
Llamadas a funciones. Ejemplo
fact: slti $t0, $a0, 1 # n<1 ?
beq $t0, $zero, L1 # Si n>=1, a L1
addi $v0, $zero, 1 # devuelve 1
jr $ra
L1: addi $sp, $sp, -8 # Reserva 2 palabras en pila
sw $ra, 4($sp) # guarda ra y
sw $a0, 0($sp) # n en pila
addi $a0, $a0, -1 # a0=n-1
jal fact
lw $a0, 0($sp) # restaura n
lw $ra, 4($sp) # y ra
addi $sp, $sp, 8 # ajusta sp
mul $v0, $a0, $v0 # devuelve
jr $ra # n * fact(n-1)
Estructura de Computadores Captulo 3b: Programacin en ensamblador del MIPS. p. 36
Al igual que el programa en C, lo primero que realiza la funcin es comprobar si su
argumento n (a0) es menor que 1 para en caso armativo devolver un 1. Para realizar la
comparacin se ha usado una versin de la instruccin slt denominada slti (del ingls set
on less than inmediate). El funcionamiento es idntico a la instruccin slt salvo que en lugar
de comparar dos registros, compara un registro con una constante. Por tanto la instruccin:
slti $t0, $a0, 1 # n<1 ?
Compara el registro a0 con la constante 1 y pondr a uno t0 si (a0) <1.
La siguiente instruccin saltar a la etiqueta L1 si el resultado de la comparacin anterior
fue falso, es decir, si n >= 1. En caso contrario (n<1) se ejecutarn las instrucciones:
addi $v0, $zero, 1 # devuelve 1
jr $ra
que devuelven 1 a la funcin llamante. Para ello se ha utilizado una nueva instruccin
denominada addi que suma una constante a un registro y almacena el resultado en un tercer
registro. En este caso dicha instruccin suma 0 + 1 y almacena el resultado en v0. El control
se devuelve a continuacin mediante la instruccin jr. Ntese que en este caso particular la
funcin fact se comporta como una funcin hoja, ya que no llama a ms funciones. Por tanto
no ha sido necesario en este caso guardar ningn registro en la pila.
Si n >= 1, entonces es necesario llamar recursivamente a la funcin fact, aunque antes
de ello es necesario guardar en la pila los registros que se vayan a necesitar ms tarde. Estos
registros son el ra y el a0. El primero es necesario para preservar la direccin de retorno y el
segundo para poder usar el valor de n al calcular n * fact(n-1). Para guardar los registros,
lo primero que se hace es reservar sitio en la pila, moviendo el puntero 8 bytes (2 palabras)
ms abajo. A continuacin se guarda ra en la segunda palabra y a0 en el tope de la pila:
L1: addi $sp, $sp, -8 # Reserva 2 palabras en pila
sw $ra, 4($sp) # guarda ra y
sw $a0, 0($sp) # n en pila
La estructura de la pila en este instante ser la mostrada en la gura de la transparen-
cia 33. Una vez salvados los dos registros, se pasa a llamar a la funcin fact. Para ello, se
sita en el registro a0 el valor n-1, lo cual se hace restando 1 a dicho registro, ya que ste
contena el valor de n. Una vez hecho esto ya se puede llamar a la funcin fact mediante la
instruccin jal:
addi $a0, $a0, -1 # a0=n-1
jal fact
Cuando dicha funcin devuelva el control, en el registro v0 estar almacenado el valor de
(n-1)!. Por tanto, lo nico que hay que hacer es restaurar los registros que se almacenaron
en la pila y calcular n*(n-1)! para devolverlo. La restauracin de registros es el proceso
inverso al realizado para guardarlos:
lw $a0, 0($sp) # restaura n
lw $ra, 4($sp) # y ra
addi $sp, $sp, 8 # ajusta sp
Como se puede apreciar, despus de la ejecucin de estas instrucciones la pila se quedar
como estaba antes de llamar a la funcin. Lo nico que resta por hacer es calcular n*(n-1)!
y devolver el control a la funcin llamante, lo cual se realiza mediante:
mult $v0, $a0, $v0 # devuelve
jr $ra # n * fact(n-1)
Ejercicio
Escriba un programa en ensamblador del MIPS para calcular el factorial de 7. Utilice
para ello la funcin fact desarrollada anteriormente.
3
6
-
3
ICAIdea
Llamadas a funciones. Bloque de activacin
La pila crece y decrece durante la ejecucin de la
funcin para:
Evaluar expresiones.
Guardar registros.
El segmento de la pila que contiene todos los datos de
una funcin se denomina bloque de activacin
Algunos programas usan el registro fp para apuntar al
principio del bloque de activacin.
Estructura de Computadores Captulo 3b: Programacin en ensamblador del MIPS. p. 37
Durante la ejecucin de la funcin la pila puede crecer y decrecer. Por ejemplo si se
declara una estructura como variable local, sta tendr que almacenarse en la pila, para lo
que ser necesario decrementar el sp en el nmero de palabras que ocupe dicha estructura.
Adems algunos programas necesitan usar la pila para evaluar expresiones matemticas com-
plejas. Por si esto fuera poco, algunos lenguajes como C++ permiten crear variables locales
en cualquier punto de la funcin, por lo que la pila puede crecer en cualquier momento. Esto
hace que si se usa el registro sp para acceder a las variables almacenadas en la pila, ser ne-
cesario utilizar desplazamientos distintos a lo largo de la funcin, dicultndose la escritura
del programa.
Por tanto, es conveniente tener una referencia a las variables almacenadas en la pila
que no vare durante la ejecucin de la funcin. Algunos programas MIPS usan el registro
fp para almacenar la primera palabra del bloque de la pila usado por la funcin, al que se
denomina bloque de activacin. Por ello al registro fp se le conoce como puntero al bloque
de activacin (frame pointer). Dicho registro se carga con el valor del sp al iniciar la funcin
y no vara hasta que sta termine. Por tanto, los accesos a las variables almacenadas en la
pila pueden referirse al fp y dichas referencias no tendrn que cambiarse cada vez que se
introduzca o elimine algn dato de la pila.
Ejercicio
El uso del puntero al bloque de activacin facilita adems la restauracin del puntero de
pila al nal de la ejecucin de la funcin. Escriba las instrucciones necesarias para guardar
el principio del bloque de activacin en fp al principio de la funcin y para restaurar el sp al
nalizar la funcin.
ICAIdea
Llamadas a funciones. Paso de arg. por pila
Si hay ms de 4 parmetros o alguno de stos no
cabe en registros se pasan por la pila.
Convencin software: se sitan por encima del bloque
de activacin.
sp
fp
sp
fp
sp
fp
Bloque
Activacin
Bloque
Activacin
Argumentos
Bloque
Activacin
Estructura de Computadores Captulo 3b: Programacin en ensamblador del MIPS. p. 38
Segn se ha dicho anteriormente, hay ocasiones en las cuales los 4 registros reservados
para el paso de argumentos entre funciones (a0-a3) no son sucientes:
-1 en 8 bits es 11111111