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