spim: un simulador del repertorio de instruccionesfsantiag/arqcomputadoras/apoyo para el uso de...

12
SPIM: Un simulador del repertorio de instrucciones Por M. C. Felipe Santiago E. Para el curso: Arquitectura de Computadoras 1. Introducción SPIM es un simulador creado por el Dr. James Larus, graduado en la Universidad de Wisconsin, Madison. Y actualmente investigador de la empresa Microsoft. SPIM es un simulador autónomo para programas en lenguaje ensamblador escritos para los procesadores R2000/R3000, los cuales son procesadores de 32 bits de la corporación MIPS. SPIM lee y ejecuta el código en lenguaje ensamblador, proporciona un depurador simple y un juego simple de servicios del sistema operativo. SPIM soporta casi el conjunto completo de instrucciones del ensamblador-extendido para el R2000/R3000 (omite algunas comparaciones de punto flotante complejas y detalles del sistema de paginación de memoria.). QTSPIM es la versión más reciente del simulador y se puede obtener desde: http://pages.cs.wisc.edu/~larus/spim.html En el sitio se encuentra el vínculo para descargar el programa QTSPIM ubicado en SourceForge. También se encuentran links para el código fuente completo y documentación. En este documento se muestran algunos aspectos del programa SPIM útiles para simular los programas hasta el momento realizados. 2. Aspecto del programa El programa QTSPIM tiene el aspecto que se presenta en la figura 1, en la que se distinguen dos columnas: La columna de registros y la columna de memoria. En la columna de registros se puede ver el contenido de todos los registros, está separada por dos secciones, los registros de enteros y los registros de punto flotante. Por default se presentan los registros para enteros, incluyendo los registros de propósito general (de $0 a $31), además del Contador del Programa (PC) y de otros registros para el manejo de excepciones (una excepción es un evento erróneo debido a alguna incongruencia durante la ejecución de un programa). También se muestran dos registros HI y LO, estos registros son dedicados a las multiplicaciones y divisiones. La segunda columna contiene una parte de la memoria en la que se colocarán los programas de usuario (el código a evaluar), mostrando las instrucciones en notación simbólica (ensamblador) y en código máquina. En esta ventana se observa al código descrito en el

Upload: vandung

Post on 02-Oct-2018

222 views

Category:

Documents


0 download

TRANSCRIPT

SPIM: Un simulador del repertorio de instrucciones

Por M. C. Felipe Santiago E.

Para el curso: Arquitectura de Computadoras

1. Introducción

SPIM es un simulador creado por el Dr. James Larus, graduado en la Universidad de

Wisconsin, Madison. Y actualmente investigador de la empresa Microsoft.

SPIM es un simulador autónomo para programas en lenguaje ensamblador escritos para los

procesadores R2000/R3000, los cuales son procesadores de 32 bits de la corporación MIPS.

SPIM lee y ejecuta el código en lenguaje ensamblador, proporciona un depurador simple y

un juego simple de servicios del sistema operativo.

SPIM soporta casi el conjunto completo de instrucciones del ensamblador-extendido para

el R2000/R3000 (omite algunas comparaciones de punto flotante complejas y detalles del

sistema de paginación de memoria.).

QTSPIM es la versión más reciente del simulador y se puede obtener desde:

http://pages.cs.wisc.edu/~larus/spim.html

En el sitio se encuentra el vínculo para descargar el programa QTSPIM ubicado en

SourceForge. También se encuentran links para el código fuente completo y

documentación.

En este documento se muestran algunos aspectos del programa SPIM útiles para simular los

programas hasta el momento realizados.

2. Aspecto del programa

El programa QTSPIM tiene el aspecto que se presenta en la figura 1, en la que se distinguen

dos columnas: La columna de registros y la columna de memoria.

En la columna de registros se puede ver el contenido de todos los registros, está separada

por dos secciones, los registros de enteros y los registros de punto flotante.

Por default se presentan los registros para enteros, incluyendo los registros de propósito

general (de $0 a $31), además del Contador del Programa (PC) y de otros registros para el

manejo de excepciones (una excepción es un evento erróneo debido a alguna incongruencia

durante la ejecución de un programa). También se muestran dos registros HI y LO, estos

registros son dedicados a las multiplicaciones y divisiones.

La segunda columna contiene una parte de la memoria en la que se colocarán los programas

de usuario (el código a evaluar), mostrando las instrucciones en notación simbólica

(ensamblador) y en código máquina. En esta ventana se observa al código descrito en el

manejador de excepciones, este código corresponde a una especie de kernel para la

máquina e incluye una llamada a la función main, de manera que cualquier programa que se

quiera simular deberá incluir al procedimiento principal (main).

Los usuarios avanzados puedan hacer sus propias rutinas para que hagan un manejo

diferente de las excepciones y configurar para que éste sea cargado en el arranque del

programa

Fig. 1 Aspecto del programa QTSPIM para Windows

La columna de la memoria tiene una ceja adicional (Data) en donde se puede ver la memoria en

en hexadecimal. La memoria incluye una sección de propósito general, una parte dedicada a la

pila (stack) y otra que forma parte del Kernel.

En la parte inferior de las columnas se muestra una ventana de mensajes, en la que se

describen los diferentes eventos que van ocurriendo durante la simulación.

3. La consola del programa SPIM

Además de la ventana principal del programa, cuando QTSPIM se ejecuta se despliega en

pantalla otra ventana conocida como la consola del programa SPIM (figura 2).

La consola es el mecanismo por medio del cual se van a insertar datos al programa o se van

a observar algunos resultados del mismo. El Kernel incluido permite el manejo de una

instrucción denominada SYSCALL. Con SYSCALL se realiza una llamada al Kernel para

solicitar algún servicio, que puede consistir en la captura de un dato o bien la presentación

de resultados en la consola.

Fig. 2 La consola del Programa QTSPIM

Antes de invocar a SYSCALL, se debe especificar el número de servicio en el registro $V0,

y si el servicio requiere argumentos, éstos se deberán colocar en los registros $a0 y $a1,

dependiendo del número de argumentos, sin embargo, si el servicio es para números en

punto flotante, se utilizará al registro $f0 para el argumento (La arquitectura MIPS incluye

32 registros para el manejo de números en punto flotante, y un hardware dedicado para las

operaciones). En la tabla 1 se muestran los servicios que soporta el Kernel.

Tabla 1. Servicios que proporciona el Kernel del Programa SPIM

4. Pseudo instrucciones.

Debido a que el repertorio MIPS es un repertorio de instrucciones reducido, para dar un

poco más de flexibilidad a los programadores, es posible generar un conjunto de pseudo

instrucciones; una pseudo instrucción realiza algún tipo de operación, sin embargo no tiene

una interpretación directa en Hardware, sino que tiene que traducirse a una o más

instrucciones reales para que pueda ser ejecutada.

Así por ejemplo, la pseudo instrucción:

move reg_destino, reg_fuente

Mueve el registro fuente al registro destino, pero no es una instrucción real, sino que el

simulador SPIM la traduce a:

Or reg_destino, $zero, reg_fuente

Para las multiplicaciones, se puede utilizar la pseudo instrucción:

mul $s1, $s2, $s3

esta pseudo instrucción en realidad es traducida en las instrucciones siguientes:

mult $s2, $s3 # Esta es la instrucción que multiplica a $s2 con $s3, pero el

# resultado queda en los registros HI y LO

mflo $s1 # Copia el contenido del registro LO en $s1

Puede notarse que la pseudo instrucción es suficiente cuando se sabe que el resultado

alcanza en un registro de 32 bits. Pero si se están manipulando números muy grandes,

además de la pesudo instrucción se debe usar la instrucción:

mfhi $s4 # Copia el contenido del registro HI en $s4

La pseudo instrucción mul también puede usarse con el segundo parámetro con un valor

inmediato, por ejemplo la pseudo instrucción:

mul $t4, $t1, 4

Es traducida a:

ori $t2, $zero, 4

mult $t2, $t1

mflo $t4

Otra pseudo instrucción muy útil es la siguiente:

la $a0, str1

Cuando se codifica un programa y se van a utilizar cadenas constantes, se sabe que éstas se

colocarán en memoria, sin embargo se ignora en qué dirección serán colocadas, por lo que

no se sabría cómo direccionarlas. Con la pseudo-instrucción se obtiene en el registro $a0 la

dirección donde inicia la cadena str1 (la – load address). Esta pseudo instrucción es

traducida a dos instrucciones, la primera para cargar la parte alta de la dirección (lui) y la

segunda para obtener la parte baja (ori).

Existen más pesudo instrucciones, sólo se han mencionado las más comunes y que son

necesarias para el desarrollo de algunos programas que se realizarán para la evaluación del

simulador. En el apéndice A del texto “Computer Organization & Design, The

hardware/software interface” se encuentra un listado con todas las instrucciones de los

procesadores MIPS R2000/R3000 y las pseudo instrucciones que soporta el simulador

SPIM. Este apéndice está disponible en formato PDF en la página del Dr. Larus, su

referencia es:

http://pages.cs.wisc.edu/~larus/HP_AppA.pdf

5. Ejemplos de uso del simulador.

Ejemplo 1: El programa “Hola Mundo”

Se trata de un programa que desplegará en la consola a la cadena “HOLA MUNDO”,

básicamente se requiere obtener la dirección del inicio de la cadena y solicitar el servicio 4

al kernel (con la instrucción SYSCALL).

El código del programa es:

main: addi $v0, $zero, 4 # Se usará el servicio 4

la $a0, cadena # Se obtiene el argumento

syscall # Solicita el servicio

jr $31 # Termina la función principal

.data

cadena: .asciiz "Hola Mundo"

La salida en la consola es:

Observaciones:

El código se puede escribir con cualquier editor de texto (texto sin formato) y salvarse

con cualquier extensión, se sugiere s ó asm por asociación con el programa.

El código principal debe incluir a la etiqueta main porque en el kernel hay un salto

hacia esa etiqueta.

La ejecución puede hacerse paso a paso con <F10> o con el botón run/continue <F5>.

Una vez que finaliza el código de usuario, si se continúa ejecutando, del main regresa al

Kernel y solicita el servicio 10 (exit).

Ejemplo 2: Un programa que suma dos números.

En este ejemplo se usará a la consola para obtener dos enteros, luego se sumarán y se

mostrará el resultado. El código del programa:

main:

addi $v0, $zero, 4 # Servicio 4

la $a0, str1 # se imprime una cadena

syscall # para pedir un número

addi $v0, $0, 5 # Servicio 5

syscall # se lee el número

add $t0, $0, $v0 # se coloca en $t0

addi $v0, $zero, 4 # Servicio 4

la $a0, str2 # se imprime una cadena

syscall # para pedir el otro número

addi $v0, $0, 5 # servicio 5

syscall # se lee el otro numero

add $t1, $0, $v0 # se coloca en $t1

addi $v0, $zero, 4 # Servicio 4

la $a0, str3 # para indicar que se

syscall # dará el resultado

add $a0, $t0, $t1 # Se coloca la suma como argumento

addi $v0, $0, 1 # Servicio 1

syscall # se muestra el resultado

addi $v0, $zero, 4 # Servicio 4

la $a0, str4 # muestra una cadena de

syscall # terminación del programa

jr $31 # 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, Adios . . ."

Una corrida generó la salida en la consola:

Ejemplo 3: El factorial de un número.

Este programa está basado en la función recursiva que se presentó con anterioridad

(Soporte de procedimientos) sólo se agregó el main y se hicieron algunas modificaciones

para el manejo correcto de las constantes.

En este programa se utilizó la pseudo instrucción li (por load immediate) para cargar una

constante en un registro, que es equivalente a hacer una operación OR del registro 0 con la

constante y colocar el resultado en el registro que se quiere cargar.

El código del programa:

main: addi $sp, $sp, -4 # Hace espacio en la Pila

sw $ra, 4 ($sp) # Salva la dirección de retorno

li $v0, 4 # Salida a la consola

la $a0, str1

syscall

li $v0, 5 # Lectura de un numero

syscall

add $s0, $0, $v0 # El numero esta en $v0, se copia a $s0

add $a0, $0, $s0 # Prepara el parametro

jal fact # Llama al factorial

add $s1, $v0, $zero # Respalda el resultado

li $v0, 4 # Una cadena a la consola

la $a0, str2

syscall

addi $v0, $0, 1 # Una entero a la consola

add $a0, $s0, $zero

syscall

li $v0, 4 # Una cadena a la consola

la $a0, str3

syscall

addi $v0, $0, 1 # Una entero a la consola

add $a0, $s1, $zero

syscall

li $v0, 4

la $a0, str4

syscall

lw $ra, 4 ($sp) # Recupera la dirección de retorno

addi $sp, $sp, 4 # Restablece el tope de la Pila

jr $31

fact:

addi $sp, $sp, -8 # adjust stack for 2 items

sw $ra, 4($sp) # save return address

sw $a0, 0($sp) # save argument

slti $t0, $a0, 1 # test for n < 1

beq $t0, $zero, L1

addi $v0, $zero, 1 # if so, result is 1

addi $sp, $sp, 8 # pop 2 items from stack

jr $ra # and return

L1: addi $a0, $a0, -1 # else decrement n

jal fact # recursive call

lw $a0, 0($sp) # restore original n

lw $ra, 4($sp) # and return address

addi $sp, $sp, 8 # pop 2 items from stack

mul $v0, $a0, $v0 # multiply to get result

jr $ra # and return

.data

str1: .asciiz "Dame un numero: "

str2: .asciiz "El factorial del numero "

str3: .asciiz " es : "

str4: .asciiz "\n\nFin del programa, Adios . . ."

Para probar el programa se obtuvo el factorial de 8:

Ejemplo 4: Manejo de un arreglo.

En este ejemplo se pretende mostrar como un arreglo de n enteros puede ser ubicado en la

pila. Para ello solo se piden algunos enteros al usuario para luego imprimirlos en la pantalla

en orden inverso.

El código C que realiza las actividades deseadas es: 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);

}

Las variables i, n, A y t se asocian con los registros: $t0, $t1, $t2 y $t3 (este procedimiento

es aislado).

El código MIPS correspondiente al código C anterior es: # Programa que maneja un arreglo

# imprimiendo los elementos en orden inverso

main:

li $v0, 4 # El Primer Mensaje

la $a0, str1

syscall

li $v0, 4 #Pide el tamaño del arreglo

la $a0, str2

syscall

li $v0, 5 # Lee el tamaño

syscall

add $t1, $v0, $0 # El tamaño del arreglo se guardo en $t1

mul $t4, $t1, 4 # $t4 para el total de bytes

sub $sp, $sp, $t4 # Hace espacio en la pila

addi $t2, $29, 0 #$t2 apunta al comienzo del arreglo

# Primer ciclo 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 # Se pide el número

la $a0, str3

syscall

li $v0, 1 # Imprime el índice del numero

add $a0, $0, $t0

syscall

li $v0, 4 # imprime los dos puntos

la $a0, str4

syscall

li $v0, 5 # Toma el valor de la consola

syscall

sw $v0, 0($t3)

addi $t0, $t0, 1 # i ++

addi $t3, $t3, 4 # t ++

j for1 # regresa al inicio del ciclo

fin_for1:

li $v0, 4 # Mensaje de aviso

la $a0, str5

syscall

# Inicia el segundo for

addi $t0, $0, 0 # i = 0

addi $t4, $t1, -1 # $t4 = n - 1

mul $t4, $t4, 4 # $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) # Carga el número

li $v0, 1

add $a0, $0, $t6 # Imprime el número

syscall

li $v0, 4 # retorno de carro

la $a0, str6

syscall

addi $t0, $t0, 1 # i ++

addi $t3, $t3, -4 # t --

j for2

fin_for2:

mul $t4, $t1, 4 # $t4 <- Bytes requeridos en la pila

add $sp, $sp, $t4 # Libera el espacio solicitado

jr $ra # fin de la función main

# Cadenas del programa

.data

str1: .asciiz " Manejo de un arreglo \n"

str2: .asciiz " Indica el tama~o del arreglo : "

str3: .asciiz " Dame el numero "

str4: .asciiz " : "

str5: .asciiz "\n Los numeros en orden inverso son: \n "

str6: .asciiz "\n "

Los resultados arrojados en la consola para una corrida del programa son:

6. Configuración del programa PSIM

El programa PSIM permite configurar algunos parámetros; la consola de configuración se

encuentra en la opción settings del menú simulator, y tiene la siguiente forma:

En la ayuda del programa se describe el objetivo de cada una de las opciones de

configuración. El programa funciona correctamente con los valores preestablecidos.

7. Ejercicios:

i. Realice un programa que solicite 3 números e indique cual es el menor de ellos.

ii. Realice el programa principal para la función SORT y simúlelo, recuerde que debe incluir

la función SWAP, el número de elementos deberá ser solicitado al usuario (sug. Utilice la

pila para almacenar el arreglo).

iii. 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).

iv. Realice un programa que evalúe la función de Fibonacci desarrollada en la tarea anterior.