EJEMPLOS DE USO DEL SIMULADOR MIPS Antes de empezar La siguiente tabla muestra material complementario que puede ser revisado para comprender mejor los ejemplos tratados en la presente guía: Documento MARS Tutorial SYSCALL MIPS_Green_Sheet firstProgs shapesCode MIPS_3000_Assembly_Pro gramming_Part2 FirstProgram R3000 MIPS-asm Descripcion Documento que explica cómo hacer uso del simulador MARS. Documento que contiene todas las llamadas a sistema implementadas en el simulador. Tabla que contiene un resumen de las instrucciones del MIPS. Archivo con algunos ejemplos de codificación de programas en el MIPS Archivo con algunos ejemplos de codificación de funciones en el MIPS Diapositivas que resumen muy bien las instrucciones más frecuentemente usadas en los programas Diapositivas que muestran los pasos a seguir para hacer un programa en el mips. Diapositivas que hablan de manera resumida sobre el procesador mips R3000. Tutorial excelente con diferentes ejemplos del mips. Ejemplos 1. Hacer un programa que solicite dos números enteros por teclado y realice la suma de estos. Solución: La solución a este problema se realiza en el archivo ejemplo1.asm adjunto con el presente documento. La siguiente figura muestra el programa en ejecución: Si analiza el programa anterior notara que en este caso se suelen hacer llamadas al sistema, esto con el fin de permitir la interacción del programa con el usuario. Para realizar llamadas al sistema se emplea la instrucción syscall. Cuando se realiza una llamada al sistema se suspende la ejecución del programa y se transfiere el control al sistema operativo, luego el sistema operativo mira el contenido del registro $v0 para determinar cuál llamada del sistema se está haciendo y procede a ejecutar las instrucciones asociadas a esta. Las llamadas de sistema de simulador, no son llamadas reales pues no transfieren el control a un sistema operativo UNIX por ejemplo, en el caso, el control es trasferido a un sistema operativo simulado muy simple que es parte del simulador. En este caso, se hace uso de 3 diferentes llamadas a sistema, tal y como se muestra a continuación: Llamada a sistema para imprimir una cadena de caracteres: esta corresponde a la llamada a sistema número 4, se pasa como argumento de entrada la cadena de caracteres a imprimir: Llamada a sistema para imprimir un numero entero: Corresponde a la llamada número 1, se pasa como argumento el entero a imprimir: Llamada a sistema para imprimir un solicitar un numero entero por teclado: Corresponde a la llamada número 5, no tiene argumentos de entrada; retorna en el registro $v0 el entero ingresado por teclado: Cuando una función retorna algo, suele ser muy común llevar este valor retornado a otro registro ya que en posteriores llamados a funciones el registro $v0 será sobrescrito. Llamada a sistema terminar el programa: Esta llamada es análogo a la función exit presente en lenguajes de alto nivel, corresponde a la llamada número 10, se invoca para terminar el programa: V Si observa la invocación a las llamadas a sistema notara algo y es que todas se llaman de manera similar. Para esto, se siguen básicamente los siguientes pasos: Pasar lo parámetros a la función: Esto se hace moviendo valores a los registros del mips para el paso de funciones ($a0, $a1, $a2, $a3). Es necesario conocer como se invoca la función para pasar los valores apropiados. V Llamar la función: Esto se hace cargando en el registro $v0, el valor asociado a la llamada del sistema y posteriormente invocándola con la instrucción syscall. V Capturar el valor retornado por la función: Aquellas funciones que retornan algo, suelen hacerlo en los registros $v0 y $v1, por eso suele ser de bastante utilidad llevar los valores contenidos en estos registros a otros registros ($t0,…,$t9,$s0,…,$s7) ya que en posteriores llamados a otras funciones los valores de los registros $v0 y $v1 serán sobrescritos. V La siguiente figura muestra las principales llamadas a sistema empleadas: 2. Hacer un programa que solicite dos números enteros por teclado y realice la suma de estos. Solución: La solución a este problema se realiza en el archivo ejemplo2.asm adjunto con el presente documento. La siguiente figura muestra el programa en ejecución: Observe que en este caso para solicitar una cadena de caracteres, el programa anterior hace uso de la llamada a sistema número 8 a la cual se le pasan como argumentos la cadena a la cual se llevara lo que se meta por teclado ($a0) y el número máximo de caracteres a leer ($a1). La siguiente figura muestra la sección de código en la cual se hace esto: Para declarar arreglos en el Mips se suele usar la directiva .space seguida del número de bytes que contendrá el arreglo. Para el caso anterior se declaró un arreglo llamado nombre y se la asigno un tamaño de 50 bytes. La siguiente figura muestra las principales directivas empleadas: 3. Hacer un programa muestre el mensaje el ciclo se repite hasta que el usuario presione la tecla q. Solución: La solución a este problema se realiza en el archivo ejemplo3.asm adjunto con el presente documento. La siguiente figura muestra el programa en ejecución: Para hacer que el programa repita de manera reiterada la solicito para el ingreso de datos se debe implementar un ciclo, esto se hace por medio de instrucciones de salto ya sea condicional o incondicional. A continuación se resalta la parte que implementa el ciclo en el programa anterior: Ciclo Instrucción de salto condicional Instrucción de salto incondicional 4. Modifique el programa anterior de manera que retorne la suma de varios números positivos hasta que el usuario meta un número negativo. Solución: La solución a este problema se realiza en el archivo ejemplo4.asm adjunto con el presente documento. La siguiente figura muestra el programa en ejecución: No hay diferencia sustancial respecto al programa anterior, básicamente la estructura es la misma. 5. Hacer un programa que permita realizar la suma de los elementos de un vector (A[]={1,2,3,4,5,6,7}) de 7 elementos el cual se encuentra en memoria. El resultado debe ser llevado a una variable y almacenado en memoria Solución: La solución a este problema se realiza en el archivo ejemplo5.asm adjunto con el presente documento. La siguiente figura muestra el programa en ejecución: Para resaltar del programa anterior, cada vez que se declara una variable a esta se asocia un espacio en memoria, la figura anterior las direcciones de memoria y los contenidos del arreglo A y las variables suma e i. Recuerde que la mayoría de las operaciones se hacen en los registros del procesador ($t0,$s0,…) por lo tanto para poder acceder a estos lugares de memoria es necesario usar las instrucciones de transferencia de datos entre memoria y procesador; esto con el fin de leer, en cuyo caso se usan las operaciones load; o escribir, para lo que se usa las operaciones store. Otra cosa de vital importancia es que para acceder a cada uno de los elementos del arreglo se deben realizar operaciones para cargar cada uno de los elementos de la matriz a un registro del procesador para realizar diferentes operaciones. A continuación se resalta la porción de código en la cual se hace dicha tarea: Note que el acceso al vector se hace de manera indirecta. En este caso, para almacenar el contenido de los registros en las diferentes variables (las cuales se encuentran en memoria) se hace uso de la instrucción store tal y como se muestra a continuación: 6. Mejorar el problema anterior, de modo que el usuario pueda meter ingresar los 6 elementos del vector por teclado, luego visualizar el contenido del vector y finalmente mostrar el contenido de la suma Solución: La solución a este problema se realiza en el archivo ejemplo6.asm adjunto con el presente documento. La siguiente figura muestra el programa en ejecución: 7. Mejorar el problema anterior de tal manera que se pueda que sume los elementos de un vector de tamaño cualquiera. Solución: La solución a este problema se realiza en el archivo ejemplo7.asm adjunto con el presente documento. La siguiente figura muestra el programa en ejecución: Este ejemplo a diferencia del anterior se caracteriza por que hace uso de estructuras dinámicas, es decir variables que se declaran en tiempo de ejecución no de compilación, por dicha razón, no hay ninguna declaración de la variable asociada al vector en la sección de datos. Para declarar variables en tiempo de ejecución los lenguajes de programación suelen hacer uso de alguna función. En el caso del lenguaje C esta función es malloc o sus variantes. Esta función lo que hace es reservar en memoria un número de bytes consecutivos y devuelve como parámetro un apuntador que contiene la dirección inicial de la cantidad de datos reservados. En el ensamblador del mips existe una llamada al sistema la cual hace esta tarea y corresponde a la llamada número 9. La siguiente tabla muestra un ejemplo comparativo de como se reserva dinámicamente memoria en C y en ensamblador. int *p; // Apuntador al cual se va a // llevar la dirección de // memoria obtenida // dinámicamente. p = malloc(4*sizeof(int)); // Declaración de un // array de 4 enteros (reserva // de 16 bytes de memoria). # En $t0 se llevara la dirección # retornada por la función, este será # análogo al apuntador p li $a0,16 li $v0,9 syscall move $t0,$v0 Cuando se usa la función del sistema para la asignación dinámica de memoria las variables declaradas se almacenan en el heap. La siguiente grafica muestra el efecto en memoria al llamar esta función: Para este caso se usó la llamada del sistema para declarar el vector al cual se llevaran los datos. Note que el valor devuelto por la llamada del sistema es la dirección base de la región asignada (0x1004000 para este caso), este valor es llevado en nuestro ejemplo al registro $s0 con el fin de poder acceder a dicha región para lectura y escritura. 8. Realice el programa del problema 6 pero impleméntelo haciendo uso de las siguientes funciones: meterVector: Esta función deberá al usuario permitir ingresar cada uno de los elementos del vector. mostrarVector: Esta función deberá mostrar el vector en pantalla. sumarElementos: Esta función debe devolver la suma de los elementos del vector. Solución: La solución a este problema se realiza en el archivo ejemplo8.asm adjunto con el presente documento. La siguiente figura muestra el programa en ejecución: El código de las funciones se muestra a continuación: Si observa el código del ejemplo notara que las funciones se definen después del main, similar a como se suele hacer en muchos lenguajes de programación. Como se puede notar se hace uso de labels para dar nombres a las funciones. Así mismo note el empleo de la instrucción jr $ra, la cual es necesaria para retornar al programa principal después de que se invoca la función. A continuación se muestra el programa principal y la forma como se invocan en este las diferentes funciones: Invocación de la función Note que para invocar la función, se deben primero llevar los valores a los argumentos de entrada y posteriormente haciendo uso de la instrucción jal se invoca la función. 9. Realice el mismo problema anterior, pero en esta ocasión divídalo en dos archivos lo cuales serán: Ejemplo9.asm: Este archivo contendrá la función principal (main). Funciones.asm: Este archivo contendrá la implementación de las funciones empleadas. Solución: Para solucionar este problema lo primero que se debe hacer es crear una nueva carpeta para nuestro caso la carpeta se llama ejemplo 9. Luego en el editor se crean los dos archivos (ejemplo9.asm y funciones.asm). Una vez se codifica el código de estos se procede a ensamblar los códigos. Antes de hacer esto debemos asegurarnos que la opción para ensamblar todos los archivos que hay en el directorio este habilitada; la siguiente figura muestra esto: Para que una función pueda ser vista en otro archivo diferente a aquel en el cual se implementó se debe hacer uso de la directiva .globl. La siguiente figura muestra trozo del archivo funciones.asm en la cual se hace uso de esta directiva. Note que este archivo contiene solo la implementación de las funciones. En lo que respecta al código del archivo ejemplo9.asm, en este, se implementa la función main y se hacen los llamados a las funciones declaradas en el archivo funciones. .globl también puede ser empleado con el fin de lograr que una variable pueda ser vista por todos los archivos, recuerde, esta directiva se debe usar en el archivo en el cual se realizó la declaración de la variable o la función. La siguiente figura muestra el uso de esta directiva con variables, en este ejemplo estas variables tuvieron que ser declaradas globales ya que son accedidas por las funciones las cuales se encuentran en un archivo diferente: La siguiente figura muestra el programa en ejecución: Para poder ensamblara el programa se debe estar parado en el archivo que contiene la función principal (ejemplo9.asm) para que el programa pueda ejecutar apropiadamente. 10. Hacer las siguientes funciones usando lenguaje ensamblador del mips: mostrarElemento: Esta función muestra devuelve un elemento de la matriz ubicado en la posición i, j. sumarFila: Esta función devuelve la suma de los elementos de una fila seleccionada. sumarColumna: Esta función devuelve la suma de los elementos de una columna dada. sumarElementos: Esta función retorna la suma de todos los elementos de la matriz. Pruebe las funciones sobre la siguiente matriz de 3x4. Solución: A continuación se muestra el resultado de la ejecución del programa. Para realizar el presente programa; se creó una nueva carpeta y en esta se crearon dos archivos, funciones.asm y ejemplo10.asm, a continuación se muestra como se declara una matriz y como esta se guarda en memoria: Si observa la gráfica anterior notara que una matriz es almacenada en memoria llevando fila por fila; conocer como se almacena una matriz es de vital importancia para poder acceder a cada uno de los elementos de esta. A continuación se muestra el código de la función sumarElementos: Push del stack Pop del stack Como la función sumar elementos invoca a la función sumarFila es necesario guardar el contexto de esta en el stack para que el programa no pierda el hilo de la ejecución y esto se hace por medio de operaciones push y pop sobre el stack. 11. El siguiente programa muestra cuando se ejecuta crea un nuevo archivo llamado testout.txt con la siguiente frase: The quick brown fox jumps over the lazy dog. Solucion: El código ejemplo11.asm contiene el programa que realizar esta tarea. Referencias http://www.arcos.inf.uc3m.es/~ec/dokuwiki/doku.php?id=ejemplos https://docs.google.com/viewer?url=http%3A%2F%2Fdac.escet.urjc.es%2Fdocencia%2FETCSuperior%2Fteoria-cuat1%2FTema8b.pdf http://www.arcos.inf.uc3m.es/~ec/dokuwiki/doku.php?id=ejemplos https://www.soe.ucsc.edu/classes/cmpe012/Winter05/labs/Lab7.htm http://www.utdallas.edu/~herman.harrison/mips/samples_webct/samples_webct.html http://www.johnloomis.org/ece533/notes/spim/SPIMQuickReference.html http://www.scss.tcd.ie/John.Waldron/itral/cahome.html http://www.neuro.gatech.edu/groups/butera/Courses/2030/ http://courses.missouristate.edu/KenVollmar/Mars/Help/SyscallHelp.html http://www.utdallas.edu/~hxh017200/cs3340/Spr11/cs3340_001.html http://www.cs.sunysb.edu/~cse320/example.html http://www.cs.ucsb.edu/~pconrad/ccs/cs1a/09F/lectures/1124/ http://www.cse.hcmut.edu.vn/~anhvu/teaching/2010/BTKT-KTMT/MIPS%20Green%20Card.pdf http://www.cs.sunysb.edu/~cse320/