Al finalizar esta práctica el estudiante será capaz de:
Para la realización de esta práctica se necesita un ordenador personal con Windows o Linux y el simulador SPIM, disponible en http://pages.cs.wisc.edu/~larus/spim.html. Es muy recomendable que e instales este programa en tu ordenador
La evaluación de la práctica comenzará antes de asistir al laboratorio. Así, el estudiante deberá reunirse con su compañero de grupo para preparar cada sesión. El trabajo previo a la prá ctica consistirá en un conjunto de actividades que nos prepararán para obtener el máximo provecho de la sesión de laboratorio. El trabajo previo tendrá una valoración variable y se mostrará al profesor de laboratorio en la primera media hora de cada sesión.
El resto de la evaluación de la práctica estará relacionado con el trabajo en la sesión presencial en el laboratorio. Cada apartado, formado por actividades diversas, tendrá una puntuación. Esta puntuación aparecerá indicada junto a cada actividad.
Para poder sacar el máximo partido a la sesión de laboratorio, lee atentamente el siguiente texto y prepara EN UNA HOJA APARTE las respuestas a los ejercicios.
Como primer ejemplo de subrutinas, el siguiente código muestra un programa que utiliza una subrutina llamada suma. Esta subrutina suma los valores almacenados en los registros $a0 y $a1, y devuelve la suma de ambos en el registro $v0. Como se puede observar, la subrutina se llama desde dos puntos del programa principal (la primera línea del programa principal está etiquetado como main).
suma-dato1-dato2-v1.s
1 .data # Zona de datos
2 dato1: .word 1
3 dato2: .word 3
4 dato3: .word 5
5 dato4: .word 4
6 res1: .space4
7 res2: .space4
8
9 .text # Zona de código
10
11 #Subrutina
12
13 suma: add $v0, $a0, $a1
14 jr $ra
15
16 #Programa invocador
17
18 main: lw $a0, dato1($0)
19 lw $a1, dato2($0)
20 primera: jal suma
21 sw $v0, res1($0)
22 lw $a0, dato3($0)
23 lw $a1, dato4($0)
24 segunda: jalsuma
25 sw $v0, res2($0)
Carga el programa anterior en el simulador y contesta a las siguientes preguntas mientras realizas una ejecución paso a paso.
El programa que aparece a continuación es idéntico al anterior salvo que se han modificado las posiciones de memoria que ocupan el programa invocador y la subrutina. En primer lugar se ha almacenado el programa invocador y a continuación la subrutina. Cárgalo en el simulador y ejecútalo paso a paso.
1 .data # Zona de datos
2 dato1: .word 1
3 dato2: .word 3
4 dato3: .word 5
5 dato4: .word 4
6 res1: .space4
7 res2: .space4
8
9 .text # Zona de código
10
11
12 #Programa invocador
13
14 main: lw $a0, dato1($0)
15 lw $a1, dato2($0)
16 primera: jal suma
17 sw $v0, res1($0)
18 lw $a0, dato3($0)
19 lw $a1, dato4($0)
20 segunda: jalsuma
21 sw $v0, res2($0)
22 #Subrutina
23
24 suma: add $v0, $a0, $a1
25 jr $ra
. ¿Ha terminado el programa invocador después de ejecutar la instrucción sw $v0, res2 ($0)? ¿O por el contrario, la ejecución ha continuado con las instrucciones de la subrutina suma? ¿Ha entrado en un bucle sin fin?
Lee el capítulo 7.1 del libro de apoyo y resuelve el problema.
El mecanismo mediante el cual el programa invocador y la subrutina intercambian información, recibe el nombre de paso de parámetros. Los parámetros intercambiados entre el programa invocador y la subrutina pueden ser de tres tipos según la dirección en la que se transmita la informaci ón: de entrada, de salida o de entrada/salida. Se denominan parámetros de entrada a los que proporcionan información del programa invocador a la subrutina. Se denominan parámetros de salida a los que devuelven información de la subrutina al programa invocador. Por último, los parámetros de entrada/salida proporcionan información del programa invocador a la subrutina y devuelven información de la subrutina al programa invocador. Por otro lado, para realizar el paso de parámetros es necesario disponer de algún lugar físico donde se pueda almacenar y leer la información que se quiere transferir. Las dos opciones más comunes son: utilizar registros o utilizar la pila. Que se utilicen registros o la pila depende de la arquitectura en cuestión y del convenio que se siga para el paso de parámetros en dicha arquitectura.
Por el momento hablaramos del paso de parámetros por medio de registros. La arquitectura MIPS32 establece un convenio para el paso de parámetros mediante registros: para los parámetros de entrada se deben utilizar los registros $a0, $a1, $a2 y $a3; y para los parámetros de salida se deben utilizar los registros $v0 y $v1.
El paso de parámetros por valor implica la siguiente secuencia de acciones
Como ejemplo de paso de parámetros por valor se presenta el siguiente código que muestra un ejemplo de paso de parámetros de entrada y de salida por valor.
suma-dato1-dato2-v3.s
1 .data # Zona de datos
2 dato1: .word 1
3 dato2: .word 3
4 dato3: .word 5
5 dato4: .word 4
6 res1: .space4
7 res2: .space4
8
9 .text # Zona de código
10
11
12 #Programa invocador
13
14 main: lw $a0, dato1($0)
15 lw $a1, dato2($0)
16 primera: jal suma
17 sw $v0, res1($0)
18 lw $a0, dato3($0)
19 lw $a1, dato4($0)
20 segunda: jalsuma
21 sw $v0, res2($0)
22 li $v0, 10 #Finalizar la ejecución del programa
23 syscall
24 #Subrutina
25
26 suma: add $v0, $a0, $a1
27 jr $ra
En cuanto al paso de parámetros por referencia, éste implica la siguiente secuencia de acciones: 1. Antes de realizar la llamada a la subrutina, el programa invocador carga en los registros elegidos para realizar el paso de parámetros, las direcciones de memoria en las que está almacenada la información que se quiere pasar. 2. La subrutina carga en registros el contenido de las direcciones de memoria indicadas por los parámetros de entrada (recuerda que el MIPS32 no puede operar directamente con datos en memoria). Y opera con ellos. 3. La subrutina, una vez ha finalizado y antes de devolver el control al programa principal, almacena los resultados en las direcciones de memoria que le había proporcionado el programa invocador.
El siguiente programa muestra un ejemplo en el que se llama a una subrutina utilizando el paso de parámetros por referencia tanto para los parámetros de entrada como los de salida.
suma-dato1-dato2-v4.s
1 .data # Zona de datos
2 dato1: .word1
3 dato2: .word3
4 dato3: .word5
5 dato4: .word4
6 res1: .space4
7 res2: .space4
8
9 .text # Zona de código
10
11 # Programa invocador
12
13 main: la $a0, dato1
14 la $a1, dato2
15 la $a2,res1
16 primera: jal suma
17 la $a0, dato3
18 la $a1, dato4
19 la $a2, res2
20 segunda: jal suma
21
22 li $v0, 10 # Finalizar la ejecución del programa
23 syscall
24
25
26 # Subrutina
27
28 suma: lw $t0, 0($a0)
29 lw $t1, 0($a1)
30 add $v0, $t0, $t1
31 sw $v0, 0($a2)
32 jr $ra
A continuación se plantea el desarrollo de un programa en lenguaje ensamblador donde se aplican todos los conceptos presentados en este capítulo. Dicho programa tiene por objeto calcular cuántos elementos de un vector dado tienen el valor 12. El programa consta de una subrutina que devuelve el número de elementos de un vector que son menores a uno dado. Llamando dos veces a dicha subrutina con los valores 12 y 13 y realizando una simple resta, el programa será capaz de determinar el número de elementos que son iguales a 12. Se debe desarrollar dicho programa paso a paso. En primer lugar, se debe desarrollar una subrutina que contabilice cuántos elementos de un vector son menores a un valor dado. Para ello, hay que determinar qué parámetros debe recibir dicha subrutina, así como qué registros se van a utilizar para ello, y cuáles se pasarán por referencia y cuáles por valor. 76 Introducción a la gestión de subrutinas Una vez desarrollada la subrutina y teniendo en cuenta los parámetros que requiere, se deberá desarrollar el programa que llame a dicha subrutina y obtenga el número de elementos del vector dado que son iguales a 12. Una descripción detallada del funcionamiento que debiera tener dicho programa se muestra en el siguiente listado en lenguaje C:
1 int nummenorque(int *vectors, intdims, intdatos)
2 {
3 inti; /* Índice */
4 intn; /* Contador */
5
6 n=0; /* Contador a 0 */
7 for(i=0;i<dims;i++){
8 if(vectors[i]<datos){
9 n=n+1;
10 }
11 }
12 return n;
13 }
14
15 int main(int argc,char **argv)
16 {
17 intvector[9]={5,3,5,5,8,12,12,15,12};
18 intdim=9;
19 intnum=12;
20 intres1,res2,res;
21
22 res1=nummenorque(vector,dim,num);
23 num=num+1;
24 res2=nummenorque(vector,dim,num);
25 res=res2-res1;
26 }
13.Contesta las siguientes preguntas
- ¿Qué parámetros debe pasar el programa invocador a la subrutina? ¿Y la subrutina al programa invocador?
- ¿Cuáles son de entrada y cuáles de salida?
- ¿Cuáles se pasan por referencia y cuáles por valor?
- ¿Qué registros se van a utilizar para cada uno de ellos?
Realiza el siguiente ejercicio:
a) Desarrolla una subrutina que sume los elementos de un vector de enteros de cualquier dimensión.
b) Desarrolla un programa que sume todos los elementos de una matriz de dimensión m × n. Utiliza la subrutina desarrollada en el apartado a) de este ejercicio para sumar los elementos de cada fila de la matriz.
En la versión que se implemente de este programa utiliza una matriz con m = 5 y n = 3, es decir, de dimensión 5 × 3 con valores aleatorios (los que se te vayan ocurriendo sobre la marcha). Se debe tener en cuenta que la matriz debería poder tener cualquier dimensión, así que se deberá utilizar un bucle para recorrer sus filas.