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