martes, 6 de marzo de 2012

Tarea Intro. Ensamblador


Para mi reporte sobre mi tarea intro, decidi hacer un programa que muestra el promedio de n cantidad de numeros.

Codigo en C
#include <stdio.h>
main(){
int num,i,suma=0,cantidad; /*declarar variables*/
printf("Cuantos numero quieres calcular su promedio: ");
scanf("%d",&num);
for(i=1;i<=num;i++)
{
printf("Numero: ");
scanf("%d",&cantidad);
suma=suma+cantidad;}
suma=suma/num;
printf("Su promedio es : %d",suma);
printf("\n");
return 0;
}
view raw prom.c hosted with ❤ by GitHub
Después se compila mediante la siguiente instrucción para asi obtener el código ensamblador

gcc -S factorial.c
Este es el resultado:
.file "prom.c" ; nombre del archivo
.section .rodata ; coloca variables globales en una seccion separada o alamcena la constante en cadena
.align 4 ;
.LC0: ; etiqueta del segmento de codigo
.string "Cuantos numero quieres calcular su promedio: " ; cadena almacenada en este segmento
.LC1: ; etiqueta de segmento de codigo
.string "%d" ; cadena almacenada en este segmento
.LC2: ; etiqueta de segmento de codigo
.string "Numero: " ; cadena alamacenada en este segmento
.LC3: ; etiqueta de segmento de codigo
.string "Su promedio es : %d" ; cadena almacenada
.LC4: ;etiqueta de segmento de codigo
.string "PAUSE" ; cadena alamacenada
.text ; inicia las instrucciones
.globl main ; declaracion de main como simbolo global
.type main, @function ; main es una funcion
main: ; etiqueta de la funcion main(principio)
pushl %ebp ; prologo respalda %ebp anterior
movl %esp, %ebp ; %esp apunta a %ebp
andl $-16, %esp ; alinea la pila
subl $48, %esp ; reservamos 48 bytes
movl $0, 36(%esp) ;
movl $.LC0, %eax ; movemos la cadena de $.LC0 a %eax
movl %eax, (%esp) ; mover la direccion de eax al valor al cual apunta %esp (tope de pila)
call printf ; manda llamar a printf
movl $.LC1, %eax ; movemos la cadena almacenad en $.LC1 a %eax
leal 44(%esp), %edx ; movemos el valor de %esp+44 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 pila)
movl $1, 40(%esp) ; asignamos 1 a la variable %esp+40('resultado')
jmp .L2 ; salto de linea a .L2
.L3:
movl $.LC2, %eax ; movemos la cadena de $.LC2 a %eax
movl %eax, (%esp) ; mover la direccion de %eax al valor al cual apunta %esp(tope de pila)
call printf ; manda llamar a printf
movl $.LC1, %eax ; movemos la cadena de $.LC1 a %eax
leal 32(%esp), %edx ; movemos el valor de %esp+32 a %edx (registro de entrada y salida)
movl %edx, 4(%esp) ; movemos la direccion de %edx al registro %esp+4
movl %eax, (%esp) ; mover la direccion de %eax al valor al que apunta %esp(tope de pila)
call __isoc99_scanf ;
movl 32(%esp), %eax ; la variable %esp+32 la asignamos al registro %eax
addl %eax, 36(%esp) ; asignamos el valor de %eax al registro de %esp+36
addl $1, 40(%esp) ;
.L2:
movl 44(%esp), %eax ; la variable %esp+44 la asignamos al registro %eax
cmpl %eax, 40(%esp) ; al registro de %esp+40
jle .L3 ; salto cuando es menor que .L3
movl 44(%esp), %eax ; la variable %esp+44 la asignamos al registro de %eax
movl %eax, 28(%esp) ; Desplazamiento de el valor de %eax al registro de %eax+36
movl 36(%esp), %eax ; la variable %esp+36 la asignamos al registro %eax
movl %eax, %edx ;
sarl $31, %edx ;
idivl 28(%esp) ;
movl %eax, 36(%esp) ; el registro %eax lo movemos a la variable %esp+36
movl $.LC3, %eax ; mover la cadena $.LC3 a %eax
movl 36(%esp), %edx ; movemos el valor de %esp+36 a %edx (registro de entrada y salida)
movl %edx, 4(%esp) ; movemos la direccion de %edx al registro %esp+4
movl %eax, (%esp) ; mover la direccion de %eax al valor al cual apunta %esp(tope de pila)
call printf ; llama a printf
movl $10, (%esp) ;
call putchar ;
movl $.LC4, (%esp) ; movemos la cadena $.LC4 a %esp
call system ; llamamos a system
movl $0, %eax ;
leave ;
ret ; termina el programa
.size main, .-main ;
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack,"",@progbits
view raw prom.asm hosted with ❤ by GitHub

Código Optimizado
Optimizar el codigo realiza modificaciones sobre el código intermedio para mejorar la eficiencia en velocidad y tamaño.  En mi caso solo quite intrucciones que me parecierón extras como movimientos de memoria innecesarios y el siguiente es el código optimizado.
.file "prom.c"
.LC0:
.string "Cuantos numero quieres calcular su promedio: "
.LC1:
.string "%d"
.LC2:
.string "Numero: "
.LC3:
.string "Su promedio es : %d"
.LC4:
.string "PAUSE"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $48, %esp
movl $0, 36(%esp)
movl $.LC0, %eax
movl %eax, (%esp)
call printf
movl $.LC1, %eax
leal 44(%esp), %edx
movl %edx, 4(%esp)
movl $1, 40(%esp)
jmp .L2
.L3:
movl $.LC2, %eax
movl %eax, (%esp)
call printf
leal 32(%esp), %edx
movl %edx, 4(%esp)
call __isoc99_scanf
movl 32(%esp), %eax
addl %eax, 36(%esp)
addl $1, 40(%esp)
.L2:
movl 44(%esp), %eax
cmpl %eax, 40(%esp)
jle .L3
movl %eax, 28(%esp)
movl 36(%esp), %eax
movl %eax, %edx
sarl $31, %edx
idivl 28(%esp)
movl %eax, 36(%esp)
movl $.LC3, %eax
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl $10, (%esp)
movl $.LC4, (%esp)
movl $0, %eax
leave
ret
.size main, .-main
view raw prom.asm hosted with ❤ by GitHub
Cuando recién compilamos aparecerán muchas líneas, algunas etiquetas como .file, .type, tambien fueron algunas que elimine. Decidi comentarizar las lineas para localizar y entender cada parte de lo que realizaba el programa en este lenguaje.

Aun me faltaron algunas lineas e instrucciones de identificar porque aun no comprendia lo que realizaban pero aqui las explico.
 leal 44(%esp), %edx -> Transfiere la dirección de eax para el registro edx
cmpl %eax, 40(%esp) -> Resta fuente de destino y actualiza las banderas 
leave -> Libera las variables locales creando por la anterior
idivl -> La instrucción IDIV divide el contenido del 64 bits sin signo "edx": 
"eax" (construido mediante la visualización "edx" como los más significativos cuatro 
bytes y "eax" (como los menos significativos cuatro bytes) por el valor de operando 
especificado. El resultado cociente de la división se almacena en "eax", mientras que 
el resto se coloca en "edx".
 
Después realice la prueba con el código optimizado de la siguiente manera 

Referencia :
También tome de base la publicación de mi compañero Juan Carlos  para comprender más algunas lineas de código.