Páginas

domingo, 12 de febrero de 2012

[Emb Comp Class] Tarea Intro: Lenguaje Ensamblador

Este es mi reporte sobre la Tarea Intro, programar en Lenguaje Ensamblador una rutina, y analizarla para entender un poco sobre las funciones de este lenguaje.

Para este reporte decidí realizar una rutina algo común, es un factorial recursivo. Lo siguiente es el código en C:

#include <stdio.h>
#include <stdlib.h>
int factorialRec(int n) {
int factorial;
if(n <= 1) {
factorial = 1;
}
else {
factorial = n * factorialRec(n-1);
}
return factorial;
}
int main(int argc, char **argv) { //recibo un argumento
if(argc < 2 || argc > 2) { // verifico si la cantidad de argumentos es valida
printf("BAD ARGUMENTS, expected 2\n"); // mensaje si no hay suficientes argumentos
}
else {
int n = atoi(argv[1]); // asigno el argumento recibido a una variable
int resultado = 0; // variable para almacenar el resultado, se inicia en 0
resultado = factorialRec(n); // el resultado es lo que regrese la funcion factorialRec.
printf("%d\n", resultado); // imprimimos el resultado
}
return 0; // fin
}
view raw factorial.c hosted with ❤ by GitHub


Después de eso, hay que compilarlo para obtener el código en Ensamblador, esto con la instrucción:
gcc -S factorial.c
Este es el resultado:

.file "factorial.c" ; Nombre del archivo
.text ; Inician las instrucciones
.globl factorialRec ; factorialRec es global
.type factorialRec, @function ; factorialRec es una funcion
factorialRec: ; etiqueta de la funcion factorialRec
.LFB0: ; etiqueta sel segmento de codigo
pushl %ebp ; prologo: respaldamos %ebp anterior
movl %esp, %ebp ; %esp apunta a %ebp
subl $40, %esp ; reservamos 40 bytes
cmpl $1, 8(%ebp) ; comparamos el primer parametro n <= 1
jg .L2 ; si es mayor, saltamos a .L2
movl $1, -12(%ebp) ; si no, asignamos 1 al registro %ebp-12 (factorial = 1)
jmp .L3 ; saltamos a .L3
.L2:
movl 8(%ebp), %eax ; movemos el primer parametro de la funcion a %eax
subl $1, %eax ; y restamos 1 (n-1)
movl %eax, (%esp) ; movemos la direccion de memoria %eax al tope de la pila (nuevo parametro de la funcion factorialRec)
call factorialRec ; llamamos a factorialRec (comienza la recursion)
imull 8(%ebp), %eax ; multiplicamos el valor de retorno de la funcion factorialRec por 'n'
movl %eax, -12(%ebp) ; cargar %eax en en el registro %ebp+12 (factorial)
.L3:
movl -12(%ebp), %eax ; cargamos el valor del registro %ebp+12 en %eax (valor de retorno)
leave ; epilogo
ret ; regresamos el control a la funcion padre
.LFE0: ; etiqueta del segmento de codigo
.size factorialRec, .-factorialRec ;
.section .rodata
.LC0: ; etiqueta del segmento codigo
.string "BAD ARGUMENTS, expected 2" ; cadena de caracteres que contiene dicha etiqueta
.LC1: ; etiqueta de segmento de codigo
.string "%d\n" ; cadena de caracteres que contiene dicha etiqueta
.text ; Inician las instrucciones
.globl main ; 'main' es global
.type main, @function ; 'main' es una funcion
main: ; etiqueta de la funcion 'main' (principio)
.LFB1: ; etiqueta del segmento de codigo
pushl %ebp ; prologo: respaldamos %ebp anterior
movl %esp, %ebp ; %esp apunta a %ebp
andl $-16, %esp ; alineamos la pila
subl $32, %esp ; reservamos 32bytes
cmpl $1, 8(%ebp) ; comparamos el primer parametro de la funcion
jle .L5 ; y si es menor o igual a 1, saltamos a .L5
cmpl $2, 8(%ebp) ; comparamos el primer parametro de la funcion
jle .L6 ; y si es menor o igual a 2, saltamos a .L6
.L5: ; etiqueta del segmento de codigo
movl $.LC0, (%esp) ; movemos la cadena de .L0 al tope de la pila
call puts ; llamamos a puts, imprime la cadena
jmp .L7 ; saltamos a .L7
.L6: ; etiqueta del segmento de codigo
movl 12(%ebp), %eax ; movemos el valor del segundo parametro de la funcion %ebp+12 (argv[1]) al registro temporal %eax
addl $4, %eax ; le agregamos 4bytes a %eax
movl (%eax), %eax ; mover el valor al cual apunta %eax a la direccion del registro %eax
movl %eax, (%esp) ; mover la direccion de %eax al valor al cual apunta %esp (tope de pila)
call atoi ; llamamos a 'atoi'
movl %eax, 24(%esp) ; asignamos el valor de %eax a la variable %esp+24 ('n')
movl $0, 28(%esp) ; asignamos 0 a la variable %esp+28 ('resultado')
movl 24(%esp), %eax ; la variable %esp+24 la asignamos al registro %eax
movl %eax, (%esp) ; movemos ese valor al tope de la pila (parametro de la funcion factorialRec)
call factorialRec ; llamamos a la funcion factorialRec
movl %eax, 28(%esp) ; asigamos lo que regrese la funcion (%eax) a resultado (resultado = factorialRec(n))
movl $.LC1, %eax ; movemos la cadena almacenada en .LC1 a %eax
movl 28(%esp), %edx ; movemos el valor de %esp+28 a %edx que es un registro de entrada y salida
movl %edx, 4(%esp) ; movemos la direccion de %edx al registro %esp+4
movl %eax, (%esp) ; movemos la direccion de %eax al valor que apunta %esp (tope de la pila)
call printf ; llamamos a la funcion 'printf'
.L7: ; etiqueta del segmento de codigo
movl $0, %eax ; asignamos el valor de 0 a %eax (valor de retorno)
leave ; epilogo del codigo
ret ; regresamos el control a la funcion padre
.LFE1: ; etiqueta del segmento de codigo
.size main, .-main ; las lineas son datos de la funcion padre, es decir, el sistema operativo
.ident "GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1"
.section .note.GNU-stack,"",@progbits
view raw factorial.asm hosted with ❤ by GitHub


Después, lo siguiente fue optimizar el código, eliminando aquellas instrucciones que no fueran necesarias, la mayoría de las lineas que elimine fueron etiquetas debug y algunos movimientos de memoria innecesarios.
Cuando recién compilamos aparecerán muchas líneas basura, la mayoría comienzan con .cfi, todas esas líneas podemos eliminarlas.
Algunas etiquetas como .file, .type, tambien podemos suprimirlas
También elimine algunos intercambios de registros innecesarios, por ejemplo, el return de la función factorialRec realizaba los siguientes movimientos:
mov %eax, 12(%esp)
mov 12(%esp), %eax
Este procedimiento lo elimine porque no tenia sentido hacer ese intercambio.

El nuevo código en Ensamblador es el siguiente:

.globl factorialRec ; factorialRec es una funcion global
factorialRec: ; etiqueta del segmento de codigo de la funcion factorialRec
pushl %ebp ; prologo: respaldamos el %ebp anterior
movl %esp, %ebp ; %esp apunta a %ebp
subl $40, %esp ; reservamos 40bytes
cmpl $1, 8(%ebp) ; comparamos el primer parametro de la funcion
jg .L2 ; y si es mayor a 1, saltamos a .L2
movl $1, -12(%ebp) ; asignamos 1 al registro %ebp-12 (factorial = 1)
jmp .L4 ; saltamos a .L4
.L2: ; etiqueta del segmento de codigo
movl 8(%ebp), %eax ; el primer parametro de la funcion lo asignamos al registro %eax
subl $1, %eax ; le restamos 1 al valor del registro %eax (n-1)
movl %eax, (%esp) ; asignamos la direccion de memoria del registro %eax al valor en el tope de la pila (enviar un parametro)
call factorialRec ; llamada a la funcion factorialRec
imull 8(%ebp), %eax ; muliplicamos el primer parametro de la funcion por lo que hay en %eax.
movl %eax, -12(%ebp) ; asignamos ese valor a una variable en el registro &ebp-12 (factorial = n * factorialRec(n-1))
.L4: ; etiqueta del segmento de codigo
leave ; epilogo de la funcion
ret ; regresamos el control a la funcion principal
.globl main ; main es una funcion global
main: ; etiqueta de la funcion main
pushl %ebp ; prologo: respaldamos el %ebp anterior
movl %esp, %ebp ; %esp apunta a %ebp
andl $-16, %esp ; alineamos la pila
subl $32, %esp ; reservamos 32bytes
cmpl $1, 8(%ebp) ; comparamos el primer parametro de la pila (entero con la cantidad de parametros)
jle .L6 ; y si es menor o igual a 1, saltamos a .L6
cmpl $2, 8(%ebp) ; comparamos el primer parametro de la pila (entero con la cantidad de parametros)
jle .L7 ; y si es menor o igual a 2, saltamos a .L7
.L6: ; etiqueta del segmento de codigo
movl $.LC0, (%esp) ; movemos la cadena almacenada en .L0 al tope de la pila
call puts ; llamamos a puts
jmp .L8 ; saltamos a .L8
.L7: ; etiqueta del segmento de codigo
movl 12(%ebp), %eax ; movemos el segundo parametro de la funcion al registro %eax
addl $4, %eax ; le agregamos 4bytes
movl (%eax), %eax ; mover el valor al cual apunta %eax a la direccion del registro %eax
movl %eax, (%esp) ; mover la direccion de %eax al valor al cual apunta %esp (tope de pila)
call atoi ; call to atoi
movl %eax, 24(%esp) ; asignamos el valor de %eax a la variable %esp+24 ('n')
movl $0, 28(%esp) ; asignamos un 0 al registro %esp+28 (resultado = 0)
movl 24(%esp), %eax ; movemos el valor del registro esp+24 (n) al registro %eax
movl %eax, (%esp) ; movemos el registro %eax al tope de la pila (enviar un parametro)
call factorialRec ; llamamos a la funcion factorialRec
movl %eax, 28(%esp) ; asignamos lo que regrese la llamada a la funcion a la variable %esp+28 (resultado = factorialRec(n))
movl $.LC1, %eax ; movemos la cadena almacenada en .LC1 al registro %eax
movl 28(%esp), %edx ; movemos el valor del registro %esp+28 a %edx que es un registro de entrada y salida
movl %edx, 4(%esp) ; movemos la direccion de %edx al registro %esp+4
movl %eax, (%esp) ; movemos la direccion de %eax al valor que apunta %esp (tope de la pila)
call printf ; call to printf
.L8: ; etiqueta del segmento de codigo
movl $0, %eax ; asignamos el valor de 0 a %eax (para retornar)
leave ; epilogo de la funcion
ret ; regresamos el control a la funcion padre
.LC0: ; etiqueta del segmento de codigo
.string "BAD ARGUMENTS, expected 1\n" ; cadena almacenada en ese segmento
.LC1: ; etiqueta del segmento de codigo
.string "%d\n" ; cadena almacenada en ese segmento


Lo siguiente son las diapositivas que explique en clase, lo dejo como evidencia y por si quieren visualizar algo, la mayoria de la informacion que les comparto se encuentra en la siguiente liga: Lenguaje Ensamblador Teoría



Cualquier comentario, sugerencia o aclaracion, dejen un comentario.

Referencias: AQUI

No hay comentarios:

Publicar un comentario