[imagen obtenida de: http://www.anumex.com/pictures/1729921-1.jpg]
El lenguaje ensamblador es el lenguaje de programación mas básico para cualquier procesador como sabemos un sistema de arquitectura de computadoras comunmente se divide en cinco capaz las cuales son: hardware, firmware, ensamblador, kernel, sistema operativo y aplicaciones. La capa de ensamblador comúnmente define un grupo de instrucciones relativamente fácil de entender por el ser humano estándar para un grupo de procesadores. Este lenguaje casi no contiene instrucciones abstractas lógicas lo que hace es trabajar directamente con operaciones del CPU y estas instrucciones de forma mas directa con los componentes de las computadoras. Otra característica del lenguaje ensamblador es que es bastante poderoso pues no tiene las limitaciones que vienen con lenguajes de programación de capaz superiores aunque resulta mas tedioso trabajar con este.
Breve repaso historico
En la década del 40, surgen representaciones para estas instrucciones. Ya no eran unos y ceros, sino que se los representaba con palabras más fáciles de recordar (instrucciones mnemónicas) como MOVE, LDA, ADD, etc.
La relación con el código binario era directa, por cada instrucción MNEMONICA, existe una instrucción en binario y viceversa. Así se construyen los primeros traductores que pasarán la instrucción MNEMONICA a Binario. Estos traductores recibieron el nombre de ensambladores, y se convirtieron en el primer lenguaje de los ordenadores. De todas maneras, la programación era complicada y difícil, porque se acercaba mucho a la forma de operar de las computadoras y no al lenguaje humano (llamados lenguajes de programación de alto nivel).
¿Por que usar ensamblador?
En primer lugar ¿Por que debería yo estar aprendiendo un lenguaje relativamente complicado si hoy en día existen lenguajes que con menor esfuerzo producen operaciones mucho mas complejas?
[imagen obtenida de:http://www.webopedia.com/FIG/PROG-LAN.gif]
Para entender esto lo que hice fue empezar a pensar como funcionaba una arquitectura desde las capas mas bajas, a grandes rasgos junta varias operaciones de su cerebro(CPU) para procesar números guardados en sus registros o memorias, para operaciones mas complicadas necesita juntar un mayor número de instrucciones o repetir muchas veces una misma, esto para ahorrar recursos al fabricar una maquina.
Por ejemplo si se necesitara un procesador cuya única operación fuera sumar un número "A" a un número "B" se podría construir el procesador para que sepa hacer eso, otra forma sería si tuvieramos una operación mas sencilla que sume uno a un valor "A" que recibe y este proceso lo repetimos "B" veces obtendremos los mismos resultados, de forma mas lenta pero ahorrariamos puertos de entrada para el procesador y además sería mas fácil de construir.
Por ejemplo si se necesitara un procesador cuya única operación fuera sumar un número "A" a un número "B" se podría construir el procesador para que sepa hacer eso, otra forma sería si tuvieramos una operación mas sencilla que sume uno a un valor "A" que recibe y este proceso lo repetimos "B" veces obtendremos los mismos resultados, de forma mas lenta pero ahorrariamos puertos de entrada para el procesador y además sería mas fácil de construir.
De la misma forma en capas superiores se juntan un grupo de operaciones que hacen mas fácil implementar las instrucciones para la maquina, pero que no siempre son las mas óptimas.
Ejemplo
Ahora para ejemplificar esto y entender mejor las características de este y por que es tan importante que un desarrollador de software entienda las mismas utilizare el lenguaje ensamblador versión x86, debido a que paso gran tiempo trabajando en linux y Ubuntu ya trae un compilador de ensamblador x86 a código maquina y también para convertirlo a código ensamblador.
Este es un código en Ansi C que parece bastante sencillo, todo lo que hace es guardar dos valores "a" y "b" en la memoria despues los divide e imprime los resultados en la terminal:
#includeint main(int argv, char** argc){ int a = 5, b = 4; printf("%d",(a/b)); return 0; }
Una vez que construí este código utilice el compilador "gcc" el cual ya viene por defecto en ubuntu para generar código ensamblador, lo cual lo hice agregando el parámetro "-S" y la dirección del archivo:
gcc -S ejemplo01.c
Lo cual me genero el archivo ejemplo01.s con el código en ensamblador x86. Para compilarlo en un ejecutable utilice el comando "cc"
cc ejemplo01.s
Que me generó un ejecutable con el nombre a.out. El código generado es el siguiente(quitando algunas lineas de encabezado y cosas innecesarias y agregando comentarios):
.LC0:
.string "%d"
.text
.globl main
main:
.LFB0:
pushq %rbp ;Busca la cima de la pila para manejar la memoria principal(RAM)
movq %rsp, %rbp ;Mueve la posicion de la cima de la pila a otro registro
subq $32, %rsp ;Reduce la dirección de la posición para asignar los valores a trabajr
movl %edi, -20(%rbp) ;Recibe valores pararametro y los pone en la memoria principal
movq %rsi, -32(%rbp) ;
movl $10, -4(%rbp) ;Mueve el valor 10 a una posición en la memoria principal
movl $2, -8(%rbp) ;hace lo mismo con el siguiente valor
movl -4(%rbp), %eax ;Mueve el valor 10 de la memoria principal a un registro
movl %eax, %edx ;Ahora mueve el valor 10 a un registro diferente
sarl $31, %edx ;Mueve algunos bits menos significativos
idivl -8(%rbp) ;Divide el valor 10 entre el valor que esta en la memoria principal en este caso 2
movl %eax, %esi ;La funcion anterior pone el resultado de la division en el registro %eax luego este
; Se mueve a el registro %esi para mandarlo como parametro a la funcion printf de C
movl $.LC0, %edi ;Otro parametro a la funcion en este caso un puntero hacia el string que imprimira en pantalla
movl $0, %eax ;Otro parametro para definir cantidad de parametros
call printf ;Manda a llamar a la funcion printf y en este momento se escribe el valor de la division en pantalla
movl $0, %eax ;
leave ;Pierde el control del programa
ret ;Regresa a la funcion donde fue llamado si no es el caso termina la ejecucion del programa
Como podemos observar la cantidad de instrucciones que tenemos que definir es mayor que en Ansi c esto puede ser una ventaja o una desventaja. La desventaja de esto es obvia aumenta la complejidad para generar código útil. La ventaja esta en que nos deja definir caracteristicas que en otros niveles computacionales el los lenguajes nos esconden por ejemplo el manejo directo de la memoria y o la capacidad de utilizar los registros como queramos los cuales son mas faciles y rapidos de acceder por el procesador.
Al pasar el lenguaje de código Ansi C el cual es mas facíl de construir no genera un código completamente ótimo para utilizarse por ejemplo en este caso mueve distintos valores a la memoria principal cuando podría haber movido uno solo y el otro solamente definirlo como parametro en la división ahorrandonos una posición en la memoria principal. Esto nos da la ídea de que los lenguajes de programación no tienen los algoritmos para traducir un lenguaje a otro nivel (Si es que existe uno) desperdiciando recursos que en algúnas ocaciones pueden ser bastante valiosos(otra ventaja de ensamblador).
Tomando en cuenta que en sistemas integrados trabajare con microcontroladores con una capacidad de almacenamiento y procesamiento muy limitada esta es una de esas ramas en las que nos es muy conveniente conocer las caracteristicas del lenguaje ensamblador.
Referencias:
Bien, te pongo 9 pts para el lab de integrados.
ResponderBorrar