lunes, 10 de octubre de 2011

Función a Ensamblador

Antes de continuar con los punteros me gustaría ver como una función, que puede parecer simple, puede cambiar mucho a la hora de transformar su código a ensamblador. Como curiosidad usaré el ensamblador de MIPS32. En esta entrada no me parare a explicar el significado de todas las instrucciones de ensamblador, es más para apreciar la interpretación de nuestros códigos. Sin embargo me parece interesante ver esto pues el ensamblador es un lenguaje de bajo nivel.

La función que me dispongo a interpretar es una de las comunes como ejercicio de programación en clase. Además es uno de los ejemplos de función recursiva, esta es la función de factorial. Veamos un ejemplo de un programa simple que emplea esta función de una manera sencilla:
Como vemos es algo sencillo. Unas pocas líneas de código y con eso hemos conseguido este pequeño programa que calcula un número factorial recursivamente.Pero esto se complica un poco más cuando pasa a ensamblador. Veamos que ocurre en ensamblador:
Podemos ver como ha crecido un código con respecto al otro. Debido a que el ensamblador es a bajo nivel debemos manejar la memoria más directamente. Esto puede ser complicado, pero a su vez puede aumentar la velocidad de nuestro programa y su control. Aunque, programando en C el control que tenemos de la memoria es muy alto gracias a los punteros.

En el código del 'main' de ejemplo en ensamblador podemos ver de las líneas 4 a 7 la creación de un marco de pila. El marco de pila es necesario antes de llamar a otras funciones y en él almacenamos los datos necesarios para continuar con la ejecución de nuestro programa después de haber pasado por la función que vayamos a ejecutar. Unos ejemplos de lo que se guarda en el marco de pila son: registros que puedan ser modificados, el registro especial '$ra' que contiene la dirección de retorno, parámetros de funciones con más de 5 parámetros. En la línea 8 almacenamos el parámetro que pasaremos a la función factorial (5), por convenio el primer parámetro ira en '$a0'. En la línea 9 tenemos la llamada a la función factorial y a continuación guardamos en '$a0' (necesario para imprimir) el valor devuelto por la función factorial ('$v0' por convenio) . Para imprimir por pantalla deberemos guardar el valor que deseamos imprimir en '$a0' (ya esta hecho), al ser un 'int' guardar un  1 en '$v0' y por ultimo realizar el 'syscall'. Por último deshacemos el marco de pila para obtener lo guardado inicialmente y salimos del programa.

En la función factorial de ensamblador lo primero que apreciamos (línea 21) es el 'if'. Si el '$a0' (parámetro de entrada) es menor o igual a 1 iremos a la etiqueta fin (línea 34) y a partir de hay guardamos uno en el registro de retorno '$v0' y realizamos el 'return' ('jal $ra'). Si no vamos a la etiqueta fin, continuamos con el resto de la función, lo primero que ocurre es la creación del marco de pila (para volver a llamar a factorial), a continuación guardamos de parámetro en el parámetro de entrada de la siguiente llamada ($a0) el numero pasado por parámetro -1. A continuación llamamos a factorial de nuevo (línea 27) y una vez que vuelva de factorial deshacemos el marco de pila (recuperamos $a0 con el número de nuestra función) y multiplicamos el valor devuelto por factorial del número -1 por nuestro número. Por ultimo realizamos el 'return' (línea 33).

Como se ha podido apreciar en estos ejemplos, algo tan sencillo se complica a la hora de aproximarnos a lo que realiza el microprocesador. Esto es una manera interesante de ver como se comporta el microprocesador a la hora de trabajar. Con esto además nos damos cuenta como en los lenguajes de más alto nivel nos abstraemos de una gran cantidad de cosas.Espero que os haya resultado interesante esta entrada, es una entrada a modo de curiosidad. Proximamente continuaré con las entradas de punteros.

Si queréis algún tema en especial tan sólo tenéis que decirlo.

No hay comentarios:

Publicar un comentario