Programación en ensamblador del MIPS. - Departamento de ...
Programación en ensamblador del MIPS. - Departamento de ... Programación en ensamblador del MIPS. - Departamento de ...
Estructura de Computadores Capítulo 3b: Programación en ensamblador del MIPS. José Daniel Muñoz Frías Universidad Pontificia Comillas. ETSI ICAI. Departamento de Electrónica y Automática ICAIdea Estructura de Computadores Capítulo 3b: Programación en ensamblador del MIPS.– p. 1 Índice 1. Introducción. 2. Registros disponibles. 3. Operaciones aritméticas. 4. Acceso a memoria. 5. Codificación en lenguaje máquina. 6. Toma de decisiones. 7. Llamadas a funciones. 8. Manejo de caracteres. 9. Aritmética. ICAIdea Estructura de Computadores Capítulo 3b: Programación en ensamblador del MIPS.– p. 2
- Page 2 and 3: Introducción En este tema se va a
- Page 4 and 5: Registros disponibles 0 zero Consta
- Page 6 and 7: Operaciones aritméticas En las sig
- Page 8 and 9: Operaciones aritméticas ¿Cómo se
- Page 10 and 11: Acceso a memoria Es necesario acced
- Page 12 and 13: Acceso a memoria. Ejemplos ¿Cómo
- Page 14 and 15: Acceso a memoria. Ejemplos ¿Cómo
- Page 16 and 17: Codificación en lenguaje máquina
- Page 18 and 19: la circuitería al máximo, los dis
- Page 20 and 21: En la transparencia se muestra la c
- Page 22 and 23: En el fragmento de código C mostra
- Page 24 and 25: al final de la sentencia if. Dicha
- Page 26 and 27: Toma de decisiones. Bucles int a, V
- Page 28 and 29: Toma de decisiones. Ejemplo int a,
- Page 30 and 31: Llamadas a funciones Para poder eje
- Page 32 and 33: La instrucción jal (del inglés ju
- Page 34 and 35: En la transparencia se muestra un e
- Page 36 and 37: En el ejemplo mostrado anteriorment
- Page 38 and 39: sw $ra, 4($sp) # guarda ra y sw $a0
- Page 40 and 41: Llamadas a funciones. Paso de arg.
- Page 42 and 43: Manejo de caracteres. Ejemplo El si
- Page 44 and 45: Aritmética. Números con y sin sig
- Page 46 and 47: Aritmética. Números con y sin sig
- Page 48 and 49: Aritmética. Números con y sin sig
- Page 50 and 51: Aritmética. Suma y resta En las in
Estructura <strong>de</strong> Computadores<br />
Capítulo 3b: <strong>Programación</strong> <strong>en</strong><br />
<strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.<br />
José Daniel Muñoz Frías<br />
Universidad Pontificia Comillas. ETSI ICAI.<br />
Departam<strong>en</strong>to <strong>de</strong> Electrónica y Automática<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 1<br />
Índice<br />
1. Introducción.<br />
2. Registros disponibles.<br />
3. Operaciones aritméticas.<br />
4. Acceso a memoria.<br />
5. Codificación <strong>en</strong> l<strong>en</strong>guaje máquina.<br />
6. Toma <strong>de</strong> <strong>de</strong>cisiones.<br />
7. Llamadas a funciones.<br />
8. Manejo <strong>de</strong> caracteres.<br />
9. Aritmética.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 2
Introducción<br />
En este tema se va a estudiar <strong>en</strong> <strong>de</strong>talle la<br />
arquitectura <strong>MIPS</strong>. Se estudiará:<br />
• El juego <strong>de</strong> instrucciones.<br />
• El l<strong>en</strong>guaje <strong>en</strong>samblador y la codificación <strong>de</strong><br />
instrucciones.<br />
• Los recursos disponibles.<br />
Cada arquitectura ti<strong>en</strong>e su propio juego <strong>de</strong><br />
instrucciones y su propio <strong>en</strong>samblador, aunque todos<br />
son similares.<br />
El apr<strong>en</strong>dizaje se apoyará con prácticas <strong>de</strong> laboratorio.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 3<br />
En el tema anterior se han estudiado las características que <strong>de</strong>be cumplir un juego <strong>de</strong><br />
instrucciones. En este capítulo se va a mostrar <strong>en</strong> <strong>de</strong>talle el juego <strong>de</strong> instrucciones <strong>de</strong> la arquitectura<br />
<strong>MIPS</strong>, así como las técnicas <strong>de</strong> programación <strong>en</strong> <strong>en</strong>samblador <strong>en</strong> dicha arquitectura.<br />
Como probablem<strong>en</strong>te sabrá, cada procesador ti<strong>en</strong>e su propia arquitectura y su propio l<strong>en</strong>guaje<br />
<strong>en</strong>samblador. Por tanto pue<strong>de</strong> parecer una pérdida <strong>de</strong> tiempo apr<strong>en</strong><strong>de</strong>r un <strong>en</strong>samblador<br />
concreto, ya que éste sólo servirá para programar una arquitectura. No obstante lo anterior no<br />
es cierto por dos razones fundam<strong>en</strong>tales:<br />
• La filosofía <strong>de</strong> programación <strong>en</strong> <strong>en</strong>samblador es idéntica para todas las arquitecturas.<br />
En todas ellas, las operaciones que se pued<strong>en</strong> realizar son muy básicas y es necesario<br />
“bajar” al nivel <strong>de</strong> la máquina.<br />
• Aunque cada arquitectura ti<strong>en</strong>e un juego <strong>de</strong> instrucciones propio, todas las arquitecturas<br />
incorporan una serie <strong>de</strong> instrucciones básicas con funcionalida<strong>de</strong>s muy parecidas.<br />
Por ejemplo todas las arquitecturas incluy<strong>en</strong> la instrucción ADD para realizar una suma.<br />
Las difer<strong>en</strong>cias estarán <strong>en</strong> el número <strong>de</strong> operandos que admit<strong>en</strong> o <strong>en</strong> el nemónico.<br />
Por tanto, una vez que se ha apr<strong>en</strong>dido a programar <strong>en</strong> <strong>en</strong>samblador con una arquitectura,<br />
programar otra es cuestión <strong><strong>de</strong>l</strong> poco tiempo que lleva familiarizarse con ella.<br />
Este tema se basa <strong>en</strong> el capítulo 3 <strong>de</strong> (Patterson y H<strong>en</strong>nessy, 2000).<br />
En el laboratorio se usará el simulador SPIM,<br />
1 disponible <strong>en</strong>:<br />
http://www.cs.wisc.edu/˜larus/spim.html<br />
1 El nombre SPIM es simplem<strong>en</strong>te <strong>MIPS</strong> escrito al revés.
Registros disponibles<br />
<strong>MIPS</strong> dispone <strong>de</strong>:<br />
• 32 registros <strong>de</strong> 32 bits para <strong>en</strong>teros<br />
• 32 registros <strong>de</strong> 32 bits para coma flotante.<br />
• Un contador <strong>de</strong> programa <strong>de</strong> 32 bits<br />
• Dos registros <strong>de</strong> 32 bits para almac<strong>en</strong>ar los<br />
resultados <strong>de</strong> multiplicaciones y divisiones.<br />
Existe una conv<strong>en</strong>ción “software” para el uso <strong>de</strong> los<br />
registros <strong>en</strong>teros.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 4<br />
La primera característica que <strong>de</strong>be conocer un programador sobre una arquitectura es<br />
qué registros están disponibles.<br />
En el caso <strong><strong>de</strong>l</strong> <strong>MIPS</strong>, se dispone <strong>de</strong> un banco <strong>de</strong> registros para almac<strong>en</strong>ar números <strong>en</strong>teros<br />
y otro para almac<strong>en</strong>ar números <strong>en</strong> coma flotante. Ambos bancos constan <strong>de</strong> 32 registros <strong>de</strong><br />
32 bits. En el caso <strong><strong>de</strong>l</strong> banco <strong>de</strong> coma flotante, los registros se pued<strong>en</strong> agrupar <strong>de</strong> dos <strong>en</strong> dos<br />
para utilizarlos <strong>en</strong> operaciones con números <strong>en</strong> doble precisión (64 bits).<br />
A<strong>de</strong>más <strong>de</strong> estos bancos, la arquitectura <strong>MIPS</strong> <strong>de</strong>fine tres registros adicionales: el contador<br />
<strong>de</strong> programa y dos registros llamados Hi y Lo que se utilizan para almac<strong>en</strong>ar el resultado<br />
<strong>de</strong> las operaciones <strong>de</strong> multiplicación y división <strong>en</strong>teras. En el caso <strong>de</strong> la multiplicación, recuer<strong>de</strong><br />
que el producto <strong>de</strong> dos números <strong>de</strong> 32 bits da como resultado un número <strong>de</strong> 64 bits.<br />
De la misma forma, el coci<strong>en</strong>te <strong>de</strong> dos números <strong>de</strong> 32 bits da como resultado un coci<strong>en</strong>te <strong>de</strong><br />
32 bits y un resto también <strong>de</strong> 32 bits.<br />
Aunque para el procesador todos los registros son iguales, existe una conv<strong>en</strong>ción seguida<br />
por todos los programadores para el uso <strong>de</strong> los registros para <strong>en</strong>teros, la cual pasamos a<br />
<strong>de</strong>scribir a continuación.
Registros disponibles<br />
0 zero Constante 0<br />
1 at Reservado para el <strong>en</strong>samblador<br />
2 v0 Evaluación <strong>de</strong> expresiones<br />
3 v1 y retorno <strong>de</strong> resultados<br />
4 a0 Argum<strong>en</strong>tos <strong>de</strong> funciones<br />
··· ··· ···<br />
7 a3<br />
8 t0 Valores temporales<br />
··· ··· La invocada pue<strong>de</strong> modificarlos<br />
15 t7 La invocadora <strong>de</strong>be guardarlos<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 5<br />
En la tabla <strong>de</strong> esta transpar<strong>en</strong>cia y la sigui<strong>en</strong>te se muestran los registros <strong>en</strong>teros <strong><strong>de</strong>l</strong><br />
<strong>MIPS</strong> junto con el uso que se <strong>de</strong>be hacer <strong>de</strong> ellos. Como se dijo antes, para el procesador<br />
todos los registros (excepto el 0) son iguales. Sin embargo para hacer más fácil y efici<strong>en</strong>te<br />
la programación, existe una conv<strong>en</strong>ción <strong>de</strong> uso <strong>de</strong> estos registros que se <strong>de</strong>scribe <strong>en</strong> <strong>de</strong>talle a<br />
continuación:<br />
• El registro zero conti<strong>en</strong>e siempre la constante 0. La utilidad <strong>de</strong> este “registro” se<br />
verá más a<strong><strong>de</strong>l</strong>ante.<br />
• El registro at está reservado para ser usado por el <strong>en</strong>samblador.<br />
• Los registros v0 y v1 se utilizan para evaluar expresiones y para que las funciones<br />
puedan <strong>de</strong>volver sus resultados.<br />
• Los registros a0···a3 se utilizan para pasarle argum<strong>en</strong>tos a las funciones. Si son<br />
necesarios más <strong>de</strong> 4 argum<strong>en</strong>tos (o éstos necesitan más <strong>de</strong> 32 bits) se recurre a la<br />
pila.<br />
• Los registros t0···t7 se utilizan para almac<strong>en</strong>ar valores temporales. Por tanto cualquier<br />
función pue<strong>de</strong> usarlos sin preocuparse <strong>de</strong> guardar su valor anterior. Ahora bi<strong>en</strong>,<br />
si una función necesita algún valor almac<strong>en</strong>ado <strong>en</strong> estos registros ha <strong>de</strong> guardarlo <strong>en</strong><br />
otro sitio (memoria, registros) antes <strong>de</strong> llamar a otra función (la invocada), pues ésta<br />
supondrá que dichos registros sólo conti<strong>en</strong><strong>en</strong> valores inútiles para qui<strong>en</strong> la ha llamado<br />
(la invocadora).
Registros disponibles<br />
16 s0 Salvados por la invocada<br />
··· ··· La invocada <strong>de</strong>be guardarlos<br />
23 s7 antes <strong>de</strong> usarlos<br />
24 t8 Valores temporales<br />
25 t9 I<strong>de</strong>m ant.<br />
26 k0 Reservados para el<br />
27 k1 sistema operativo<br />
28 gp Puntero al área global<br />
29 sp Puntero <strong>de</strong> pila (stack pointer)<br />
30 fp Puntero al bloque <strong>de</strong> activación (frame pointer)<br />
31 ra Dirección <strong>de</strong> retorno (return address)<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 6<br />
• Los registros s0···s7 se utilizan para almac<strong>en</strong>ar variables que <strong>de</strong>b<strong>en</strong> ser preservadas<br />
<strong>en</strong>tre llamadas a funciones. Por tanto, si alguna función necesita usar alguno <strong>de</strong> estos<br />
registros, ha <strong>de</strong> guardar antes su valor (por ejemplo <strong>en</strong> la pila). De esta forma, la<br />
función que usa estos registros no ha <strong>de</strong> preocuparse <strong>de</strong> guardar sus valores antes <strong>de</strong><br />
llamar a otra función.<br />
• Los registros t8 y t9 se utilizan <strong>de</strong> la misma manera que los registros t0···t7.<br />
• Los registros k0 y k1 están reservados para el sistema operativo.<br />
• El registro gp se utiliza para apuntar al área <strong>de</strong> memoria don<strong>de</strong> están los datos <strong><strong>de</strong>l</strong><br />
programa. Su utilidad se verá más a<strong><strong>de</strong>l</strong>ante.<br />
• El registro sp es el puntero <strong>de</strong> la pila (conti<strong>en</strong>e la dirección <strong><strong>de</strong>l</strong> tope <strong>de</strong> la pila).<br />
• El registro fp conti<strong>en</strong>e la dirección <strong>de</strong> la zona <strong>de</strong> la pila <strong>en</strong> la que están guardados<br />
los argum<strong>en</strong>tos y las variables locales <strong>de</strong> la función que no cab<strong>en</strong> <strong>en</strong> los registros.<br />
Esta zona se conoce como bloque <strong>de</strong> activación. Su utilidad es la <strong>de</strong> simplificar el<br />
acceso a estas variables cuando es necesario modificar la pila durante la ejecución <strong>de</strong><br />
la función (por ejemplo durante la evaluación <strong>de</strong> expresiones).<br />
• Por último, el registro ra conti<strong>en</strong>e la dirección a la que <strong>de</strong>be retornar la función invocada<br />
cuando finalice su labor. El funcionami<strong>en</strong>to <strong>de</strong>tallado <strong>de</strong> estos tres últimos<br />
registros se verá cuando se estudie el mecanismo <strong>de</strong> llamada a funciones <strong>de</strong> la arquitectura<br />
<strong>MIPS</strong>.
Operaciones aritméticas<br />
En las sigui<strong>en</strong>tes transpar<strong>en</strong>cias se muestran varios<br />
ejemplos <strong>en</strong> <strong>en</strong>samblador <strong>MIPS</strong> para realizar<br />
operaciones aritméticas.<br />
Se partirá <strong>de</strong> la expresión <strong>en</strong> C para estudiar cómo se<br />
implanta dicha expresión <strong>en</strong> <strong>en</strong>samblador.<br />
Características <strong>de</strong> las operaciones aritméticas <strong>MIPS</strong>:<br />
• Operaciones <strong>de</strong> 3 direcciones.<br />
• Operandos y resultados han <strong>de</strong> estar <strong>en</strong> registros.<br />
• Un operando pue<strong>de</strong> ser una constante <strong>de</strong> 16 bits.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 7<br />
Para estudiar el <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>, se van a introducir sus instrucciones principales<br />
a través <strong>de</strong> ejemplos. En estos ejemplos se partirá <strong>de</strong> un trozo <strong>de</strong> código <strong>en</strong> l<strong>en</strong>guaje C y se<br />
estudiará como se implanta dicho código <strong>en</strong> <strong>en</strong>samblador.<br />
En primer lugar se van a estudiar algunas <strong>de</strong> las instrucciones disponibles <strong>en</strong> la arquitectura<br />
<strong>MIPS</strong> para realizar operaciones aritméticas con números <strong>en</strong>teros. Todas estas instrucciones,<br />
junto con las instrucciones para realizar operaciones lógicas compart<strong>en</strong> tres características<br />
fundam<strong>en</strong>tales:<br />
• Operaciones <strong>de</strong> 3 direcciones. La primera dirección será don<strong>de</strong> se almac<strong>en</strong>e el<br />
resultado y las otras dos los operandos.<br />
• Operandos y resultados han <strong>de</strong> estar <strong>en</strong> registros. Por tanto antes <strong>de</strong> realizar<br />
cualquier operación, si alguno <strong>de</strong> los datos está <strong>en</strong> memoria será necesario cargarlo<br />
<strong>en</strong> un registro.<br />
• Un operando pue<strong>de</strong> ser una constante <strong>de</strong> 16 bits. En estos casos se realiza la operación<br />
<strong>en</strong>tre dicha constante y un registro para almac<strong>en</strong>ar el resultado <strong>en</strong> otro registro.
Operaciones aritméticas<br />
La expresión <strong>en</strong> C:<br />
int a, b, c;<br />
c = a + b;<br />
Se traduce <strong>en</strong> <strong>en</strong>samblador (suponi<strong>en</strong>do la asignación<br />
<strong>de</strong> registros: a->s0, b->s1 y c->s2) como:<br />
add $s2, $s0, $s1<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 8<br />
En este ejemplo, suponemos que las tres variables <strong>en</strong>teras a, b y c han sido situadas por<br />
el compilador <strong>en</strong> los registros s0, s1 y s2 respectivam<strong>en</strong>te (<strong>en</strong> el compilador <strong>de</strong> C <strong><strong>de</strong>l</strong> <strong>MIPS</strong><br />
el tipo int es <strong>de</strong> 32 bits).<br />
La instrucción <strong>de</strong> suma <strong>en</strong> el <strong>MIPS</strong> se repres<strong>en</strong>ta con el nemónico add. A<strong>de</strong>más, tal como<br />
se ha dicho anteriorm<strong>en</strong>te, las instrucciones aritméticas <strong><strong>de</strong>l</strong> <strong>MIPS</strong> son <strong>de</strong> tres direcciones.<br />
Esto implica que siempre realizan la operación <strong>en</strong>tre dos registros, o <strong>en</strong>tre un registro y una<br />
constante <strong>de</strong> 16 bits, y almac<strong>en</strong>an su resultado <strong>en</strong> un registro. En el <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong> se<br />
escribe <strong>en</strong> primer lugar el registro <strong>de</strong>stino <strong><strong>de</strong>l</strong> resultado y a continuación los dos operandos.<br />
La restricción <strong>de</strong> que todas las operaciones aritméticas sean <strong>de</strong> tres direcciones pue<strong>de</strong><br />
parecer caprichosa. Sin embargo dicha restricción está motivada por el <strong>de</strong>seo <strong>de</strong> simplificar la<br />
circuitería, ya que si se quisieran implantar instrucciones con distintos números <strong>de</strong> operandos<br />
se necesitaría circuitería específica para cada uno <strong>de</strong> los casos. Esto se pue<strong>de</strong> resumir <strong>en</strong> uno<br />
<strong>de</strong> los principios <strong>de</strong> diseño hardware:<br />
Principio <strong>de</strong> diseño 1: la uniformidad simplifica el hardware.<br />
Cabe preguntarse cómo se pue<strong>de</strong> realizar la suma <strong>de</strong> más <strong>de</strong> dos variables. En la sigui<strong>en</strong>te<br />
transpar<strong>en</strong>cia se ilustra con otro ejemplo.
Operaciones aritméticas<br />
¿Cómo se traduce <strong>en</strong> <strong>en</strong>samblador la expresión <strong>en</strong> C<br />
int a, b, c, d, e;<br />
a = b + c + d + e;<br />
Suponi<strong>en</strong>do la asignación <strong>de</strong> registros: a->s0, b->s1,<br />
c->s2, d->s3 y e->s4, se necesitan ahora tres<br />
instrucciones:<br />
add $s0, $s1, $s2 # a conti<strong>en</strong>e b+c<br />
add $s0, $s0, $s3 # ahora a vale b+c+d<br />
add $s0, $s0, $s4 # y por fin a es b+c+d+e<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 9<br />
Como se pue<strong>de</strong> apreciar, ahora es necesario sumar 4 variables, pero las instrucciones<br />
aritméticas <strong><strong>de</strong>l</strong> <strong>MIPS</strong> sólo permit<strong>en</strong> sumar dos. Para resolver este problema, se realizan tres<br />
sumas parciales:<br />
1. b+c<br />
2. b+c+d<br />
3. b+c+d+e<br />
Dichas sumas parciales se han ido almac<strong>en</strong>ando <strong>en</strong> el registro asignado a la variable a (s0).<br />
En este ejemplo se ilustran dos cosas nuevas:<br />
• Un mismo registro pue<strong>de</strong> ser a la vez fu<strong>en</strong>te y <strong>de</strong>stino. Así, <strong>en</strong> la segunda instrucción<br />
se está sumando s0 (que conti<strong>en</strong>e ya b+c) con d y el resultado se vuelve a guardar <strong>en</strong><br />
s0.<br />
• El texto que aparece a la <strong>de</strong>recha <strong><strong>de</strong>l</strong> carácter # es un com<strong>en</strong>tario y por tanto es<br />
ignorado por el <strong>en</strong>samblador. A difer<strong>en</strong>cia <strong>de</strong> C no es necesario especificar el final<br />
<strong><strong>de</strong>l</strong> com<strong>en</strong>tario, ya que éste acaba al final <strong>de</strong> la línea. Si se <strong>de</strong>sean más líneas <strong>de</strong><br />
com<strong>en</strong>tario será necesario usar caracteres # adicionales <strong>en</strong> cada línea.<br />
También es necesario <strong>de</strong>stacar que cada línea pue<strong>de</strong> cont<strong>en</strong>er como mucho una instrucción<br />
<strong>de</strong> <strong>en</strong>samblador.
Operaciones aritméticas<br />
¿Cómo se traduce <strong>en</strong> <strong>en</strong>samblador la expresión <strong>en</strong> C<br />
int a, b, c, d, e;<br />
a = (b + c) - (d + e);<br />
Se realiza la asignación <strong>de</strong> registros: a->s0, b->s1,<br />
c->s2, d->s3 y e->s4.<br />
add $t0, $s1, $s2<br />
add $t1, $s3, $s4<br />
sub $s0, $t0, $t1<br />
# se usa t0 para b+c<br />
# se usa t1 para d+e<br />
# s0 vale ahora t0-t1<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 10<br />
Para evaluar la expresión a = (b + c) - (d + e) es necesario evaluar <strong>en</strong> primer lugar<br />
las expresiones <strong>en</strong>tre paréntesis y a continuación restar ambos resultados. Para almac<strong>en</strong>ar los<br />
resultados <strong>de</strong> las expresiones <strong>en</strong>tre paréntesis se usan variables temporales, ya que lo más<br />
probable es que no necesitemos nunca más dichos resultados. Se han elegido para ello los<br />
registros temporales t0 y t1.<br />
Otra novedad <strong>de</strong> este ejemplo es la instrucción sub, la cual resta el tercer argum<strong>en</strong>to <strong><strong>de</strong>l</strong><br />
segundo y lo almac<strong>en</strong>a <strong>en</strong> el primero, es <strong>de</strong>cir:<br />
sub $s0, $t0, $t1<br />
hace que s0 = t0 - t1.<br />
Ejercicio<br />
Modifique el programa anterior para evitar el uso <strong>de</strong> los dos registros temporales. Pista:<br />
Pue<strong>de</strong> usar el registro s0 para ir almac<strong>en</strong>ando los resultados parciales.
Acceso a memoria<br />
Es necesario acce<strong>de</strong>r a memoria para usar:<br />
• Vectores y matrices.<br />
• Estructuras <strong>de</strong> datos.<br />
• Variables que no están <strong>en</strong> registros.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 11<br />
En los ejemplos mostrados hasta ahora se ha supuesto que todas las variables involucradas<br />
<strong>en</strong> las operaciones estaban almac<strong>en</strong>adas <strong>en</strong> los registros <strong><strong>de</strong>l</strong> procesador. Ahora bi<strong>en</strong>, esto<br />
no siempre es así. Todos los l<strong>en</strong>guajes <strong>de</strong> programación soportan tipos <strong>de</strong> datos complejos<br />
que no pued<strong>en</strong> ser almac<strong>en</strong>ados d<strong>en</strong>tro <strong>de</strong> unos cuantos registros <strong>de</strong> 32 bits. Ejemplos claros<br />
son los vectores, las matrices y las estructuras <strong>de</strong> datos.<br />
A<strong>de</strong>más, <strong>en</strong> muchas ocasiones los programas necesitan manejar más variables <strong>de</strong> las que<br />
pued<strong>en</strong> almac<strong>en</strong>arse <strong>en</strong> los registros. En estos casos no queda más remedio que mant<strong>en</strong>er<br />
algunas variables <strong>en</strong> memoria y traerlas a los registros temporales cuando necesitemos operar<br />
con ellas.<br />
Por último, antes <strong>de</strong> po<strong>de</strong>r usar una variable es necesario cargarla <strong>en</strong> un registro.<br />
En consecu<strong>en</strong>cia, la arquitectura <strong>MIPS</strong>, al igual que todas las <strong>de</strong>más, dispone <strong>de</strong> instrucciones<br />
<strong>de</strong> transfer<strong>en</strong>cia <strong>de</strong> datos que permit<strong>en</strong> cargar un valor <strong>de</strong>s<strong>de</strong> la memoria a un registro<br />
y para almac<strong>en</strong>ar un registro <strong>en</strong> la memoria.
Acceso a memoria. Organización<br />
El bus <strong>de</strong> datos <strong><strong>de</strong>l</strong> <strong>MIPS</strong> es <strong>de</strong> 32 bits.<br />
Es posible direccionar cada byte individualm<strong>en</strong>te.<br />
Dirección <strong>de</strong> palabra =<br />
dir. byte más significativo:<br />
Arquitectura big <strong>en</strong>dian<br />
Dirección <strong>de</strong> palabra =<br />
múltiplo <strong>de</strong> 4<br />
Restricción <strong>de</strong> alineación<br />
Dir<br />
Palabra<br />
8<br />
4<br />
0<br />
msB lsB<br />
8 9 A B<br />
4 5 6 7<br />
0 1 2 3<br />
Dir. Byte<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 12<br />
Antes <strong>de</strong> estudiar <strong>en</strong> <strong>de</strong>talle las instrucciones <strong>de</strong> transfer<strong>en</strong>cia <strong>de</strong> datos <strong><strong>de</strong>l</strong> <strong>MIPS</strong>, es<br />
conv<strong>en</strong>i<strong>en</strong>te ver cómo está estructurada su memoria.<br />
Los procesadores <strong>MIPS</strong> ti<strong>en</strong><strong>en</strong> un bus <strong>de</strong> datos <strong>de</strong> 32 bits, por lo que <strong>en</strong> cada acceso a<br />
memoria se lee una palabra <strong>de</strong> 32 bits. Como se ha dicho antes, <strong>de</strong>bido <strong>en</strong>tre otras cosas a<br />
que un carácter ocupa un byte, es necesario po<strong>de</strong>r acce<strong>de</strong>r a éstos individualm<strong>en</strong>te. Por ello,<br />
aunque la memoria física está organizada <strong>en</strong> palabras <strong>de</strong> 32 bits, <strong>de</strong>s<strong>de</strong> el punto <strong>de</strong> vista lógico<br />
(o <strong><strong>de</strong>l</strong> programador), la memoria ha <strong>de</strong> estar organizada <strong>en</strong> bytes. Para ello, tal como pue<strong>de</strong><br />
apreciarse <strong>en</strong> la figura, cada palabra está “dividida” <strong>en</strong> 4 bytes, cada uno con su dirección<br />
para po<strong>de</strong>r acce<strong>de</strong>r a él individualm<strong>en</strong>te. Las direcciones <strong>de</strong> palabra <strong>en</strong> este caso coincid<strong>en</strong><br />
con la <strong><strong>de</strong>l</strong> byte más significativo. Se dice <strong>en</strong>tonces que la arquitectura es big <strong>en</strong>dian. Otros<br />
ejemplos <strong>de</strong> arquitecturas big <strong>en</strong>dian son: IBM 360/370, Motorola 68k y Sparc. Existe otra<br />
alternativa, seguida fundam<strong>en</strong>talm<strong>en</strong>te por la arquitectura IA-32 <strong>en</strong> la que la dirección <strong>de</strong><br />
palabra coinci<strong>de</strong> con el byte m<strong>en</strong>os significativo. En este caso se dice que la arquitectura es<br />
little <strong>en</strong>dian.<br />
El que una arquitectura sea big <strong>en</strong>dian o little <strong>en</strong>dian es más o m<strong>en</strong>os transpar<strong>en</strong>te al<br />
programador. Sólo cuando éste ti<strong>en</strong>e que examinar la memoria <strong>en</strong> busca <strong>de</strong> números <strong>de</strong> más <strong>de</strong><br />
8 bits ha <strong>de</strong> t<strong>en</strong>er <strong>en</strong> cu<strong>en</strong>ta su organización. Ahora bi<strong>en</strong>, este tema es crítico si se intercambian<br />
datos <strong>en</strong>tre dos arquitecturas con distintas “<strong>en</strong>dianess”.<br />
Por último, cabe <strong>de</strong>stacar que <strong>en</strong> la arquitectura <strong>MIPS</strong> las direcciones <strong>de</strong> palabra <strong>de</strong>b<strong>en</strong><br />
<strong>de</strong> ser siempre múltiplos <strong>de</strong> 4. Esto es lo que se conoce como restricción <strong>de</strong> alineación.<br />
Exist<strong>en</strong> arquitecturas que no ti<strong>en</strong><strong>en</strong> esta restricción, aunque <strong>en</strong> estos casos, los accesos a<br />
palabras alineadas son mucho más rápidos que a palabras <strong>de</strong>salineadas.
Acceso a memoria. Ejemplos<br />
¿Cómo se traduce <strong>en</strong> <strong>en</strong>samblador la expresión <strong>en</strong> C<br />
int a, b, V[40];<br />
a = b + V[8];<br />
Se realiza la asignación <strong>de</strong> registros: a->s0, b->s1,<br />
V->s2.<br />
lw $t0, 32($s2) # t0 se carga con V[8]<br />
add $s0, $s1, $t0 # se realiza la operación<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 13<br />
En este ejemplo, es necesario acce<strong>de</strong>r a un elem<strong>en</strong>to <strong>de</strong> un vector. Obviam<strong>en</strong>te el vector<br />
no cabe <strong>en</strong> los registros <strong><strong>de</strong>l</strong> procesador, por lo que es necesario mant<strong>en</strong>erlo <strong>en</strong> la memoria.<br />
Ahora bi<strong>en</strong>, lo que sí se pue<strong>de</strong> guardar <strong>en</strong> los registros internos es la dirección <strong><strong>de</strong>l</strong> primer elem<strong>en</strong>to<br />
<strong><strong>de</strong>l</strong> vector (lo que <strong>en</strong> C se d<strong>en</strong>ota por el nombre <strong><strong>de</strong>l</strong> vector). En este ejemplo se supone<br />
que se guarda dicha dirección <strong>en</strong> el registro s2. Por tanto, para acce<strong>de</strong>r a la octava palabra <strong><strong>de</strong>l</strong><br />
vector, basta con sumar a la dirección <strong>de</strong> comi<strong>en</strong>zo <strong><strong>de</strong>l</strong> vector el <strong>de</strong>splazami<strong>en</strong>to necesario. La<br />
instrucción <strong>de</strong> carga <strong>de</strong> <strong>MIPS</strong>, d<strong>en</strong>ominada lw (<strong>de</strong> load word), hace precisam<strong>en</strong>te esto: toma<br />
la dirección <strong>de</strong> un registro base y le suma un <strong>de</strong>splazami<strong>en</strong>to,<br />
1 cargando <strong>en</strong>tonces la palabra que<br />
hay <strong>en</strong> la dirección calculada <strong>en</strong> el registro <strong>de</strong>stino. El formato <strong>de</strong> la instrucción es por tanto:<br />
lw Reg_Dest, Despl(Reg_Base)<br />
Hay que t<strong>en</strong>er <strong>en</strong> cu<strong>en</strong>ta que, según se ha discutido antes, las direcciones <strong>de</strong> palabra<br />
aum<strong>en</strong>tan <strong>de</strong> 4 <strong>en</strong> 4, por lo que la octava palabra <strong>de</strong> un vector estará 8 pal · 4Byte/pal =<br />
32Bytes por <strong>en</strong>cima <strong>de</strong> la base <strong><strong>de</strong>l</strong> vector. Por tanto, para cargar V[8] <strong>en</strong> el registgro t0, si la<br />
dirección base <strong><strong>de</strong>l</strong> vector V está almac<strong>en</strong>ada <strong>en</strong> el registro s2, basta con hacer:<br />
lw $t0, 32($s2)<br />
La instrucción complem<strong>en</strong>taria a lw se d<strong>en</strong>omina sw (<strong>de</strong> store word). Su funcionami<strong>en</strong>to<br />
es similar al <strong>de</strong> lw, salvo que <strong>en</strong> lugar <strong>de</strong> traer una palabra <strong>de</strong> la memoria y almac<strong>en</strong>arla <strong>en</strong><br />
un registro; toma la palabra almac<strong>en</strong>ada <strong>en</strong> el registro y la almac<strong>en</strong>a <strong>en</strong> la memoria, concretam<strong>en</strong>te<br />
<strong>en</strong> la dirección Reg_Base + Despl. En la sigui<strong>en</strong>te transpar<strong>en</strong>cia se ilustra su uso<br />
mediante otro ejemplo.<br />
1 El <strong>de</strong>splazami<strong>en</strong>to pue<strong>de</strong> ser positivo o negativo.
Acceso a memoria. Ejemplos<br />
¿Cómo se traduce <strong>en</strong> <strong>en</strong>samblador la expresión <strong>en</strong> C<br />
int b, V[40];<br />
V[12] = b + V[8];<br />
Se realiza la asignación <strong>de</strong> registros: b->s1, V->s2.<br />
lw $t0, 32($s2) # t0 se carga con V[8]<br />
add $t0, $s1, $t0 # se realiza la operación<br />
sw $t0, 48($s2) # t0 se guarda <strong>en</strong> V[12]<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 14<br />
Como se pue<strong>de</strong> apreciar las dos primeras líneas son prácticam<strong>en</strong>te iguales a las <strong><strong>de</strong>l</strong> ejemplo<br />
anterior: tan solo varía el registro <strong>de</strong>stino <strong>de</strong> la suma, que ahora es el registro temporal<br />
t0, <strong>en</strong> don<strong>de</strong> se almac<strong>en</strong>a el resultado <strong>de</strong> la suma antes <strong>de</strong> escribirlo <strong>en</strong> la memoria. De esta<br />
escritura <strong>en</strong> memoria se <strong>en</strong>carga la instrucción sw, la cual ti<strong>en</strong>e el mismo formato que la<br />
instrucción lw:<br />
sw Reg_Orig, Despl(Reg_Base)<br />
En don<strong>de</strong> Reg_Orig es el registro cuyo valor se <strong>de</strong>sea guardar y el resto <strong>de</strong> argum<strong>en</strong>tos<br />
ti<strong>en</strong><strong>en</strong> el mismo significado que antes. Al igual que antes hay que convertir la dirección <strong>de</strong><br />
palabra <strong><strong>de</strong>l</strong> vector a dirección <strong>de</strong> byte (12 pal · 4Byte/pal = 32Bytes) para obt<strong>en</strong>er el <strong>de</strong>splazami<strong>en</strong>to<br />
correcto.<br />
En la mayoría <strong>de</strong> los programas, el acceso a vectores se realiza utilizando un índice<br />
variable. En la sigui<strong>en</strong>te transpar<strong>en</strong>cia se muestra un ejemplo para ilustrar este tipo <strong>de</strong> accesos.
Acceso a memoria. Ejemplos<br />
¿Cómo se traduce <strong>en</strong> <strong>en</strong>samblador la expresión <strong>en</strong> C<br />
int a, b, V[40], i;<br />
a = b + V[i];<br />
Se realiza la asignación <strong>de</strong> registros: a->s0, b->s1,<br />
V->s2, i->s3.<br />
add $t1, $s3, $s3 # t1 = 2*i<br />
add $t1, $t1, $t1 # t1 = 4*i<br />
add $t1, $t1, $s2 # t1 = dir. <strong>de</strong> V[i]<br />
lw $t0, 0($t1) # t0 se carga con V[i]<br />
add $s0, $s1, $t0 # se realiza la operación<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 15<br />
En este ejemplo, al ser el índice variable, no po<strong>de</strong>mos multiplicarlo nosotros a mano<br />
como hemos hecho <strong>en</strong> los ejemplos anteriores cuando el índice era constante. No obstante el<br />
<strong>MIPS</strong> ti<strong>en</strong>e instrucciones que nos permit<strong>en</strong> hacer dicha multiplicación fácilm<strong>en</strong>te. Sin embargo,<br />
como no hemos visto aún la instrucción <strong>de</strong> multiplicación, realizaremos la multiplicación<br />
por 4 mediante dos sumas: En primer lugar calcularemos i + i = 2i y luego 2i + 2i = 4i. De<br />
esto se <strong>en</strong>cargan las dos primeras instrucciones <strong>de</strong> <strong>en</strong>samblador, guardando 4i <strong>en</strong> el registro<br />
t1. Ahora bi<strong>en</strong>, este valor no pue<strong>de</strong> ser usado como <strong>de</strong>splazami<strong>en</strong>to <strong>en</strong> una instrucción <strong>de</strong><br />
carga o almac<strong>en</strong>ami<strong>en</strong>to <strong><strong>de</strong>l</strong> <strong>MIPS</strong>, ya que estas instrucciones sólo admit<strong>en</strong> un <strong>de</strong>splazami<strong>en</strong>to<br />
constante. No obstante la suma <strong><strong>de</strong>l</strong> <strong>de</strong>splazami<strong>en</strong>to con el registro base se pue<strong>de</strong> realizar<br />
mediante otra instrucción add para así calcular la dirección <strong>de</strong> V[i]. De esto se <strong>en</strong>carga la<br />
tercera instrucción <strong><strong>de</strong>l</strong> programa, almac<strong>en</strong>ando dicha dirección <strong>en</strong> el registro t1. En este mom<strong>en</strong>to<br />
ya se pue<strong>de</strong> cargar el valor <strong><strong>de</strong>l</strong> elem<strong>en</strong>to <strong><strong>de</strong>l</strong> vector V[i] mediante la instrucción lw.<br />
Nótese que, como el registro t1 ya conti<strong>en</strong>e la dirección <strong>de</strong> V[i], se ha utilizado un <strong>de</strong>splazami<strong>en</strong>to<br />
<strong>de</strong> 0 <strong>en</strong> la instrucción <strong>de</strong> carga. Una vez cargado el elem<strong>en</strong>to <strong><strong>de</strong>l</strong> vector <strong>en</strong> el registro<br />
t0 ya solo hace falta realizar la operación <strong>de</strong> suma, <strong>de</strong> lo que se <strong>en</strong>carga la última instrucción.<br />
Ejercicio<br />
1. Traduzca a <strong>en</strong>samblador el sigui<strong>en</strong>te código <strong>en</strong> C:<br />
int a, b, V[40], i;<br />
V[i+1] = b + V[i];<br />
V[i-1] = a + V[i];
Codificación <strong>en</strong> l<strong>en</strong>guaje máquina<br />
D<strong>en</strong>tro <strong><strong>de</strong>l</strong> ord<strong>en</strong>ador sólo hay 0’s y 1’s.<br />
Las instrucciones han <strong>de</strong> codificarse <strong>en</strong> binario.<br />
Por ejemplo:<br />
add $t0, $s1, $s2<br />
Se traduce <strong>en</strong> una secu<strong>en</strong>cia <strong>de</strong> 32 1’s y 0’s:<br />
000000 10001 10010 01000 00000 100000<br />
La repres<strong>en</strong>tación binaria está dividida <strong>en</strong> campos<br />
para simplificar su <strong>de</strong>codificación <strong>en</strong> el hardware.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 16<br />
Los programas se escrib<strong>en</strong> <strong>en</strong> l<strong>en</strong>guaje <strong>en</strong>samblador, pero no hay que olvidar que este<br />
l<strong>en</strong>guaje no es más que una repres<strong>en</strong>tación más amigable <strong>de</strong> la única repres<strong>en</strong>tación que<br />
<strong>en</strong>ti<strong>en</strong><strong>de</strong> el procesador: el l<strong>en</strong>guaje máquina. Así, antes <strong>de</strong> po<strong>de</strong>r ejecutar un programa es necesario<br />
traducir cada instrucción <strong>de</strong> l<strong>en</strong>guaje <strong>en</strong>samblador <strong>en</strong> su instrucción correspondi<strong>en</strong>te<br />
<strong>de</strong> l<strong>en</strong>guaje máquina. Este proceso no es muy complicado, según se verá más a<strong><strong>de</strong>l</strong>ante, ya<br />
que para simplificar el hardware esta repres<strong>en</strong>tación binaria se divi<strong>de</strong> <strong>en</strong> campos d<strong>en</strong>tro <strong>de</strong><br />
los cuales se codifica cada una <strong>de</strong> las partes <strong>de</strong> la instrucción: código <strong>de</strong> operación, registros.<br />
. .<br />
En las sigui<strong>en</strong>tes transpar<strong>en</strong>cias se muestra <strong>en</strong> <strong>de</strong>talle cómo es la codificación <strong>de</strong> instrucciones<br />
<strong>en</strong> la arquitectura <strong>MIPS</strong>.
Codificación <strong>en</strong> l<strong>en</strong>guaje máquina<br />
add $t0, $s1, $s2<br />
Operación Destino Fu<strong>en</strong>te 1 Fu<strong>en</strong>te 2<br />
Operación<br />
Fu<strong>en</strong>te 2 no usado<br />
Fu<strong>en</strong>te 1 Destino<br />
0 17 18 8 0 32<br />
000000 10001 10010 01000 00000 100000<br />
Tipo R<br />
ICAI<strong>de</strong>a<br />
6 bits 5 bits 5 bits 5 bits 5 bits 6 bits<br />
op rs rt rd shamtfunct<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 17<br />
InstrucioMichaelG.WPg15ComputerAchiExample<br />
En la transpar<strong>en</strong>cia se muestra la codificación <strong>de</strong> la instrucción:<br />
add $t0, $s1, $s2<br />
Como se dijo <strong>en</strong> el tema anterior, las instrucciones aritméticas <strong><strong>de</strong>l</strong> <strong>MIPS</strong> que operan sólo<br />
con registros se codifican <strong>en</strong> el formato tipo R. Dicho formato comi<strong>en</strong>za con el campo <strong><strong>de</strong>l</strong><br />
código <strong>de</strong> operación (op) <strong>de</strong> 6 bits, que <strong>en</strong> el caso <strong>de</strong> las instrucciones <strong><strong>de</strong>l</strong> tipo R es siempre<br />
0. A continuación vi<strong>en</strong>e un campo <strong>de</strong> 5 bits (rs) <strong>en</strong> don<strong>de</strong> se codifica el número <strong><strong>de</strong>l</strong> primer<br />
registro fu<strong>en</strong>te. En este caso el registro fu<strong>en</strong>te es s1, que según se vio <strong>en</strong> la página 6 es el<br />
registro número 17. En el sigui<strong>en</strong>te campo (rt) se codifica el número <strong><strong>de</strong>l</strong> otro registro fu<strong>en</strong>te,<br />
que <strong>en</strong> este caso es número 18 (s2). En el sigui<strong>en</strong>te campo (rd), también <strong>de</strong> 5 bits, se codifica<br />
el registro <strong>de</strong>stino, 8 <strong>en</strong> este caso (t0, según se pue<strong>de</strong> ver <strong>en</strong> la página 5). El sigui<strong>en</strong>te campo<br />
(shamt) no se usa <strong>en</strong> esta instrucción y por tanto se <strong>de</strong>ja a 0. El último campo (funct) es una<br />
ext<strong>en</strong>sión <strong><strong>de</strong>l</strong> código <strong>de</strong> operación. Este último campo especifica qué instrucción particular<br />
<strong>de</strong> todas las <strong><strong>de</strong>l</strong> tipo R se ha <strong>de</strong> ejecutar. La operación suma se codifica con el número 32.<br />
Nótese que el ord<strong>en</strong> <strong>de</strong> los registros fu<strong>en</strong>te y <strong>de</strong>stino es distinto <strong>en</strong> l<strong>en</strong>guaje <strong>en</strong>samblador<br />
que <strong>en</strong> l<strong>en</strong>guaje máquina.<br />
Las instrucciones <strong>de</strong> carga y almac<strong>en</strong>ami<strong>en</strong>to necesitan especificar dos registros y una<br />
constante. En una primera aproximación al problema podría p<strong>en</strong>sarse <strong>en</strong> utilizar uno <strong>de</strong> los<br />
campos reservados para registros (por ejemplo el rt) para almac<strong>en</strong>ar la constante <strong>en</strong> lugar <strong><strong>de</strong>l</strong><br />
número <strong>de</strong> registro). No obstante con 5 bits sólo se pued<strong>en</strong> repres<strong>en</strong>tar números <strong>en</strong> el rango<br />
0 a 32 o -16 a 15 si se trabaja <strong>en</strong> complem<strong>en</strong>to a 2. Obviam<strong>en</strong>te los vectores y las estructuras<br />
<strong>de</strong> datos usados <strong>en</strong> los programas suel<strong>en</strong> ser mayores <strong>de</strong> 32 bytes, por lo que esta solución no<br />
es muy eficaz. Es preciso por tanto usar otro formato <strong>de</strong> instrucción para las instrucciones <strong>de</strong><br />
carga y almac<strong>en</strong>ami<strong>en</strong>to que permita especificar una constante con un mayor número <strong>de</strong> bits.
Codificación <strong>en</strong> l<strong>en</strong>guaje máquina<br />
lw $t0, 32($s2)<br />
Operación<br />
Destino<br />
Despl.<br />
R. Base<br />
Operación Destino<br />
Fu<strong>en</strong>te 1<br />
35 18 8<br />
32<br />
100011 10010 01000 0000000000010000<br />
Tipo I<br />
ICAI<strong>de</strong>a<br />
6 bits 5 bits 5 bits 16 bits<br />
op rs rt Inmediato<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 18<br />
InstrucioMichaelG.WPg15ComputerAchiExample<br />
En la transpar<strong>en</strong>cia se muestra la codificación <strong>de</strong> la instrucción: lw $t0, 32($s2)<br />
Como esta instrucción necesita una constante para especificar el <strong>de</strong>splazami<strong>en</strong>to, ha <strong>de</strong><br />
codificarse con el formato tipo I. Como se pue<strong>de</strong> apreciar <strong>en</strong> la figura, dicho formato es muy<br />
similar al formato <strong>de</strong> las instrucciones <strong>de</strong> tipo R. La única difer<strong>en</strong>cia consiste <strong>en</strong> que los tres<br />
últimos campos <strong><strong>de</strong>l</strong> tipo R (rd, shamt y funct) se han unido <strong>en</strong> un sólo campo <strong>de</strong> 16 bits<br />
(Inmediato). El hecho <strong>de</strong> que los formatos sean similares simplifica la <strong>de</strong>codificación <strong>de</strong> las<br />
distintas instrucciones.<br />
El significado <strong>de</strong> los dos primeros campos es igual al <strong>de</strong> las instrucciones <strong>de</strong> tipo R: el<br />
primer campo <strong>de</strong> 6 bits especifica el código <strong>de</strong> operación (35 para la instrucción lw) y el<br />
segundo campo especifica el número <strong><strong>de</strong>l</strong> registro base para el acceso a memoria (18 <strong>en</strong> este<br />
ejemplo, que se correspon<strong>de</strong> con el registro s2). El tercer campo especifica un registro, pero<br />
al contrario que <strong>en</strong> las instrucciones <strong>de</strong> tipo R, este registro es el <strong>de</strong>stino <strong>de</strong> la operación.<br />
En este ejemplo, el valor leído <strong>de</strong> la memoria (<strong>en</strong> la dirección (s2) + 32 ) se escribe <strong>en</strong> el<br />
registro número 8 (t0).<br />
Como se pue<strong>de</strong> apreciar, la solución adoptada va contra el primer principio <strong>de</strong> diseño (la<br />
uniformidad simplifica el hardware), ya que <strong>en</strong> lugar <strong>de</strong> t<strong>en</strong>er un formato <strong>de</strong> instrucción fijo<br />
se ti<strong>en</strong><strong>en</strong> formatos distintos <strong>en</strong> función <strong><strong>de</strong>l</strong> tipo <strong>de</strong> instrucción. Esto conduce a otro principio<br />
<strong>de</strong> diseño hardware:<br />
Principio <strong>de</strong> diseño 2: un bu<strong>en</strong> diseño necesita bu<strong>en</strong>as soluciones <strong>de</strong> compromiso.<br />
Obviam<strong>en</strong>te, la solución adoptada <strong>en</strong> este caso complica el hardware al t<strong>en</strong>er que <strong>de</strong>cidir<br />
<strong>en</strong>tre distintos modos <strong>de</strong> <strong>de</strong>codificación <strong>en</strong> función <strong><strong>de</strong>l</strong> tipo <strong>de</strong> instrucción (lo cual, recuer<strong>de</strong>,<br />
vi<strong>en</strong>e marcado por el valor <strong><strong>de</strong>l</strong> código <strong>de</strong> operación). No obstante, con el fin <strong>de</strong> simplificar
la circuitería al máximo, los distintos formatos se han mant<strong>en</strong>ido lo más parecidos posibles.<br />
En este caso, según se ha com<strong>en</strong>tado antes, los tres primeros campos son iguales a los <strong>de</strong> las<br />
instrucciones <strong>de</strong> tipo R.<br />
18-2<br />
Codificación <strong>en</strong> l<strong>en</strong>guaje máquina. Ej.<br />
¿Cómo es el código máquina <strong>de</strong> la expresión <strong>en</strong> C<br />
int a, V[400];<br />
V[300] = V[300] + a;<br />
Si se realiza la asignación <strong>de</strong> registros: a->s0, V->s1.<br />
Inmediato<br />
op rs st rd shamt funct<br />
lw $t0, 1200($s1) 35 17 8 1200<br />
add $t0, $t0, $s0 0 8 16 8 0 32<br />
sw $t0, 1200($s1) 43 17 8 1200<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 19
En la transpar<strong>en</strong>cia se muestra la traducción a código máquina <strong>de</strong> una s<strong>en</strong>t<strong>en</strong>cia s<strong>en</strong>cilla<br />
<strong>en</strong> C:<br />
int a, V[400];<br />
V[300] = V[300] + a;<br />
La primera instrucción conti<strong>en</strong>e un 35 como código <strong>de</strong> operación (lw) seguido <strong>de</strong> un 17<br />
para id<strong>en</strong>tificar el registro base (s1), un 8 para indicar el registro <strong>de</strong>stino (t0) y un 1200 para<br />
seleccionar un <strong>de</strong>splazami<strong>en</strong>to <strong>de</strong> 300 palabras (300 pal · 4Byte/pal = 1200Bytes).<br />
De la misma manera, la instrucción sigui<strong>en</strong>te (add) se codifica con un 0 <strong>en</strong> el campo<br />
<strong>de</strong> código <strong>de</strong> operación y un 32 <strong>en</strong> el último campo (funct). El segundo y tercer campo<br />
id<strong>en</strong>tifican los registros fu<strong>en</strong>te (t0 y s0) y el cuarto campo indica el registro <strong>de</strong>stino (t0). El<br />
p<strong>en</strong>último campo no se usa <strong>en</strong> esta instrucción y por eso se <strong>de</strong>ja a 0.<br />
Por último, nótese que la tercera instrucción es similar a la primera: se guarda el cont<strong>en</strong>ido<br />
<strong><strong>de</strong>l</strong> registro <strong>en</strong> la posición <strong>de</strong> memoria <strong>en</strong> lugar <strong>de</strong> leerlo. Por tanto, lo único que varía<br />
es el código <strong>de</strong> operación (43 <strong>en</strong> lugar <strong>de</strong> 35), si<strong>en</strong>do el resto <strong>de</strong> campos idénticos a los <strong>de</strong> la<br />
primera instrucción.<br />
En la sigui<strong>en</strong>te transpar<strong>en</strong>cia se muestra la codificación <strong>en</strong> binario, que es la que “ve”<br />
realm<strong>en</strong>te el procesador.<br />
Codificación <strong>en</strong> l<strong>en</strong>guaje máquina. Ej.<br />
La codificación <strong>en</strong> binario es:<br />
Inmediato<br />
op rs st rd shamt funct<br />
100011 10001 01000 0000 0100 1011 0000<br />
000000 01000 10000 01000 00000 100000<br />
101011 10001 01000 0000 0100 1011 0000<br />
La única difer<strong>en</strong>cia <strong>en</strong>tre la 1 a y la 3 a instrucción es el<br />
tercer bit.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 20
En la transpar<strong>en</strong>cia se muestra la codificación <strong>de</strong> las instrucciones <strong>en</strong> binario, que es el<br />
formato <strong>en</strong> el que las “ve” el procesador. No obstante esta notación es muy oscura para los<br />
programadores, por lo que no se usa <strong>en</strong> la práctica. Se ha usado aquí simplem<strong>en</strong>te con fines<br />
ilustrativos.<br />
Nótese que para simplificar la <strong>de</strong>codificación, instrucciones similares se codifican <strong>de</strong><br />
forma similar. En este caso la difer<strong>en</strong>cia <strong>en</strong>tre la 1 a y la 3 a instrucción consiste solam<strong>en</strong>te <strong>en</strong><br />
el tercer bit empezando por la izquierda (bit 29 <strong>de</strong> la palabra).<br />
Por último, es necesario resaltar que las instrucciones <strong>de</strong> un programa no son más que<br />
una secu<strong>en</strong>cia <strong>de</strong> números. Así, el código anterior no son más que tres números binarios <strong>de</strong><br />
32 bits. En concreto:<br />
240386168<br />
17842208<br />
2921858224<br />
Por tanto las instrucciones se pued<strong>en</strong> almac<strong>en</strong>ar <strong>en</strong> la memoria al igual que los datos.<br />
Esta es una <strong>de</strong> las i<strong>de</strong>as fundam<strong>en</strong>tales <strong>de</strong> la informática y es precisam<strong>en</strong>te lo que hace que<br />
los ord<strong>en</strong>adores sean unas máquinas trem<strong>en</strong>dam<strong>en</strong>te flexibles: basta con cambiar el programa<br />
para que un ord<strong>en</strong>ador pase a realizar una tarea totalm<strong>en</strong>te distinta.<br />
Toma <strong>de</strong> <strong>de</strong>cisiones<br />
La toma <strong>de</strong> <strong>de</strong>cisiones es lo que distingue a un<br />
ord<strong>en</strong>ador <strong>de</strong> una calculadora.<br />
Permite ejecutar distintas instrucciones <strong>en</strong> función <strong>de</strong><br />
datos <strong>de</strong> <strong>en</strong>trada o resultados <strong>de</strong> cálculos.<br />
En <strong>MIPS</strong> se incluy<strong>en</strong> 2 instrucciones para toma <strong>de</strong><br />
<strong>de</strong>cisiones:<br />
• beq reg1, reg2, L1<br />
• bne reg1, reg2, L1<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 21
La posibilidad <strong>de</strong> tomar <strong>de</strong>cisiones <strong>en</strong> función <strong>de</strong> los datos <strong>de</strong> <strong>en</strong>trada o <strong><strong>de</strong>l</strong> resultado <strong>de</strong><br />
un cálculo es lo que distingue a un ord<strong>en</strong>ador <strong>de</strong> una simple calculadora. Por ello todos los<br />
procesadores incluy<strong>en</strong> instrucciones que permitan ejecutar una serie <strong>de</strong> instrucciones u otras<br />
<strong>en</strong> función <strong>de</strong> un resultado lógico. Esta toma <strong>de</strong> <strong>de</strong>cisiones se repres<strong>en</strong>ta <strong>en</strong> los l<strong>en</strong>guajes <strong>de</strong><br />
programación mediante la s<strong>en</strong>t<strong>en</strong>cia if.<br />
La arquitectura <strong>MIPS</strong> incluye dos instrucciones que permit<strong>en</strong> tomar <strong>de</strong>cisiones <strong>en</strong> función<br />
<strong>de</strong> los valores almac<strong>en</strong>ados <strong>en</strong> dos <strong>de</strong> sus registros:<br />
• beq reg1, reg2, L1. Esta instrucción compara los cont<strong>en</strong>idos <strong>de</strong> los registros<br />
reg1 y reg2. Si son iguales, el programa salta a la instrucción etiquetada con L1. Si<br />
son distintos, el programa continúa <strong>en</strong> la instrucción sigui<strong>en</strong>te (a beq). El nemónico<br />
beq vi<strong>en</strong>e <strong>de</strong> branch if equal.<br />
• bne reg1, reg2, L1. Al igual que la instrucción anterior, esta instrucción compara<br />
los cont<strong>en</strong>idos <strong>de</strong> los registros reg1 y reg2. Si son distintos, el programa salta a la<br />
instrucción etiquetada con L1. Si son iguales, el programa continúa <strong>en</strong> la instrucción<br />
sigui<strong>en</strong>te. El nemónico bne vi<strong>en</strong>e <strong>de</strong> branch if not equal.<br />
Debido a su modo <strong>de</strong> funcionami<strong>en</strong>to, este tipo <strong>de</strong> instrucciones se conoc<strong>en</strong> como saltos<br />
condicionales.<br />
El equival<strong>en</strong>te <strong>en</strong> C <strong>de</strong> ambas instrucciones es, respectivam<strong>en</strong>te:<br />
• if(i==j) goto L1;<br />
• if(i!=j) goto L1;<br />
En don<strong>de</strong> se ha supuesto que las variables i y j están almac<strong>en</strong>adas <strong>en</strong> los registros Reg1<br />
y Reg2.<br />
Toma <strong>de</strong> <strong>de</strong>cisiones. Ejemplo<br />
¿Cómo se traduce <strong>en</strong> <strong>en</strong>samblador la expresión <strong>en</strong> C<br />
int a, b, c, i, j;<br />
if(i == j) goto L1;<br />
b = b + c;<br />
L1: a = b - c;<br />
Si las variables a-j se asignan a los registros s0-s4.<br />
beq $s3, $s4, L1 # Si i==j ir a L1<br />
add $s1, $s1, $s2 # se ejecuta si i!=j<br />
L1: sub $s0, $s1, $s2 # se ejecuta siempre<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 22
En el fragm<strong>en</strong>to <strong>de</strong> código C mostrado, se realiza un salto a la instrucción etiquetada<br />
con L1 sólo si las variables i y j son iguales. Por tanto, la instrucción b = b + c sólo se<br />
ejecutará si i!=j y la instrucción a = b - c se ejecutará siempre. Nótese que este ejemplo<br />
es muy “artificial”, ya que sólo persigue ilustrar las instrucciones <strong>de</strong> salto condicional <strong>de</strong> la<br />
arquitectura <strong>MIPS</strong>. De hecho, la mayoría <strong>de</strong> los programadores evita el uso <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias<br />
goto <strong>en</strong> sus programas. En el sigui<strong>en</strong>te ejemplo se mostrará cómo codificar la s<strong>en</strong>t<strong>en</strong>cia<br />
if-th<strong>en</strong>-else, que es la que se usa normalm<strong>en</strong>te.<br />
La traducción a <strong>en</strong>samblador es directa. La s<strong>en</strong>t<strong>en</strong>cia if se traduce por:<br />
beq $s3, $s4, L1 # Si i==j ir a L1<br />
En don<strong>de</strong> la etiqueta L1, al igual que <strong>en</strong> C, id<strong>en</strong>tifica la instrucción a la que es necesario<br />
saltar si la condición es cierta. La sigui<strong>en</strong>te s<strong>en</strong>t<strong>en</strong>cia también equivale a una sola instrucción:<br />
add $s1, $s1, $s2 # se ejecuta si i!=j<br />
La última s<strong>en</strong>t<strong>en</strong>cia <strong>en</strong> C también se traduce por una sola instrucción <strong>en</strong> <strong>en</strong>samblador,<br />
sólo que ahora es necesario incluir la etiqueta <strong>de</strong> forma que la instrucción <strong>de</strong> salto condicional<br />
pueda saltar a ella cuando la comparación sea cierta. Para ello basta con añadir el nombre <strong>de</strong><br />
la etiqueta terminado con el carácter ’:’ <strong><strong>de</strong>l</strong>ante <strong>de</strong> la instrucción:<br />
L1: sub $s0, $s1, $s2 # se ejecuta siempre<br />
De este modo el <strong>en</strong>samblador hace correspon<strong>de</strong>r la etiqueta L1 con la dirección <strong>de</strong> la<br />
instrucción sub $s0, $s1, $s2. Cuando éste traduzca la primera instrucción a l<strong>en</strong>guaje<br />
máquina, la etiqueta L1 será sustituida por la dirección <strong>de</strong> la tercera instrucción. Por tanto el<br />
<strong>en</strong>samblador está liberando al programador <strong>de</strong> otra tarea muy tediosa y prop<strong>en</strong>sa a errores: el<br />
cálculo <strong>de</strong> las direcciones <strong>de</strong> salto.<br />
22-2
Toma <strong>de</strong> <strong>de</strong>cisiones. Ejemplo<br />
ICAI<strong>de</strong>a<br />
int a, b, c, i, j;<br />
if(i == j){<br />
a = b + c;<br />
}else{<br />
a = b - c;<br />
}<br />
Se traduce <strong>en</strong> (a-j se asignan a los registros s0-s4):<br />
bne $s3, $s4, SiNo # Si i!=j ir a SiNo<br />
add $s0, $s1, $s2 # se evita si i!=j<br />
j Fin<br />
# Salta a Fin<br />
SiNo: sub $s0, $s1, $s2 # se ejecuta si i!=j<br />
Fin:<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 23<br />
En la transpar<strong>en</strong>cia se muestra cómo se traduce una s<strong>en</strong>t<strong>en</strong>cia if-else a <strong>en</strong>samblador<br />
<strong>MIPS</strong>. En primer lugar, al igual que <strong>en</strong> el ejemplo anterior, la s<strong>en</strong>t<strong>en</strong>cia if se traduce por una<br />
instrucción <strong>de</strong> salto condicional. Sin embargo, ahora se comprueba la condición contraria<br />
para saltar al bloque else si la condición <strong><strong>de</strong>l</strong> if es falsa:<br />
bne $s3, $s4, SiNo # Si i!=j ir a SiNo<br />
La sigui<strong>en</strong>te s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> C, al t<strong>en</strong>er todos sus operandos situados <strong>en</strong> registros se traduce<br />
por una sola instrucción <strong>de</strong> <strong>en</strong>samblador:<br />
add $s0, $s1, $s2 # se evita si i!=j<br />
Una vez ejecutada esta instrucción, es necesario que el programa continúe <strong>de</strong>spués <strong>de</strong> la<br />
s<strong>en</strong>t<strong>en</strong>cia if. Para ello es necesario introducir una nueva instrucción <strong><strong>de</strong>l</strong> <strong>en</strong>samblador <strong>MIPS</strong>:<br />
el salto incondicional. Esta instrucción se id<strong>en</strong>tifica mediante el nemónico j (<strong>de</strong> jump). Su<br />
formato es simplem<strong>en</strong>te j Etiqueta. En este caso:<br />
j Fin # Salta a Fin<br />
Esta instrucción hace que el procesador salte siempre a la instrucción marcada por la<br />
etiqueta Fin. A continuación se sitúa el resultado <strong>de</strong> traducir la s<strong>en</strong>t<strong>en</strong>cia <strong><strong>de</strong>l</strong> else <strong><strong>de</strong>l</strong> programa<br />
<strong>en</strong> C. Al igual que antes, esta s<strong>en</strong>t<strong>en</strong>cia se traduce a una sola instrucción aritmética. No<br />
obstante, es necesario prece<strong>de</strong>r dicha instrucción por la etiqueta SiNo, ya que esta instrucción<br />
es el <strong>de</strong>stino <strong><strong>de</strong>l</strong> salto <strong>de</strong> la comaración:<br />
SiNo: sub $s0, $s1, $s2 # se ejecuta si i!=j<br />
Por último, es necesario incluir la etiqueta Fin, que es el <strong>de</strong>stino <strong><strong>de</strong>l</strong> salto incondicional
al final <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia if. Dicha etiqueta repres<strong>en</strong>tará la dirección <strong>de</strong> la instrucción sigui<strong>en</strong>te,<br />
que no se ha mostrado <strong>en</strong> la transpar<strong>en</strong>cia para simplificar. Por ejemplo, si la sigui<strong>en</strong>te<br />
instrucción <strong><strong>de</strong>l</strong> programa <strong>en</strong> <strong>en</strong>samblador fuese add $t0, $t0, $t1, <strong>en</strong>tonces el final <strong><strong>de</strong>l</strong><br />
programa anterior sería:<br />
Fin:<br />
add $t0, $t0, $t1<br />
Nótese que no es necesario que la etiqueta esté <strong>en</strong> la misma línea que la instrucción.<br />
Únicam<strong>en</strong>te ha <strong>de</strong> escribirse antes <strong>de</strong> ella.<br />
Ejercicios<br />
1. Traduzca a <strong>en</strong>samblador el ejemplo mostrado <strong>en</strong> la transpar<strong>en</strong>cia usando la instrucción<br />
beq para realizar la comparación <strong>en</strong>tre i y j.<br />
2. Traduzca a <strong>en</strong>samblador el sigui<strong>en</strong>te código <strong>en</strong> C:<br />
int a, b, c, i, j;<br />
if(i == j){<br />
a = b + c;<br />
}
Toma <strong>de</strong> <strong>de</strong>cisiones. Bucles<br />
int a, V[100], i, j, k;<br />
Bucle: a = a + V[i];<br />
i = i + j;<br />
if( i != k) goto Bucle;<br />
Se traduce <strong>en</strong> (a-k se asignan a los registros s0-s4):<br />
Bucle: add $t1, $s2, $s2 # t1 = 2i<br />
add $t1, $t1, $t1 # t1 = 4i<br />
add $t1, $t1, $s1 # t1 = dir. <strong>de</strong> V[i]<br />
lw $t0, 0($t1) # t0 = V[i]<br />
add $s0, $s0, $t0 # a = a + V[i]<br />
add $s2, $s2, $s3 # i = i + j<br />
bne $s2, $s4, Bucle # si i!=k salta<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 24<br />
Al igual que se hizo con el ejemplo <strong><strong>de</strong>l</strong> if, esta forma <strong>de</strong> escribir un bucle <strong>en</strong> C no<br />
es la más común. Es más, no es nada recom<strong>en</strong>dable su uso. Sin embargo, su traducción<br />
a <strong>en</strong>samblador es directa y por ello se ha usado <strong>en</strong> este primer ejemplo. En la sigui<strong>en</strong>te<br />
transpar<strong>en</strong>cia se mostrará cómo se traduce un bucle while a <strong>en</strong>samblador <strong>MIPS</strong>.<br />
El programa <strong>en</strong> C repite las dos primeras instrucciones mi<strong>en</strong>tras i!=k. La primera instrucción<br />
conti<strong>en</strong>e un acceso a un vector. Por lo tanto es necesario multiplicar por 4 el índice<br />
i (s2), <strong>de</strong> lo que se <strong>en</strong>cargan las dos primeras instrucciones. La tercera instrucción calcula la<br />
dirección <strong><strong>de</strong>l</strong> elem<strong>en</strong>to <strong><strong>de</strong>l</strong> vector y por último la cuarta lo carga <strong>en</strong> el registro t0. La sigui<strong>en</strong>te<br />
instrucción calcula a = a + V[i] y la sexta i = i + j. La última instrucción saltará al<br />
principio <strong><strong>de</strong>l</strong> bucle si se cumple la condición i!=k. En caso contrario el programa continuará<br />
<strong>en</strong> la instrucción que sigue a bne, terminándose así el bucle.<br />
Ejercicio<br />
Modifique el programa anterior para evitar el cálculo <strong>de</strong> 4*i <strong>en</strong> cada iteración <strong><strong>de</strong>l</strong> bucle.<br />
¿Cuantas instrucciones se repit<strong>en</strong> ahora <strong>en</strong> cada iteración Suponi<strong>en</strong>do que todas las instrucciones<br />
tardan lo mismo <strong>en</strong> ejecutarse, ¿Cual será la mejora <strong>en</strong> el tiempo <strong>de</strong> ejecución <strong>de</strong> un<br />
programa que repita este bucle 10 veces
Toma <strong>de</strong> <strong>de</strong>cisiones. Bucles<br />
int a, V[100], i, j, k;<br />
while(V[i] == k){<br />
i = i + j;<br />
}<br />
Se traduce <strong>en</strong> (a-k se asignan a los registros s0-s4):<br />
Bucle: add $t1, $s2, $s2 # t1 = 2i<br />
add $t1, $t1, $t1 # t1 = 4i<br />
add $t1, $t1, $s1 # t1 = dir. <strong>de</strong> V[i]<br />
lw $t0, 0($t1) # t0 = V[i]<br />
bne $t0, $s4, Fin # si i!=k salta<br />
add $s2, $s2, $s3 # i = i + j<br />
j Bucle<br />
Fin:<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 25<br />
En esta transpar<strong>en</strong>cia se muestra la traducción a <strong>en</strong>samblador <strong>de</strong> un bucle while <strong>en</strong> C.<br />
Como se pue<strong>de</strong> apreciar, aunque este bucle es más fácil <strong>de</strong> usar que el anterior, su traducción<br />
a <strong>en</strong>samblador es un poco más compleja. Afortunadam<strong>en</strong>te este trabajo extra <strong>de</strong> traducción<br />
lo realiza normalm<strong>en</strong>te el compilador.<br />
Las 4 primeras instrucciones son las mismas que <strong>en</strong> el ejemplo anterior, ya que lo primero<br />
que hay que hacer es cargar el valor V[i] <strong>de</strong>s<strong>de</strong> la memoria al registro t0. Una vez cargado<br />
dicho valor, se pue<strong>de</strong> evaluar la condición <strong><strong>de</strong>l</strong> bucle while. Esto se realiza <strong>en</strong> la 5 a instrucción.<br />
En ella se compara el valor <strong>de</strong> V[i] (t0) con el valor <strong>de</strong> k (s4). Si son distintos se salta a la<br />
etiqueta Fin, terminándose así el bucle. Si son iguales, se continuará ejecutando la sigui<strong>en</strong>te<br />
instrucción para realizar la suma i = i+j, es <strong>de</strong>cir, se ejecuta el “cuerpo” <strong><strong>de</strong>l</strong> bucle. Una vez<br />
realizada la suma se salta al principio <strong><strong>de</strong>l</strong> bucle mediante un salto incondicional. Nótese que<br />
<strong>en</strong> la instrucción <strong>de</strong> comparación se ha cambiado la condición que aparece <strong>en</strong> el código C<br />
para conseguir un código más compacto.<br />
Ejercicio<br />
Traduzca el bucle while anterior a <strong>en</strong>samblador <strong>MIPS</strong> pero utilizando la instrucción beq<br />
<strong>en</strong> lugar <strong>de</strong> la instrucción bne.
Toma <strong>de</strong> <strong>de</strong>cisiones. M<strong>en</strong>or que<br />
• Las comparaciones más habituales <strong>en</strong> los<br />
programas son la igualdad y la <strong>de</strong>sigualdad.<br />
• Es necesario también saber si una variable es<br />
m<strong>en</strong>or que otra. Para ello <strong>MIPS</strong> <strong>de</strong>fine la<br />
instrucción slt (set on less than).<br />
• Por ejemplo: la instrucción slt $t0, $s3, $s4<br />
Pone a 1 t0 si s3 es m<strong>en</strong>or que s4. Si no t0 se<br />
pone a 0.<br />
• Combinando slt, bne, beq y el registro zero se<br />
pued<strong>en</strong> realizar todas las comparaciones.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 26<br />
Aunque las comparaciones más habituales <strong>en</strong> los programas son la igualdad y la <strong>de</strong>sigualdad,<br />
<strong>en</strong> muchas ocasiones es necesario saber si el valor <strong>de</strong> una variable es m<strong>en</strong>or que otra.<br />
Por ejemplo <strong>en</strong> un bucle for que recorre un vector es muy frecu<strong>en</strong>te comparar si el índice<br />
es m<strong>en</strong>or que la dim<strong>en</strong>sión <strong><strong>de</strong>l</strong> vector. Para realizar este tipo <strong>de</strong> comparaciones, la arquitectura<br />
<strong>MIPS</strong> <strong>de</strong>fine la instrucción slt (<strong>de</strong> set on less than). Dicha instrucción consta <strong>de</strong> tres<br />
argum<strong>en</strong>tos, un registro <strong>de</strong>stino y dos registros fu<strong>en</strong>te:<br />
slt <strong>de</strong>stino, fu<strong>en</strong>te1, fu<strong>en</strong>te2<br />
Su funcionami<strong>en</strong>to consiste <strong>en</strong> comparar si el cont<strong>en</strong>ido <strong><strong>de</strong>l</strong> registro fu<strong>en</strong>te1 es m<strong>en</strong>or<br />
que el <strong><strong>de</strong>l</strong> registro fu<strong>en</strong>te2. En caso afirmativo escribe un 1 <strong>en</strong> el registro <strong>de</strong>stino y <strong>en</strong> caso<br />
negativo escribe un 0.<br />
En la sigui<strong>en</strong>te transpar<strong>en</strong>cia se muestra un ejemplo <strong><strong>de</strong>l</strong> uso <strong>de</strong> esta instrucción para<br />
traducir una s<strong>en</strong>t<strong>en</strong>cia if a <strong>en</strong>samblador.<br />
Ejercicio<br />
Los compiladores <strong><strong>de</strong>l</strong> <strong>MIPS</strong> utilizan las instrucciones slt, bne, beq y el valor cero (almac<strong>en</strong>ado<br />
<strong>en</strong> el “registro” zero) para realizar todas las comparaciones posibles (==, !=, , =). Escriba las secu<strong>en</strong>cias <strong>de</strong> instrucciones <strong>MIPS</strong> que permit<strong>en</strong> evaluar dichas condiciones<br />
<strong>en</strong>tre los registros s0 y s1 y saltar a la etiqueta Fin si el resultado <strong>de</strong> la comparación<br />
es cierto. Por ejemplo, para evaluar la condición s0 > s1 se escribiría:<br />
slt $t0, $s1, Ss0<br />
bne $t0, $zero, Fin
Toma <strong>de</strong> <strong>de</strong>cisiones. Ejemplo<br />
int a, b, c, i, j;<br />
if(i >= j){<br />
a = b + c;<br />
}<br />
Se traduce <strong>en</strong> (a-j se asignan a los registros s0-s4):<br />
slt $t0, $s3, $s4 # evalúa i < j<br />
bne $t0, $zero, Fin # si i= j, se ha evaluado la contraria (i < j) para saltarse el<br />
“cuerpo” <strong><strong>de</strong>l</strong> if si dicha condición contraria es cierta.<br />
En este caso la evaluación <strong>de</strong> la condición y el salto se realizan <strong>en</strong> dos pasos. En primer<br />
lugar con la instrucción:<br />
slt $t0, $s3, $s4 # evalúa i < j<br />
Se evalúa la condición i < j y se actualiza el valor <strong><strong>de</strong>l</strong> registro t0 con el resultado <strong>de</strong> la<br />
comparación. A continuación, mediante una instrucción <strong>de</strong> salto condicional se salta al final<br />
<strong><strong>de</strong>l</strong> if si el resultado <strong>de</strong> la comparación anterior ha sido cierto:<br />
bne $t0, $zero, Fin # si i
Llamadas a funciones<br />
Las funciones permit<strong>en</strong> programas estructurados,<br />
reutilizar código, etc.<br />
Dos aspectos importantes:<br />
• Ti<strong>en</strong><strong>en</strong> un interfaz bi<strong>en</strong> <strong>de</strong>finido: Parámetros <strong>de</strong><br />
<strong>en</strong>trada y resultados <strong>de</strong> salida.<br />
• Realizan su trabajo sin interferir con la función<br />
que las llama.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 28<br />
En cualquier programa, salvo que éste sea extremadam<strong>en</strong>te simple, es necesario usar<br />
funciones. El uso <strong>de</strong> funciones pres<strong>en</strong>ta numerosas v<strong>en</strong>tajas, <strong>en</strong>tre las que <strong>de</strong>stacan la mejora<br />
<strong>de</strong> la legibilidad <strong><strong>de</strong>l</strong> programa, la posibilidad <strong>de</strong> reutilizar código, el facilitar la programación<br />
al permitir dividir un problema complejo <strong>en</strong> varios “subproblemas” más fáciles <strong>de</strong> abordar,<br />
etc.<br />
Aunque ya <strong>de</strong>be <strong>de</strong> estar familiarizado con el uso <strong>de</strong> funciones <strong>en</strong> l<strong>en</strong>guajes <strong>de</strong> alto nivel,<br />
convi<strong>en</strong>e recalcar dos aspectos <strong>de</strong> su uso:<br />
• Constan <strong>de</strong> un interfaz bi<strong>en</strong> <strong>de</strong>finido con el resto <strong><strong>de</strong>l</strong> programa. Recib<strong>en</strong> una serie <strong>de</strong><br />
argum<strong>en</strong>tos con los que realiza su tarea y <strong>de</strong>vuelv<strong>en</strong> el resultado <strong>de</strong> ésta a “qui<strong>en</strong>” la<br />
ha llamado.<br />
• Son capaces <strong>de</strong> realizar su tarea sin interferir con el resto <strong><strong>de</strong>l</strong> programa. Esto se consigue<br />
mediante un espacio <strong>de</strong> trabajo propio aislado <strong><strong>de</strong>l</strong> resto <strong><strong>de</strong>l</strong> programa. Así, las<br />
variables que utiliza son locales a la función y no son accesibles <strong>de</strong>s<strong>de</strong> fuera <strong>de</strong> ella.<br />
De la misma forma, la función no pue<strong>de</strong> acce<strong>de</strong>r a las variables locales <strong>de</strong> las <strong>de</strong>más<br />
funciones.
Llamadas a funciones<br />
Para po<strong>de</strong>r ejecutar una función es necesario:<br />
1. Situar los parámetros don<strong>de</strong> la función llamada<br />
pueda leerlos. a0-a3, pila<br />
2. Transferir el control a la función llamada.<br />
3. Reservar espacio para las variables locales.<br />
t0-t9, pila.<br />
4. Realizar la tarea necesaria.<br />
5. Situar el resultado don<strong>de</strong> la función llamante<br />
pueda leerlos. v0-v1, pila<br />
6. Devolver el control a la función llamante.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 29<br />
En la transpar<strong>en</strong>cia se muestran los distintos pasos que es necesario realizar <strong>en</strong> la llamada<br />
a una función.<br />
Tanto el <strong>en</strong>vío <strong>de</strong> argum<strong>en</strong>tos (paso 1) como la reserva <strong>de</strong> espacio para las variables<br />
locales (paso 3) y la recepción <strong>de</strong> los resultados (paso 5) son tarea <strong><strong>de</strong>l</strong> programador (o <strong><strong>de</strong>l</strong><br />
compilador si se programa <strong>en</strong> un l<strong>en</strong>guaje <strong>de</strong> alto nivel). Ahora bi<strong>en</strong>, todo ello se realiza<br />
sigui<strong>en</strong>do una conv<strong>en</strong>ción software para que todas las funciones puedan interactuar sin problemas,<br />
in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>tem<strong>en</strong>te <strong>de</strong> su autor y <strong><strong>de</strong>l</strong> l<strong>en</strong>guaje <strong>en</strong> que hayan sido escritas. En el caso<br />
<strong><strong>de</strong>l</strong> <strong>MIPS</strong>, con el objetivo <strong>de</strong> conseguir mejores prestaciones, se reservan 4 registros (a0-a3)<br />
para <strong>en</strong>viar argum<strong>en</strong>tos a la función y 2 registros (v0-v1) para que la función <strong>de</strong>vuelva su<br />
resultado. Si se necesita <strong>en</strong>viar a la función un número mayor <strong>de</strong> argum<strong>en</strong>tos o datos que no<br />
puedan ser almac<strong>en</strong>ados <strong>en</strong> registros, <strong>en</strong>tonces es necesario recurrir a la pila. De la misma<br />
forma, si la función ha <strong>de</strong> <strong>de</strong>volver algún dato que no quepa <strong>en</strong> los registros (v0-v1), como<br />
por ejemplo una estructura <strong>de</strong> datos, será necesario recurrir a la pila. Las variables locales se<br />
pued<strong>en</strong> almac<strong>en</strong>ar <strong>en</strong> los registros reservados para valores temporales (t0-t9) o <strong>en</strong> la pila. En<br />
caso necesario se pued<strong>en</strong> usar también los registros (s0-s7), aunque antes <strong>de</strong> escribir nada<br />
<strong>en</strong> ellos será necesario guardar su valor <strong>en</strong> la pila y restaurarlos justo antes <strong>de</strong> <strong>de</strong>volver el<br />
control a la función llamante, pues dicha función supone que la función llamada <strong>de</strong>jará estos<br />
registros intactos. En otras arquitecturas (p. ej. IA-32) <strong>de</strong>bido al escaso número <strong>de</strong> registros<br />
disponible (8) no se reservan registros para el paso <strong>de</strong> argum<strong>en</strong>tos ni para valores temporales,<br />
si<strong>en</strong>do <strong>en</strong>tonces necesario un uso int<strong>en</strong>sivo <strong>de</strong> la pila.<br />
El último aspecto <strong>de</strong> la llamada a una función (paso 6) es la continuación <strong>de</strong> la ejecución<br />
<strong>de</strong> la función llamante justo <strong>en</strong> la instrucción sigui<strong>en</strong>te a la usada para realizar la llamada a la<br />
función (paso 2). Para ello es necesario guardar la dirección <strong>de</strong> dicha instrucción sigui<strong>en</strong>te <strong>en</strong><br />
algún sitio, lo cual necesita que el procesador incluya hardware específico para dicha tarea.
En la sigui<strong>en</strong>te transpar<strong>en</strong>cia se discute la solución adoptada por los arquitectos <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.<br />
29-2<br />
Llamadas a funciones. Instrucción jal<br />
• Al transferir el control a la función es necesario<br />
guardar la dirección <strong>de</strong> retorno.<br />
• En el <strong>MIPS</strong> esta tarea la realiza la instrucción jal<br />
(jump and link)<br />
• Formato: jal Dir_<strong>de</strong>_la_función<br />
• Funcionami<strong>en</strong>to:<br />
• Guarda la dirección <strong>de</strong> retorno (PC+4) <strong>en</strong> el<br />
registro ra.<br />
• Salta a la dirección Dir_<strong>de</strong>_la_función.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 30
La instrucción jal (<strong><strong>de</strong>l</strong> inglés jump and link, saltar y <strong>en</strong>lazar) permite saltar a la dirección<br />
<strong>de</strong> su argum<strong>en</strong>to (jump) y a la vez almac<strong>en</strong>ar la dirección <strong>de</strong> la instrucción <strong>de</strong> retorno<br />
(link), que será la instrucción que esté situada a continuación <strong>de</strong> jal. Dicha dirección será<br />
obviam<strong>en</strong>te PC+4. El lugar elegido por los arquitectos <strong><strong>de</strong>l</strong> <strong>MIPS</strong> para almac<strong>en</strong>ar la dirección<br />
<strong>de</strong> retorno es un registro reservado para este fin, d<strong>en</strong>ominado ra (<strong><strong>de</strong>l</strong> inglés return address,<br />
dirección <strong>de</strong> retorno). Esto permite que la ejecución <strong>de</strong> la instrucción se realice <strong>en</strong> un solo<br />
ciclo <strong>de</strong> reloj. Las arquitecturas CISC guardan la dirección <strong>de</strong> retorno <strong>en</strong> la pila, aunque<br />
esto conlleva un tiempo <strong>de</strong> ejecución mayor, pues hace falta hacer un acceso adicional a la<br />
memoria para guardar la dirección <strong>de</strong> retorno.<br />
Por ejemplo, si se <strong>de</strong>sea saltar a la función llamada fun bastará con escribir:<br />
jal fun<br />
A<strong>de</strong>más <strong>de</strong> esto, es necesario que la primera instrucción <strong>de</strong> la función t<strong>en</strong>ga como etiqueta<br />
fun:<br />
fun: add $t0, $a0, $a1<br />
...<br />
Llamadas a funciones. Instrucción jr<br />
• Para volver <strong>de</strong> una función es necesario saltar a<br />
la dirección almac<strong>en</strong>ada <strong>en</strong> ra<br />
• En el <strong>MIPS</strong> esta tarea se realiza con la<br />
instrucción jr (jump register).<br />
• Formato: jr registro<br />
• Funcionami<strong>en</strong>to: Salta a la dirección almac<strong>en</strong>ada<br />
<strong>en</strong> el registro.<br />
Por tanto, el retorno <strong>de</strong> la función se realiza con:<br />
jr $ra<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 31
Por último, una vez finalizada su tarea, la función ha <strong>de</strong> colocar el resultado <strong>en</strong> los<br />
registros v0-v1 o la pila y <strong>de</strong>volver el control a la función que la ha llamado. Como la llamada<br />
a la función mediante la instrucción jal guarda la dirección <strong>de</strong> la instrucción que le sigue<br />
<strong>en</strong> el registro ra, basta con saltar a la dirección almac<strong>en</strong>ada <strong>en</strong> dicho registro. Para ello,<br />
el <strong>MIPS</strong> dispone <strong>de</strong> una instrucción que permite saltar incondicionalm<strong>en</strong>te a la dirección<br />
almac<strong>en</strong>ada <strong>en</strong> un registro, llamada jr (<strong><strong>de</strong>l</strong> ingles jump register, saltar registro). Por tanto, la<br />
última instrucción <strong>de</strong> la función será precisam<strong>en</strong>te:<br />
jr $ra<br />
Resumi<strong>en</strong>do, la función llamante coloca los argum<strong>en</strong>tos <strong>en</strong> los registros a0-a3 y mediante<br />
la instrucción “jal fun” llama a la función fun. Dicha función realiza su tarea y<br />
<strong>de</strong>posita sus resultados <strong>en</strong> los registros v0-v1 para a continuación <strong>de</strong>volver el control a la<br />
función llamante mediante la instrucción jr $ra. En la sigui<strong>en</strong>te transpar<strong>en</strong>cia se muestra<br />
un ejemplo para aclarar el proceso.<br />
Llamadas a funciones. Ejemplo<br />
int funHoja(int g, int h, int i, int j)<br />
{<br />
int f;<br />
f = (g + h) - (i + j);<br />
return f;<br />
}<br />
Se traduce <strong>en</strong> (g-j se pasan <strong>en</strong> los registros a0-a3):<br />
funHoja: add $t0, $a0, $a1 # t0 = g + h<br />
add $t1, $a2, $a3 # t1 = i + j<br />
sub $t0, $t0, $t1 # t0 = (g+h) - (i+j)<br />
add $v0, $t0, $zero # v0 = t0<br />
jr $ra<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 32
En la transpar<strong>en</strong>cia se muestra un ejemplo <strong>de</strong> traducción a <strong>en</strong>samblador <strong>de</strong> una función<br />
muy simple escrita <strong>en</strong> C. Como se pue<strong>de</strong> apreciar <strong>en</strong> primer lugar, dicha función no llama<br />
a ninguna otra. A este tipo <strong>de</strong> funciones se les d<strong>en</strong>omina funciones hoja. A<strong>de</strong>más, dicha<br />
función sólo necesita 4 argum<strong>en</strong>tos, por lo que no es necesario recurrir a la pila. De la misma<br />
forma, como la función <strong>de</strong>vuelve un valor <strong>en</strong>tero, basta con un sólo registro para ello (v0).<br />
Como se pue<strong>de</strong> observar, la función sólo necesita almac<strong>en</strong>ar dos valores <strong>en</strong> variables<br />
locales (los resultados parciales <strong>de</strong> sus cálculos), por lo que basta con usar dos <strong>de</strong> los 10<br />
registros temporales disponibles (t0 y t1).<br />
Una vez terminados sus cálculos, la función ha <strong>de</strong> copiar el resultado obt<strong>en</strong>ido al registro<br />
v0 para <strong>de</strong>volverlo a la función llamante. En <strong>MIPS</strong> no existe una instrucción específica para<br />
mover datos <strong>en</strong>tre registros, aunque dicho movimi<strong>en</strong>to se pue<strong>de</strong> realizar sumándole cero al<br />
registro orig<strong>en</strong> y usando el registro <strong>de</strong>stino como resultado <strong>de</strong> la suma. Así, lo que <strong>en</strong> otras<br />
arquitecturas se escribiría como:<br />
move $v0, $t0<br />
En <strong>MIPS</strong> se escribe como:<br />
add $v0, $t0, $zero<br />
Ejercicio<br />
La codificación <strong>de</strong> la función anterior dista bastante <strong>de</strong> ser la óptima, tanto <strong>en</strong> uso <strong>de</strong><br />
registros como <strong>en</strong> número <strong>de</strong> instrucciones. Escriba otra versión <strong>de</strong> la función más efici<strong>en</strong>te.<br />
Llamadas a funciones. La pila<br />
• En algunas arquitecturas exist<strong>en</strong> instrucciones<br />
explícitas para el manejo <strong>de</strong> la pila (push y pop).<br />
• El <strong>MIPS</strong> no ti<strong>en</strong>e soporte explícito <strong>de</strong> la pila sino<br />
una conv<strong>en</strong>ción software:<br />
• Se reserva el registro sp (stack pointer) para<br />
apuntar al “tope” <strong>de</strong> la pila.<br />
• La pila “crece” hacia abajo.<br />
sp<br />
sp<br />
Cont<strong>en</strong>ido ra<br />
Cont<strong>en</strong>ido a0<br />
sp<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 33
Como se ha com<strong>en</strong>tado antes, el uso <strong>de</strong> la pila es imprescindible <strong>en</strong> la llamada a las<br />
funciones. Esto es <strong>de</strong>bido a que sólo es necesario conocer el ord<strong>en</strong> <strong>en</strong> que se han almac<strong>en</strong>ado<br />
los datos, no su posición absoluta y a<strong>de</strong>más pue<strong>de</strong> crecer “in<strong>de</strong>finidam<strong>en</strong>te”; permitiéndose<br />
así el anidami<strong>en</strong>to <strong>de</strong> varias funciones sin que los datos <strong>de</strong> éstas interfieran <strong>en</strong>tre sí.<br />
Aunque muchos procesadores incluy<strong>en</strong> instrucciones específicas para el manejo <strong>de</strong> la<br />
pila (push y pop), el <strong>MIPS</strong> no dispone <strong>de</strong> ellas. No obstante existe una conv<strong>en</strong>ción software<br />
para implantar una pila:<br />
• Se reserva el registro sp (<strong><strong>de</strong>l</strong> inglés stack pointer, puntero <strong>de</strong> pila) para apuntar al<br />
“tope” <strong>de</strong> la pila. Es responsabilidad <strong><strong>de</strong>l</strong> programador el manejo <strong>de</strong> este registro, es<br />
<strong>de</strong>cir, si se introduce o se retira un dato <strong>en</strong> la pila, habrá que modificar el sp para que<br />
continúe apuntando al tope <strong>de</strong> la pila.<br />
• La pila “crece” <strong>de</strong>s<strong>de</strong> posiciones altas <strong>de</strong> memoria hacia posiciones bajas. No hay<br />
ninguna razón para hacerlo así salvo por los preced<strong>en</strong>tes históricos. En consecu<strong>en</strong>cia<br />
el “tope” <strong>de</strong> la pila será la dirección más baja <strong>de</strong> ésta.<br />
En la figura <strong>de</strong> la transpar<strong>en</strong>cia se ilustra la evolución <strong>de</strong> la pila cuando se llama a una<br />
función que necesita guardar dos registros (ra y a0) <strong>en</strong> la pila. Como se pue<strong>de</strong> apreciar <strong>en</strong><br />
primer lugar (figura izquierda) el registro sp estará apuntando al último dato introducido <strong>en</strong><br />
la pila (no mostrado por simplificar). Cuando se llama a la función, ésta necesita guardar<br />
<strong>en</strong> la pila dos registros, por lo que el sp se <strong>de</strong>crem<strong>en</strong>ta <strong>en</strong> dos palabras (8 bytes) y <strong>en</strong> el<br />
espacio creado se copiará el cont<strong>en</strong>ido <strong>de</strong> los dos registros (ra y a0) (figura c<strong>en</strong>tral). Una vez<br />
finalizada la función, la pila ha <strong>de</strong> quedar como estaba, tal como se muestra <strong>en</strong> la figura <strong>de</strong> la<br />
<strong>de</strong>recha.<br />
Llamadas a funciones. Anidami<strong>en</strong>tos<br />
En el ejemplo anterior se mostró una función hoja.<br />
Lam<strong>en</strong>tablem<strong>en</strong>te hay funciones que llaman a otras<br />
funciones.<br />
En estos casos es necesario:<br />
• Guardar la dirección <strong>de</strong> retorno ra<br />
• Guardar los registros (a0-a3 y t0-t9) que se<br />
necesit<strong>en</strong> <strong>de</strong>spués.<br />
Obviam<strong>en</strong>te estos registros se guardan <strong>en</strong> la pila.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 34
En el ejemplo mostrado anteriorm<strong>en</strong>te, la función era una función hoja, es <strong>de</strong>cir, la<br />
función no llamaba a ninguna otra función. Desgraciadam<strong>en</strong>te, no todas las funciones son<br />
funciones hoja, por lo que el mecanismo <strong>de</strong> llamada se complica un poco <strong>en</strong> estos casos.<br />
En concreto, la función llamante ha <strong>de</strong> guardar los registros que la función llamada pueda<br />
modificar:<br />
• El primer registro que se modifica es el registro <strong>de</strong> la dirección <strong>de</strong> retorno ra, ya que<br />
<strong>en</strong> cuanto se ejecute la instrucción jal dicho registro se sobreescribirá con la nueva<br />
dirección <strong>de</strong> retorno. Por tanto, antes <strong>de</strong> llamar a una función es necesario<br />
guardar el valor <strong>de</strong> ra <strong>en</strong> la pila.<br />
• Si la función necesita algún argum<strong>en</strong>to, este ha <strong>de</strong> copiarse <strong>en</strong> los registros a0-a3.<br />
Por tanto, si la función llamante ha <strong>de</strong> usar algún valor <strong>de</strong> estos registros <strong>de</strong>spués <strong>de</strong><br />
la llamada a la función, ha <strong>de</strong> guardar éstos <strong>en</strong> la pila. Nótese que aunque no se<br />
necesite usar alguno <strong>de</strong> los registros a0-a3 para pasar argum<strong>en</strong>tos a la función, ésta<br />
pue<strong>de</strong> llamar a otra función que necesite ese registro como argum<strong>en</strong>to. Por tanto,<br />
siempre se guardarán los registros a0-a3 cuyos valores sean necesarios<br />
<strong>de</strong>spués <strong>de</strong> la llamada a una función.<br />
• Si la función llamante necesita usar algún valor almac<strong>en</strong>ado <strong>en</strong> los registros temporales<br />
t0-t9 <strong>de</strong>spués <strong>de</strong> la llamada a la función, también t<strong>en</strong>drá que guardarlos <strong>en</strong> la<br />
pila antes <strong>de</strong> llamarla.<br />
El ejemplo más complicado <strong>de</strong> función no hoja es una función recursiva. En la sigui<strong>en</strong>te<br />
transpar<strong>en</strong>cia se muestra una función para calcular el factorial <strong>de</strong> forma recursiva. Aunque<br />
este es el peor ejemplo <strong>de</strong> uso <strong>de</strong> la recursividad, pues es mucho más efici<strong>en</strong>te resolverlo con<br />
un bucle, su simplicidad justifica su uso <strong>en</strong> este caso.<br />
Llamadas a funciones. Ejemplo<br />
int fact(int n)<br />
{<br />
if (n < 1){<br />
return 1;<br />
}else{<br />
return n * fact(n-1);<br />
}<br />
}<br />
Se traduce <strong>en</strong> (n se pasa <strong>en</strong> el registro a0):<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 35
Llamadas a funciones. Ejemplo<br />
fact: slti $t0, $a0, 1 # n=1, a L1<br />
addi $v0, $zero, 1 # <strong>de</strong>vuelve 1<br />
jr $ra<br />
L1: addi $sp, $sp, -8 # Reserva 2 palabras <strong>en</strong> pila<br />
sw $ra, 4($sp) # guarda ra y<br />
sw $a0, 0($sp) # n <strong>en</strong> pila<br />
addi $a0, $a0, -1 # a0=n-1<br />
jal fact<br />
lw $a0, 0($sp) # restaura n<br />
lw $ra, 4($sp) # y ra<br />
addi $sp, $sp, 8 # ajusta sp<br />
mul $v0, $a0, $v0 # <strong>de</strong>vuelve<br />
jr $ra # n * fact(n-1)<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 36<br />
Al igual que el programa <strong>en</strong> C, lo primero que realiza la función es comprobar si su<br />
argum<strong>en</strong>to n (a0) es m<strong>en</strong>or que 1 para <strong>en</strong> caso afirmativo <strong>de</strong>volver un 1. Para realizar la<br />
comparación se ha usado una versión <strong>de</strong> la instrucción slt d<strong>en</strong>ominada slti (<strong><strong>de</strong>l</strong> inglés set<br />
on less than inmediate). El funcionami<strong>en</strong>to es idéntico a la instrucción slt salvo que <strong>en</strong> lugar<br />
<strong>de</strong> comparar dos registros, compara un registro con una constante. Por tanto la instrucción:<br />
slti $t0, $a0, 1 # n
sw $ra, 4($sp) # guarda ra y<br />
sw $a0, 0($sp) # n <strong>en</strong> pila<br />
La estructura <strong>de</strong> la pila <strong>en</strong> este instante será la mostrada <strong>en</strong> la figura <strong>de</strong> la transpar<strong>en</strong>cia<br />
33. Una vez salvados los dos registros, se pasa a llamar a la función fact. Para ello, se<br />
sitúa <strong>en</strong> el registro a0 el valor n-1, lo cual se hace restando 1 a dicho registro, ya que éste<br />
cont<strong>en</strong>ía el valor <strong>de</strong> n. Una vez hecho esto ya se pue<strong>de</strong> llamar a la función fact mediante la<br />
instrucción jal:<br />
addi $a0, $a0, -1 # a0=n-1<br />
jal fact<br />
Cuando dicha función <strong>de</strong>vuelva el control, <strong>en</strong> el registro v0 estará almac<strong>en</strong>ado el valor <strong>de</strong><br />
(n-1)!. Por tanto, lo único que hay que hacer es restaurar los registros que se almac<strong>en</strong>aron<br />
<strong>en</strong> la pila y calcular n*(n-1)! para <strong>de</strong>volverlo. La restauración <strong>de</strong> registros es el proceso<br />
inverso al realizado para guardarlos:<br />
lw $a0, 0($sp) # restaura n<br />
lw $ra, 4($sp) # y ra<br />
addi $sp, $sp, 8 # ajusta sp<br />
Como se pue<strong>de</strong> apreciar, <strong>de</strong>spués <strong>de</strong> la ejecución <strong>de</strong> estas instrucciones la pila se quedará<br />
como estaba antes <strong>de</strong> llamar a la función. Lo único que resta por hacer es calcular n*(n-1)!<br />
y <strong>de</strong>volver el control a la función llamante, lo cual se realiza mediante:<br />
mult $v0, $a0, $v0 # <strong>de</strong>vuelve<br />
jr $ra # n * fact(n-1)<br />
Ejercicio<br />
Escriba un programa <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong> para calcular el factorial <strong>de</strong> 7. Utilice<br />
para ello la función fact <strong>de</strong>sarrollada anteriorm<strong>en</strong>te.<br />
36-3
Llamadas a funciones. Bloque <strong>de</strong> activación<br />
La pila crece y <strong>de</strong>crece durante la ejecución <strong>de</strong> la<br />
función para:<br />
• Almac<strong>en</strong>ar variables locales.<br />
• Evaluar expresiones.<br />
• Guardar registros.<br />
El segm<strong>en</strong>to <strong>de</strong> la pila que conti<strong>en</strong>e todos los datos <strong>de</strong><br />
una función se d<strong>en</strong>omina bloque <strong>de</strong> activación<br />
Algunos programas usan el registro fp para apuntar al<br />
principio <strong><strong>de</strong>l</strong> bloque <strong>de</strong> activación.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 37<br />
Durante la ejecución <strong>de</strong> la función la pila pue<strong>de</strong> crecer y <strong>de</strong>crecer. Por ejemplo si se<br />
<strong>de</strong>clara una estructura como variable local, ésta t<strong>en</strong>drá que almac<strong>en</strong>arse <strong>en</strong> la pila, para lo<br />
que será necesario <strong>de</strong>crem<strong>en</strong>tar el sp <strong>en</strong> el número <strong>de</strong> palabras que ocupe dicha estructura.<br />
A<strong>de</strong>más algunos programas necesitan usar la pila para evaluar expresiones matemáticas complejas.<br />
Por si esto fuera poco, algunos l<strong>en</strong>guajes como C++ permit<strong>en</strong> crear variables locales<br />
<strong>en</strong> cualquier punto <strong>de</strong> la función, por lo que la pila pue<strong>de</strong> crecer <strong>en</strong> cualquier mom<strong>en</strong>to. Esto<br />
hace que si se usa el registro sp para acce<strong>de</strong>r a las variables almac<strong>en</strong>adas <strong>en</strong> la pila, será necesario<br />
utilizar <strong>de</strong>splazami<strong>en</strong>tos distintos a lo largo <strong>de</strong> la función, dificultándose la escritura<br />
<strong><strong>de</strong>l</strong> programa.<br />
Por tanto, es conv<strong>en</strong>i<strong>en</strong>te t<strong>en</strong>er una refer<strong>en</strong>cia a las variables almac<strong>en</strong>adas <strong>en</strong> la pila<br />
que no varíe durante la ejecución <strong>de</strong> la función. Algunos programas <strong>MIPS</strong> usan el registro<br />
fp para almac<strong>en</strong>ar la primera palabra <strong><strong>de</strong>l</strong> bloque <strong>de</strong> la pila usado por la función, al que se<br />
d<strong>en</strong>omina bloque <strong>de</strong> activación. Por ello al registro fp se le conoce como puntero al bloque<br />
<strong>de</strong> activación (frame pointer). Dicho registro se carga con el valor <strong><strong>de</strong>l</strong> sp al iniciar la función<br />
y no varía hasta que ésta termine. Por tanto, los accesos a las variables almac<strong>en</strong>adas <strong>en</strong> la<br />
pila pued<strong>en</strong> referirse al fp y dichas refer<strong>en</strong>cias no t<strong>en</strong>drán que cambiarse cada vez que se<br />
introduzca o elimine algún dato <strong>de</strong> la pila.<br />
Ejercicio<br />
El uso <strong><strong>de</strong>l</strong> puntero al bloque <strong>de</strong> activación facilita a<strong>de</strong>más la restauración <strong><strong>de</strong>l</strong> puntero <strong>de</strong><br />
pila al final <strong>de</strong> la ejecución <strong>de</strong> la función. Escriba las instrucciones necesarias para guardar<br />
el principio <strong><strong>de</strong>l</strong> bloque <strong>de</strong> activación <strong>en</strong> fp al principio <strong>de</strong> la función y para restaurar el sp al<br />
finalizar la función.
Llamadas a funciones. Paso <strong>de</strong> arg. por pila<br />
Si hay más <strong>de</strong> 4 parámetros o alguno <strong>de</strong> éstos no<br />
cabe <strong>en</strong> registros se pasan por la pila.<br />
Conv<strong>en</strong>ción software: se sitúan por <strong>en</strong>cima <strong><strong>de</strong>l</strong> bloque<br />
<strong>de</strong> activación.<br />
fp<br />
sp<br />
Bloque<br />
Activación<br />
fp<br />
sp<br />
Argum<strong>en</strong>tos<br />
Bloque<br />
Activación<br />
fp<br />
sp<br />
Bloque<br />
Activación<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 38<br />
Según se ha dicho anteriorm<strong>en</strong>te, hay ocasiones <strong>en</strong> las cuales los 4 registros reservados<br />
para el paso <strong>de</strong> argum<strong>en</strong>tos <strong>en</strong>tre funciones (a0-a3) no son sufici<strong>en</strong>tes:<br />
• Si hay más <strong>de</strong> 4 argum<strong>en</strong>tos. En este caso los 4 primeros se pasan <strong>en</strong> los registros y<br />
los restantes <strong>en</strong> la pila.<br />
• Si algún o algunos argum<strong>en</strong>tos no cab<strong>en</strong> <strong>en</strong> un registro. En este caso los argum<strong>en</strong>tos<br />
que no quepan se pasan por la pila.<br />
En estos casos, los argum<strong>en</strong>tos se sitúan justo <strong>en</strong>cima <strong><strong>de</strong>l</strong> bloque <strong>de</strong> activación <strong>de</strong> la función<br />
llamada (o visto <strong>de</strong> otra manera, al final <strong><strong>de</strong>l</strong> bloque <strong>de</strong> activación <strong>de</strong> la función llamante).<br />
Para acce<strong>de</strong>r a ellos, lo más conv<strong>en</strong>i<strong>en</strong>te es usar el puntero al bloque <strong>de</strong> actuvación fp.<br />
Ejercicios<br />
1. Escriba la sigui<strong>en</strong>te función <strong>en</strong> C <strong>en</strong> <strong>en</strong>samblador <strong>MIPS</strong>:<br />
int suma(int a, int b, int c, int d, int e, int f){<br />
return a+b+c+d+e+f;<br />
}<br />
2. Escriba la secu<strong>en</strong>cia <strong>de</strong> intrucciones <strong>de</strong> <strong>en</strong>samblador <strong>MIPS</strong> para sumar los valores<br />
almac<strong>en</strong>ados <strong>en</strong> los 6 registros s0-s5 mediante una llamada a la función suma. El<br />
resultado se guardará <strong>en</strong> el registro s6.
Manejo <strong>de</strong> caracteres<br />
Los caracteres se codifican <strong>en</strong> ASCII.<br />
Un carácter ASCII ocupa 1 byte.<br />
El <strong>MIPS</strong> dispone <strong>de</strong> dos instrucciones para cargar y<br />
almac<strong>en</strong>ar bytes:<br />
• lb (load byte). Lee un byte <strong>de</strong> la memoria y lo<br />
almac<strong>en</strong>a <strong>en</strong> los 8 bits m<strong>en</strong>os significativos <strong><strong>de</strong>l</strong><br />
registro <strong>de</strong>stino. Ej. lb $t0, 0($s0).<br />
• sb (store byte). Almac<strong>en</strong>a los 8 bits m<strong>en</strong>os<br />
significativos <strong><strong>de</strong>l</strong> registro orig<strong>en</strong> <strong>en</strong> la memoria.<br />
Ej. sb $t0, 0($s0).<br />
Los caracteres pued<strong>en</strong> estar <strong>en</strong> cualquier posición <strong>de</strong><br />
memoria.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 39<br />
La mayoría <strong>de</strong> las aplicaciones informáticas necesitan procesar caracteres. Exist<strong>en</strong> varios<br />
métodos <strong>de</strong> codificación, aunque el más ext<strong>en</strong>dido es el ASCII, <strong>en</strong> el cual un carácter ocupa<br />
un byte.<br />
El <strong>MIPS</strong> dispone <strong>de</strong> dos instrucciones para cargar y almac<strong>en</strong>ar bytes: lb y sb. El funcionami<strong>en</strong>to<br />
<strong>de</strong> ambas instrucciones es análogo a lw y sw, excepto que sólo le<strong>en</strong> un byte <strong>de</strong><br />
la memoria y lo almac<strong>en</strong>an <strong>en</strong> los 8 bits m<strong>en</strong>os significativos <strong><strong>de</strong>l</strong> registro <strong>de</strong>stino. A<strong>de</strong>más el<br />
byte pue<strong>de</strong> estar situado <strong>en</strong> cualquier posición <strong>de</strong> memoria.
Manejo <strong>de</strong> caracteres. Ejemplo<br />
El sigui<strong>en</strong>te ejemplo muestra la función strcpy <strong>de</strong> C:<br />
void strcpy(char <strong>de</strong>st[], char orig[])<br />
{<br />
int i;<br />
i = 0;<br />
/*Copia y comprueba final*/<br />
while( (<strong>de</strong>st[i] = orig[i]) != NULL){<br />
i++;<br />
}<br />
}<br />
¿Cuál es la traducción a <strong>en</strong>samblador <strong>MIPS</strong><br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 40<br />
En la transpar<strong>en</strong>cia se muestra una implem<strong>en</strong>tación <strong>de</strong> la función strcpy <strong>de</strong> la librería<br />
estándar <strong>de</strong> C. Dicha función copia una cad<strong>en</strong>a oríg<strong>en</strong> (orig) <strong>en</strong> otra cad<strong>en</strong>a <strong>de</strong>stino (<strong>de</strong>st).<br />
Ambas cad<strong>en</strong>as sigu<strong>en</strong> la conv<strong>en</strong>ción <strong>de</strong> C para indicar su final mediante el carácter nulo<br />
(NULL).<br />
La función copia el caracter i <strong>de</strong> la cad<strong>en</strong>a oríg<strong>en</strong> <strong>en</strong> la posición i <strong>de</strong> la cad<strong>en</strong>a <strong>de</strong>stino<br />
y <strong>de</strong>spúes comprueba si éste es distinto <strong>de</strong> NULL. Si no lo es increm<strong>en</strong>ta el índice y continúa<br />
el bucle. Cuando el carácter copiado sea el terminador nulo <strong>de</strong> la cad<strong>en</strong>a, el bucle terminará,<br />
aunque <strong>de</strong>spués <strong>de</strong> copiar el NULL a la cad<strong>en</strong>a <strong>de</strong> <strong>de</strong>stino, con lo que ésta será correcta.<br />
En la sigui<strong>en</strong>te transpar<strong>en</strong>cia se muestra el código <strong>en</strong> <strong>en</strong>samblador <strong>MIPS</strong> para implem<strong>en</strong>tar<br />
esta misma función.
Manejo <strong>de</strong> caracteres. Ejemplo<br />
strcpy:<br />
add $t0, $zero, $zero # i = 0<br />
L1: add $t1, $t0, $a1 # t1=dir(orig[i])<br />
lb $t2, 0($t1) # t2 = orig[i]<br />
add $t3, $t0, $a0 # t3=dir(<strong>de</strong>st[i])<br />
sb $t2, 0($t3) # <strong>de</strong>st[i] = t2<br />
add $t0, $t0, 1 # i++<br />
bne $t2, $zero, L1<br />
jr $ra<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 41<br />
La primera instrucción símplem<strong>en</strong>te pone a cero el registro t0, que va a ser usado para<br />
almac<strong>en</strong>ar el índice i. Nótese que dicha operación se hace almac<strong>en</strong>ando el resultado <strong>de</strong> sumar<br />
0+0.<br />
La sigui<strong>en</strong>te instrucción calcula la dirección <strong><strong>de</strong>l</strong> carácter <strong>de</strong> la cad<strong>en</strong>a orig<strong>en</strong> que se va a<br />
copiar. Para ello se suma el índice i a la base <strong>de</strong> la cad<strong>en</strong>a, que según pue<strong>de</strong> <strong>de</strong>spr<strong>en</strong><strong>de</strong>rse <strong><strong>de</strong>l</strong><br />
código <strong>en</strong> C, estará almac<strong>en</strong>ado <strong>en</strong> el registro a1, pues es el segundo argum<strong>en</strong>to <strong>de</strong> la función.<br />
Una vez calculada la dirección, se pue<strong>de</strong> usar la instrucción lb para cargar el carácter <strong>en</strong> el<br />
registro t2, que es lo que se hace <strong>en</strong> la tercera instrucción.<br />
A continuación se calcula la dirección <strong>en</strong> la que hay que almac<strong>en</strong>ar el carácter, que será<br />
el resultado <strong>de</strong> sumar el índice i a la base <strong>de</strong> la cad<strong>en</strong>a <strong>de</strong>stino (almac<strong>en</strong>ada <strong>en</strong> a0) y se<br />
almac<strong>en</strong>a el carácter <strong>en</strong> dicha dirección mediante la instrucción sb<br />
Por último se increm<strong>en</strong>ta el índice y se salta al principio <strong><strong>de</strong>l</strong> bucle si el carácter es distinto<br />
<strong>de</strong> cero (recuer<strong>de</strong> que <strong>en</strong> C NULL es un 0). Si se <strong>en</strong>cu<strong>en</strong>tra el terminador nulo <strong>de</strong> la cad<strong>en</strong>a, no<br />
se producirá el salto y se <strong>de</strong>vuelve el control mediante la instrucción jr.<br />
Ejercicios<br />
1. ¿Por qué no hace falta multiplicar por 4 el índice <strong>de</strong> la cad<strong>en</strong>a<br />
2. La codificación <strong>en</strong> <strong>en</strong>samblador no refleja exáctam<strong>en</strong>te el código C <strong>de</strong> la función.<br />
¿Por qué Codifique una función que refleje exáctam<strong>en</strong>te el código C y compare<br />
su efici<strong>en</strong>cia con la mostrada <strong>en</strong> la transpar<strong>en</strong>cia. Indique para ello el número <strong>de</strong><br />
instrucciones ejecutadas <strong>en</strong> cada versión <strong>de</strong> la función si se copia una cad<strong>en</strong>a <strong>de</strong> 80<br />
caracteres.
Aritmética. Números con y sin signo<br />
El <strong>MIPS</strong> trabaja con “ristras” <strong>de</strong> 8, 16 o 32 bits.<br />
Estas ristras <strong>de</strong> bits pued<strong>en</strong> repres<strong>en</strong>tar, <strong>en</strong>tre otras<br />
cosas:<br />
• Un número sin signo (rango 0 ↔ 2 n − 1).<br />
• Un número con signo <strong>en</strong> complem<strong>en</strong>to a 2 (rango<br />
−2 n−1 ↔ 2 n−1 − 1).<br />
Tanto las instrucciones aritméticas como las <strong>de</strong> carga<br />
y comparación han <strong>de</strong> distinguir si sus operandos son<br />
con o sin signo.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 42<br />
El procesador es una máquina capaz <strong>de</strong> procesar “ristras” <strong>de</strong> dígitos binarios. En el caso<br />
<strong><strong>de</strong>l</strong> <strong>MIPS</strong>, éste pue<strong>de</strong> trabajar con “ristras” <strong>de</strong> 8, 16 y 32 bits. El significado <strong>de</strong> dichas “ristras”<br />
<strong>de</strong> bits es totalm<strong>en</strong>te indifer<strong>en</strong>te para el procesador, 1 si<strong>en</strong>do responsabilidad <strong><strong>de</strong>l</strong> programador<br />
su interpretación.<br />
Cuando se trabaja con números <strong>en</strong>teros, éstos pued<strong>en</strong> consi<strong>de</strong>rarse como números sin<br />
signo, con lo cual podremos repres<strong>en</strong>tar números <strong>en</strong> el rango <strong>de</strong> 0 a 2 n − 1. o bi<strong>en</strong> como<br />
números codificados <strong>en</strong> complem<strong>en</strong>to a dos (rango <strong>de</strong> −2 n−1 a 2 n−1 − 1). Ahora bi<strong>en</strong>, la<br />
forma <strong>de</strong> operar con ambos tipos <strong>de</strong> números es distinta, por lo que se usan instrucciones<br />
distintas según el tipo <strong>de</strong> datos utilizados. Así, las instrucciones aritméticas, <strong>de</strong> carga y <strong>de</strong><br />
comparación son distintas según oper<strong>en</strong> con datos sin signo o con signo.<br />
En el caso <strong>de</strong> los l<strong>en</strong>guajes <strong>de</strong> alto nivel como C, al <strong>de</strong>clarar una variable se informa al<br />
sistema <strong><strong>de</strong>l</strong> tipo <strong>de</strong> ésta. Así, para crear un <strong>en</strong>tero con signo se <strong>de</strong>clara como int y sin signo<br />
como unsigned int. Es <strong>en</strong>tonces tarea <strong><strong>de</strong>l</strong> compilador elegir las operaciones necesarias<br />
para operar correctam<strong>en</strong>te con los datos. En cambio, cuando se programa <strong>en</strong> <strong>en</strong>samblador,<br />
esta tarea recae <strong>en</strong> el programador.<br />
1 Salvo <strong>en</strong> el caso <strong>en</strong> que dihas “ristras” <strong>de</strong> bits sean parte <strong>de</strong> un programa.
Aritmética. Números con y sin signo<br />
Cuando se carga un número con signo <strong>de</strong> 16 u 8 bits<br />
<strong>en</strong> un registro <strong>de</strong> 32, es necesario ext<strong>en</strong><strong>de</strong>r el bit <strong>de</strong><br />
signo. Ej:<br />
• -1 <strong>en</strong> 8 bits es 11111111<br />
• -1 <strong>en</strong> 16 bits es 11111111 11111111<br />
• -1 <strong>en</strong> 32 bits es 11111111 11111111 11111111<br />
11111111<br />
Esta ext<strong>en</strong>sión es necesaria porque los circuitos<br />
aritméticos operan sólo con números <strong>de</strong> 32 bits.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 43<br />
Aritmética. Números con y sin signo<br />
Cuando se carga un número sin signo <strong>de</strong> 16 u 8 bits<br />
<strong>en</strong> un registro <strong>de</strong> 32, no hay que ext<strong>en</strong><strong>de</strong>r el bit <strong>de</strong><br />
signo. Ej:<br />
• 255 <strong>en</strong> 8 bits es 11111111<br />
• 255 <strong>en</strong> 32 bits es 00000000 00000000 00000000<br />
11111111<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 44
Aritmética. Números con y sin signo<br />
Las instrucciones <strong>de</strong> carga vistas hasta ahora cargan<br />
números con signo:<br />
• lb carga un byte ext<strong>en</strong>di<strong>en</strong>do el signo.<br />
• lh carga una media palabra (16 bits)<br />
ext<strong>en</strong>di<strong>en</strong>do el signo. La media palabra ha <strong>de</strong><br />
estar <strong>en</strong> una dirección par.<br />
• lw carga una palabra. No ti<strong>en</strong>e s<strong>en</strong>tido ext<strong>en</strong><strong>de</strong>r<br />
el signo, pues no hay más bits.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 45<br />
Debido a que internam<strong>en</strong>te el <strong>MIPS</strong> siempre realiza las operaciones con 32 bits, cuando<br />
se carga un dato <strong>de</strong> 8 o 16 bits <strong>en</strong> un registro, es necesario que la cantidad repres<strong>en</strong>tada por<br />
los 32 bits sea la misma que la repres<strong>en</strong>tada por los 8 o los 16 bits que se han cargado. Si<br />
las cantida<strong>de</strong>s almac<strong>en</strong>adas <strong>en</strong> 8 o 16 bits son sin signo, basta con copiar esos 8 o 16 bits <strong>en</strong><br />
los 8 o 16 bits m<strong>en</strong>os significativos <strong><strong>de</strong>l</strong> registro <strong>de</strong> 32 bits. Ahora bi<strong>en</strong>, si los números <strong>de</strong> 8<br />
o 16 bits son con signo, es necesario copiar el bit <strong>de</strong> signo <strong>en</strong> los bits superiores <strong><strong>de</strong>l</strong> registro<br />
<strong>de</strong> 32 bits para que se mant<strong>en</strong>ga el signo <strong><strong>de</strong>l</strong> número. Así, si t<strong>en</strong>emos almac<strong>en</strong>ado <strong>en</strong> una<br />
variable <strong>de</strong> 8 bits con signo el valor 11111110 (-2), al copiar dicha variable <strong>en</strong> un registro <strong>de</strong><br />
32 bits, habrá que copiar el bit <strong>de</strong> signo <strong>en</strong> los bits 9 a 31 para que el número almac<strong>en</strong>ado<br />
<strong>en</strong> el registro sea también un -2. En cambio, si t<strong>en</strong>emos almac<strong>en</strong>ado un número sin signo,<br />
por ejemplo 11111110 (254), al copiar este número <strong>en</strong> un registro <strong>de</strong> 32 bits no habrá que<br />
ext<strong>en</strong><strong>de</strong>r el “bit signo” para que se almac<strong>en</strong>e el 254 correctam<strong>en</strong>te <strong>en</strong> el registro <strong>de</strong> 32 bits.<br />
Las instruciones <strong>de</strong> carga vistas hasta ahora (lb y lw) trabajan con números con signo.<br />
En el caso <strong>de</strong> lw no ti<strong>en</strong>e s<strong>en</strong>tido hacer ninguna distinción, pues carga un número <strong>de</strong> 32 bits<br />
<strong>en</strong> un registro <strong>de</strong> 32 bits y por tanto no hay que hacer ext<strong>en</strong>sión <strong>de</strong> signo.<br />
A<strong>de</strong>más <strong>de</strong> estas dos instrucciones <strong>de</strong> carga existe una tercera lh que carga media palabra<br />
(16 bits) <strong>en</strong> los 16 bits m<strong>en</strong>os significativos <strong>de</strong> un registro, ext<strong>en</strong>di<strong>en</strong>do el bit <strong>de</strong> signo. Por<br />
ejemplo, para cargar <strong>en</strong> t0 una media palabra cuya dirección está almac<strong>en</strong>ada <strong>en</strong> el registro<br />
s0, basta con ejecutar la instrucción:<br />
lh $t0, 0($s0)<br />
Las medias palabras han <strong>de</strong> estar alineadas <strong>en</strong> posiciones <strong>de</strong> memoria múltiplos <strong>de</strong> 2.
Aritmética. Números con y sin signo<br />
Las instrucciones <strong>de</strong> carga para números sin signo<br />
son:<br />
• lbu carga un byte sin ext<strong>en</strong><strong>de</strong>r el signo.<br />
• lhu carga una media palabra (16 bits) sin<br />
ext<strong>en</strong><strong>de</strong>r el signo.<br />
• lw carga una palabra. No ti<strong>en</strong>e s<strong>en</strong>tido ext<strong>en</strong><strong>de</strong>r<br />
el signo, pues no hay más bits.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 46<br />
Las instrucciones para cargar números sin signo son iguales que las correspondi<strong>en</strong>tes<br />
para números con signo, salvo que su nemónico termina <strong>en</strong> u (<strong>de</strong> unsigned).<br />
Aunque no se m<strong>en</strong>ciona <strong>en</strong> la transpar<strong>en</strong>cia, la media palabra a cargar con lhu ha <strong>de</strong><br />
estar almac<strong>en</strong>ada <strong>en</strong> una dirección par.<br />
Al igual que antes, no ti<strong>en</strong>e s<strong>en</strong>tido distinguir <strong>en</strong>tre números con signo o sin signo para<br />
la carga <strong>de</strong> palabras <strong>de</strong> 32 bits, pues no hay que ext<strong>en</strong><strong>de</strong>r ningún bit <strong>de</strong> signo.
Aritmética. Números con y sin signo<br />
Las instrucciones <strong>de</strong> comparación son distintas para<br />
números con signo o sin signo:<br />
• slt compara dos números con signo.<br />
• sltu compara dos números sin signo.<br />
• slti Compara un registro con una constante.<br />
Ambas con signo.<br />
• sltiu Compara un registro con una constante.<br />
Ambas sin signo.<br />
Ej: slti $t0, $s0, -7<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 47<br />
La comparación <strong>en</strong>tre dos números <strong>de</strong>p<strong>en</strong><strong>de</strong> <strong>de</strong> si éstos son con signo o sin signo. Por<br />
tanto el <strong>MIPS</strong> dispone <strong>de</strong> versiones <strong>de</strong> las instrucciones <strong>de</strong> comparación para ambos tipos <strong>de</strong><br />
datos.<br />
Al igual que antes, las instrucciones estudiadas hasta ahora comparan números con signo.<br />
Para comparar números sin signo el formato <strong>de</strong> las instrucciones es el mismo salvo que el<br />
nemónico termina <strong>en</strong> una u (<strong>de</strong> unsigned).
Aritmética. Números con y sin signo<br />
Ejemplo:<br />
• s0 = 11111111 11111111 11111111 11111111<br />
• s1 = 00000000 00000000 00000000 00000001<br />
¿Cuanto val<strong>en</strong> t0 y t1 <strong>de</strong>spués <strong>de</strong> ejecutar las<br />
instrucciones<br />
slt $t0, $s0, $s1 # Con signo<br />
sltu $t1, $s0, $s1 # Sin signo<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 48<br />
En el ejemplo, el registro s0 vale -1 si se interpreta como un número <strong>en</strong> complem<strong>en</strong>to a<br />
2 ó 4.294.967.295 si se interpreta como un número sin signo. El registro s1, <strong>en</strong> cambio, vale<br />
1 in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>tem<strong>en</strong>te <strong>de</strong> cómo se interprete.<br />
En la primera instrucción se realiza la comparación con signo. Por tanto, como −1 < 1,<br />
t0 se pondrá a 1.<br />
En cambio, <strong>en</strong> la segunda instrucción se realiza la comparación sin signo, por lo que<br />
como 4,294,967,295 > 1, t1 se pondrá a 0.
Aritmética. Suma y resta<br />
En las instrucciones <strong>de</strong> suma y resta pue<strong>de</strong> ser<br />
necesario saber si ha existido un <strong>de</strong>sbordami<strong>en</strong>to.<br />
• En los números sin signo, utilizados para<br />
aritmética <strong>de</strong> direcciones, se ignora.<br />
• En los números con signo, <strong>de</strong>p<strong>en</strong>di<strong>en</strong>do <strong>de</strong> la<br />
aplicación, pue<strong>de</strong> ser necesario <strong>de</strong>tectarlo.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 49<br />
Al realizar una suma o una resta <strong>de</strong> dos números <strong>de</strong> 32 bits, el resultado pue<strong>de</strong> necesitar<br />
33 bits si ambos números son <strong>de</strong>masiado gran<strong>de</strong>s. Como el resultado se almac<strong>en</strong>a <strong>en</strong> un<br />
registro <strong>de</strong> 32 bits, el bit más significativo se pier<strong>de</strong>. En estos casos se dice que se ha producido<br />
un <strong>de</strong>sbordami<strong>en</strong>to (overflow <strong>en</strong> inglés).<br />
El uso fundam<strong>en</strong>tal <strong>de</strong> los números sin signo es la realización <strong>de</strong> operaciones con direcciones,<br />
<strong>en</strong> las cuales no se suele <strong>de</strong>tectar el <strong>de</strong>sbordami<strong>en</strong>to. En cambio, los números con<br />
signo suel<strong>en</strong> usarse <strong>en</strong> operaciones matemáticas, <strong>en</strong> las que, <strong>de</strong>p<strong>en</strong>di<strong>en</strong>do <strong>de</strong> la aplicación,<br />
pue<strong>de</strong> ser necesario <strong>de</strong>tectar si se ha producido un <strong>de</strong>sbordami<strong>en</strong>to.<br />
La mayoría <strong>de</strong> los procesadores dispon<strong>en</strong> <strong>de</strong> dos bits <strong>en</strong> el registro <strong>de</strong> estado para indicar<br />
si se ha producido un <strong>de</strong>sbordami<strong>en</strong>to:<br />
• El bit <strong>de</strong> accarreo (carry <strong>en</strong> inglés), el cual se pone a 1 cuando se produce un<br />
<strong>de</strong>sbordami<strong>en</strong>to <strong>en</strong> la suma sin signo.<br />
• El bit <strong>de</strong> <strong>de</strong>sbordami<strong>en</strong>to (overflow), el cual se pone a 1 cuando se produce un <strong>de</strong>sbordami<strong>en</strong>to<br />
<strong>en</strong> la suma con signo.<br />
En estos procesadores se incluy<strong>en</strong> a<strong>de</strong>más instrucciones <strong>de</strong> salto condicional que saltan si<br />
alguno <strong>de</strong> estos bits está a 1. Por tanto, para <strong>de</strong>tectar un <strong>de</strong>sbordami<strong>en</strong>to basta con ejecutar la<br />
instrucción <strong>de</strong> salto condicional justo <strong>de</strong>spués <strong>de</strong> la operación aritmética cuyo <strong>de</strong>sbordami<strong>en</strong>to<br />
queremos comprobar. De esta forma el programa pue<strong>de</strong> ejecutar una serie <strong>de</strong> isntrucciones<br />
u otras <strong>en</strong> función <strong>de</strong> si se ha producido o no un <strong>de</strong>sbordami<strong>en</strong>to. En el <strong>MIPS</strong>, según se<br />
muestra <strong>en</strong> la sigui<strong>en</strong>te transpar<strong>en</strong>cia, la solución adoptada es radicalm<strong>en</strong>te distinta.
Aritmética. Suma y resta<br />
<strong>MIPS</strong> dispone <strong>de</strong> dos tipos <strong>de</strong> instrucciones para la<br />
suma y la resta <strong>en</strong> función <strong><strong>de</strong>l</strong> tratami<strong>en</strong>to <strong><strong>de</strong>l</strong><br />
<strong>de</strong>sbordami<strong>en</strong>to.<br />
• add, addi, sub, g<strong>en</strong>eran una excepción si se<br />
produce un <strong>de</strong>sbordami<strong>en</strong>to.<br />
• addu, addiu, subu no g<strong>en</strong>eran excepciones <strong>en</strong><br />
caso <strong>de</strong> <strong>de</strong>sbordami<strong>en</strong>to.<br />
Las instrucciones <strong>de</strong> suma se han estudiado antes. La<br />
instrucción sub $t0, $s0, $s1 resta s0-s1 y<br />
almac<strong>en</strong>a el resultado <strong>en</strong> t0.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 50<br />
El <strong>MIPS</strong> dispone <strong>de</strong> dos tipos <strong>de</strong> instrucciones aritméticas: unas que ti<strong>en</strong><strong>en</strong> <strong>en</strong> cu<strong>en</strong>ta el<br />
<strong>de</strong>sbordami<strong>en</strong>to y otras que lo ignoran.<br />
Las instrucciones add, addi y sub g<strong>en</strong>eran una excepción si se produce un <strong>de</strong>sbordami<strong>en</strong>to.<br />
Una excepción es una llamada a una función no planificada, es <strong>de</strong>cir, que sólo ocurre<br />
cuando se da una situación excepcional, como es un <strong>de</strong>sbordami<strong>en</strong>to <strong>en</strong> este caso. Así, cuando<br />
al ejecutar alguna <strong>de</strong> estas instrucciones se produce un <strong>de</strong>sbordami<strong>en</strong>to, la dirección <strong>de</strong><br />
la instrucción que ha provocado el <strong>de</strong>sbordami<strong>en</strong>to se sitúa <strong>en</strong> un registro especial y la ejecución<br />
<strong><strong>de</strong>l</strong> programa salta a una direción pre<strong>de</strong>finida <strong>en</strong> la que se <strong>en</strong>contrará una rutina <strong>de</strong><br />
at<strong>en</strong>ción <strong>de</strong> la excepción. Esta rutina podrá infomar al usuario <strong><strong>de</strong>l</strong> error y abortar el programa<br />
o int<strong>en</strong>tar tomar medidas correctoras y continuar el programa <strong>de</strong>spués <strong>de</strong> la instrucción que<br />
causó el <strong>de</strong>sbordami<strong>en</strong>to.<br />
Por otro lado, las instrucciones addu, addiu y subu realizan las mismas operaciones que<br />
sus versiones con signo salvo que no g<strong>en</strong>eran una excepción si se produce un <strong>de</strong>sbordami<strong>en</strong>to.<br />
El formato <strong>de</strong> la instrucción <strong>de</strong> resta es sub <strong>de</strong>st, orig1, orig2. Esta instrucción<br />
resta el cont<strong>en</strong>ido <strong>de</strong> los registros orig1-orig2 y <strong>de</strong>posita el resultado <strong>en</strong> el registro <strong>de</strong>st.<br />
Nótese que no existe la instrucción subi. Esto es <strong>de</strong>bido a que la instrucción addi suma a<br />
un registro una constante <strong>de</strong> 16 bits con signo, por lo que para restar una constante basta<br />
con sumar dicha constante cambiada <strong>de</strong> signo. Convi<strong>en</strong>e <strong>de</strong>stacar que tanto las instrucciones<br />
addi como addiu exti<strong>en</strong>d<strong>en</strong> siempre el signo <strong>de</strong> su constante <strong>de</strong> 16 bits antes <strong>de</strong> realizar la<br />
operación. Como se ha dicho antes la única difer<strong>en</strong>cia <strong>en</strong>tre las operaciones “sin signo” y las<br />
“con signo” es que las primeras no g<strong>en</strong>eran excepciones y las segundas sí.<br />
Como se ha dicho antes, otras arquitecturas pose<strong>en</strong> instrucciones <strong>de</strong> salto condicional<br />
<strong>en</strong> función <strong>de</strong> si se ha producido o no un <strong>de</strong>sbordami<strong>en</strong>to. Lam<strong>en</strong>tablem<strong>en</strong>te el <strong>MIPS</strong> no<br />
dispone <strong>de</strong> dicha instrucción, por lo que si queremos <strong>de</strong>tectar el <strong>de</strong>sbordami<strong>en</strong>to sin utilizar
excepciones, es necesario realizarlo mediante una secu<strong>en</strong>cia <strong>de</strong> instrucciones, tal como se<br />
muestra a continuación<br />
50-2<br />
Aritmética. Suma y resta<br />
addu $t0, $t1, $t2 # suma sin <strong>de</strong>sbordami<strong>en</strong>to<br />
xor $t3, $t1, $t2 # signos distintos<br />
slt $t3, $t3, $zero #si t3 no <strong>de</strong>sb.<br />
xor $t3, $t0, $t1 # si signos iguales<br />
slt $t3, $t3, $zero # ¿suma también<br />
bne $t3, $zero, Desb # Si sign(suma)!= sign(op)<br />
# => <strong>de</strong>sbordami<strong>en</strong>to<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 51
Claram<strong>en</strong>te la comprobación <strong><strong>de</strong>l</strong> <strong>de</strong>sbordami<strong>en</strong>to <strong>en</strong> el <strong>MIPS</strong> es realm<strong>en</strong>te compleja.<br />
Afortunadam<strong>en</strong>te <strong>en</strong> la mayoría <strong>de</strong> los casos no es necesario realizar dicha comprobación <strong>de</strong><br />
forma explícita. Por ejemplo <strong>en</strong> C no se ti<strong>en</strong><strong>en</strong> <strong>en</strong> cu<strong>en</strong>ta nunca los <strong>de</strong>sbordami<strong>en</strong>tos para<br />
obt<strong>en</strong>er programas más efici<strong>en</strong>tes, aun a costa <strong>de</strong> que se produzcan resultados erróneos. Otros<br />
l<strong>en</strong>guajes como Fortran o Ada si precisan que se <strong>de</strong>tect<strong>en</strong> los <strong>de</strong>sbordami<strong>en</strong>tos, pues <strong>en</strong> las<br />
aplicaciones escritas <strong>en</strong> dichos l<strong>en</strong>guajes prima mucho más la fiabilidad <strong>de</strong> los programas<br />
que su velocidad <strong>de</strong> ejecución. No obstante, <strong>en</strong> estos l<strong>en</strong>guajes se suele utilizar una rutina <strong>de</strong><br />
excepción para at<strong>en</strong><strong>de</strong>r las excepciones numéricas, por lo que la aproximación seguida por el<br />
<strong>MIPS</strong> es apropiada <strong>en</strong> estos casos.<br />
El programa mostrado <strong>en</strong> la transpar<strong>en</strong>cia se basa <strong>en</strong> verificar <strong>en</strong> primer lugar si los signos<br />
<strong>de</strong> los dos sumandos son distintos, <strong>en</strong> cuyo caso nunca se producirá un <strong>de</strong>sbordami<strong>en</strong>to.<br />
Para ello realiza una or exclusiva bit a bit <strong>de</strong> ambos sumandos con la instrucción <strong>MIPS</strong> xor.<br />
El bit <strong>de</strong> signo <strong><strong>de</strong>l</strong> resultado <strong>de</strong> la XOR será 1 si los signos <strong>de</strong> ambos sumandos son distintos<br />
y 0 si son iguales. Por tanto, si los signos son distintos, <strong>en</strong> t3 t<strong>en</strong>dremos un número negativo,<br />
por lo que mediante la instrucción <strong>de</strong> salto condicional se saltará a la etiqueta no<strong>de</strong>sb. Si ambos<br />
números ti<strong>en</strong><strong>en</strong> el mismo signo, se pue<strong>de</strong> producir un <strong>de</strong>sbordami<strong>en</strong>to, <strong>en</strong> cuyo caso el<br />
resultado t<strong>en</strong>drá distinto signo que los operandos. El resto <strong><strong>de</strong>l</strong> programa es similar al anterior,<br />
sólo que ahora se comprueba si los signos <strong><strong>de</strong>l</strong> resultado y <strong>de</strong> los sumandos son distintos, <strong>en</strong><br />
cuyo caso se salta a la etiqueta Desb para ejecutar el tratami<strong>en</strong>to <strong><strong>de</strong>l</strong> <strong>de</strong>sbordami<strong>en</strong>to.<br />
Como se ha com<strong>en</strong>tado antes, <strong>en</strong> las sumas sin signo no suele ser necesario comprobar<br />
el <strong>de</strong>sbordami<strong>en</strong>to. Por ello el <strong>MIPS</strong> no g<strong>en</strong>erará ninguna excepción <strong>en</strong> este caso. Si se necesita<br />
<strong>de</strong>tectar un <strong>de</strong>sbordami<strong>en</strong>to, la única manera es usando la secu<strong>en</strong>cia <strong>de</strong> instrucciones<br />
mostradas <strong>en</strong> la sigui<strong>en</strong>te transpar<strong>en</strong>cia.<br />
Aritmética. Suma y resta<br />
addu $t0, $t1, $t2 # suma sin <strong>de</strong>sbordami<strong>en</strong>to<br />
nor $t3, $t1, $zero # t3 = not(t1) =<br />
# C2(t1) - 1 = 2^32 -t1-1<br />
sltu $t3, $t3, $t2 # 2^32 -t1-1 < t2 =><br />
# 2^32 -1 < t1 + t2 =><br />
# <strong>de</strong>sbordami<strong>en</strong>to.<br />
bne $t3, $zero, Desb<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 52
En este caso el programa es más corto, aunque más difícil <strong>de</strong> <strong>en</strong>t<strong>en</strong><strong>de</strong>r.<br />
En primer lugar se inviert<strong>en</strong> todos los bits <strong>de</strong> t1. Para ello se utiliza la operación nor que<br />
calcula una OR negada bit a bit. Esta inversión es equival<strong>en</strong>te a calcular el complem<strong>en</strong>to a 2 -<br />
1 (recuer<strong>de</strong> que el complem<strong>en</strong>to a 2 se calcula invirti<strong>en</strong>do todos los bits y sumando 1). Como<br />
el complem<strong>en</strong>to a 2 <strong>de</strong> t1 es 2 32 -t1, el resultado <strong>de</strong> la operación anterior será 2 32 -t1-1. Si se<br />
compara este resultado con t2, t<strong>en</strong>emos que:<br />
(1)<br />
2 32 − t1 − 1 < t2 ⇔ 2 32 − 1 < t1 + t2<br />
Por tanto, si el resultado <strong>de</strong> la comparación es cierto, el resultado <strong>de</strong> t1+t2 será mayor <strong>de</strong><br />
2 32 − 1 y por tanto se necesitarán más <strong>de</strong> 32 bits para repres<strong>en</strong>tarlo. En consecu<strong>en</strong>cia, al no<br />
caber el resultado <strong>en</strong> 32 bits se habrá producido un <strong>de</strong>sbordami<strong>en</strong>to.<br />
Aritmética. Multiplicación<br />
El <strong>MIPS</strong> dispone <strong>de</strong> dos instrucciones para multiplicar:<br />
mult (con signo) o multu (sin signo).<br />
El resultado se almac<strong>en</strong>a <strong>en</strong> un registro <strong>de</strong> 64 bits,<br />
accesible <strong>en</strong> su mitad superior hi y <strong>en</strong> su mitad<br />
inferior lo mediante las instrucciones mfhi y mflo.<br />
Ej: mult $s0, $s1 multiplica s0 por s1<br />
Estas instrucciones no <strong>de</strong>tectan <strong>de</strong>sbordami<strong>en</strong>tos. El<br />
resultado cabrá <strong>en</strong> 32 bits si hi vale 0 <strong>en</strong> multu o la<br />
repetición <strong><strong>de</strong>l</strong> bit <strong>de</strong> signo <strong>en</strong> mult.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 53
En el caso <strong>de</strong> la multiplicación, el hardware es ligeram<strong>en</strong>te distinto según se multipliqu<strong>en</strong><br />
números con signo o números sin signo (<strong>de</strong> Miguel Anasagasti, 2000). Debido a esto, el<br />
<strong>MIPS</strong> dispone <strong>de</strong> dos instrucciones para multiplicar: mult para números con signo y multu<br />
para números sin signo. Ambas instrucciones multiplican el cont<strong>en</strong>ido <strong>de</strong> dos registros y almac<strong>en</strong>an<br />
el resultado <strong>en</strong> un registro <strong>de</strong> 64 bits. Como el producto <strong>de</strong> dos números <strong>de</strong> 32 bits<br />
nunca será mayor <strong>de</strong> 64 bits, no se producirá nunca ningún <strong>de</strong>sbordami<strong>en</strong>to, por lo que ambas<br />
instrucciones <strong>de</strong> multiplicación no g<strong>en</strong>eran excepciones.<br />
El registro <strong>de</strong> 64 bits <strong>en</strong> don<strong>de</strong> se almac<strong>en</strong>a el resultado es accesible <strong>en</strong> sus dos mita<strong>de</strong>s<br />
(hi y lo) mediante dos instrucciones especiales. La instrucción mfhi (<strong><strong>de</strong>l</strong> inglés move from<br />
high) traslada los 32 bits más significativos a un registro <strong>de</strong>stino. De la misma forma, mflo<br />
(<strong><strong>de</strong>l</strong> inglés move from low) traslada los 32 bits m<strong>en</strong>os significativos <strong><strong>de</strong>l</strong> resultado a un registro<br />
<strong>de</strong>stino. Por ejemplo, la instrucción mflo $s0 traslada los 32 bits m<strong>en</strong>os significativos <strong><strong>de</strong>l</strong><br />
resultado al registro s0.<br />
Cuando se está evaluando una expresión aritmética, interesa que todas las variables t<strong>en</strong>gan<br />
el mismo tamaño. Por tanto, <strong>de</strong>spués <strong>de</strong> realizar una multiplicación, interesa almac<strong>en</strong>ar<br />
el resultado <strong>en</strong> 32 bits <strong>en</strong> lugar <strong>de</strong> 64 para proseguir operando con el. Si se necesitan más<br />
<strong>de</strong> 32 bits para almac<strong>en</strong>ar el resultado, se habrá producido un “<strong>de</strong>sbordami<strong>en</strong>to <strong>de</strong> 32 bits”.<br />
El <strong>MIPS</strong> no ti<strong>en</strong>e ningún mecanismo hardware para <strong>de</strong>tectarlo, si<strong>en</strong>do responsabilidad <strong><strong>de</strong>l</strong><br />
programador su <strong>de</strong>tección. En el caso <strong>de</strong> multiplicaciones sin signo basta con comprobar que<br />
el registro hi esté a cero. En el caso <strong>de</strong> números con signo, el registro hi ha <strong>de</strong> ser todo ceros<br />
si lo es positivo o todo unos si lo es negativo.<br />
Ejercicio<br />
Escriba la secu<strong>en</strong>cia <strong>de</strong> instrucciones para multiplicar sin signo s0 por s1 y almac<strong>en</strong>ar<br />
el resultado <strong>en</strong> s2. Si se produce un <strong>de</strong>sbordami<strong>en</strong>to <strong>de</strong> 32 bits se pondrá t0 a uno y si no se<br />
<strong>de</strong>jará a cero. Repita el ejercicio para números con signo.<br />
Aritmética. División<br />
El <strong>MIPS</strong> dispone <strong>de</strong> dos instrucciones para dividir:<br />
div (con signo) o divu (sin signo).<br />
El coci<strong>en</strong>te se almac<strong>en</strong>a <strong>en</strong> lo y el resto <strong>en</strong> hi.<br />
Ej: div $s0, $s1 divi<strong>de</strong> s0 <strong>en</strong>tre s1<br />
Estas instrucciones no <strong>de</strong>tectan división por cero.<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 54
Al igual que <strong>en</strong> la multiplicación, son necesarias dos instrucciones para la división: div<br />
para números con signo y divu para números sin signo. En ambos casos el resultado se<br />
almac<strong>en</strong>a <strong>en</strong> el mismo registro <strong>de</strong> 64 bits utilizado para la multiplicación. En la parte alta<br />
(hi) se almac<strong>en</strong>a el resto y <strong>en</strong> la parte baja (lo) se almac<strong>en</strong>a el coci<strong>en</strong>te.<br />
Convi<strong>en</strong>e <strong>de</strong>stacar que el <strong>MIPS</strong> no <strong>de</strong>tecta la división por cero. A<strong>de</strong>más el resultado obt<strong>en</strong>ido<br />
<strong>en</strong> este caso es inpre<strong>de</strong>cible. Por tanto es tarea <strong><strong>de</strong>l</strong> programador el evitar las divisiones<br />
por cero para evitar sorpresas <strong>de</strong>sagradables.<br />
Ejercicio<br />
Escriba una secu<strong>en</strong>cia <strong>de</strong> instrucciones para dividir s0 <strong>en</strong>tre s1 y almac<strong>en</strong>ar el coci<strong>en</strong>te<br />
<strong>en</strong> el registro s2 y el resto <strong>en</strong> el registro s3. Si el divisor vale cero no se realizará la división<br />
y se pondrá a uno el registro t0. En caso contrario, <strong>de</strong>spués <strong>de</strong> realizar la división se <strong>de</strong>jará<br />
t0 a cero. Los números almac<strong>en</strong>ados <strong>en</strong> s0 y s1 son números con signo.<br />
Aritmética. Ej: (a0+a1)*a2/a3<br />
1 func: beq $a3, $zero, Error<br />
2 add $t0, $a0, $a1<br />
3 mult $t0, $a2<br />
4 mflo $t0<br />
5 mfhi $t1<br />
6 slt $t2, $t0, $zero #res0<br />
9 j L1<br />
10 Nega: sltiu $t2, $t1, -1 # -1=0xffff<br />
11 bne $t2, $zero, Error<br />
12 L1: div $t0, $a3<br />
13 mflo $v0<br />
14 add $v1, $zero, $zero<br />
15 jr $ra<br />
16 Error: addi $v1, $zero, 1<br />
17 jr $ra<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 55
En la transpar<strong>en</strong>cia se muestra el código para implem<strong>en</strong>tar una función que calcule la<br />
expresión (a0+a1)*a2/a3 (variables con signo). El resultado se <strong>de</strong>volverá <strong>en</strong> v0 y <strong>en</strong> v1 se<br />
<strong>de</strong>volverá un 0 si no se ha producido ningún error <strong>en</strong> el cálculo <strong>de</strong> la expresión o un 1 si se<br />
produce <strong>de</strong>sbordami<strong>en</strong>to <strong>en</strong> la multiplicación o una división por cero.<br />
En primer lugar se comprueba que a3 no sea cero. Si lo es, se salta al final <strong>de</strong> la función,<br />
<strong>de</strong>s<strong>de</strong> don<strong>de</strong> se pone v1 a 1 para indicar el error y se retorna <strong>de</strong> la función.<br />
Si a3 no es cero, se comi<strong>en</strong>za a evaluar la expresión. En primer lugar se realiza la suma.<br />
Si ocurriese un <strong>de</strong>sbordami<strong>en</strong>to se produciría una excepción, <strong>de</strong>s<strong>de</strong> don<strong>de</strong> se podría tratar el<br />
error. Como aún no se han estudiado las excepciones, se <strong>de</strong>ja la implem<strong>en</strong>tación <strong>de</strong> la rutina<br />
<strong>de</strong> at<strong>en</strong>ción a la excepción para más a<strong><strong>de</strong>l</strong>ante. A continuación se realiza la multiplicación,<br />
<strong>de</strong>jando el resultado <strong>en</strong> los registros t0 y t1. Como se pret<strong>en</strong><strong>de</strong> continuar la evaluación <strong>de</strong><br />
la expresión usando números <strong>de</strong> 32 bits, el resultado ha <strong>de</strong> po<strong>de</strong>rse almac<strong>en</strong>ar <strong>en</strong> t0, por lo<br />
que t1 t<strong>en</strong>drá que valer 0 si el resultado es positivo o 0xffffffff si el resultado es negativo.<br />
Esta comprobación se realiza <strong>en</strong> las instrucciones 6 a 11. En la instrucción 6 se comprueba el<br />
signo <strong><strong>de</strong>l</strong> resultado. Si es positivo se ejecutará la instrucción 8 que saltará a la etiqueta Error<br />
si t1 no vale cero. Si el resultado es negativo, <strong>en</strong> la instrucción 10 se comprueba si t1 vale<br />
0xffffffff. Como las instrucciones <strong>de</strong> comparación exti<strong>en</strong>d<strong>en</strong> el signo <strong>de</strong> la constante <strong>de</strong> 16<br />
bits, comparar con -1 es comparar con 0xffffffff. Como la comparación se realiza sin signo,<br />
si t1 es distinto <strong>de</strong> 0xffffffff se producirá el salto <strong>en</strong> la instrucción 11 para ir a la etiqueta<br />
Error.<br />
Si no hay <strong>de</strong>sbordami<strong>en</strong>to <strong>en</strong> la multiplicación, se proce<strong>de</strong> a realizar la división y a<br />
<strong>de</strong>volver el control a “qui<strong>en</strong>” ha llamado a la función, aunque previam<strong>en</strong>te se <strong>de</strong>posita el<br />
resultado <strong>en</strong> v0 y un 0 <strong>en</strong> v1 para indicar que la evaluación <strong>de</strong> la expresión se ha realizado<br />
con éxito.<br />
Ejercicio<br />
Modifique la función anterior para comprobar explícitam<strong>en</strong>te el <strong>de</strong>sbordami<strong>en</strong>to <strong>de</strong> la<br />
suma utilizando el algoritmo mostrado <strong>en</strong> la página 51<br />
55-2
Aritmética y lógica<br />
Aparte <strong>de</strong> la +,-,* y /, el <strong>MIPS</strong> dispone <strong>de</strong> una serie <strong>de</strong><br />
operaciones lógicas y aritméticas adicionales:<br />
• and <strong>de</strong>st, orig1, orig2<br />
• andi <strong>de</strong>st, orig1, const<br />
• or <strong>de</strong>st, orig1, orig2<br />
• ori <strong>de</strong>st, orig1, const<br />
• xor <strong>de</strong>st, orig1, orig2<br />
• xori <strong>de</strong>st, orig1, const<br />
• nor <strong>de</strong>st, orig1, orig2<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 56<br />
Aparte <strong>de</strong> las operaciones aritméticas estudiadas hasta ahora, <strong>en</strong> muchos programas es<br />
necesario realizar operaciones lógicas a nivel <strong>de</strong> bit.<br />
El <strong>MIPS</strong> incluye las instrucciones lógicas mostradas <strong>en</strong> la transpar<strong>en</strong>cia. Todas ellas<br />
realizan una operación lógica bit a bit <strong>en</strong>tre los dos registros orig<strong>en</strong> y <strong>de</strong>positan su resultado<br />
<strong>en</strong> el registro <strong>de</strong>stino. Se incluy<strong>en</strong> las dos operaciones lógicas fundam<strong>en</strong>tales, AND y OR, así<br />
como la XOR. Se incluye a<strong>de</strong>más la operación NOR, que realiza la misma operación que una<br />
OR salvo que se niega el resultado. Esta instrucción permite realizar la operación fundam<strong>en</strong>tal<br />
NOT. ¿Cómo<br />
Las operaciones AND, OR y XOR dispon<strong>en</strong> <strong>de</strong> versiones <strong>en</strong> las cuales el segundo operando<br />
es una constante <strong>de</strong> 16 bits. Como el registro con el que se opera es <strong>de</strong> 32 bits, los<br />
16 bits superiores <strong>de</strong> la constante se pon<strong>en</strong> a 0 antes <strong>de</strong> realizar la operación lógica. Recuer<strong>de</strong><br />
que este comportami<strong>en</strong>to es distinto <strong><strong>de</strong>l</strong> ofrecido <strong>en</strong> las constantes <strong>de</strong> las operaciones <strong>de</strong><br />
suma, resta y comparación, <strong>en</strong> las cuales se exti<strong>en</strong><strong>de</strong> el signo.
Aritmética y lógica<br />
Por último, el <strong>MIPS</strong> dispone <strong>de</strong> una serie <strong>de</strong><br />
instrucciones <strong>de</strong> <strong>de</strong>splazami<strong>en</strong>to <strong>de</strong> bits.<br />
• Despl. lógico a izq.: sll rd, ro, <strong>de</strong>sp<br />
• Despl. lógico a izq. var.: sllv rd, ro, r<strong>de</strong>s<br />
• Despl. lógico a <strong>de</strong>r.: srl rd, ro, <strong>de</strong>sp<br />
• Despl. lógico a <strong>de</strong>r. var.: srlv rd, ro, r<strong>de</strong>s<br />
• Despl. aritmético a <strong>de</strong>r.: sra rd, ro, <strong>de</strong>sp<br />
• Despl. arit. a <strong>de</strong>r. var.: srav rd, ro, r<strong>de</strong>s<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 57<br />
El <strong>MIPS</strong> dispone <strong>de</strong> varias instrucciones <strong>de</strong> <strong>de</strong>splazami<strong>en</strong>to <strong>de</strong> bits. Estas instrucciones<br />
pued<strong>en</strong> <strong>de</strong>splazar un registro orig<strong>en</strong> el número <strong>de</strong> bits especificado <strong>en</strong> la constante <strong>de</strong>sp o <strong>en</strong><br />
el registro r<strong>de</strong>s para los <strong>de</strong>splazami<strong>en</strong>tos variables. El resultado se almac<strong>en</strong>a <strong>en</strong> el registro<br />
<strong>de</strong>stino.<br />
Cuando se <strong>de</strong>splaza a la izquierda se rell<strong>en</strong>an los bits vacantes con ceros. Por ejemplo<br />
si se <strong>de</strong>splaza 01000111 a la izquierda tres posiciones, el resultado será 00111000.<br />
1 En cambio,<br />
cuando se <strong>de</strong>splaza a la <strong>de</strong>recha cab<strong>en</strong> dos posibilida<strong>de</strong>s: rell<strong>en</strong>ar con ceros (<strong>de</strong>splazami<strong>en</strong>to<br />
lógico) o rell<strong>en</strong>ar con el bit <strong>de</strong> signo (<strong>de</strong>splazami<strong>en</strong>to aritmético). Por ejemplo, si se <strong>de</strong>sea<br />
<strong>de</strong>splazar el valor 10010011 dos bits a la <strong>de</strong>recha, si se realiza un <strong>de</strong>splazami<strong>en</strong>to lógico, el<br />
resultado será 00100100, pero si se realiza un <strong>de</strong>splazami<strong>en</strong>to aritmético, el resultado será<br />
11100100.<br />
Las instrucciones <strong>de</strong> <strong>de</strong>splazami<strong>en</strong>to ti<strong>en</strong><strong>en</strong> dos usos fundam<strong>en</strong>tales. Los <strong>de</strong>splazami<strong>en</strong>tos<br />
lógicos se usan para manipular bits. Por ejemplo si se <strong>de</strong>sean extraer los 16 bits más<br />
significativos <strong>de</strong> un registro <strong>de</strong> 32 bits (s0) y almac<strong>en</strong>arlos <strong>en</strong> s1 se pue<strong>de</strong> ejecutar la instrucción<br />
srl $s1, $s0, 16. En este tipo <strong>de</strong> operaciones no es necesario ext<strong>en</strong><strong>de</strong>r el bit <strong>de</strong><br />
signo, por lo que se usan las instrucciones <strong>de</strong> <strong>de</strong>splazami<strong>en</strong>to lógico.<br />
El otro uso <strong>de</strong> las instrucciones <strong>de</strong> <strong>de</strong>splazami<strong>en</strong>to es para realizar multiplicaciones y<br />
divisiones por pot<strong>en</strong>cias <strong>de</strong> dos. Cada bit que se <strong>de</strong>splaza un número binario a la izquierda es<br />
equival<strong>en</strong>te a multiplicar por 2. De la misma forma, cada bit que se <strong>de</strong>splaza a la <strong>de</strong>recha es<br />
equival<strong>en</strong>te a dividir por 2, aunque <strong>en</strong> este caso si el número es negativo es necesario introducir<br />
unos por la izquierda para que se mant<strong>en</strong>ga el signo. Por tanto, cuando se <strong>de</strong>see <strong>de</strong>splazar<br />
1 Se han usado <strong>en</strong> el ejemplo números <strong>de</strong> 8 bits para simplificar. El <strong>MIPS</strong> <strong>de</strong>splaza siempre<br />
valores <strong>de</strong> 32 bits.
a la <strong>de</strong>recha para dividir por pot<strong>en</strong>cias <strong>de</strong> dos, será necesario utilizar <strong>de</strong>splazami<strong>en</strong>tos aritméticos.<br />
Convi<strong>en</strong>e resaltar que <strong>en</strong> estas operaciones el <strong>MIPS</strong> no comprueba si se produc<strong>en</strong><br />
<strong>de</strong>sbordami<strong>en</strong>tos.<br />
57-2<br />
Aritmética y lógica. Ejemplo<br />
int a, V[100], i, j, k;<br />
while(V[i] == k){<br />
i = i + j;<br />
}<br />
Se traduce <strong>en</strong> (a-k se asignan a los registros s0-s4):<br />
Bucle: sll $t1, $s2, 2 # t1 = 4i<br />
add $t1, $t1, $s1 # t1 = dir. <strong>de</strong> V[i]<br />
lw $t0, 0($t1) # t0 = V[i]<br />
bne $t0, $s4, Fin # si i!=k salta<br />
add $s2, $s2, $s3 # i = i + j<br />
j Bucle<br />
Fin:<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 58
El código C mostrado <strong>en</strong> la transpar<strong>en</strong>cia es el mismo que se utilizó <strong>en</strong> la página 25. Al<br />
igual que antes, es necesario multiplicar por 4 el índice <strong><strong>de</strong>l</strong> vector para acce<strong>de</strong>r a cada uno <strong>de</strong><br />
los elem<strong>en</strong>tos. En el programa <strong>en</strong> <strong>en</strong>samblador <strong>de</strong> la página 25 se utilizaron dos instrucciones<br />
<strong>de</strong> suma para realizar dicha multiplicación. Otra alternativa sería usar una instrucción <strong>de</strong><br />
multiplicación, aunque <strong>en</strong> este caso se necesitarían tres instrucciones:<br />
addi $t0, $zero, 4<br />
multu $s2, $t0<br />
mflo $t0<br />
Obviam<strong>en</strong>te no es la solución más apropiada. No obstante, si se ti<strong>en</strong>e <strong>en</strong> cu<strong>en</strong>ta que 4 es<br />
una pot<strong>en</strong>cia <strong>de</strong> 2, po<strong>de</strong>mos realizar la multiplicación por 4 mediante <strong>de</strong>splazami<strong>en</strong>tos. Como<br />
4 = 2 2 , para multiplicar por 4 basta con <strong>de</strong>splazar 2 bits a la izquierda, que es lo que se ha<br />
hecho <strong>en</strong> la primera instrucción <strong><strong>de</strong>l</strong> programa <strong>en</strong> <strong>en</strong>samblador.<br />
Ejercicio<br />
En muchas ocasiones es más efici<strong>en</strong>te implantar una multiplicación por una constante<br />
como una serie <strong>de</strong> <strong>de</strong>splazami<strong>en</strong>tos y sumas. Escriba una secu<strong>en</strong>cia <strong>de</strong> instrucciones <strong>en</strong><br />
<strong>en</strong>samblador que multipliqu<strong>en</strong> el registro s0 por 5 y <strong>de</strong>posit<strong>en</strong> el resultado <strong>en</strong> t0.<br />
Carga <strong>de</strong> constantes<br />
Hasta ahora se ha visto cómo cargar una constante<br />
<strong>de</strong> 16 bits <strong>en</strong> un registro.<br />
Para cargar un valor <strong>de</strong> 32 bits (p. ej. 0x12345678)<br />
son necesarias dos instrucciones:<br />
lui $s0, 0x1234<br />
ori $s0, $s0, 0x5678<br />
La pseudoinstrucción li hace el mismo trabajo:<br />
li<br />
$s0, 0x12345678<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 59
Aunque la mayoría <strong>de</strong> las constantes usadas <strong>en</strong> los programas son m<strong>en</strong>ores <strong>de</strong> 16 bits,<br />
<strong>en</strong> ciertas ocasiones es necesario usar constantes <strong>de</strong> 32 bits. Para ello, el <strong>MIPS</strong> dispone <strong>de</strong><br />
una instrucción <strong>de</strong> carga especial que toma una constante <strong>de</strong> 16 bits y la sitúa <strong>en</strong> los 16 bits<br />
más significativos <strong><strong>de</strong>l</strong> registro <strong>de</strong>stino, <strong>de</strong>jando los 16 bits m<strong>en</strong>os significativos a cero. Dicha<br />
instrucción se d<strong>en</strong>omina lui (<strong><strong>de</strong>l</strong> inglés load upper inmediate). Para cargar los bits m<strong>en</strong>os<br />
significativos <strong><strong>de</strong>l</strong> registro se usa la instrucción ori, que realiza una OR bit a bit <strong><strong>de</strong>l</strong> registro<br />
<strong>de</strong>stino con una constante <strong>de</strong> 16 bits ext<strong>en</strong>dida con ceros <strong>en</strong> su parte superior. Así, <strong>en</strong> el<br />
ejemplo <strong>de</strong> la transpar<strong>en</strong>cia se realizará una OR <strong>en</strong>tre:<br />
0x12340000<br />
0x00005678<br />
Cuyo resultado es 0x12345678.<br />
Como el l<strong>en</strong>guaje nativo <strong><strong>de</strong>l</strong> <strong>MIPS</strong> es un poco limitado, dada su naturaleza RISC, los <strong>en</strong>sambladores<br />
<strong><strong>de</strong>l</strong> <strong>MIPS</strong> dispon<strong>en</strong> <strong>de</strong> una serie <strong>de</strong> pseudoinstrucciones para facilitarle la vida<br />
a los programadores. Dichas pseudoinstrucciones no son más que una especie <strong>de</strong> “abreviaturas”<br />
<strong>de</strong> varias instrucciones <strong><strong>de</strong>l</strong> <strong>MIPS</strong>. Una pseudoinstrucción muy útil es la li (<strong>de</strong> load<br />
inmediate), que carga una constante, in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>tem<strong>en</strong>te <strong>de</strong> su tamaño, <strong>en</strong> un registro. Cuando<br />
el <strong>en</strong>samblador <strong>en</strong>cu<strong>en</strong>tra esta pseudoinstrucción evalúa el tamaño <strong>de</strong> la constante y si ésta<br />
es m<strong>en</strong>or <strong>de</strong> 16 bits g<strong>en</strong>era la instrucción ori rd, const. Si la constante es mayor <strong>de</strong> 16<br />
bits, <strong>en</strong>tonces g<strong>en</strong>era la combinación lui+ori vista <strong>en</strong> la transpar<strong>en</strong>cia.<br />
Ejercicio<br />
Utilizando el simulador SPIM, indique cual es la traducción a instrucciones <strong>MIPS</strong> <strong>de</strong> la<br />
pseudoinstrucción li para las sigui<strong>en</strong>tes constantes: 1, -1 y 0x10007. ¿Se realiza siempre la<br />
traducción óptima<br />
Para saber más<br />
En este tema sólo se han mostrado algunas <strong>de</strong> las<br />
instrucciones disponibles <strong>en</strong> el <strong>MIPS</strong>.<br />
Un listado completo se <strong>en</strong>cu<strong>en</strong>tra <strong>en</strong> el apéndice A <strong>de</strong><br />
(Patterson y H<strong>en</strong>nessy, 2000), disponible on-line <strong>en</strong> la<br />
página <strong><strong>de</strong>l</strong> SPIM.<br />
Los manuales completos <strong>de</strong> la arquitectura <strong>MIPS</strong><br />
(<strong>MIPS</strong> Technologies, 2002) se <strong>en</strong>cu<strong>en</strong>tran también<br />
disponibles on-line <strong>en</strong> la página <strong>de</strong> <strong>MIPS</strong><br />
Technologies (accesible <strong>de</strong>s<strong>de</strong> la página web <strong>de</strong> la<br />
asignatura).<br />
ICAI<strong>de</strong>a<br />
Estructura <strong>de</strong> Computadores Capítulo 3b: <strong>Programación</strong> <strong>en</strong> <strong>en</strong>samblador <strong><strong>de</strong>l</strong> <strong>MIPS</strong>.– p. 60
Refer<strong>en</strong>cias<br />
<strong>de</strong> Miguel Anasagasti, P. (2000). Fundam<strong>en</strong>tos <strong>de</strong> los computadores. Paraninfo-Thomson<br />
learning.<br />
<strong>MIPS</strong> Technologies (2002). <strong>MIPS</strong>32 TM Architecture For Programmers. <strong>MIPS</strong> Technologies,<br />
Mountain View, CA, 1 edition.<br />
Patterson, D. A. y H<strong>en</strong>nessy, J. L. (2000). Estructura y diseño <strong>de</strong> computadores. Interficie<br />
circuitería/programación. Reverté.