El término ensamblador (del inglés assembler) se refiere a
un tipo de programa informático que se encarga de traducir un fichero fuente
escrito en un lenguaje ensamblador, a un fichero objeto que contiene código
máquina, ejecutable directamente por el microprocesador.
Funcionamiento
El programa lee el fichero escrito en lenguaje ensamblador y
sustituye cada uno de los códigos nemotécnicos que aparecen por su código de
operación correspondiente en sistema binario para la plataforma que se eligió
como destino en las opciones específicas del ensamblador.
Tipos de ensambladores
Podemos distinguir entre dos tipos de ensambladores:
• Ensambladores básicos. Son de muy bajo
nivel, y su tarea consiste básicamente en ofrecer nombres simbólicos a las
distintas instrucciones, parámetros y cosas tales como los modos.
• Ensambladores modulares 32-bits o de alto
nivel. Son ensambladores que aparecieron como respuesta a una nueva
arquitectura de procesadores de 32 bits, muchos de ellos teniendo
compatibilidad hacia atrás pudiendo trabajar con programas con estructuras de
16 bits. Además de realizar la misma tarea que los anteriores, permitiendo
también el uso de macros, permiten utilizar estructuras de programación más
complejas propias de los lenguajes de alto nivel.
Lenguaje ensamblador
El lenguaje ensamblador, o assembler (assembly language en
inglés), es un lenguaje de programación de bajo nivel para los computadores,
microprocesadores, micro controladores y otros circuitos integrados programables.
Implementa una representación simbólica de los máquina binarios y otras
constantes necesarias para programar una arquitectura dada de CPU y constituye
la representación más directa del código máquina específico para cada
arquitectura legible por un programador. Esta representación es usualmente
definida por el fabricante de hardware, y está basada en los mnemónicos que
simbolizan los pasos de procesamiento (las instrucciones), los registros del
procesador, las posiciones de memoria y otras características del lenguaje. Un
lenguaje ensamblador es por lo tanto específico de cierta arquitectura de
computador física (o virtual). Esto está en contraste con la mayoría de los
lenguajes de programación de alto nivel, que idealmente son portátiles.
Un programa utilitario llamado ensamblador es usado para
traducir sentencias del lenguaje ensamblador al código de máquina del
computador objetivo. El ensamblador realiza una traducción más o menos isomorfa
(un mapeo de uno a uno) desde las sentencias mnemónicas a las instrucciones y
datos de máquina. Esto está en contraste con los lenguajes de alto nivel, en
los cuales una sola declaración generalmente da lugar a muchas instrucciones de
máquina.
Muchos sofisticados ensambladores ofrecen mecanismos
adicionales para facilitar el desarrollo del programa, controlar el proceso de
ensamblaje, y la ayuda de depuración. Particularmente, la mayoría de los
ensambladores modernos incluyen una facilidad de macro (descrita más abajo), y
son llamados macro ensambladores.
Fue usado principalmente en los inicios del desarrollo de
software, cuando aún no se contaba con potentes lenguajes de alto nivel y los
recursos eran limitados. Actualmente se utiliza con frecuencia en ambientes
académicos y de investigación, especialmente cuando se requiere la manipulación
directa de hardware, alto rendimiento, o un uso de recursos controlado y
reducido.
Características
• El código
escrito en lenguaje ensamblador posee una cierta dificultad de ser entendido ya
que su estructura se acerca al lenguaje máquina, es decir, es un lenguaje de
bajo nivel.
• El
lenguaje ensamblador es difícilmente portable, es decir, un código escrito para
un microprocesador, puede necesitar ser modificado, para poder ser usado en
otra máquina distinta. Al cambiar a una máquina con arquitectura diferente,
generalmente es necesario reescribirlo completamente.
• Los
programas hechos por un programador experto en lenguaje ensamblador son
generalmente mucho más rápidos y consumen menos recursos del sistema (memoria
RAM y ROM) que el programa equivalente compilado desde un lenguaje de alto
nivel. Al programar cuidadosamente en lenguaje ensamblador se pueden crear
programas que se ejecutan más rápidamente y ocupan menos espacio que con
lenguajes de alto nivel.
• Con el
lenguaje ensamblador se tiene un control muy preciso de las tareas realizadas
por un microprocesador por lo que se pueden crear segmentos de código difíciles
y/o muy ineficientes de programar en un lenguaje de alto nivel, ya que, entre
otras cosas, en el lenguaje ensamblador se dispone de instrucciones del CPU que
generalmente no están disponibles en los lenguajes de alto nivel.
• También
se puede controlar el tiempo en que tarda una rutina en ejecutarse, e impedir
que se interrumpa durante su ejecución.
Programa ensamblador
Típicamente, un programa ensamblador (assembler en inglés)
moderno crea código objeto traduciendo instrucciones mnemónicas de lenguaje
ensamblador en opcodes, y resolviendo los nombres simbólicos para las
localizaciones de memoria y otras entidades.1 El uso de referencias simbólicas
es una característica clave del lenguaje ensamblador, evitando tediosos
cálculos y actualizaciones manuales de las direcciones después de cada
modificación del programa. La mayoría de los ensambladores también incluyen facilidades
de macros para realizar sustitución textual - ej. generar cortas secuencias de
instrucciones como expansión en línea en vez de llamar a subrutinas.
Los ensambladores son generalmente más simples de escribir
que los compiladores para los lenguajes de alto nivel, y han estado disponibles
desde los años 1950. Los ensambladores modernos, especialmente para las
arquitecturas basadas en RISC, tales como MIPS, Sun SPARC, y HP PA-RISC, así
como también para el x86 (-64), optimizan la planificación de instrucciones
para explotar la segmentación del CPU eficientemente.
En los compiladores para lenguajes de alto nivel, son el
último paso antes de generar el código ejecutable.
Número de
pasos
Hay dos tipos de ensambladores basados en cuántos pasos a
través de la fuente son necesarios para producir el programa ejecutable.
• Los
ensambladores de un solo paso pasan a través del código fuente una vez y asumen
que todos los símbolos serán definidos antes de cualquier instrucción que los
refiera.
• Los
ensambladores de dos pasos crean una tabla con todos los símbolos y sus valores
en el primer paso, después usan la tabla en un segundo paso para generar
código. El ensamblador debe por lo menos poder determinar la longitud de cada
instrucción en el primer paso para que puedan ser calculadas las direcciones de
los símbolos.
La ventaja de un ensamblador de un solo paso es la
velocidad, que no es tan importante como lo fue en un momento dados los avances
en velocidad y capacidades del computador. La ventaja del ensamblador de dos
pasos es que los símbolos pueden ser definidos dondequiera en el código fuente
del programa. Esto permite a los programas ser definidos de maneras más lógicas
y más significativas, haciendo los programas de ensamblador de dos paso más
fáciles leer y mantener.
Ensambladores
de alto nivel
Los más sofisticados ensambladores de alto nivel
proporcionan abstracciones del lenguaje tales como:
• Estructuras
de control avanzadas
• Declaraciones
e invocaciones de procedimientos/funciones de alto nivel
• Tipos de
datos abstractos de alto nivel, incluyendo las estructuras/records, uniones,
clases, y conjuntos
• Procesamiento
de macros sofisticado (aunque está disponible en los ensambladores ordinarios
desde finales 1960 para el IBM/360, entre otras máquinas)
• Características
de programación orientada a objetos
Uso del
término
Note que, en el uso profesional normal, el término
ensamblador es frecuentemente usado tanto para referirse al lenguaje
ensamblador como también al programa ensamblador (que convierte el código
fuente escrito en el lenguaje ensamblador a código objeto que luego será
enlazado para producir lenguaje de máquina). Las dos expresiones siguientes
utilizan el término "ensamblador":
• "El
CP/CMS fue escrito en ensamblador del IBM S/360"
• "El
ASM-H fue un ensamblador del S/370 ampliamente usado"
La primera se refiere al lenguaje y la segundo se refiere al
programa.
INSTRUCCIONES
DE CPU
La mayoría de las CPU tienen más o menos los mismos grupos
de instrucciones, aunque no necesariamente tienen todas las instrucciones de
cada grupo. Las operaciones que se pueden realizar varían de una CPU a otra.
Una CPU particular puede tener instrucciones que no tenga otro y viceversa. Los
primeros microprocesadores de 8 bits no tenían operaciones para multiplicar o
dividir números, por ejemplo, y había que hacer subrutinas para realizar esas
operaciones. Otras CPU puede que no tengan operaciones de punto flotante y
habría que hacer o conseguir bibliotecas que realicen esas operaciones.
Las instrucciones de la CPU pueden agruparse, de acuerdo a
su funcionalidad, en:
Operaciones con
enteros: (de 8, 16, 32 y 64 bits dependiendo de la arquitectura de la CPU,
en los sistemas muy viejos también de 12, 18, 24, 36 y 48 bits)
Estas son operaciones realizadas por la Unidad aritmético
lógica de la CPU
• Operaciones aritméticas. Como suma, resta, multiplicación,
división, módulo, cambio de signo
• Operaciones booleanas. Operaciones lógicas bit a bit como
AND, OR, XOR, NOT
• Operaciones de bits. Como desplazamiento y rotaciones de
bits (hacia la derecha o hacia la izquierda, a través del bit del acarreo o sin
él)
• Comparaciones
Operaciones
de mover datos:
Entre los registros y la memoria:
Aunque la instrucción se llama "mover", en la CPU,
"mover datos" significa en realidad copiar datos, desde un origen a
un destino, sin que el dato desaparezca del origen.
Se pueden mover valores:
• desde un
registro a otro
• desde un
registro a un lugar de la memoria
• desde un
lugar de la memoria a un registro
• desde un
lugar a otro de la memoria
• un valor
inmediato a un registro
• un valor
inmediato a un lugar de memoria
Operaciones
de stack:
• PUSH
(escribe datos hacia el tope del stack)
• POP (lee
datos desde el tope del stack)
Operaciones
de entrada/salida:
Son operaciones que mueven datos de un registro, desde y
hacia un puerto; o de la memoria, desde y hacia un puerto
• INPUT
Lectura desde un puerto de entrada
• OUTPUT
Escritura hacia un puerto de salida.
Operaciones
para el control del flujo del programa:
• Llamadas
y retornos de subrutinas
• Llamadas
y retornos de interrupciones
• Saltos
condicionales de acuerdo al resultado de la comparaciones
• Saltos
incondicionales
Operaciones con
números reales:
El estándar para las operaciones con números reales en las
CPU está definido por el IEEE 754.
Una CPU puede tener operaciones de punto flotante con
números reales mediante el coprocesador numérico (si lo hay), como las
siguientes:
• Operaciones
aritméticas. Suma, resta, multiplicación, división, cambio de signo, valor
absoluto, parte entera
• Operaciones
trascendentales
Diseño del
lenguaje y Elementos básicos
Hay un grado grande de diversidad en la manera en que los
autores de los ensambladores categorizan las sentencias y en la nomenclatura
que usan. En particular, algunos describen cualquier cosa como pseudo-operación
(pseudo-Op), con excepción del mnemónico de máquina o del mnemónico extendido.
Un típico lenguaje ensamblador consiste en 3 tipos de
sentencias de instrucción que son usadas para definir las operaciones del
programa:
• Mnemónicos
de opcode
• Secciones
de datos
• Directivas
de ensamblador
Mnemónicos de opcode y mnemónicos extendidos
A diferencia de las instrucciones (sentencias) de los
lenguajes de alto nivel, instrucciones en el lenguaje ensamblador son
generalmente muy simples. Generalmente, una mnemónico es un nombre simbólico
para una sola instrucción en lenguaje de máquina ejecutable (un opcode), y hay
por lo menos un mnemónico de opcode definido para cada instrucción en lenguaje
de máquina. Cada instrucción consiste típicamente en una operación u opcode más
cero o más operandos. La mayoría de las instrucciones refieren a un solo valor,
o a un par de valores. Los operandos pueden ser inmediatos (típicamente valores
de un byte, codificados en la propia instrucción), registros especificados en
la instrucción, implícitos o las direcciones de los datos localizados en otra
parte de la memoria. Esto está determinado por la arquitectura subyacente del
procesador, el ensamblador simplemente refleja cómo trabaja esta arquitectura.
Los mnemónicos extendidos son frecuentemente usados para especificar una
combinación de un opcode con un operando específico, ej, el ensamblador del
System/360 usa a B como un mnemónico extendido para el BC con una máscara de 15
y NOP al BC con una máscara de 0.