Ensamblador | ||
![]() | ||
Fecha de la primera versión | 1949 | |
---|---|---|
Extensión de archivo | asm ys | |
Un lenguaje ensamblador o lenguaje ensamblador es, en programación de computadoras , el lenguaje de nivel más bajo que representa el lenguaje de máquina en una forma legible por humanos. Las combinaciones de bits del lenguaje de máquina están representadas por los llamados símbolos " mnemónicos " , es decir, fáciles de recordar. El programa ensamblador convierte estos mnemónicos en lenguaje de máquina, así como los valores (escritos en decimal) en binarios y las etiquetas de ubicaciones en direcciones, con el fin de crear, por ejemplo, un archivo objeto o un archivo ejecutable .
En la práctica actual, el mismo término ensamblador se usa tanto para designar el lenguaje ensamblador como el programa ensamblador que lo traduce. Hablamos así de “programación en ensamblador”.
La traducción de una vez por todas por muchos intérpretes de cada nombre de variable encontrado en una instrucción (avanzada) por la posición de memoria asociada y de cada constante (escrita por el usuario en decimal) a binario es típica de una operación d. 'Ensamblado, aunque el ensamblador de nombres no se usa comúnmente en este caso particular.
Los programas del EDSAC (1949), la primera computadora con programas grabados , fueron escritos usando mnemotécnicos alfabéticos de una letra para cada instrucción. Luego, los programadores hicieron la traducción a mano, una operación larga, tediosa y propensa a errores.
El primer programa de ensamblaje fue escrito por Nathaniel Rochester para IBM 701 (la primera computadora lanzada por IBM ) en 1954.
Los lenguajes ensambladores han eliminado muchos de los errores cometidos por los programadores de la primera generación de computadoras al prescindir de la necesidad de memorizar los códigos numéricos de las instrucciones y hacer cálculos de direcciones. La programación en ensamblador se utilizó para escribir todo tipo de programas.
En las décadas de 1970 y 1980, el uso de ensamblador para escribir aplicaciones fue reemplazado en gran medida por el uso de lenguajes de programación de alto nivel: Fortran , COBOL , PL / I , etc. : la potencia de las máquinas lo permitía y dedicar unos minutos de tiempo de computadora a una compilación para ahorrar unas horas de tiempo de programador era una operación rentable, incluso si los compiladores de la época proporcionaban un código menos eficiente (más grande y a menudo más lento). Además, estos lenguajes de alto nivel permitieron superar la dependencia de una única arquitectura de hardware.
Los sistemas operativos se escribieron en lenguaje ensamblador hasta la introducción de MCP a Burroughs en 1961, que estaba escrito en ESPOL, un dialecto de Algol .
El ensamblador ha vuelto algo a favor de los primeros microordenadores, donde las características técnicas (tamaño de memoria reducido, baja potencia de cálculo, arquitectura de memoria específica, etc.) imponían fuertes restricciones, a las que se suma un factor psicológico importante, la actitud "aficionada". de los primeros usuarios de microcomputadoras, que no se conformaron con la lentitud de los programas escritos con el BASIC interpretado generalmente suministrado con la computadora.
Grandes programas fueron escritos íntegramente en ensamblador para microcomputadoras, como el sistema operativo DOS del IBM PC (alrededor de 4000 líneas de código) y la hoja de cálculo Lotus 1-2-3 (su rival Multiplan, que ya existía bajo CP / M , era escrito en C ). En la década de 1990, este fue también el caso de la mayoría de los juegos para videoconsolas (por ejemplo, Mega Drive o Super Nintendo ).
El lenguaje de máquina es el único lenguaje que puede ejecutar un procesador . Sin embargo, cada familia de procesadores usa un conjunto diferente de instrucciones .
Por ejemplo, un procesador de la familia x86 reconoce una instrucción del tipo:
10110000 01100001En lenguaje ensamblador, esta instrucción está representada por un equivalente más fácil de entender para el programador:
movb $0x61,%al(10110000 = movb% al
01100001 = $ 0x61)
Lo que significa: "escribe el número 97 (el valor se da en hexadecimal : 61 16 = 97 10 ) en el registro AL".
Por lo tanto, el lenguaje ensamblador, una representación exacta del lenguaje de máquina, es específico para cada arquitectura de procesador . Además, pueden existir varios grupos de mnemónicos o sintaxis de lenguaje ensamblador para un solo conjunto de instrucciones, creando así macroinstrucciones .
La transformación del código ensamblador en lenguaje de máquina se logra mediante un programa llamado programa ensamblador . La operación inversa , es decir, encontrar el ensamblador equivalente a una pieza de código de máquina, tiene un nombre: es desmontaje .
Al contrario de lo que podría pensarse, no siempre existe una correspondencia uno a uno (una biyección ) entre el código ensamblador y el lenguaje de máquina. Por lo tanto, en algunos procesadores, el desmontaje puede dar como resultado un código que es muy difícil de entender para un ser humano, mientras que sigue siendo perfectamente compilable por una computadora. La imposibilidad de un desmontaje puede tener varios motivos: uso de código auto modificable, instrucciones de tamaño variable, imposibilidad de distinguir entre código y datos, etc. ( código impenetrable )
Además, muchos elementos presentes en el código ensamblador se pierden durante su traducción al lenguaje de máquina. Al crear código en ensamblador, el programador puede asignar nombres a posiciones en la memoria, comentar su código , usar macroinstrucciones o usar código generado bajo condiciones en el momento del ensamblaje. Todos estos elementos se reducen durante el montaje a lo estrictamente necesario para la máquina y por tanto no aparecen claramente durante el desmontaje: por ejemplo, una posición en la memoria solo se marca con su dirección numérica o con un offset .
Algunas operaciones fundamentales están disponibles en la mayoría de los conjuntos de instrucciones.
Y hay instrucciones específicas con una o algunas instrucciones para operaciones que deberían haber tomado mucho. Ejemplos:
Además de codificar las instrucciones de la máquina, los lenguajes ensambladores tienen directivas adicionales para ensamblar bloques de datos y asignar direcciones a instrucciones definiendo etiquetas o rótulos.
Son capaces de definir expresiones simbólicas que se evalúan en cada ensamblado, lo que hace que el código sea aún más fácil de leer y comprender.
Por lo general, tienen un lenguaje de macros incorporado para facilitar la generación de códigos complejos o bloques de datos.
A continuación, se muestran algunos ejemplos sencillos:
(Los comentarios van después del punto y coma)
str: .ascii "Bonjour\n" .global _start _start: movl $4, %eax movl $1, %ebx movl $str, %ecx movl $8, %edx int $0x80 movl $1, %eax movl $0, %ebx int $0x80 ;Compilation: ;as code.s -o code.o ;ld code.o -o code ;Execution: ;./codeA continuación, se muestran los mismos ejemplos, con algunas diferencias:
(Los comentarios van después del punto y coma)
section .data ; Variables initialisées Buffer: db 'Bonsoir', 10 ; En ascii, 10 = '\n'. La virgule sert à concaténer les chaines BufferSize: equ $-Buffer ; Taille de la chaine section .text ; Le code source est écrit dans cette section global _start ; Définition de l'entrée du programme _start: ; Entrée du programme mov eax, 4 ; Appel de sys_write mov ebx, 1 ; Sortie standard STDOUT mov ecx, Buffer ; Chaine à afficher mov edx, BufferSize ; Taille de la chaine int 80h ; Interruption du kernel mov eax, 1 ; Appel de sys_exit mov ebx, 0 ; Code de retour int 80h ; Interruption du kernelHay debates sobre la utilidad del lenguaje ensamblador. En muchos casos, los compiladores - optimizadores pueden transformar el lenguaje de alto nivel en código que se ejecuta tan eficientemente como el código ensamblador escrito a mano por un muy buen programador, sin dejar de ser mucho más fácil, rápido (y por lo tanto menos eficiente). mantener.
La eficiencia ya era una preocupación en la década de 1950, encontramos un rastro de ella en el manual de lenguaje de Fortran (publicado en 1956) para la computadora IBM 704 : los programas de objetos producidos por Fortran serán casi tan eficientes como los escritos por buenos programadores .
Mientras tanto, los compiladores han hecho un enorme progreso, por lo tanto, es obvio que la gran mayoría de los programas ahora están escritos en lenguajes de alto nivel por razones económicas, el costo de programación adicional supera la ganancia resultante de la mejora esperada en el rendimiento.
Sin embargo, todavía hay algunos casos muy específicos en los que el uso de ensamblador todavía está justificado:
Algunos compiladores transforman, cuando su opción de optimización más alta no está habilitada , programas escritos en lenguaje de alto nivel en código ensamblador, cada instrucción de alto nivel resulta en una serie de instrucciones ensambladoras rigurosamente equivalentes y usando los mismos símbolos; esto le permite ver el código con fines de depuración y creación de perfiles , lo que a veces puede ahorrar mucho más tiempo al revisar un algoritmo . En ningún caso se podrán conservar estas técnicas para la optimización final.
La programación de sistemas embebidos , a menudo basados en microcontroladores , es un “nicho” tradicional para la programación de ensamblajes. De hecho, estos sistemas a menudo tienen recursos muy limitados (por ejemplo, un microcontrolador PIC 16F84 está limitado a 1.024 instrucciones de 14 bits y su RAM contiene 136 bytes) y, por lo tanto, requieren una programación de bajo nivel muy optimizada para aprovechar sus posibilidades. Sin embargo, la evolución del hardware hace que los componentes de estos sistemas se vuelvan cada vez más potentes a un coste constante y con un consumo energético constante, la inversión en una programación "cualquier ensamblador" mucho más cara en horas de trabajo se convierte entonces en una inversión tonterías en términos de esfuerzo. Normalmente, la programación en ensamblador es mucho más larga, más delicada (porque el programador debe tener en cuenta todos los microdetalles del desarrollo de los que se abstiene en el lenguaje de alto nivel) y, por lo tanto, considerablemente más cara que la programación en lenguaje de alto nivel. Por lo tanto, solo debe reservarse para situaciones para las que no se puede hacer de otra manera.
Muchos ensambladores admiten un lenguaje de macros . Se trata de agrupar varias instrucciones para tener una secuencia más lógica y menos tediosa.
Por ejemplo (en ensamblador MASM de Microsoft ):
es una macro que muestra un carácter en MS-DOS . Se utilizará, por ejemplo, de la siguiente manera:
Y generará:
mov dl,"X" mov ah,2 int 21hUna pseudoinstrucción es un tipo especial de macroinstrucción. Está predefinido por el editor del software de ensamblaje y su función es emular una instrucción faltante del procesador o facilitar el uso de una instrucción existente. Como la pseudoinstrucción tiene un nombre muy similar al de una instrucción de procesador real, a primera vista es posible confundirla con una de estas últimas. Por ejemplo, es posible que un procesador RISC no tenga una instrucción JMP, lo que le permite saltar a un punto particular del programa y continuar ejecutándolo en secuencia. En este caso, el editor de software habrá creado para el programador una pseudoinstrucción “JMP <parámetro>”, que será reemplazada durante el ensamblaje por una instrucción “mov pc , <parámetro>”, siendo pc la instrucción de puntero a punto de ser ejecutado. Otro ejemplo, una pseudoinstrucción “PUSH <parámetro>” será reemplazada por un almacenamiento de <parámetro> en la dirección apuntada por sp con pre-decremento de esta última, siendo sp el puntero de pila del procesador.
En microprocesadores o microcontroladores RISC como los de la familia ARM , no existe una instrucción de ensamblaje que permita cargar cualquier constante inmediata en un registro, independientemente de su valor. La mayoría de los ensambladores tienen una pseudoinstrucción que permite tal carga de la manera más eficiente posible en términos de tiempo de ejecución, ahorrándole esta tarea al programador.
Soporte para programación estructurada : algunos elementos de programación estructurada han sido integrados para codificar el flujo de ejecución por Dr. HD Mills (Marzo de 1970), e implementado por Marvin Kessler, quien extendió el ensamblador de macros S / 360 con if / else / endif e incluso bloques de control de flujo. Esta fue una forma de reducir o eliminar el uso de operaciones de salto en código ensamblador.