Post on 30-Apr-2020
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 1
2. Arquitectura del repertorio de instrucciones
Para comandar una computadora se le debe “hablar” en su lenguaje.
• Instrucciones: Palabras del lenguaje.
• Repertorio de instrucciones: Vocabulario de la computadora.
Los lenguajes de máquina son muy similares entre sí porque:
• Las computadoras se construyen con la misma tecnología de hardware.
• Hay un número finito de operaciones básicas que todas las máquinas deben considerar.
Los diseñadores de computadoras tienen una meta común:
Encontrar un lenguaje que simplifique la construcción del hardware y el compilador; mientras
se maximiza el rendimiento y minimiza el costo.
A lo largo del curso revisaremos la Arquitectura MIPS (MIPS: Microprocessor without
Interlocked Pipeline Stages). Un procesador que tiene las características típicas de los
procesadores modernos.
El procesador MIPS fue diseñado en la Universidad de Stanford, posteriormente fue
comercializado por MIPS Technologies ( www.mips.com ).
La arquitectura MIPS, similar a ARM, es ampliamente usada como núcleo en el mercado de los
sistemas embebidos, encontrándose en aplicaciones de electrónica de consumo, equipo de
almacenamiento y redes, cámaras, impresoras, entre otros….
La arquitectura MIPS es del tipo registro-registro, con tres operandos por instrucción.
2.1 Lenguaje ensamblador
El diseño de un repertorio de instrucciones debe basarse en principios bien establecidos.
MIPS se fundamenta en los 4 principios siguientes:
1. El diseño debe ser simple y regular. La regularidad hace que la implementación sea
más simple y la simplicidad permite alcanzar el más alto rendimiento al más bajo costo.
2. Si es más pequeño, es más rápido. Un procesador va a operar a una frecuencia más
alta si las señales eléctricas viajan una menor distancia.
3. Favorecer los casos comunes. El procesador debe resolver con fluidez lo que ocurre
con mayor frecuencia y dar atención especial a las situaciones poco frecuentes. Por
ejemplo, es mucho más frecuente el uso de constantes pequeñas que constantes grandes.
4. Un buen diseño demanda compromisos. Un sistema no puede cubrir todas las
expectativas, algunos compromisos deben establecerse durante su diseño.
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 2
2.1.1 Operaciones y operandos
Toda computadora debe realizar operaciones aritméticas y lo natural es que incluyan 3
operandos, dos fuentes y un destino (no necesariamente deben ser diferentes).
El hardware para un número fijo de operandos es más simple que el hardware para un
número variable (principio 1).
El repertorio MIPS incluye las instrucciones aritméticas y lógicas básicas:
ADD a, b, c # a = b + c SUB a, b, c # a = b – c AND a, b, c # a = a AND b OR a, b, c # a = a OR c XOR a, b, c # a = a XOR c NOR a, b, c a = NOT (a OR c) SLT a, b, c # a = 1 si b < c, sino a = 0
Ejercicio: Para la siguiente asignación en C:
f = (g + h) – (i + j);
¿Qué producirá el compilador?
Los lenguajes de alto nivel utilizan variables como operandos, durante la compilación, las
variables se asocian con un número especial de localidades llamadas registros.
Los registros son los ladrillos en la construcción de una computadora, se definen durante el
diseño del hardware y quedarán visibles al programador cuando la computadora esté
completada, a pesar de estar dentro de la CPU.
MIPS tiene 32 registros y cada uno es de 32 bits, las instrucciones aritméticas pueden elegir
hasta tres de ellos para sus operandos. La arquitectura es muy regular, porque las
instrucciones también se codifican en palabras de 32 bits (principio 1).
Un programador desearía un número mayor de registros, sin embargo, el desarrollador de
hardware tiene como meta conseguir un ciclo de reloj pequeño (principios 2 y 4).
Los registros son referidos con el símbolo $: $0, $1, $2, … $31, sin embargo, para
simplificar el trabajo del compilador a los diferentes registros se les designan tareas
especiales y se establecen nombres relacionados con cada tarea.
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 3
Nombre Registro (s) Uso
$zero 0 Constante con el valor 0
$at 1 Temporal para el ensamblador
$v0 - $v1 2 – 3 Resultados de una función o expresión
$a0 - $a3 4 – 7 Argumentos de una función
$t0 - $t7 8 – 15 Temporales
$s0 - $s7 16 – 23 Registros seguros
$t8 - $t9 25 – 25 Más temporales
$gp 28 Apuntador global
$sp 29 Apuntador de pila
$fp 30 Apuntador de marco
$ra 31 Dirección de retorno
El registro $0 o $zero siempre tiene 0, está protegido por hardware y es útil como una
referencia para comparaciones.
Ejercicio: Empleando los nombres de los registros, ¿Cómo se traduce la sentencia?
f = (g + h) – (i + j);
2.1.2 Acceso a memoria
La memoria es un espacio externo a la CPU con una capacidad de almacenamiento mucho
mayor que los registros, como contra parte del principio 2, si es más grande es más lenta.
La memoria es usada para datos compuestos como: Arreglos, estructuras, datos dinámicos,
etc., físicamente la memoria está organizada por bytes, sin embargo, las palabras están
alineadas y las direcciones son múltiplos de 4.
MIPS utiliza un acceso del tipo Big Endian, significa que el byte más significativo se ubica
en la dirección menos significativa de una palabra.
Para tener acceso a la memoria, el repertorio MIPS incluye las instrucciones:
LW $t0, cte($t1) # $t0 = Mem[$t1 + cte], (carga) SW $t0, cte($t1) # Mem[$t1 + cte] = $t1, (almacenamiento)
Ambas emplean un registro como base y una constante como desplazamiento, para calcular
la dirección efectiva de acceso a memoria.
La memoria se puede ver como un arreglo lineal grande, las instrucciones son capaces de
direccionar hasta 230 palabras de 32 bits (4 GBytes).
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 4
Dado que el acceso a registros es más rápido que el acceso a memoria, el compilador debe
asociar las variables con los registros y enviar a memoria sólo a aquellas variables que son
usadas con menor frecuencia o que tienen un tamaño significativo.
Ejercicio: Carga y Almacenamiento.
Para la siguiente asignación en C: A[12] = h + A[8];
¿Qué producirá el compilador? Suponiendo que el comienzo del arreglo A se encuentra en
el registro $s0 y que la variable h se asocia con el registro $s1.
Debe tomarse en cuenta que cada elemento del arreglo ocupa 4 bytes.
Ejercicio: Usando una variable como índice de un arreglo.
La siguiente asignación utiliza a la variable i como índice del arreglo A:
g = h + A[i];
¿Qué producirá el compilador? Suponiendo que el comienzo del arreglo A se encuentra en
el registro $s0, y que las variables g, h e i se asocian con los registros: $s1, $s2 y $s3,
respectivamente.
2.1.3 Instrucciones para tomar decisiones
Las computadoras se distinguen de las calculadoras por su capacidad para tomar decisiones
con base en los datos de entrada. Para ello, la arquitectura MIPS cuenta con dos
instrucciones de brincos condicionales y un salto incondicional:
BEQ $t0, $t1, L1 # Brinco sobre igual BNE $t0, $t1, L1 # Brinco sobre diferente J etiqueta # Salto incondicional JR $t0 # Salto a registro
Las operaciones relacionales más complejas se consiguen combinando estás instrucciones
con la instrucción SLT (ajuste sobre menor que). Las comparaciones sobre igual o diferente
son más comunes que otras como >, <, >= ó <= (principio 3).
Estas instrucciones modifican al PC (Program Counter, Contador de Programa), registro de
propósito especial que contiene la dirección de la instrucción bajo ejecución.
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 5
Ejercicios: Traduzca a código MIPS las diferentes secuencias de código en alto nivel:
Código en lenguaje C Consideraciones
if ( i == j ) f = g + h
Las variables f - j deben asociarse con
los registros $s0 - $s4.
if ( i < j ) f = g + h;
else f = g – h;
Las variables f - j deben asociarse con
los registros $s0 - $s4.
do { g = g + A[i]; i = i + j; } while( i != h );
Las variables g - j se asocian con los
registros $s1 - $s4. El registro base para
el arreglo A es $s5.
while (save[i] == k)
i = i + j;
Las variables i - k se asocian con los
registros $s3 - $s5. El registro base para
el arreglo save es $s6.
2.1.4 Manejo de Constantes
Usos típicos de constantes: Incrementar el índice de un arreglo, contar iteraciones en un
lazo, ajustar el apuntador de la pila, etc.
En los programas reales, alrededor del 50 % de operaciones aritméticas involucran el uso
de constantes; por ejemplo, en el compilador gcc el 52 % de operaciones aritméticas se
aplican sobre constantes, en el simulador de circuitos llamado spice este parámetro
corresponde al 69 %.
Si las constantes son muy utilizadas, por qué no favorecer su uso (principio 3).
Algunas de las instrucciones incluidas en el repertorio MIPS, relacionadas con el uso de
constantes son:
ADDI $t0, $t1, cte # $t0 = $t1 + cte ANDI $t0, $t1, cte # $t0 = $t1 AND cte ORI $t0, $t1, cte # $t0 = $t1 OR cte SLTI $t0, $t1, cte # $t0 = 1 si $t1 < cte, sino $t0 = 0 LUI $t0, cte # $t0 = cte << 16
# Cargar inmediato superior Las constantes son de 16 bits, si se requiere el uso de una constante grande se debe emplear
una instrucción Lui seguida de una instrucción Ori.
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 6
Ejercicios: Nuevamente se traducirá de alto a bajo nivel:
Código en lenguaje C Consideraciones
x = 0; for( i = 0; i < 10; i++ )
x = x + i;
Las variables i se asociarse con $s0, x
con $s1.
switch ( k ) { case 0: f = i + h; break; case 1: f = g + h; break; case 2: f = g - h; break; case 3: f = i - j; break;
}
Las variables f - k deben asociarse con
los registros $s0 - $s5.
Con la instrucción: La $t0, Label
(load address), se puede obtener la dirección de una etiqueta en el programa. La = Lui + Ori
Los desplazamientos lógicos también involucran constantes, aunque en estos casos la
constante es de 5 bits, suficientes para todos los posibles desplazamientos en un registro de
32 bit. Las instrucciones para desplazamientos son:
SLL $t0, $t1, cte # $t0 = $t1 << cte (izquierda) SRL $t0, $t1, cte # $t0 = $t1 >> cte (derecha)
2.1.5 Manejo de funciones
El manejo de funciones es uno de los aspectos más importantes de la programación
estructurada; por lo que cualquier repertorio de instrucciones soportarlo.
Los pasos que intrínsecamente se realizan al invocar una función son:
a) Se colocan los argumentos en un lugar donde los espera la función.
b) Se transfiere el control a la función, a la vez que se respalda la dirección de retorno.
c) Se adquieren los recursos de almacenamiento necesarios.
d) Se realiza la tarea deseada.
e) Se coloca el valor del resultado en algún lugar donde lo espera la función invocadora.
f) Se libera el espacio destinado para el almacenamiento de recursos.
g) Se regresa el control al punto de origen.
Los registros involucrados en el manejo de funciones son:
• $a0 - $a3: Cuatro registros para los argumentos.
• $v0 - $v1: Dos registros para los valores de retorno.
• $ra: Un registro para almacenar la dirección de retorno.
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 7
Para transferir el control a la función y respaldar la dirección de retorno, MIPS incluye la
instrucción:
JAL Label # Jump and Link
La instrucción realiza el salto a la etiqueta y de manera simultánea respalda en el registro
$ra el valor del contador de programa previamente incrementado ($ra = PC + 4).
Una función es aislada cuando no invoca a otras funciones, en esos casos no requiere
recursos de almacenamiento local y puede usar sólo registros temporales.
Ejercicio: Función aislada.
Considere la secuencia de código que incluye una función aislada:
… a = 10; b = 30; c = suma(a + b); …
int suma( int x, int y) { int aux; aux = x + y; return aux; }
¿Cómo se traduce a código MIPS? Asociando las variables a, b y c con los registros $s0,
$s1 y $s2.
Los registros $s0 a $s7 son seguros porque su valor debe conservarse a través de llamadas a
funciones. Una función que no es aislada (invoca a otras funciones) debe respaldar los
registros que utilice así como la dirección de retorno.
El espacio de almacenamiento temporal es la pila y se alojada en memoria principal. El
registro $sp es el apuntador de la pila. La pila crece de las direcciones altas a las
direcciones bajas, de manera que restando un valor constante al registro $sp se hace espacio
a la pila para respaldar los registros seguros, la dirección de retorno y disponer de un
espacio para datos locales complejos como los arreglos.
Ejercicio: Función recursiva.
Considere la función recursiva que obtiene el factorial de un número y con el uso de la
pseudo-instrucción mul $t0, $t1, $t2, traduzca a código MIPS.
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 8
int fact ( int n ) {
if( n < 2) return 1; return n * fact(n – 1);
}
El registro apuntador de marco ($fp) tiene una función similar al apuntador de pila ($sp)
pero trabaja al nivel de funciones y no al nivel de datos, permitiendo restaurar al $sp ante
salidas abruptas de una función. El compilador determina si lo usa o no.
2.1.6 Manejo de Cadenas
Una cadena es una secuencia de caracteres. La mayoría de computadoras utilizan 8 bits
para representar un carácter de acuerdo al código ASCII (American Standar Code for
Information Interchange).
Un entero en MIPS utiliza 32 bits, que corresponde con el tamaño de los registros, sin
embargo, utilizar 32 bits por carácter sería un desperdicio de memoria.
MIPS incluye instrucciones para cargar y almacenar bytes:
• La instrucción LB (load byte) carga un byte desde la memoria colocándolo en los 8 bits
más a la derecha de un registro.
• La instrucción SB (store byte) toma un byte de los 8 bits más a la derecha de un registro
y los coloca en la memoria.
El formato para LB y SB es similar que el empleado para LW y SW.
LB $t0, cte ($t1) # $t0 = Mem[$t1 + cte] SB $t0, cte ($t1) # Mem[$t1 + cte] = $t0
LB y SB se distinguen de LW y SW porque no realizan un acceso a datos alineados en
memoria (la dirección efectiva de acceso no debe ser múltiplo de 4).
Ejercicio: Compilando una función para cadenas.
Traduzca a código MIPS el procedimiento strcpy, el cual copia una cadena y, terminada con
null, en una cadena x:
void strcpy ( char x[ ], char y[ ]) { int i = 0;
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 9
while( ( x[i] = y[i] ) != 0 ) /* copia y compara */
i = i + 1; }
Tarea:
1. Con el ensamblador MIPS, indique la secuencia de instrucciones que evalúe a los
registros $s0, $s1 y $s2 y deje el valor del menor en $s3.
2. El siguiente código acumula los valores del arreglo A en la variable x:
for ( x = 0, i = 0; i < 10; i++ ) x = x + A[i];
¿Cuál es el código MIPS para este código? Suponga que el comienzo del arreglo A esta
en el registro $s3, que la variable x se asocia con $s1 y la variable i con $s2.
3. Transforme la siguiente asignación: c = ( a > b ) ? a : b; a código MIPS.
Asocie a, b y c con $s0, $s1 y $s2, respectivamente.
4. Realice una función en C que devuelva el mayor de un arreglo de n elementos y
posteriormente tradúzcala a código MIPS, respetando las convenciones establecidas
para la asociación de registros con variables.
5. Desarrolle el procedimiento bfind, en lenguaje ensamblador MIPS, que reciba como
argumento un apuntador a una cadena terminada con NULL (correspondería a $a0) y
localice la primer letra b en la cadena (se regresaría en $v0).
Si no hay b’s en la cadena, entonces bfind deberá regresar un apuntador al carácter
nulo (localizado al final de la cadena). Por ejemplo, si bfind recibe como argumento
un apuntador a la cadena “embebido” deberá devolver un apuntador al tercer carácter en
la cadena.
6. Escriba un procedimiento bcount, en lenguaje ensamblador MIPS, que reciba como
argumento un apuntador a una cadena terminada con NULL (correspondería a $a0) y
devuelva el número de b’s que aparecen en la cadena (en el registro $v0). Para la
implementación de bcount deberá utilizar la función bfind desarrollada en el
ejercicio anterior.
7. Escribir un procedimiento en código MIPS para calcular el n-ésimo término de la serie
de Fibonacci (F(n)), donde:
F(0) = 0 F(1) = 1 F(n) = F(n – 1) + F(n – 2) Si n > 1
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 10
2.2 Tipos de instrucciones
Si se comparan las instrucciones aritméticas con las de acceso a memoria, se notará que
ambos tipos tiene tres operandos.
• Aritméticas: Los tres operandos son registros, con 5 bits es suficiente para definir un
registro.
• Acceso a memoria: Dos operandos son registros y el tercero es una constante, no es
conveniente disponer sólo de 5 bits para la constante, su valor sería muy limitado.
Si se quiere conservar el formato para los dos tipos de instrucciones, éstas van a tener
diferentes tamaños. La otra opción es conservar el tamaño, generando con ello diferentes
formatos de instrucciones.
Los diseñadores de MIPS optaron por la segunda opción y para mantener una arquitectura
regular, todas las instrucciones son de 32 bits, compatible con el tamaño de los datos
(principio 4).
En MIPS se tienen tres tipos de instrucciones: Tipo-R (por el uso de registros), Tipo-I (que
emplean una constante) y Tipo-J (destinadas a los saltos).
El formato para las instrucciones Tipo-R es:
6 bits 5 bits 5 bits 5 bits 5 bits 6 bits
OP RS RT RD shamt FUNCT
El significado para cada uno de los campos es:
• OP: Opcode, define el tipo de operación.
• RS: El primer operando fuente.
• RT: El segundo operando fuente.
• RD: El registro destino, obtiene el resultado de la operación.
• shamt: Cantidad de desplazamiento (shift amount), para instrucciones de
desplazamiento.
• FUNCT: Función, selecciona una variante de la operación, las operaciones aritméticas
tienen el mismo opcode pero se distinguen por este campo.
El formato para las instrucciones Tipo-I es:
6 bits 5 bits 5 bits 16 bits
OP RS RT Inmediato
El significado para cada uno de los campos es:
• OP: Opcode, define el tipo de operación.
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 11
• RS: El primer operando fuente.
• RT: El segundo operando fuente.
• Inmediato: Constante requerida por la instrucción.
El formato para las instrucciones Tipo-J es:
6 bits 26 bits
OP Dirección
El significado para cada uno de los campos es:
• OP: Opcode, define el tipo de operación.
• Dirección: Destino del salto.
Organización de las instrucciones por su tipo:
Tipo – R Tipo – I Tipo – J
Instrucción Opcode Función Instrucción Opcode Instrucción Opcode
ADD SUB AND OR XOR NOR SLT JR SLL SRL
0 0 0 0 0 0 0 0 0 0
32 34 36 37 38 39 42 8 0 2
LW SW BEQ BNE ADDI ANDI ORI SLTI LUI LB SB
35 43 4 5 8 12 13 10 15 36 40
J JAL
2 3
Organización de las instrucciones por su función:
Aritméticas Lógicas Transferencia
de datos
Brincos
condicionales
Saltos
incondicionales
ADD SUB ADDI
AND OR XOR NOR SLL SRL ANDI ORI
LW SW LB SB LUI
SLT SLTI BEQ BNE
JR J JAL
2.3 Modos de direccionamiento
Los modos de direccionamiento determinan la forma en que las instrucciones tienen acceso
a sus operandos, en MIPS se tienen 5 modos de direccionamiento.
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 12
1. Direccionamiento por registro: Los 3 operandos son registros, dos fuentes y un
destino.
Ejemplos de instrucciones que usan este modo de direccionamiento: add, sub, slt, etc.
2. Direccionamiento base con desplazamiento: Uno de los operandos está en una
localidad de memoria cuya dirección es la suma de un registro base y una constante que
forma parte de la misma instrucción.
Ejemplos de instrucciones que usan este modo de direccionamiento: lw, sw, etc.
3. Direccionamiento inmediato: Uno de los operandos es una constante y es parte de la
instrucción.
Ejemplos de instrucciones que usan este modo de direccionamiento: addi, slti, etc.
4. Direccionamiento relativo al PC: La dirección para un brinco condicional se forma
sumando el registro PC (Program Counter) con una constante que está en la instrucción.
Ejemplos de instrucciones que usan este modo de direccionamiento: beq y bne.
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 13
5. Direccionamiento pseudo-directo: La dirección destino de un salto corresponde a la
concatenación de 26 bits que están en la misma instrucción con los bits más significativos
del PC.
Ejemplos de instrucciones que usan este modo de direccionamiento: j y jal.
2.4 Lenguaje máquina
Las instrucciones dentro de la memoria son almacenadas como secuencias de 1’s y 0’s,
para ello, básicamente se debe considerar el tipo de instrucción y los operandos, por
ejemplo, para la siguiente sentencia en alto nivel:
A[12] = h + A[8];
Suponiendo que el registro $s0 tiene el inicio del arreglo A y la variable h se asocia con
$s1, produce el siguiente código ensamblador:
LW $t0, 32($s0) # $t0 = A[8] (Tipo-I) ADD $t0, $s1, $t0 # $t0 = h + A[8] (Tipo-R) SW $t0, 48($s0) # A[12]= h + A[8] (Tipo-I)
Tomando en cuenta la distribución de los campos para cada tipo de instrucción,
corresponde con el código máquina siguiente (en decimal):
35 16 8 32
0 17 8 8 0 32
43 16 8 48
El código máquina en binario es (considerando el tamaño de cada campo):
100011 10000 01000 0000 0000 0010 0000
000000 10001 01000 01000 00000 100000
101011 10000 01000 0000 0000 0011 0000
Las instrucciones BEQ y BNE son relativas al PC, es decir, la dirección destino de un
brinco se obtiene sumando una constante con el PC previamente incrementado.
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 14
En ensamblador se emplean etiquetas que posteriormente serán traducidas a números, los
números emplean una notación en complemento-2 para poder representar saltos hacía
adelante o hacia atrás. Así, para el siguiente código:
Loop: ... # Instrucción 1 ... # Instrucción 2
... # Instrucción 3 BEQ $t0, $t1, exit # 4 instrucciones adelante ... # Instrucción 4 ... # Instrucción 5 ... # Instrucción 6 BNE $t1, $t2, Loop # 9 instrucciones atrás Exit:
El código máquina, en decimal, para la instrucción BEQ es:
4 16 17 4
Mientras que para la instrucción BNE:
5 17 18 -9
Para ambas instrucciones, en binario, se tiene el siguiente código máquina:
BEQ 000100 10000 10001 0000 0000 0000 0100 BNE 000101 10001 10010 1111 1111 1111 0111
Las instrucciones J y JAL realizan saltos pseudo-directos, esto es, la instrucción incluye un
campo de 26 bits con la dirección de la instrucción, estos bits hacen referencia al número de
instrucciones dentro de un segmento de código y no al número de bytes.
Por hardware se realiza la multiplicación por 4 para obtener la dirección de byte y con el
resultado se remplaza a los 28 bits menos significativos del PC.
El registro PC conserva sus 4 bits más significativos, esto significa que el espacio total de
direccionamiento (4 Gbytes) es dividido en 16 páginas (de 256 MBytes).
Por ello, para generar el código máquina de las instrucciones J y JAL, se debe conocer la
dirección de la instrucción. Por ejemplo, si se considera la instrucción:
Dirección (decimal): Instrucción: 100 J Exit ... . . . 115 Exit: . . .
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 15
El código máquina, en decimal, para la instrucción J es:
2 115
En binario se tiene:
000010 00 0000 0000 0000 0111 0011
Tarea:
1. Considerando el código MIPS generado para la secuencia de instrucciones que evalúa
los registros $s0, $s1 y $s2 y deja el valor del menor en $s3, traduzca a código máquina
asumiendo que el código inicia en la dirección 100 (en decimal).
2. Para el código MIPS que acumula los valores de un arreglo A en una variable x:
for ( x = 0, i = 0; i < 10; i++ ) x = x + A[i];
Escriba el código máquina que corresponde, nuevamente asuma que la dirección inicial
para la secuencia de código es la 100 (en decimal).
2.5 Programas de ejemplo
Cuando se traduce de un lenguaje de alto nivel a ensamblador, en general, se realizan los
pasos siguientes:
a) Se asocian los registros con las variables del programa.
b) Se produce el código para el cuerpo de la función.
c) Se respaldan los registros a conservar en la función.
2.5.1 La función SWAP
La función swap intercambia dos localidades de memoria. El código C de la función es:
void swap ( int v[ ], int k ) { int temp;
temp = v[k]; v[k] = v[k+1]; v[k+1] = temp;
}
a) Asociación de registros con variables. La función swap recibe sus argumentos en los
registros $a0 (arreglo v) y $a1 (variable k).
Puesto que swap es una función aislada, para la variable temp se utiliza el registro $t0.
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 16
b) El cuerpo de la función. Para el traducir el cuerpo de la función se debe tener en cuenta
que los datos se constituyen de palabras de 4 bytes, de manera que para obtener la dirección
de v[k] primero se debe multiplicar a k por 4.
SLL $t1, $a1, 2 # $t1 = 4*k ADD $t1, $a0, $t1 # $t1 tiene la dirección de v[k]
Ahora es posible hacer la carga de v[k] y de v[k + 1]
LW $t0, 0($t1) # reg $t0 (temp) = v[k] LW $t2, 4($t1) # reg $t2 = v[k + 1]
Finalmente se hace el almacenamiento en memoria.
SW $t2, 0($t1) # v[k] = reg $t2 SW $t0, 4($t1) # v[k + 1] = reg $t0 (temp)
c) Respaldo de registros. Esta función es aislada, por lo que no requiere respaldar
información en la pila.
El código completo para la función swap es:
Cuerpo del procedimiento SWAP:
SLL $t1, $a1, 2 # $t1 = 4*k ADD $t1, $a0, $t1 # $t1 tiene la dirección de v[k] LW $t0, 0($t1) # reg $t0 (temp) = v[k] LW $t2, 4($t1) # reg $t2 = v[k + 1] SW $t2, 0($t1) # v[k] = reg $t2 SW $t0, 4($t1) # v[k + 1] = reg $t0 (temp)
Retorno del procedimiento JR $ra # Regresa a la función invocadora
2.5.2 La función SORT
La función sort ordena los elementos de un arreglo, su código C es:
void sort ( int v[ ], int n ) { int i, j;
for ( i = 0; i < n; i++ ) for (j = i – 1; j >= 0 && v[j] > v[j + 1]; j--)
swap( v, j);
}
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 17
a) Asociación de registros con variables. Los dos argumentos se reciben en los registros $a0
(inicio del arreglo v) y $a1 (la variable n). La variable i se asocia con el registro $s0 y j con
el registro $s1 (registros seguros porque no es una función aislada).
b) El cuerpo de la función. Para el ciclo más externo:
for ( i = 0; i < n; i++ )
La inicialización y comparación:
ADD $s0, $zero, $zero # i = 0 for1: SLT $t0, $s0, $a1 # $t0 = 1 si i < n
BEQ $t0, $zero, exit1 # Si $t0 = 0, termina
Continuaría con el cuerpo del for, para que posteriormente se realice el incremento:
ADDI $s0, $s0, 1 # i ++ J for1o # Siguiente iteración
El segundo for en C es:
for (j = i – 1; j >= 0 && v[j] > v[j + 1]; j--)
La inicialización de la variable j corresponde a:
ADDI $s1, $s0, -1 # j = i - 1
Este ciclo prueba dos expresiones ligadas con un operador AND, si la primera falla, es
suficiente para terminar con el ciclo. El cuerpo del for se realiza solo cuando las dos
expresiones son verdaderas. La comparación del ciclo es:
for2o: SLT $t0, $s1, $zero # $t0 = 1 si j < 0 BNE $t0, $zero, exit2 # Termina si $t0 = 1 SLL $t1, $s1, 2 # $t1 = 4*j ADD $t2, $a0, $t1 # $t2 = &(v[j]) LW $t3, 0($t2) # $t3 = v[j] LW $t4, 4($t2) # $t4 = v[j + 1] SLT $t0, $t4, $t3 # $t0 = 1 si $t4 < $t3 BEQ $t0, $zero, exit2 # Si $t0 = 0, termina
Después del cuerpo del ciclo se culmina con el decremento de j y el paso a la siguiente
iteración:
ADDI $s1, $s1, -1 # j— J for2o # Siguiente iteración
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 18
El cuerpo del ciclo for interno incluye la llamada a la función swap, para ello
primeramente deben revisarse los argumentos.
El primer argumento de sort coincide con el primer argumento de swap. El segundo
argumento cambia, éste debe respaldarse en un registro seguro:
ADD $s2, $a1, $zero # Respalda n en $s2 ADD $a1, $s1, $zero # $a1 = j, 2º argumento JAL SWAP ADD $a1, $s2, $zero # Recuperación de n
Es conveniente realizar el respaldo de argumentos en registros seguros al inicio de la
función y emplear los registros seguros en el cuerpo de la misma.
Esto hace posible la modificación de los registros de los argumentos cada vez que sea
necesario y no se deben recuperar después de la llamada.
c) Respaldo de registros. Además de los tres registros seguros empleados ($s0, $s1 y $s2)
debe respaldarse la dirección de retorno, para que sort regrese adecuadamente y el
retorno no se vea afectado por el llamado a swap.
El prólogo de la función sort es:
ADDI $sp, $sp, -16 # Hace espacio para 4 registros SW $ra, 12( $sp ) # Salva a $ra en la pila SW $s2, 8( $sp ) # Salva a $s2 en la pila SW $s1, 4( $sp ) # Salva a $s1 en la pila SW $s0, 0( $sp ) # Salva a $s0 en la pila
Al final de la función se debe hacer la recuperación de registros, antes de regresar a la
función invocadora.
El código completo para la función sort es:
Respaldo de registros SORT: ADDI $sp, $sp, -16 # Hace espacio para 4 registros
SW $ra, 12( $sp ) # Salva a $ra en la pila SW $s2, 8( $sp ) # Salva a $s2 en la pila SW $s1, 4( $sp ) # Salva a $s1 en la pila SW $s0, 0( $sp ) # Salva a $s0 en la pila ADD $s2, $a1, $zero # Respalda n en $s2
Inicio del ciclo for externo ADD $s0, $zero, $zero # i = 0
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 19
for1: SLT $t0, $s0, $a1 # $t0 = 1 si i < n BEQ $t0, $zero, Exit1 # Si $t0 = 0, termina
Cuerpo del for externo, inicio del for interno ADDI $s1, $s0, -1 # j = i - 1
for2o: SLT $t0, $s1, $zero # $t0 = 1 si j < 0 BNE $t0, $zero, Exit2 # Termina si $t0 = 1 SLL $t1, $s1, 2 # $t1 = 4*j ADD $t2, $a0, $t1 # $t2 = &(v[j]) LW $t3, 0($t2) # $t3 = v[j] LW $t4, 4($t2) # $t4 = v[j + 1] SLT $t0, $t4, $t3 # $t0 = 1 si $t4 < $t3 BEQ $t0, $zero, exit2 # Si $t0 = 0, termina
Cuerpo del for interno ADD $a1, $s1, $zero # $a1 = j, 2º argumento JAL SWAP
Cierre del for interno ADDI $s1, $s1, -1 # j— J for2o # Siguiente iteración
Cierre del for externo Exit2: ADDI $s0, $s0, 1 # i ++
J for1o # Siguiente iteración
Recuperación de registros LW $ra, 12( $sp ) # Recupera $ra de la pila LW $s2, 8( $sp ) # Recupera $s2 de la pila LW $s1, 4( $sp ) # Recupera $s1 de la pila LW $s0, 0( $sp ) # Recupera $s0 de la pila ADDI $sp, $sp, 16 # Restablece al registro $SP
2.5.2 Arreglos vs Apuntadores
A pesar de que los arreglos y apuntadores comparten algunas características y es posible
pasar un arreglo a una función que espera un apuntador o vice-versa, los compiladores
traducen de manera diferente un programa con arreglos que otro con apuntadores.
Estas diferencias se reflejan al traducir la función clear, encargada de colocar 0’s a todos
los elementos de un arreglo:
Versión con un arreglo Versión con un apuntador void clear1(int array[], int size) { int i; for( i = 0; i < size; i++) array[i] = 0; }
void clear2(int *array, int size) { int *p; for(p=&array[0];p<&array[size]; p++) *p = 0; }
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 20
El código MIPS de ambas versiones:
Versión con un arreglo Versión con un apuntador clear1: add $t0, $zero, $zero for1: slt $t1, $t0, $a1 beq $t1, $zero, fin_for1 sll $t2, $t0, 2 add $t2, $t2, $a0 sw $zero, 0($t2) addi $t0, $t0, 1 j for1 fin_for1: jr $ra
clear2: add $t0, $a0, $zero sll $t1, $a1, 2 add $t1, $t1, $a0 for2: slt $t2, $t0, $t1 beq $t2, $zero, fin_for2 sw $zero, 0($t0) addi $t0, $t0, 4 j for2 fin_for2: jr $ra
El código de la versión con apuntadores es más rápido porque incluye menos instrucciones
en la parte repetitiva. En general, los compiladores capaces de optimizar código intentan
manejar los arreglos sumando una variable para obtener la dirección del i-ésimo elemento,
en lugar de realizar multiplicaciones. El código resultante será más rápido porque para toda
arquitectura, una suma es más rápida que una multiplicación.
2.5.3 Un simulador para el repertorio de instrucciones
El simulador QtSpim fue creado por el Dr. James Larus en la Universidad de Wisconsin,
Madison. El Dr. Larus actualmente es investigador de la empresa Microsoft.
QtSpim es un simulador para programas escritos en lenguaje ensamblador para los
procesadores R2000/R3000, los cuales son procesadores de 32 bits de la compañía MIPS.
QtSpim proporciona un depurador simple y un juego simple de servicios del sistema
operativo.
El programa se puede descargar del sitio:
https://sourceforge.net/projects/spimsimulator/
El simulador incluye un kernel que inicializa al simulador y una consola para interacción
con el usuario. El kernel realiza un salto a la etiqueta main, por lo que debe incluirse en el
programa a simular. El acceso a la consola se realiza mediante servicios del sistema
(SYSCALL), permitiendo al usuario introducir información y leer resultado.
El número de servicio se coloca en el registro $v0 y sus argumentos en $a0 o $f12,
dependiendo de si es entero o flotante, de manera similar, el resultado es dejado en $v0 o
$f0.
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 21
En la siguiente tabla se muestran los diferentes servicios del programa.
Servicio Número Argumentos Resultados
print_int 1 $a0 = integer print_float 2 $f12 = float print_double 3 $f12 = doublé print_string 4 $a0 = string
read_int 5 integer (in $v0) read _float 6 float (in $f0) read _double 7 double (in $f0) read _string 8 $a0 = buffer, $a1 = length
sbrk 9 $a0 = amount Address (in $v0) exit 10
Pseudo-instrucciones:
Son instrucciones que el simulador reconoce pero no tienen una interpretación directa en
hardware, sino que deben traducirse a una o más instrucciones reales para que puedan ser
ejecutadas.
Las pseudo-instrucciones dan flexibilidad a los programadores, algunos ejemplos:
Pseudo-instrucción Instrucción o instrucciones reales move $t1, $t0 ori $t1, $zero, $t0 mul $s1, $s2, $s3 mult $s2, $s3 (resultado en HI y LO)
mflo $s1 (mueve el reg. LO a $s1) (mfhi es para mover el reg. HI)
mul $t4, $t1, 4 ori $1, $0, 4 mult $9, $1 mflo $12
la $a0, str1 lui $a0, HIGH(str1) << 16 ori $a0, LOW(str1)
Ejemplos en el uso del simulador:
1. Programa “Hola Mundo”. El programa muestra cómo se usa el servicio 4 para enviar
una cadena a la consola.
main: addi $v0, $zero, 4 # Servicio 4 la $a0, cadena # Ubica el argumento syscall # Solicita el servicio jr $31 .data
cadena: .asciiz "Hola Mundo"
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 22
2. Suma de dos números. Programa para mostrar el uso de los servicios 5 y 1 (lee un
entero y escribe un entero).
main: addi $v0, $zero, 4 la $a0, str1 syscall # Pide un número addi $v0, $0, 5 syscall # Lee el número add $t0, $0, $v0 # se coloca en $t0 addi $v0, $zero, 4 la $a0, str2 syscall # Pide otro número addi $v0, $0, 5 syscall # Lee el otro numero add $t1, $0, $v0 # se coloca en $t1 addi $v0, $zero, 4 la $a0, str3 syscall # Mensaje para dar el resultado add $a0, $t0, $t1 addi $v0, $zero, 1 syscall # Muestra el resultado addi $v0, $zero, 4 la $a0, str4 syscall # Mensaje final jr $ra # fin del main .data
str1: .asciiz "Dame un numero: " str2: .asciiz "Dame otro numero: " str3: .asciiz "La suma de los numeros es : " str4: .asciiz "\n\nFin del programa. . ."
3. Llamada a una función. Desde el main se invocará a la función factorial. El
main ya no es una función aislada, por lo que debe emplear registros seguros. No se
incluye el código de la función factorial.
main: addi $sp, $sp, -12 # Respaldos en la Pila sw $ra, 0 ($sp) sw $s0, 4 ($sp) sw $s1, 8 ($sp) addi $v0, $zero, 4 la $a0, str1 syscall # Pide un número addi $v0, $0, 5 syscall # Lee el número
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 23
add $s0, $0, $v0 # se coloca en $s0 add $a0, $s0, $zero jal factorial # Llamada a factorial add $s1, $v0, $zero addi $v0, $zero, 4 la $a0, str2 syscall # Mensaje para dar el resultado add $a0, $s0, $zero addi $v0, $zero, 1 syscall # El numero addi $v0, $zero, 4 la $a0, str3 syscall add $a0, $s1, $zero addi $v0, $zero, 1 syscall # Su factorial
addi $v0, $zero, 4 la $a0, str4 syscall # Mensaje final lw $ra, 0 ($sp) # Recuperacion de la Pila lw $s0, 4 ($sp) lw $s1, 8 ($sp) addi $sp, $sp, 12 jr $ra # fin del main
factorial: . . . # El código desarrollado . . . # en ejercicios anteriores jr $ra
.data
str1: .asciiz "Dame un numero: " str2: .asciiz "El factorial del numero " str3: .asciiz " es : " str4: .asciiz "\n\nFin del programa. . ."
4. Manejo de un arreglo. La pila es el espacio en donde se puede colocar un arreglo para
su posterior manipulación. En este ejemplo se muestra cómo solicitar espacio, cómo
tener acceso y cómo liberarlo.
El código C pide los datos de un arreglo y los imprime en orden inverso:
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 24
void main(){ int i, n, *A, *t;
printf("Manejo de un arreglo\n"); printf("Indica el tamaño del arreglo: "); scanf("%d", &n); A = (int *) malloc(n*sizeof(int));
for( t = A, i = 0; i < n; i++, t++) {
printf(" Dame el dato %d : ", i); scanf("%d", t);
}
printf("\nLos números en orden inverso: \n");
for( t = &A[n-1], i = 0; i < n; i++, t--) printf(" %d\n", *t);
free(A); }
El código MIPS, considerando que es una función aislada, es:
main: addi $v0, $zero, 4 la $a0, str1 syscall # Primer mensaje addi $v0, $zero, 4 la $a0, str2 syscall # Pide el tamaño addi $v0, $0, 5 syscall # Lee el tamaño add $t1, $0, $v0 # se coloca en $t1 # Reserva el espacio en memoria sll $t4, $t1, 2 # Total de bytes sub $sp, $sp, $t4 # Espacio en la pila addi $t2, $sp, 0 # $t2 = &A[0]
# Inicia el primer for:
addi $t0, $0, 0 # i = 0 addi $t3, $t2, 0 # t = A
for1: slt $t5, $t0, $t1 beq $t5, $zero, fin_for1 li $v0, 4 la $a0, str3 syscall # Pide el numero li $v0, 1 add $a0, $0, $t0
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 25
syscall # Imprime el índice li $v0, 4 la $a0, str4 syscall # dos puntos li $v0, 5 syscall # Lee el número sw $v0, 0($t3) # Almancena addi $t0, $t0, 1 # i ++ addi $t3, $t3, 4 # t ++ j for1
fin_for1: li $v0, 4 la $a0, str5 syscall # Mensaje de notificación
# Inicia el segundo for
addi $t0, $0, 0 # i = 0 addi $t4, $t1, -1 # $t4 = n - 1 sll $t4, $t4, 2 # $t4 = 4 * (n - 1) add $t3, $t2, $t4 # t = &A[n-1]
for2: slt $t5, $t0, $t1 beq $t5, $zero, fin_for2 lw $t6, 0($t3) # Obtiene el número li $v0, 1 add $a0, $0, $t6 syscall # lo imprime li $v0, 4 la $a0, str6 syscall #imprime el retorno de carro addi $t0, $t0, 1 #i ++ addi $t3, $t3, -4 #t -- j for2
fin_for2: sll $t4, $t1, 2 # Total de bytes add $sp, $sp, $t4 # Libera espacio jr $ra # Termina
#Cadenas del programa .data
str1: .asciiz " Manejo de un arreglo \n" str2: .asciiz "Indica el tamaño del arreglo : " str3: .asciiz "Dame el número " str4: .asciiz " : " str5: .asciiz "\nLos numeros en orden inverso son: \n " str6: .asciiz "\n "
Arquitectura de Computadoras 2. Arquitectura del repertorio de instrucciones
Santiago E. Felipe/UTM-2019 26
Tarea:
Los siguientes ejercicios permitirán evaluar al simulador:
1. Realice un programa que solicite 3 números e indique cual es el menor de ellos.
2. Realice la función principal para el programa SORT y simúlelo, recordar que debe
incluirse a la función SWAP, el número de elementos deberá ser solicitado al usuario
(sug. Puede usarse la pila para almacenar el arreglo).
3. Realice un programa que obtenga el menor y el mayor de un arreglo de n elementos
proporcionados por el usuario (sug. Acondicione la función desarrollada en la tarea
anterior).
4. Realice un programa recursivo que obtenga al n-ésimo término de la serie de Fibonacci.
Reutilice la función desarrollada en una tarea previa.