UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA ELO312 Laboratorio de Estructuras de Computadores Simulación de programas en assembler MIPS. Uso de SPIM. Objetivos. • Dominar el uso de un simulador assembler. • Observar cómo las directivas assembler establecen las variables en la memoria. • Estudiar en detalle el significado de los diferentes tipos de instrucciones, ejecutando instrucciones y observando cómo se modifican los registros. • Analizar la ejecución de instrucciones, observando el flujo de ejecución, cuando existen saltos y bifurcaciones. • Observar la creación de frames en el stack, cuando se invoca a funciones. Analizando la forma de pasar parámetros, la forma en que se direccionan las variables locales y argumentos, los registros que deben salvarse, fijándose en los cambios en la zona de memoria asignada al stack. • Utilizar llamados al sistema para entrada y salida. • Diseñar interfases assembler para programas escritos en un lenguaje de alto nivel. Preparación previa. La instalación de la aplicación (versión 6.3) se describe en aplicaciones/spim/spinwin.pdf. Se muestra como fijar las opciones y sus significados, se describe las ventanas y cómo cargar y ejecutar un programa asembler. Se dispone del apéndice del texto guía como manual de referencia, y de algunas referencias en español(tutorial y ejemplos). Se tiene una versión html del assembler que permite navegar selectivamente y encontrar(en una referencia rápida) las definiciones de las directivas, los llamados al sistema y los diferentes tipos de instrucciones. También se tiene una versión pdf. a) Cargar en el simulador Spim, el texto assembler generado por lcc. Observando la dirección de inicio de la zona de texto y de la zona de datos. Observar en la ventana de datos la información que se despliega en las columnas y la numeración de los renglones. Cómo y donde se almacenan los números enteros. ¿Cuándo debe emplearse la directiva .align 2 y cuando .align 0?. ¿Qué dirección se guarda en p?. El valor ascci decimal de la h es 104, y su valor hexadecimal es 68, ¿cuál es el orden de los bytes dentro de la palabra?. ¿En qué dirección guarda el nulo del fin del string hola?. ¿Existe diferencia en la forma de almacenar un int y un long?. Observar el almacenamiento de la estructura m1. ¿Dónde empieza el código de main?. Notar que existe un código de arranque que invoca a la función main, y que luego se invoca al sistema para salir de la simulación. Específicamente cuál es el código assembler para la función main? Prof. Leopoldo Silva Bijit. Lab. 03 02-08-2002 10 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA ELO312 Laboratorio de Estructuras de Computadores int i1= 0, i2= 1, i3 = 2; int arr[5]={0,1, 2, 3, 4}; char ch='A'; int brr[]= {3,2,1,0}; char *p = "hola"; long int lin = 5L; /*no puede usarse li como identificador ¿porqué? */ struct molde { char c; int x; float f; } m1 = { ‘c’, -5, 3.2}; int main(void) { int j1, j2; return(0); } b) Cargar el siguiente programa en el simulador. Observar el argumento de .text y analizar que sucede si se cambia a 0x00400040. Analizar la información que el simulador despliega en las columnas de la ventana de texto. Cómo se implementa la macro instrucción assembler li. Observar cómo cambia PC(en la ventana de registros), y la instrucción que se va a ejecutar(en la ventana de texto), a medida que se ejecutan paso a paso las instrucciones con F10. Analizar la ejecución del add, observando antes y después la ventana de registros. Cambiar los valores iniciales de los registros para que se ejecuta o no la bifurcación beq. Cambiar la instrucción add por otra operación observando la actividad en los registros. .text 0x00400020 main: li $t1,4 li $t2,0 li $t3,0 add $t1,$t2,$t3 beq $t4,$t1,exit j main exit: add $t1, $t1, $t2 c) Llamados al sistema. Cargar el siguiente programa en el simulador. ¿ En la ejecución del programa cómo se termina el ingreso de un entero? Dónde almacena el número entero ingresado el llamado al sistema. .data Prof. Leopoldo Silva Bijit. Lab. 03 02-08-2002 11 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA ELO312 Laboratorio de Estructuras de Computadores mensaje: entero: .asciiz "\nentre un entero = " .word 1 .text .globl main main: li $v0,4 la $a0, mensaje syscall #imprime string li $v0,5 syscall #ingresa entero la $t0, entero sw $v0,0($t0) li $v0,10 syscall #salida. Con estos segmentos puede probarse algoritmos en forma interactiva. Modificar el segmento, para hacerlo repetitivo y salir de la repetición cuando se ingrese 0(por ejemplo). d) Agregar rutinas de biblioteca de usuario. El siguiente programa calcula el producto escalar de dos vectores. compilando y ejecutando el programa en Windows. #include <stdio.h> int ip(int *x, int *y, int n) { int i,sum; for(i=0,sum=0; i<n; i++, x++, y++) sum += (*x)*(*y); return sum; } int main(void) { int x[] = {2,4,3,5,1}; int y[] = {3,3,2,5,4}; int n = 5; printf("%d El producto escalar es: \n" ,ip(x,y,n)); return(0); } Comprobar esto Quitar la primera línea con el include y ensamblarlo en MIPS empleando lcc. Luego agregar la rutina para printf que se ilustra a continuación y simular en SPIM. También es preciso agregar el String con el texto: El producto escalar es:. Prof. Leopoldo Silva Bijit. Lab. 03 02-08-2002 12 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA ELO312 Laboratorio de Estructuras de Computadores Debe cuidarse el paso de parámetros entre el programa y la rutina, así como la ubicación del código(con la directiva .text) y la ubicación del string dentro del archivo(con la directiva .data). La siguiente es una rutina fuente que podría formar parte de una biblioteca de usuario. #**********************************************************# # printf("$a0 %d",$a1); #**********************************************************# #imprime mensaje apuntado por $a0, seguido de numero decimal en $a1 printf: addiu $sp, $sp,-12 sw $ra, 0($sp) #crea espacio del frame de 12 bytes. #salva dirección de retorno #debug para ver argumento decimal de printd en stack sw $a0, 4($sp) #salva arg sw $a1, 8($sp) #salva a1 en frame li $v0, 4 syscall move $a0, $a1 li $v0, 1 syscall #imprime string en $a0 #macro #imprime decimal en $a0 lw $ra, 0($sp) addiu $sp, $sp, 12 jr $ra #y retorna. Las reglas y requerimientos del paso de parámetros que existen entre rutinas diseñadas en assembler y otros lenguajes son variadas y en general complejas. Un método experimental simple para diseñar interfases entre una rutina assembler y el código generado, para un programa de alto nivel, por un compilador es el siguiente: En el lenguaje de alto nivel escribir la función, con sus argumentos y tipo de retorno, y sus variables locales. Luego compilar, empleando la opción –S, para generar un archivo assembler. Finalmente estudiar el listado generado para la función, para descubrir, en forma experimental, las convenciones y reglas que usa el compilador, facilitando el diseño de la rutina. Prof. Leopoldo Silva Bijit. Lab. 03 02-08-2002 13 UNIVERSIDAD TECNICA FEDERICO SANTA MARIA DEPARTAMENTO DE ELECTRONICA ELO312 Laboratorio de Estructuras de Computadores En el Laboratorio. a) Dar respuesta a las preguntas a las preguntas de la preparación b) Mostrar la ejecución de corrimientos aritméticos y lógicos. c) Escribir programa en assembler que permita en forma repetitiva, ingresar un entero entre 1 y 12, y que se imprima un string con el mes asociado al entero. Si es un entero fuera de rango, debe indicar cuáles son las entradas válidas. El programa termina cuando se ingresa un cero. Prof. Leopoldo Silva Bijit. Lab. 03 02-08-2002 14