jueves, 2 de febrero de 2012

Tarea 1: Lenguaje Ensamblador

El lenguaje ensamblador es un lenguaje que provee a la computadora o a algún otro dispositivo una secuencia de instrucciones dado por una secuencia de códigos binarios.Los programas que se hacen en ensamblador tienden a tener una mayor velocidad y a consumir menos recursos que el programa compilado desde otro lenguaje. El tiempo que tarda en ejecutarse una rutina disminuye,  y el espacio que ocupa es menor.
Por otro lado, un programa escrito en lenguaje ensamblador es mucho más complejo y dificil de crear y leer. A continuación se mostrará un simple hola mundo creado en lenguaje C y después ese mismo programa será traducido en lenguaje ensamblador para hacer una comparación y entender un poco mejor las instrucciones que se muestran en el lenguaje ensamblador. Este programa simple nos servirá para ver las funciones básicas que se requieren en un programa escrito en ensamblador.

Este es un Hola mundo escrito en C:
#include 
int main()
{
printf("Hola mundo");
}
Para que este programa se pueda traducir a ensamblador se necesita seguir los siguientes pasos: 

1. Se compila el programa:
gcc hola.c
2. Se corre el programa:
./a.out
3. Se crea un ejecutable en ensablador. Esto se hará con -S después de gcc:
gcc -S hola.c
4. Ahora estará un archivo con el mismo nombre pero con terminación .s :
hola.s

Este programa se puede abrir en cualquir editor de texto (emacs) y se va a mostrar en lenguaje ensamblador.
Este es el código que se mostró:
   
.file    "hola.c"        #especifica el nombre del archivo original
.section    .rodata      #contiene información de solo lectura del programa inicializado.
.LC0:
.string    "Hola mundo"  #se especifica el texto que se va a imprimir. 
.text                    #agrega el código a la sección de texto del programa
.globl main              #indica que main es global.
.type    main, @function  
main:                    #aqui empieza a correr el main
pushl    %ebp //push local a ebp
movl    %esp, %ebp       #move local a esp y ebp
andl    $-16, %esp       #alinea la pila
subl    $16, %esp        #resta 16 bytes
movl    $.LC0, %eax      #move la cadena a la parte superior de la pila
movl    %eax, (%esp)     #move local a eax y esp
call    printf           #aqui llama a la función printf.
leave
ret                      #return
.size    main, .-main
.ident    "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
.section    .note.GNU-stack,"",@progbits

Aqui podemos observar que el programa es mucho más largo y contiene más instrucciones que el programa escrito en lenguaje C.  
Después pude reducir la cantidad de líneas de código del programa y así quedo:
.file   "hola.c"     
.LC0:
.string "Hola mundo"
.text
.globl main
main:
pushl   %ebp
movl    %esp, %ebp                              
movl    $.LC0, %eax
movl    %eax, (%esp)
call    printf
leave

El programa todavía corre y ejecuta las mismas acciones que el programa original, y las líneas de código se reducieron un 40%.
Para poder ejecutar este programa y saber que funciona tenemos que seguir los siguientes pasos:
1. Crear el archivo ejecutable a partir del archivo .s:
gcc -o hola.exe hola.s 
2. Se ejecuta el archivo .exe:
./hola.exe

Ahora el programa se va a ejecutar en la terminal. Con esto podemos modificar el código en ensamblador y verificar si todavía funciona.

Ahora hice un programa que genera palindromos de diferentes frases que escriba el usuario. 
Este es el programa en C:

#include 
void inverso (const char * const ptrS ); //prototipo                           
int main()
{
  char enunciado [ 80 ]; //crea un arreglo de caracteres                         
  printf("Escribe una frase: \n");
  gets( enunciado );
  printf("La frase al revez es: \n");
  inverso(enunciado );
  printf("\n");
  return 0;
}
void inverso (const char * const ptrS)
{
  //si es el final de la cadena                                                
  if (ptrS[ 0 ] == '\0' ){
    return;
  }
  else {
    inverso( &ptrS[ 1 ] );
    putchar( ptrS[ 0 ] );
  }
}

Este es el programa generado en ensamblador:
.LC0:
.string    "Escribe una frase: "  #se especifica el texto que se va a imprimir.
.LC1:
.string    "La frase al revez es: " #otro string
.text        #agrega el codigo a la seccion de texto del programa
.globl    main    #indica que la funcion main es global
.type    main, @function
main:  #aqui empieza a correr el main
.LFB0:
.cfi_startproc
pushl    %ebp         #se respalda el %ebp anterior
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl    %esp, %ebp    #move local a esp
.cfi_def_cfa_register 5
andl    $-16, %esp
subl    $112, %esp
movl    %gs:20, %eax
movl    %eax, 108(%esp)
xorl    %eax, %eax
movl    $.LC0, (%esp)
call    puts        #llama a puts
leal    28(%esp), %eax
movl    %eax, (%esp)
call    gets         #llama a gets
movl    $.LC1, (%esp)
call    puts
leal    28(%esp), %eax
movl    %eax, (%esp)
call    inverso
movl    $10, (%esp)
call    putchar
movl    $0, %eax
movl    108(%esp), %edx
xorl    %gs:20, %edx
je    .L2
call    __stack_chk_fail
.L2:
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE0:
.size    main, .-main
.globl    inverso
.type    inverso, @function
inverso:
.LFB1:
.cfi_startproc
pushl    %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl    %esp, %ebp
.cfi_def_cfa_register 5
subl    $24, %esp
movl    8(%ebp), %eax
movzbl    (%eax), %eax
testb    %al, %al
je    .L6
.L4:
movl    8(%ebp), %eax
addl    $1, %eax
movl    %eax, (%esp)
call    inverso
movl    8(%ebp), %eax
movzbl    (%eax), %eax
movsbl    %al, %eax
movl    %eax, (%esp)
call    putchar
jmp    .L3
.L6:
nop
.L3:
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE1:
.size    inverso, .-inverso
.ident"GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1"
.section.note.GNU-stack,"",@progbits

Después pude reducir la cantidad de líneas de código del programa y así quedo:
.globl    main    #indica que la funcion main es global
.LC0:
.string    "Escribe una frase: "  #se especifica el texto que se va a imprimir.
.LC1:
.string    "La frase al revez es: " #string
.text        #inicia la seccion de codigo
.globl    main    #indica que la funcion main es global
main:     #indica que empieza el main
.LFB0:
.cfi_startproc
pushl    %ebp         #se respalda el %ebp anterior
movl    %esp, %ebp    #move local a esp
movl    %gs:20, %eax  
movl    %eax, 108(%esp)
movl    $.LC0, (%esp)
call    puts        #llama a puts
leal    28(%esp), %eax
movl    %eax, (%esp)
call    gets         #llama a gets
movl    $.LC1, (%esp)
call    puts        #llama a puts
leal    28(%esp), %eax
movl    %eax, (%esp)
call    inverso     #llama a inverso
movl    $10, (%esp)
call    putchar        #llama a putchar
movl    108(%esp), %edx
xorl    %gs:20, %edx
je    .L2
.L2:
leave
ret
.cfi_endproc
.type    inverso, @function
inverso:
.LFB1:
.cfi_startproc
pushl    %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl    %esp, %ebp
subl    $24, %esp
movzbl    (%eax), %eax
testb    %al, %al
je    .L6
.L4:
movl    8(%ebp), %eax
addl    $1, %eax
movl    %eax, (%esp)
call    inverso
movl    8(%ebp), %eax
movzbl    (%eax), %eax
movl    %eax, (%esp)
call    putchar
jmp    .L3
.L6:
nop
.L3:
leave
.cfi_endproc
.LFE1:

Bibliografía


 

1 comentario: