0% encontró este documento útil (0 votos)
420 vistas18 páginas

Hanoi

Este documento presenta una práctica de programación en ensamblador MIPS. La práctica incluye tres secciones: 1) Un repaso del entorno de simulación MIPS utilizando un programa que verifica si una cadena es un palíndromo. 2) Programación de subrutinas en MIPS. 3) Programación recursiva para resolver el problema clásico de las Torres de Hanoi utilizando MIPS. El documento proporciona el código fuente de los programas y ejercicios para cada sección con el objetivo de profundizar en conceptos de program

Cargado por

Ismael Monroe
Derechos de autor
© Attribution Non-Commercial (BY-NC)
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
420 vistas18 páginas

Hanoi

Este documento presenta una práctica de programación en ensamblador MIPS. La práctica incluye tres secciones: 1) Un repaso del entorno de simulación MIPS utilizando un programa que verifica si una cadena es un palíndromo. 2) Programación de subrutinas en MIPS. 3) Programación recursiva para resolver el problema clásico de las Torres de Hanoi utilizando MIPS. El documento proporciona el código fuente de los programas y ejercicios para cada sección con el objetivo de profundizar en conceptos de program

Cargado por

Ismael Monroe
Derechos de autor
© Attribution Non-Commercial (BY-NC)
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 18

ESTRUCTURA Y TECNOLOGA DE COMPUTADORES

Ingeniera Tcnica de Informtica de Gestin

CURSO 2005-2006

Prctica 4: Repaso de programacin en ensamblador MIPS

David Miraut Andrs


Escuela Superior de Ciencias Experimentales y Tecnologa

Universidad Rey Juan Carlos

Did you know...? If you have a digital cable set-top box, chances are it's MIPS-based. If you have a video game console, it's probably MIPS-based. Your email very likely travels through a MIPS-based Cisco router. Your company's laser printers are probably powered by 64-bit MIPS-based processors. De la Web de MIPS Technologies

Objetivo de la prctica
El objetivo de esta prctica consiste en profundizar en los conceptos de programacin a bajo nivel explicados en clase, una vez ya nos hemos familiarizado con programas sencillos como los vistos en la prctica anterior1. De nuevo haremos hincapi en el efecto de las instrucciones sobre la memoria y los registros de la CPU, tratando de comprender mejor el porqu de lo que sucede. En concreto, trataremos los siguientes temas: Repaso del entorno del simulador y el juego de instrucciones del procesador. Programacin de subrutinas con el ensamblador. Programacin recursiva en MIPS. Manejo de cadenas. Para hacer esta prctica utilizaremos nuevamente la aplicacin MipsIt Studio 2000 (MipsIt.exe) y el simulador MipsSim (Mips.exe), en los que podemos escribir los programas en ensamblador y realizar su simulacin2. IMPORTANTE: En esta prctica habr que entregar una memoria escrita en la que se respondan claramente a las preguntas y cuestiones propuestas en cada apartado. Ser absolutamente obligatorio comentar los resultados obtenidos.

ndice
Objetivo de la prctica............................................................................................................ 2 ndice ...................................................................................................................................... 2 1. Repaso del entorno de Simulacin ..................................................................................... 3 1.1 Ejercicios de la primera parte de la memoria ............................................................... 4 2. Programacin de subrutinas en el ensamblador MIPS ....................................................... 6 2.1 Ejercicios de la segunda parte de la memoria............................................................... 8 3. Programacin recursiva con MIPS: Caso de las Torres de Hanoi...................................... 9 3.1 La leyenda .................................................................................................................... 9 3.2 Recursividad en las Torres de Hanoi.......................................................................... 10 3.3 Ejercicios de la tercera parte de la memoria............................................................... 13 Apndice A: Cdigo fuente de los programas...................................................................... 14 Cdigo fuente de palindromo.s................................................................................. 14 Cdigo fuente de hanoi.c............................................................................................. 16 Cdigo fuente de hanoi.pas ........................................................................................ 16 Cdigo fuente de hanoi.s............................................................................................. 17
1 2

La tercera del primer cuatrimestre: Introduccin a la programacin en el ensamblador MIPS Si no recuerdas de dnde bajarlo cmo instalarlo, te invitamos a que consultes el Apndice B de la prctica anterior.

1. Repaso del entorno de Simulacin


Para calentar motores vamos a ejecutar un sencillo programa y con l repasaremos las distintas partes del simulador, los mecanismos de control de flujo, la utilizacin de cadenas y buena parte de las instrucciones vistas en clase. El primer programa que vamos a estudiar (cuyo cdigo se encuentra dentro de dos pginas) se llama palindromo.s , tal como su nombre indica es un programa que nos permite comprobar si una determinada cadena forma un palndromo. Los palndromos son aquellas palabras, frases o nmeros que se pueden leer igual del derecho que del revs (en el caso de los nmeros tambin se les llama capicas). Un par de ejemplos bien conocidos son: Dbale arroz a la zorra el abad La ruta natural. Nuestro programa de partida examinar sistemticamente la cadena mirando y comparando los caracteres a ambos extremos, y avanzando hacia el centro en ambos lados a cada paso.
NOTA: Si en el ordenador en el que os encontris no est instalado el ensamblador /

simulador de MIPS, podis bajarlo de: http://dac.escet.urjc.es/docencia/ETC-Gestion/practicas-cuat2-06/ Basta descomprimirlo en un directorio para poder utilizarlo. Tened cuidado con el path del directorio, porque no es capaz de manejar directorios con nombres largos ni caracteres especiales. En la misma pgina web se encuentra este enunciado y el cdigo fuente de los programas de esta prctica Repaso de programacin en Ensamblador MIPS (7 de Marzo). Es necesario iniciar tanto el ensamblador/editor (MipsIt.exe) como el simulador (Mips.exe). En el primero abriremos el proyecto palindromo, que se encontrar en los directorios dnde hayamos descomprimido el cdigo de los programas de la prctica. Lelo y trata de comprenderlo, en especial el funcionamiento de los bucles. Una vez te hayas hecho una idea de su funcionamiento complalo (F7) y lnzalo al simulador (F5) para ejecutarlo y ver el flujo de control paso a paso. El listado del cdigo comienza con una serie de comentarios (los comentarios vienen precedidos por el carcter almohadilla (splash) # en este ensamblador). Los comentarios tienen una gran importancia en los programas, y esta se vuelve crtica en el caso de los escritos en lenguaje ensamblador debido a la complejidad de comprensin que tienen los proyectos de tamao medio. En este listado se ha omitido la explicacin de algunas ideas que se pedirn en los siguientes ejercicios. En la parte inicial de la prctica anterior (con el programa Hola Mundo) se explic que las cadenas se suelen encontrar en el segmento de datos, que es un segmento distinto al del cdigo ejecutable3, a pesar de que el nombre de este ltimo se preste a confusin al ser el segmento de texto.
3

Si bien es posible meter ste tipo de datos en la zona ejecutable, es una prctica poco recomendada.

Mediante directivas podemos especificar dnde queremos guardar cada elemento (cdigo, datos, partes del ncleo de sistema). Por defecto, el ensamblador prepara el cdigo para que el simulador inicie la ejecucin en el segmento de texto/cdigo y si no lo indicamos expresamente lo tomar todo como tal. En nuestro caso, el segmento de datos ser todo aquello que se encuentre entre la directiva .data y .text y por tanto se colocar en ste. Como puede leerse en el cdigo, se reserva e inicializa el espacio para ellas con las directivas .asciiz (para las cadenas terminadas en cero, tpicas de rutinas de lenguaje C) y .ascii (para cadenas con otra terminacin). Tambin existen otras directivas con las que se pueden almacenar datos en este segmento como .byte o .word pero no las utilizaremos por ahora. As pues, por simplicidad, nuestro programa tiene escrito en duro la palabra que vamos a analizar en la cadena posible_palindromo. Puedes cambiarla por otras expresiones4 palindromas o por otras que no lo sean para comprobar su funcionamiento. Una vez comienza el segmento de texto se pueden distinguir tres partes funcionales en el programa: Clculo de las direcciones de memoria de los extremos de la cadena para los punteros Recorrido de estos punteros hacia el centro de la cadena, comparando el contenido de sus posiciones de memoria para comprobar si se trata de un palndromo o no de acuerdo a su simetra Salida por pantalla del resultado de la comparacin Cuando hayis ejecutado unas cuantas veces el programa, leedlo ms detenidamente (con ayuda de la chuleta de instrucciones anexa al enunciado de esta prctica). Ejecutad paso a paso el programa tratando de comprender cada uno de sus detalles: cambios en los registros de la CPU, correspondencia de los saltos y condiciones con las estructuras de control y bucles tpicos, flujo de ejecucin del programa (podis ayudaros de la opcin PC tracking) tal y como se explic con el entorno del simulador en la prctica anterior.

1.1 Ejercicios de la primera parte de la memoria:


1. Comprobar dnde se guarda la cadena de texto en el resultado ensamblado. Explicar porqu. 2. Tras las instrucciones de salto suele aparecer una instruccin NOP a qu se debe? 3. Cita al menos 3 pseudoinstrucciones y su correspondiente cdigo ensamblador real. 4. Explcalos algoritmos y dibuja el diagrama de flujo del programa, con especial cuidado en la correspondencia de saltos y condiciones con las estructuras tpicas de control. 5. Los verdaderos palndromos no tienen en cuenta los espacios, maysculas y signos de puntuacin. Modifica el programa para que soporte espacios (supondremos que no escribimos con tildes y todo esta en minsculas). Aydate de una tabla ASCII. Incluye el cdigo de este nuevo programa junto con la memoria con el nombre: palidromo2.s
4

En Espaol tenemos una gran cantidad de expresiones palindrmicas, se han contabilizado ms de 36.000, puedes encontrar numerosos ejemplos en: http://www.carbajo.net/varios/pal.html

################################################################################ # Fichero: palidromo.s # Descripcin: Verifica si una expresin forma un palidromo # Autor: David Miraut # Asignatura: ETC-II (ITIG) - URJC # Fecha: Febrero 2005 # Lenguaje: MIPS-assembler, GNU/AT&T-syntax # Web: http://dac.escet.urjc.es/docencia/ETC-Gestion/ ################################################################################ ## Uso de los registros: ## t1 - Direccin del extremo inicial de la cadena ## t2 - Direccin del extremo final de la cadena ## t3 - Carcter en el extremo inicial de la cadena ## t4 - Carcter en el extremo final de la cadena # Incluimos los alias de los registros #include <iregdef.h> ### Segmento de datos Ejemplos .data de directivas .globl posible_palindromo posible_palindromo: .asciz "reconocer" # Hay miles de palindromos, puedes probar unos cuantos # cambiando el valor de la cadena para comprobar el # func ionamiento del programa (como: rapar, orejero...) msg_positivo: .asciiz "La cadena <<%s>> es un palindromo.\n" msg_negativo: .asciiz "La cadena <<%s>> no es un palindromo (%c) != (%c).\n" ### Segmento de texto (programa) .text # Comienzo de seccion de codigo de usuario .align 2 # Alineamiento en formato palabra (multiplo de 4) .globl main # La etiqueta "main" se hace conocida a nivel global .ent main # La etiqueta "main" marca punto de entrada un

Segmento de datos

Cadenas terminadas en carcter nulo

main: la t1, posible_palindromo # Colocarnos en el extremo inicial es fcil ## Lo ideal sera tener una funcin a parte que contara la longitud de la ## cadena para luego posicionar un puntero (t2) al final de sta ## En esta versin inicial del programa lo calculamos dentro de main la t2, posible_palindromo # Lo ponemos al principio # y lo movemos hacia el final bucle_longitud: lb t3, (t2) # Carga en t3 el byte al que apunta t2 beqz t3, fin_bucle_long # si t3 es igual a 0, hemos llegado # al final de la cadena addu t2, t2, 1 # sino nos movemos un caracter en la cadena b bucle_longitud # y repetimos el bucle. fin_bucle_long: subu t2, t2, 1 # La cadena adems de terminar en el caracter # nulo tiene dos caracteres que no nos interesan # De modo que nos movemos "2 bytes" hacia atrs

Ejemplos de directivas

Segmento de texto (cdigo)

Hace el clculo para poner uno de los punteros en el extremo final de la cadena

bucle_pal indromo: bge t1, t2, es_palin # Condicin de final de bucle (explicar) lb t3, (t1) # Carga el byte en el extremo inicial (desplazado) lb t4, (t2) # Carga el byte en el extremo final (desplazado) bne t3, t4, no_es_palin # Condicin de final de bucle (explicar) # Si no se sale del bucle (explicar) addu t1, t1, 1 # Desplazamos el extremo inicial subu t2, t2, 1 # Desplazamos el extremo final j bucle_palindromo # Y repetimos el bucle es_palin: # Imprime el mensaje la a0, msg_positivo la a1, posible_palindromo jal printf j fin # Saltamos a la rutina de salida para terminar no_es_palin: la a0, msg_negativo la a1, posible_palindromo move a2, t3 move a3, t4 jal printf j fin fin: jal .end # Imprime el mensaje

Comprueba que se trata de una expresin palindrmica

Imprime el mensaje con el resultado

# carteres "responsables" # de la falta de simetra # Saltamos la rutina de salida para terminar a # finaliza el programa (explicar su importancia)

_exit main

# Final de la seccion "main

2. Programacin de subrutinas en el ensamblador MIPS


A estas alturas los lectores de esta prctica ya habrn comprobado que la programacin en ensamblador se vuelve tediosa y catica si no se tiene orden y cuidado, an ms si lo que buscamos es un error en el cdigo (debugging) de otra persona, o de nosotros mismos pasado un tiempo. Los lenguajes de alto nivel como el lenguaje C- dan una serie de facilidades en forma de abstracciones que simplifican mucho la labor del programador. La nocin de funcin supuso una revolucin5 y un gran avance en los aos 70, ya que nos permite dividir el problema en partes ms fciles de abordar (divide y vencers). Estas ideas pueden ser aprovechadas igualmente en lenguajes de bajo nivel (como el ensamblador de procesadores MIPS) siendo metdicos y atendiendo a una serie de convenciones entre desarrolladores, y de esta forma crear un mecanismo similar, que nos permita: Hacer una correspondencia entre los parmetros (registros) que tienen la informacin que queremos utilizar y los parmetros formales que por convencin utilicemos en este mecanismo. Reserva e inicializacin de almacenamiento temporal. Esto es particularmente interesante si queremos que nuestros programas puedan hacer uso de recursin (como veremos en la seccin siguiente): cada llamada a la funcin debe tener su propia copia de las variables locales, para evitar que se pisen entre ellas. La informacin que describe el estado de una funcin durante su ejecucin (los parmetros que tenga en ese momento incluido la direccin del programa desde la que fue llamada-, los valores de sus variables locales, el puntero a la instruccin que se ejecuta en ese momento) conforman lo que llamaremos el entorno de la funcin. En un programa de ensamblador MIPS el entorno de una funcin viene dado por el valor de todos los registros a los que se hace referencia en la funcin. De forma genrica, justo antes de que una funcin A llame a una funcin B, sta vuelva todo su entorno en la pila y luego ya salta a la funcin B. Cuando la funcin B termina y devuelve el control a la funcin A, sta ltima recupera su entorno extrayndolo de la pila. Si bien las funciones de los procesadores MIPS pueden ejecutarse con cualquier registro, los programadores y desarrolladores han llegado a una especie de convenio implcito para el uso de los registros, de modo que el cdigo que escriban sea compatible y respetuoso con el de los dems en entornos de programacin funcional con o sin sistema operativo. En el tema 12 se explicaron esta serie de conceptos en una transparencia que reproducimos en la tabla.
5

Adems, en aquella poca se programaba casi todo en lenguajes de muy bajo nivel (ensabladores) dependientes de la maquina y su arquitectura. De modo que la la falta de lenguajes protables haca que fuera necesario reescribir todo el cdigo cada vez que se cambiaba / actualizaba el hardware.

Registro $0 at v0 v1 a0 a3 t0 t7 s0 s7 t8 t9 k0 k1 gp sp fp ra

Nmero 0 1 23 47 8 15 16 23 24 25 26 27 28 29 30 31

Utilizacin Constante con valor 0 Reservada para el ensamblador Valores devueltos en las funciones Argumentos para funciones Temporales Temporales guardados Temporales Reservados para el ncleo Puntero global Puntero de pila Puntero de Marco Direccin de retorno a la llamada de la funcin

Es necesario respetarlo en las llamadas a funciones? No aplicable No No No No S No No aplicable (en nuestro caso) S S S S

Siendo conservadores en la arquitectura de los procesadores MIPS esto puede realizarse atendiendo a estas reglas: La funcin que llama: o Pone los parmetros en los registros a0, a1, a2 y a3. Si hubiera ms de tres parmetros, stos se colocaran en la pila. o Guardar cualquiera de los registros temporales t0 a t7 que pudiera ser utilizado por la funcin que se va a llamar. o Ejecutar jal (o jalr) para llamar a la funcin (esta funcin se encarga de guardar la posicin del contador de programa en el registro 31 -ra-) La funcin6 llamada, debe antes de ejecutar su cdigo: o Crear un marco de pila7, restando el tamao del marco de la posicin del puntero a pila (sp). Date cuenta que el tamao mnimo de marco en la arquitectura MIPS es de 32 bytes, por tanto an cuando el espacio de memoria que vayamos a utilizar sea menor, deberamos utilizar este tamao. o Guardar en la pila cualquiera de los registros que se deben preservar segn la tabla de arriba (s0 s7, fp y ra), para evitar efectos colaterales con la funcin (o serie de funciones) que ha/n dado lugar a la llamada. Preservar ra es especialmente importante en funciones que se llaman a s mismas (recursivas). o Colocar el puntero de marco en la posicin del puntero de pila ms el tamao del mismo.
6

En el enunciado de esta prctica no se distingue entre el concepto de funcin y procedimiento, aunque la distincin implice ciertas diferencias. 7 Comprobaremos ms adelante, cmo el compilador de C que tenemos integrado en MipsIt.exe utiliza de forma sistemtica ste mecanismo, aunque pueda ser aligerado en casos concretos como nuestros pequeos programas.

La funcin llamada ejecuta su cdigo propiamente dicho. Antes de devolver el control y volver a la funcin que la llamo, la funcin invocada debe: o Poner lo valores de los resultados en los registros v0 (y v1) o Recuperar los registros que se haban guardado en la pila (indicados en la tabla) o Devolver el control a la funcin que la llamo saltando a ra con jr (muy importante no utilizar de nuevo jal) Una vez recupera el control la funcin que haba llamado, debe: o Recuperar los registros que se haban guardado en la pila o Si se haban pasado argumentos en la pila (en vez o adems de a0, a1, a2 y a3) se ha de recolocar su posicin. o Extraer el valor del resultado devuelto en v0 (y v1)

2.1 Ejercicios de la segunda parte de la memoria


1. Por qu no se debe utilizar jal a la hora de regresar a la funcin desde la que hemos sido llamados? 2. Por qu es importante preservar el registro ra cuando tratamos con funciones recursivas? 3. Atendiendo a las reglas enumeradas en esta seccin, reescribir el cdigo del programa de verificacin de palndromos de forma funcional, con al menos dos o tres funciones / subrutinas: o Una funcin main desde la que se pasa la cadena que deseamos comprobar o Una funcin que realiza dicha tarea y que a su vez llama a distintas subrutinas que: Calculan la longitud de la cadena. Verifican que dos caracteres sean iguales o no. Imprimen el resultado de la operacin. Incluye el cdigo de este nuevo programa junto con la memoria con el nombre:
palidromo3.s

3. Programacin recursiva con MIPS: Caso de las Torres de Hanoi


3.1 La leyenda
Cuenta una leyenda milenaria, que en el gran templo de Varanasi, bajo la cpula que seala el centro del mundo, se venera una bandeja de cobre sobre la cual estn colocadas tres agujas de diamante. La historia dice que en el principio de los tiempos un dios hind, en una maana lluviosa, coloc en una de estas agujas sesenta y cuatro discos de oro puro, ordenados por tamaos; desde el mayor, que reposa en la bandeja, hasta el ms pequeo, en lo alto de la aguja. Y lo llam la torre de Brahma. Incansablemente, da tras da, los sacerdotes del templo mueven los discos hacindolos pasar de una aguja a otra, de acuerdo a las indicaciones de Brahma: El sacerdote encargado no podr mover ms de un disco simultneamente, Ni lo podr situar encima de un disco de menor tamao.

Si hacemos el clculo, en este caso, el nmero mnimo de movimientos que necesitan es: 264 - 1 (2 a la 64 menos 1), o sea 18,446,744,073,709,551,615 movimientos. Suponiendo que los sacerdotes realicen un movimiento por segundo y trabajen las 24 horas del da, durante los 365 das del ao, tomando en cuenta los aos bisiestos tardaran 58,454,204,609 siglos ms 6 aos en concluir la obra8, siempre que no se equivoquen, pues un pequeo descuido podra echar por tierra todo lo hecho.

Dado que nuestro planeta tiene unos 5 mil millones de aos (y la edad del Universo se estima entre 15 y 20 mil millones de aos) no parece que tengamos que preocuparnos demasiado del fin del mundo, por lo menos os dar tiempo a la entrega de esta prctica.

3.2 Recursividad en las Torres de Hanoi


Hoy da el templo ya no existe, pero la leyenda ha perdurado en forma de juego, y es un excelente ejemplo para explicar y practicar algoritmos recursivos. La forma de resolver recursivamente este puzzle es muy sencilla. Si numeramos los discos desde 1 hasta n, y llamamos ORIGEN a la primera pila de discos, DESTINO a la tercera y AUXILIAR a la intermedia, el algoritmo a seguir sera el siguiente: Traslada la torre 1...n 1 (movemos n-1 discos) usando este ORIGEN a DESTINO, usando como ayuda el AUXILIAR. Esto deja en ORIGEN Lleva el disco n de ORIGEN a AUXILIAR. Traslada la torre 1...n 1 (movemos n-1 discos) usando este DESTINO a AUXILIAR, usando como ayuda el. ORIGEN, de modo disco ensimo. mismo algoritmo, de slo el ensimo disco mismo algoritmo, de que quedan sobre ese

Una imagen vale ms que mil palabras, en esta figura tenemos los pasos para resolver el caso con cuatro discos:

Probablemente viendo y ejecutando el cdigo en C correspondiente se entienda incluso mejor, en el proyecto hanoic se encuentra el cdigo de la siguiente pgina. Abre el proyecto desde MipsIt.exe (no olvides lanzar tambin el simulador Mips.exe), complalo (CTRL+F7) y ejectalo (F5) para distintos valores de n. En la consola del simulador se irn indicando los movimientos necesarios para resolver el puzzle con el nmero de discos indicado en la variable n de la funcin main.

#include <stdio.h> static char varilla1[] = "fuente"; static char varilla2[] = "aux"; static char varilla3[] = "destino"; int num = 0;

Cdigo C de las Torres de Hanoi

void Hanoi (char * origen, char * auxiliar, char * destino, int n) { Observa como se if (n > 0) Primera llamada recursiva intercambian { auxiliar y destino Hanoi (origen, destino, auxiliar, n-1); num++; printf("(Mov. %d) Mueve el disco de %s a %s \n", num, origen, destino); Hanoi (auxiliar, origen, destino, n-1); Observa como se } intercambia } Segunda llamada recursiva

origen y auxiliar

int main(void) { int n = 3; // Nmero de discos Hanoi(varilla1, varilla2, varilla3, n); return 0; }

El ensamblador/compilador MipsIt.exe se encarga de generar automticamente cdigo ensamblador a partir del cdigo C que le damos, si abrimos el editor de ensamblador (CTRL+F9) podemos verlo9. Al principio aparecen unas directivas muy similares a las que utilizbamos en el programa del palndromo, en las que se reserva espacio e inicializan los elementos del segmento de datos. El cdigo propiamente dicho comienza en el segmento de texto con la funcin (etiqueta) Hanoi, si se examina cuidadosamente este cdigo (puedes hacer uso de la tabla de la pgina 7 para ver la correspondencia entre los registros por nombre funcional y su nmero) se aprecian claramente unas secciones del cdigo en las que se estn guardando y sacando de la pila los registros, as como la preparacin de los datos y su colocacin en los registros para argumentos antes de llamar a la funcin, siguiendo las indicaciones de la seccin anterior. El cdigo que genera el compilador es muy conservador (introduce ms operaciones de las que son realmente necesarias), de ah que la ejecucin del mismo en el simulador en modo tracking PC sea tan lenta. Hay algunos comentarios generados automticamente, pero el cdigo resultante no tiene la claridad suficiente como para ilustrar todas las cosas importantes que esconde. Por ello y con nimo de centrarnos en los aspectos didcticos hemos escrito una nueva versin de las Torres de Hanoi en ensamblador MIPS.

Tienes tambin una versin de este cdigo en Pascal en el Apndice de la prctica, para que te ayude a entenderlo, aunque en ambos lenguajes las operaciones son sencillas, muy similares a pseudocdigo.

################################################################################ # Descripcin: Torres de Hanoi # Autor: David Miraut # Asignatura: ETC-II (ITIG) - URJC # Fecha: Febrero 2005 # Web: http://dac.escet.urjc.es/docencia/ETC-Gestion/ ################################################################################ #include <iregdef.h> ### Segmento de datos .data .globl cadena1 introduccion: .asciz "Prctica 1 -> Torres de Hanoi\n" cadena1: .asciz "(%d) Mueve un disco del palo %d al palo %d \n" ### Segmento de texto (programa) .text # Comienzo de seccion de codigo de usuario .align 2 # Alineamiento en formato palabra (multiplo de 4) .globl main # La etiqueta "main" se hace conocida a nivel global .ent main # La etiqueta "main" marca un punto de entrada main: li li li li li jal .end ## TdeH: j main a0, 6 a1, 1 a2, 2 a3, 3 t7, 0 TdeH _exit # Saltamos a la rutina de salida para terminar # Final de la seccion "main" # Num. de discos # Palo de inicio (donde estan los discos) # Palo auxiliar (vaco) # Palo destino (adonde deberan acabar los discos)

Segmento de datos

Segmento de texto (cdigo)


Antes de llamar a la funcin prepara los parmetros

Procedimiento de las Torres de Hanoi addi sw sw sw sw sw sp, sp, -20 ra, 16(sp) a0, 0(sp) a1, 4(sp) a2, 8(sp) a3, 12(sp) # Hace hueco en la pila para 5 elementos # Guarda la direccin de llamada arriba de la pila # Guarda el num. de discos # Guarda origen # Guarda destino # Guarda auxiliar

Hace sitio en la pila y guarda los parmetros de llamada (incluido ra)

slt t1, a0, 1 bne t1, 1, Recursion j ending Recursion: lw lw lw lw add a0, a1, a2, a3, a0, 0(sp) 4(sp) 12(sp) 8(sp) a0, -1 # Recuperamos los valores de la pila # Intercambiamos el de enmedio con el final # Dismi nuye en uno el nmero de discos # que ver la funcin llamada

Si no quedan discos acaba

Recupera los parmetros de la pila y prepara los nuevos parametros para la llamada recursiva

jal TdeH addi la move lw lw jal lw lw lw lw sub t7, t7, 1 a0, cadena1 a1, t7 a2, 4(sp) a3, 12(sp) printf a0, a3, a1, a2, a0, 0(sp) 12(s p) 8(sp) 4(sp) a0, 1 # indicamos la cadena y aprovechamos # los otros argumentos

Llamada recursva

Imprime el resultado de este movimiento

# llama a printf que est en la libreria I/O # Recuperamos los valores de la pila # Intercambiamos el primero con el de enmedio # Disminuye en uno el nmero de discos # que ver la funcin llamada Recupera los parmetros de la pila y prepara los nuevos parametros para la llamada recursiva

jal TdeH ending: lw a0, 0(sp) lw a1, 4(sp) lw a2, 8(sp) lw a3, 12(sp) lw ra, 16(sp) addi sp, sp, 20 jr ra

Llamada recursva

# carga de vuelta el num. de discos en a0 # E igualmente deshace la operacin con el resto # de los registros # Vuelve a colocar el puntero de pila

Restaura el valor de los registros y la posicin del puntero de la pila Devuelve el control

Esta nueva versin se encuentra en el proyecto/directorio hanoi10, brelo con MipsIt.exe, complalo (F7) y ejectalo en el simulador (F5). Al igual que antes, prueba recompilar y ejecutar cambiando el nmero de discos para ver qu sucede. Una vez entendis el funcionamiento general del programa, leedlo ms detenidamente (con ayuda de la chuleta de instrucciones anexa al enunciado de esta prctica). Ejecutad paso a paso esta versin de las Torres de Hanoi (para un nmero pequeo de discos, por ejemplo 3) tratando de comprender cada uno de sus detalles: uso de la pila en cada seccin del programa, el porqu de la eleccin de esos registros de la CPU, el uso de las expresiones condicionales, la preparacin de los argumentos de las funciones, el significado de las expresiones condicionales, el flujo de ejecucin del programa (podis ayudaros de la opcin PC tracking) tal y como se explic con el entorno del simulador en la prctica anterior.

3.3 Ejercicios de la tercera parte de la memoria


1. Diferencias y semejanzas entre el cdigo generado automticamente y sta versin de las torres de Hanoi. Por qu la nueva versin es ms rpida? 2. Qu ha ocurrido con el puntero a marco? es desaconsejable no hacer uso de l? por qu? 3. Qu registros se guardan en la pila cada vez que se ve invocada la funcin hanoi? Es importante el orden en el que se hace? 4. Por qu al preparar los argumentos antes de llamar a las funciones recursivas el programa coge los valores de la pila y no de los propios registros despus de la etiqueta Recursion? 5. Y por qu lo vuelve a hacer despus de haber impreso el mensaje por pantalla? 6. El uso de t7 para contar el nmero de pasos desde el punto de vista de la programacin en lenguajes de alto nivel- es una chapuza crees que es un error en este caso? si este proyecto fuera ms grande (o creciera) cmo lo arreglaras? 7. Dibuja en una tabla todas las llamadas a la funcin Hanoi en el caso de 3 discos, indicando los argumentos que recibe y cuales de esas llamadas son llamadas hoja en la recursin (y por tanto no producen movimiento de discos).
N de llamada Parametros que recibe la funcin Origen Destino (Auxiliar) N de discos Es llamada hoja?

8. OPCIONAL11: Si observas detenidamente el movimiento indicado para los discos en la consola, apreciaras un cierto patrn en el movimiento de los discos que tiene cierta similitud con los cdigos cclicos de Gray vistos en el tema 2. Implementar la versin iterativa de las Torres de Hanoi teniendo en cuenta esta relacin. Ms informacin en: http://www.juntadeandalucia.es/averroes/iesarroyo/matematicas/taller/aspectosweb/aspectosweb.htm
10 11

Sin c a diferencia del proyecto anterior en el que el cdigo estaba en lenguaje C No es necesario entregarlo, pero es un buen ejercicio. El arte de la programacin se aprende con la prctica.

Apndice A: Cdigo fuente de los programas


Cdigo fuente de palindromo.s
################################################################################ # # Fichero: palidromo.s # Descripcin: Verifica si una expresin forma un palidromo # Autor: David Miraut # Asignatura: ETC-II (ITIG) - URJC # Fecha: Febrero 2005 # Lenguaje: MIPS-assembler, GNU/AT&T-syntax # Web: http://dac.escet.urjc.es/docencia/ETC-Gestion/ # ################################################################################ ## ## ## ## ## Uso de los registros: t1 - Direccin del extremo inicial de la cadena t2 - Direccin del extremo final de la cadena t3 - Carcter en el extremo inicial de la cadena t4 - Carcter en el extremo final de la cadena

### Segmento de datos # Incluimos los alias de los registros #include <iregdef.h> .data .globl posible_palindromo posible_palindromo: .asciiz "reconocer" # Hay miles de palindromos, puedes probar unos cuantos # cambiando el valor de la cadena para comprobar el # funcionamiento del programa (como: rapar, orejero...) msg_positivo: .asciiz "La cadena <<%s>> es un palindromo.\n" msg_negativo: .asciiz "La cadena <<%s>> no es un palindromo (%c) != (%c).\n" ### Segmento de texto (programa) .text .align 2 .globl main .ent main main: la t1, posible_palindromo # Colocarnos en el extremo inicial es fcil # # # # Comienzo de seccion de codigo de usuario Alineamiento en formato palabra (multiplo de 4) La etiqueta "main" se hace conocida a nivel global La etiqueta "main" marca un punto de entrada

## Lo ideal sera tener una funcin a parte que contara la longitud de la ## cadena para luego posicionar un puntero (t2) al final de sta ## En esta versin inicial del programa lo calculamos dentro de main la t2, posible_palindromo bucle_longitud: lb t3, (t2) # Lo ponemos al principio # y lo movemos hacia el final # Carga en t3 el byte al que apunta t2

beqz t3, fin_bucle_long addu t2, t2, 1 b bucle_longitud fin_bucle_long: subu t2, t2, 1 bucle_palindromo: bge t1, t2, es_palin lb t3, (t1) lb t4, (t2) bne t3, t4, no_es_palin # Si no se sale del bucle addu t1, t1, 1 subu t2, t2, 1 j bucle_palindromo

# si t3 es igual a 0, hemos llegado # al final de la cadena # sino nos movemos un caracter en la cadena # y repetimos el bucle. # La cadena adems de terminar en el caracter # nulo tiene dos caracteres que no nos interesan # De modo que nos movemos "2 bytes" hacia atrs # Condicin de final de bucle (explicar) # Carga el byte en el extremo inicial (desplazado) # Carga el byte en el extremo final (desplazado) # Condicin de final de bucle (explicar) (explicar) # Desplazamos el extremo inicial # Desplazamos el extremo final # Y repetimos el bucle

es_palin: # Imprime el mensaje la a0, msg_positivo la a1, posible_palindromo jal printf j fin # Saltamos a la rutina de salida para terminar no_es_palin: la a0, msg_negativo la a1, posible_palindromo move a2, t3 move a3, t4 jal printf j fin fin: jal .end _exit main # Imprime el mensaje # carteres "responsables" # de la falta de simetra # Saltamos a la rutina de salida para terminar # finaliza el programa (explicar su importancia) # Final de la seccion "main"

Cdigo fuente de hanoi.c


#include <stdio.h> static char varilla1[] = "fuente"; static char varilla2[] = "aux"; static char varilla3[] = "destino"; int num = 0; void Hanoi (char * origen, char * auxiliar, char * destino, int n) { if (n > 0) { Hanoi (origen, destino, auxiliar, n-1); num++; printf("(Mov. %d) Mueve el disco de %s a %s \n", num, origen, destino); Hanoi (auxiliar, origen, destino, n-1); } } int main(void) { int n = 3; // Nmero de discos Hanoi(varilla1, varilla2, varilla3, n); return 0; }

Cdigo fuente de hanoi.pas


(* Las Torres de Hanoi en Pascal *) (* Como referencia para aquell@s que no programen an en lenguaje C. *) (* 1 representa la varilla inicial, 2 la auxiliar y 3 la destino *) PROGRAM HanoiPascal(input, output); VAR N:integer; VAR Num:integer; PROCEDURE hanoi(Torigen, Tauxiliar, Tdestino, N : integer); BEGIN if N > 0 THEN BEGIN hanoi(Torigen, Tdestino, Tauxiliar, N-1); writeln('(Mov. ',Num, ') Mueve el disco de', Torigen:1, ' a ', Tdestino:1); hanoi(Tauxiliar, Torigen, Tdestino, N-1); END END; BEGIN Num := 0; N := 3; (* Nmero de discos *) hanoi(1, 2, 3, N) END.

Cdigo fuente de hanoi.s


################################################################################ # # Fichero: hanoi.s # Descripcin: Torres de Hanoi # Autor: David Miraut # Asignatura: ETC-II (ITIG) - URJC # Fecha: Febrero 2005 # Lenguaje: MIPS-assembler, GNU/AT&T-syntax # Web: http://dac.escet.urjc.es/docencia/ETC-Gestion/ # ################################################################################ # Incluimos los alias de los registros #include <iregdef.h> ### Segmento de datos .data .globl cadena1 introduccion: .asciiz "Prctica 1 -> Torres de Hanoi\n" cadena1: .asciiz "(%d) Mueve un disco del palo %d al palo %d \n" ### Segmento de texto (programa) .text # Comienzo de seccion de codigo de usuario .align 2 # Alineamiento en formato palabra (multiplo de 4) .globl main # La etiqueta "main" se hace conocida a nivel global .ent main # La etiqueta "main" marca un punto de entrada main: li li li li li jal a0, a1, a2, a3, t7, TdeH # Saltamos a la rutina de salida para terminar # Final de la seccion "main" 6 1 2 3 0 # # # # Num. Palo Palo Palo de discos de inicio (donde estan los discos) auxiliar (vaco) destino (adonde deberan acabar los discos)

j _exit .end main ## TdeH:

Procedimiento de las Torres de Hanoi addi sw sw sw sw sw sp, sp, -20 ra, 16(sp) a0, 0(sp) a1, 4(sp) a2, 8(sp) a3, 12(sp) # # # # # # Hace hueco en la pila para 5 elementos Guarda la direccin de llamada arriba de la pila Guarda el num. de discos Guarda origen Guarda destino Guarda auxiliar

slt t1, a0, 1 bne t1, 1, Recursion j fin Recursion: lw a0, 0(sp) # Recuperamos los valores de la pila

lw lw lw add

a1, a2, a3, a0,

4(sp) 12(sp) 8(sp) a0, -1

# Intercambiamos el de enmedio con el final # Disminuye en uno el nmero de discos # que ver la funcin llamada

jal TdeH addi la move lw lw jal lw lw lw lw sub t7, t7, 1 a0, cadena1 a1, t7 a2, 4(sp) a3, 12(sp) printf a0, a3, a1, a2, a0, 0(sp) 12(sp) 8(sp) 4(sp) a0, 1

# indicamos la cadena y aprovechamos # los otros argumentos

# llama a printf que est en la libreria I/O # Recuperamos los valores de la pila # Intercambiamos el primero con el de enmedio # Disminuye en uno el nmero de discos # que ver la funcin llamada

jal TdeH fin: lw a0, 0(sp) lw a1, 4(sp) lw a2, 8(sp) lw a3, 12(sp) lw ra, 16(sp) addi sp, sp, 20 jr ra # carga de vuelta el num. de discos en a0 # E igualmente deshace la operacin con el resto # de los registros # Vuelve a colocar el puntero de pila

También podría gustarte