Lenguaje ensamblador
En programación informática, lenguaje ensamblador (o lenguaje ensamblador, o código de máquina simbólico), a menudo denominado simplemente como ensamblado y comúnmente abreviado como ASM o asm, es cualquier lenguaje de programación de bajo nivel con una fuerte correspondencia entre las instrucciones del lenguaje y la arquitectura' s instrucciones de código de máquina. El lenguaje ensamblador generalmente tiene una declaración por instrucción de máquina (1: 1), pero generalmente también se admiten constantes, comentarios, directivas del ensamblador, etiquetas simbólicas de, por ejemplo, ubicaciones de memoria, registros y macros.
El primer código ensamblador en el que se usa un lenguaje para representar instrucciones de código de máquina se encuentra en el trabajo de Kathleen y Andrew Donald Booth de 1947, Coding for A.R.C.. El código ensamblador se convierte en código de máquina ejecutable mediante un programa de utilidad denominado ensamblador. El término "ensamblador" se atribuye generalmente a Wilkes, Wheeler y Gill en su libro de 1951 La preparación de programas para una computadora digital electrónica, quienes, sin embargo, usaron el término para referirse a "un programa que ensambla otro programa que consiste en de varias secciones en un solo programa". El proceso de conversión se denomina ensamblaje, como en ensamblaje del código fuente. El paso computacional cuando un ensamblador está procesando un programa se llama tiempo de ensamblaje.
Debido a que el ensamblado depende de las instrucciones del código máquina, cada lenguaje ensamblador es específico para una arquitectura de computadora en particular.
A veces hay más de un ensamblador para la misma arquitectura y, a veces, un ensamblador es específico para un sistema operativo o para sistemas operativos en particular. La mayoría de los lenguajes ensambladores no proporcionan una sintaxis específica para las llamadas al sistema operativo, y la mayoría de los lenguajes ensambladores se pueden usar universalmente con cualquier sistema operativo, ya que el lenguaje brinda acceso a todas las capacidades reales del procesador, sobre las cuales descansan en última instancia todos los mecanismos de llamada al sistema. A diferencia de los lenguajes ensambladores, la mayoría de los lenguajes de programación de alto nivel generalmente son portátiles a través de múltiples arquitecturas pero requieren interpretación o compilación, tareas mucho más complicadas que el ensamblaje.
En las primeras décadas de la informática, era común que tanto la programación de sistemas como la programación de aplicaciones se realizaran completamente en lenguaje ensamblador. Si bien todavía es irremplazable para algunos propósitos, la mayoría de la programación ahora se lleva a cabo en lenguajes interpretados y compilados de alto nivel. En "No Silver Bullet", Fred Brooks resumió los efectos del abandono de la programación en lenguaje ensamblador: "Seguramente el golpe más poderoso para la productividad, confiabilidad y simplicidad del software ha sido el uso progresivo de alta -Lenguajes de nivel para programar. La mayoría de los observadores acreditan ese desarrollo con al menos un factor de cinco en productividad, y con ganancias concomitantes en confiabilidad, simplicidad y comprensibilidad."
Hoy en día, es típico usar pequeñas cantidades de código de lenguaje ensamblador dentro de sistemas más grandes implementados en un lenguaje de nivel superior, por motivos de rendimiento o para interactuar directamente con el hardware de formas no admitidas por el lenguaje de nivel superior. Por ejemplo, poco menos del 2% de la versión 4.9 del código fuente del kernel de Linux está escrito en ensamblador; más del 97% está escrito en C.
Sintaxis del lenguaje ensamblador
El lenguaje ensamblador usa un mnemotécnico para representar, por ejemplo, cada instrucción de máquina de bajo nivel o código de operación, cada directiva, generalmente también cada registro arquitectónico, bandera, etc. Algunos de los mnemotécnicos pueden estar integrados y otros definidos por el usuario. Muchas operaciones requieren uno o más operandos para formar una instrucción completa. La mayoría de los ensambladores permiten constantes con nombre, registros y etiquetas para ubicaciones de programa y memoria, y pueden calcular expresiones para operandos. Por lo tanto, los programadores se liberan de los tediosos cálculos repetitivos y los programas ensambladores son mucho más legibles que el código de máquina. Dependiendo de la arquitectura, estos elementos también pueden combinarse para instrucciones específicas o modos de direccionamiento utilizando compensaciones u otros datos, así como direcciones fijas. Muchos ensambladores ofrecen mecanismos adicionales para facilitar el desarrollo de programas, controlar el proceso de ensamblaje y ayudar en la depuración.
Algunos están orientados a columnas, con campos específicos en columnas específicas; esto era muy común para las máquinas que usaban tarjetas perforadas en la década de 1950 y principios de la de 1960. Algunos ensambladores tienen sintaxis de forma libre, con campos separados por delimitadores, por ejemplo, puntuación, espacios en blanco. Algunos ensambladores son híbridos, con, por ejemplo, etiquetas, en una columna específica y otros campos separados por delimitadores; esto se volvió más común que la sintaxis orientada a columnas en la década de 1960.
Sistema IBM/360
Todos los ensambladores de IBM para System/360, por defecto, tienen una etiqueta en la columna 1, campos separados por delimitadores en las columnas 2-71, un indicador de continuación en la columna 72 y un número de secuencia en las columnas 73-80. El delimitador para etiqueta, código de operación, operandos y comentarios son espacios, mientras que los operandos individuales están separados por comas y paréntesis.
Terminología
- A macro ensamblador es un ensamblador que incluye una instalación de macroinstrucción para que (parametrado) texto de lenguaje de montaje pueda ser representado por un nombre, y ese nombre se puede utilizar para insertar el texto expandido en otro código.
- Código abierto se refiere a cualquier entrada de ensamblador fuera de una definición macro.
- A ensamblador cruzado (ver también el compilador cruzado) es un ensamblador que se ejecuta en un ordenador o sistema operativo (el anfitrión sistema) de un tipo diferente del sistema en el que se ejecuta el código resultante (el sistema de destino). El montaje cruzado facilita el desarrollo de programas para sistemas que no tienen los recursos para apoyar el desarrollo del software, como un sistema integrado o un microcontrolador. En tal caso, el código de objeto resultante debe ser transferido al sistema de destino, a través de memoria sólo lectura (ROM, EPROM, etc.), un programador (cuando la memoria sólo lectura se integra en el dispositivo, como en microcontroladores), o un enlace de datos usando una copia exacta bit-by-bit del código de objeto o una representación basada en texto de ese código (como Intel hex o Motorola S-record).
- A ensamblador de alto nivel es un programa que proporciona abstracciones lingüísticas más a menudo asociadas con lenguajes de alto nivel, como estructuras de control avanzadas (IF/THEN/ELSE, DO CASE, etc.) y tipos de datos abstractos de alto nivel, incluyendo estructuras/recordes, sindicatos, clases y conjuntos.
- A microassembler es un programa que ayuda a preparar un microprograma, llamado firmware, para controlar la operación de bajo nivel de un ordenador.
- A meta-assembler es "un programa que acepta la descripción sintáctica y semántica de un lenguaje de ensamblaje, y genera un ensamblador para ese idioma", o que acepta un archivo de origen ensamblador junto con tal descripción y reúne el archivo fuente de acuerdo con esa descripción. Los ensambladores "Meta-Symbol" para la serie SDS 9 y SDS Sigma de ordenadores son metáforas. Sperry Univac también proporcionó un Meta-Assembler para la serie UNIVAC 1100/2200.
- ensamblador inline (o montador embebido) es código de ensamblador contenido en un programa de idiomas de alto nivel. Esto se utiliza más a menudo en programas de sistemas que necesitan acceso directo al hardware.
Conceptos clave
Ensamblador
Un programa ensamblador crea código objeto traduciendo combinaciones de mnemónicos y sintaxis para operaciones y modos de direccionamiento en sus equivalentes numéricos. Esta representación suele incluir un código de operación ("opcode"), así como otros bits de control y datos. El ensamblador también calcula expresiones constantes y resuelve nombres simbólicos para ubicaciones de memoria y otras entidades. El uso de referencias simbólicas es una característica clave de los ensambladores, ahorrando cálculos tediosos y actualizaciones manuales de direcciones después de las modificaciones del programa. La mayoría de los ensambladores también incluyen funciones de macro para realizar la sustitución textual, por ejemplo, para generar secuencias cortas comunes de instrucciones en línea, en lugar de subrutinas llamadas.
Algunos ensambladores también pueden realizar algunos tipos simples de optimizaciones específicas del conjunto de instrucciones. Un ejemplo concreto de esto pueden ser los ubicuos ensambladores x86 de varios proveedores. Llamados jump-sizing, la mayoría de ellos pueden realizar reemplazos de instrucciones de salto (saltos largos reemplazados por saltos cortos o relativos) en cualquier número de pases, a pedido. Otros pueden incluso realizar una simple reorganización o inserción de instrucciones, como algunos ensambladores para arquitecturas RISC que pueden ayudar a optimizar una programación de instrucciones sensata para explotar la canalización de la CPU de la manera más eficiente posible.
Los ensambladores han estado disponibles desde la década de 1950, como el primer paso por encima del lenguaje de máquina y antes de los lenguajes de programación de alto nivel como Fortran, Algol, COBOL y Lisp. También ha habido varias clases de traductores y generadores de código semiautomáticos con propiedades similares a los lenguajes ensamblador y de alto nivel, con Speedcode como quizás uno de los ejemplos más conocidos.
Puede haber varios ensambladores con sintaxis diferente para una arquitectura de conjunto de instrucciones o CPU en particular. Por ejemplo, una instrucción para agregar datos de memoria a un registro en un procesador de la familia x86 podría ser add eax,[ebx]
, en la sintaxis Intel original, mientras que esto sería escrito addl (%ebx),%eax
en la sintaxis de AT&T utilizada por GNU Assembler. A pesar de las diferentes apariencias, las diferentes formas sintácticas generalmente generan el mismo código numérico de máquina. Un solo ensamblador también puede tener diferentes modos para admitir variaciones en las formas sintácticas, así como sus interpretaciones semánticas exactas (como la sintaxis FASM, la sintaxis TASM, el modo ideal, etc., en el caso especial de programación de ensamblaje x86).
Número de pases
Hay dos tipos de ensambladores basados en cuántas pasadas a través de la fuente se necesitan (cuántas veces el ensamblador lee la fuente) para producir el archivo de objeto.
- Montadores de paso único procesar el código fuente una vez. Para los símbolos utilizados antes de definirlos, el ensamblador emitirá "errata" después de la definición final, diciéndole al enlace o al cargador que parche los lugares donde se habían utilizado los símbolos aún no definidos.
- Montadores multipaso crear una tabla con todos los símbolos y sus valores en los primeros pases, luego utilizar la tabla en los pases posteriores para generar código.
En ambos casos, el ensamblador debe poder determinar el tamaño de cada instrucción en los pases iniciales para calcular las direcciones de los símbolos subsiguientes. Esto significa que si el tamaño de una operación que se refiere a un operando definido posteriormente depende del tipo o la distancia del operando, el ensamblador hará una estimación pesimista cuando encuentre la operación por primera vez y, si es necesario, la rellenará con uno o más "sin operación" instrucciones en un pase posterior o la fe de erratas. En un ensamblador con optimización de mirilla, las direcciones se pueden volver a calcular entre pases para permitir reemplazar el código pesimista con un código adaptado a la distancia exacta del objetivo.
La razón original para el uso de ensambladores de un solo paso fue el tamaño de la memoria y la velocidad de ensamblaje; a menudo, un segundo paso requeriría almacenar la tabla de símbolos en la memoria (para manejar las referencias hacia adelante), rebobinar y volver a leer la fuente del programa en la cinta, o releyendo una baraja de cartas o una cinta de papel perforada. Las computadoras posteriores con memorias mucho más grandes (especialmente almacenamiento en disco) tenían el espacio para realizar todo el procesamiento necesario sin tal relectura. La ventaja del ensamblador de pasos múltiples es que la ausencia de erratas hace que el proceso de vinculación (o la carga del programa si el ensamblador produce directamente el código ejecutable) sea más rápido.
Ejemplo: en el siguiente fragmento de código, un ensamblador de un solo paso podría determinar la dirección de la referencia anterior BKWD al ensamblar la instrucción S2, pero no podría determinar la dirección de la referencia directa FWD al ensamblar la declaración de rama S1; de hecho, FWD puede no estar definido. Un ensamblador de dos pasos determinaría ambas direcciones en el paso 1, por lo que se conocerían al generar el código en el paso 2.
S1 B FWD... FWD EQU * ... BKWD EQU * ... S2 B BKWD
Montadores de alto nivel
Los ensambladores de alto nivel más sofisticados proporcionan abstracciones de lenguaje como:
- Declaraciones e invocaciones sobre procedimientos o funciones de alto nivel
- Estructuras de control avanzadas (IF/THEN/ELSE, SWITCH)
- Tipos de datos abstractos de alto nivel, incluyendo estructuras/recordes, sindicatos, clases y conjuntos
- Procesamiento macro sofisticado (aunque disponible en montadores ordinarios desde finales de la década de 1950 para, por ejemplo, la serie IBM 700 y la serie IBM 7000, y desde la década de 1960 para IBM System/360 (S/360), entre otras máquinas)
- Características de programación orientadas a objetos como clases, objetos, abstracción, polimorfismo y herencia
Consulte Diseño de lenguaje a continuación para obtener más detalles.
Lenguaje ensamblador
Un programa escrito en lenguaje ensamblador consta de una serie de instrucciones de procesador mnemotécnicas y metainstrucciones (conocidas como operaciones declarativas, directivas, pseudoinstrucciones, pseudooperaciones y pseudooperaciones), comentarios y datos. Las instrucciones del lenguaje ensamblador generalmente consisten en un nemotécnico de código de operación seguido de un operando, que puede ser una lista de datos, argumentos o parámetros. Algunas instrucciones pueden ser "implícitas" lo que significa que los datos sobre los que opera la instrucción están implícitamente definidos por la propia instrucción; tal instrucción no toma un operando. La declaración resultante es traducida por un ensamblador a instrucciones en lenguaje de máquina que pueden cargarse en la memoria y ejecutarse.
Por ejemplo, la siguiente instrucción le dice a un procesador x86/IA-32 que mueva un valor inmediato de 8 bits a un registro. El código binario para esta instrucción es 10110 seguido de un identificador de 3 bits para qué registro usar. El identificador del registro AL es 000, por lo que el siguiente código de máquina carga el registro AL con los datos 01100001.
10110000 01100001
Este código informático binario se puede hacer más legible para los humanos expresándolo en hexadecimal de la siguiente manera.
B0 61
Aquí, B0
significa 'Mover una copia del siguiente valor a AL, y 61
es una representación hexadecimal del valor 01100001, que es 97 en decimal. El lenguaje ensamblador para la familia 8086 proporciona el mnemotécnico MOV (una abreviatura de mover) para instrucciones como esta, por lo que el código de máquina anterior se puede escribir de la siguiente manera en lenguaje ensamblador, completo con un comentario explicativo si es necesario, después del punto y coma. Esto es mucho más fácil de leer y recordar.
MOVIMIENTO AL, 61h ; Carga AL con 97 decimal (61 hex)
En algunos lenguajes ensambladores (incluido este), el mismo mnemónico, como MOV, se puede usar para una familia de instrucciones relacionadas para cargar, copiar y mover datos, ya sean valores inmediatos, valores en registros o ubicaciones de memoria. señalado por valores en registros o por direcciones inmediatas (también conocidas como directas). Otros ensambladores pueden usar mnemónicos de código de operación separados como L para "mover memoria a registro", ST para "mover registro a memoria", LR para "mover registro a registro", MVI para "mover el operando inmediato a la memoria", etc.
Si se usa el mismo mnemónico para diferentes instrucciones, eso significa que el mnemónico corresponde a varios códigos de instrucción binarios diferentes, excluyendo datos (por ejemplo, el 61h
en este ejemplo), dependiendo de los operandos que siguen el mnemotécnico. Por ejemplo, para las CPU x86/IA-32, la sintaxis del lenguaje ensamblador de Intel MOV AL, AH
representa una instrucción que mueve el contenido del registro AH al registro AL. La forma hexadecimal de esta instrucción es:
88 E0
El primer byte, 88h, identifica un movimiento entre un registro del tamaño de un byte y otro registro o memoria, y el segundo byte, E0h, está codificado (con tres campos de bits) para especificar que ambos operandos son registros, el el origen es AH y el destino es AL.
En un caso como este, donde el mismo mnemotécnico puede representar más de una instrucción binaria, el ensamblador determina qué instrucción generar examinando los operandos. En el primer ejemplo, el operando 61h
es una constante numérica hexadecimal válida y no es un nombre de registro válido, por lo que solo se puede aplicar la instrucción B0
. En el segundo ejemplo, el operando AH
es un nombre de registro válido y no una constante numérica válida (hexadecimal, decimal, octal o binaria), por lo que solo la instrucción 88
puede ser aplicable.
Los lenguajes ensambladores siempre están diseñados para que este tipo de falta de ambigüedad se imponga universalmente mediante su sintaxis. Por ejemplo, en el lenguaje ensamblador Intel x86, una constante hexadecimal debe comenzar con un dígito numérico, de modo que el número hexadecimal 'A' (igual al decimal diez) se escribiría como 0Ah
o 0AH
, no AH
, específicamente para que no parezca ser el nombre del registro AH. (La misma regla también evita la ambigüedad con los nombres de los registros BH, CH y DH, así como con cualquier símbolo definido por el usuario que termina con la letra H y contiene solo caracteres que son dígitos hexadecimales, como la palabra "BEACH").
Volviendo al ejemplo original, mientras que el código de operación x86 10110000 (B0
) copia un valor de 8 bits en el registro AL, 10110001 (B1
) lo mueve a CL y 10110010 (B2
) lo hace a DL. A continuación se muestran ejemplos en lenguaje ensamblador para estos.
MOVIMIENTO AL, 1h ; Carga AL con valor inmediato 1MOVIMIENTO CL, 2h ; Carga CL con valor inmediato 2MOVIMIENTO DL, 3h ; Carga DL con valor inmediato 3
La sintaxis de MOV también puede ser más compleja, como muestran los siguientes ejemplos.
MOVIMIENTO EAX, [EBX] ; Mover los 4 bytes en memoria en la dirección contenida en EBX en EAXMOVIMIENTO [ESI+EAX] CL ; Mover el contenido de CL en el byte en la dirección ESI+EAXMOVIMIENTO DS, DX ; Mover el contenido de DX en registro de segmentos DS
En cada caso, el mnemotécnico MOV se traduce directamente a uno de los códigos de operación 88-8C, 8E, A0-A3, B0-BF, C6 o C7 por un ensamblador, y el programador normalmente no tiene que saberlo ni recordarlo. cuales.
Transformar el lenguaje ensamblador en código de máquina es el trabajo de un ensamblador, y un desensamblador puede lograr, al menos parcialmente, lo contrario. A diferencia de los lenguajes de alto nivel, existe una correspondencia uno a uno entre muchas declaraciones de ensamblaje simples e instrucciones en lenguaje de máquina. Sin embargo, en algunos casos, un ensamblador puede proporcionar pseudoinstrucciones (esencialmente macros) que se expanden en varias instrucciones de lenguaje de máquina para proporcionar la funcionalidad comúnmente necesaria. Por ejemplo, para una máquina que carece de una "rama si mayor o igual" instrucción, un ensamblador puede proporcionar una pseudoinstrucción que se expande al 'set if less than' de la máquina. y "branch si cero (en el resultado de la instrucción set)". La mayoría de los ensambladores con funciones completas también proporcionan un lenguaje de macros enriquecido (que se analiza a continuación) que utilizan los proveedores y programadores para generar códigos y secuencias de datos más complejos. Dado que la información sobre pseudoinstrucciones y macros definidas en el entorno del ensamblador no está presente en el programa objeto, un desensamblador no puede reconstruir las invocaciones de macros y pseudoinstrucciones, sino que solo puede desensamblar las instrucciones reales de la máquina que el ensamblador generó a partir de esas entidades abstractas del lenguaje ensamblador. Del mismo modo, dado que los comentarios en el archivo fuente del lenguaje ensamblador son ignorados por el ensamblador y no tienen efecto en el código objeto que genera, un desensamblador siempre es completamente incapaz de recuperar los comentarios fuente.
Cada arquitectura informática tiene su propio lenguaje de máquina. Las computadoras difieren en el número y tipo de operaciones que soportan, en los diferentes tamaños y números de registros, y en las representaciones de los datos almacenados. Si bien la mayoría de las computadoras de propósito general pueden realizar esencialmente la misma funcionalidad, las formas en que lo hacen difieren; los lenguajes ensambladores correspondientes reflejan estas diferencias.
Pueden existir múltiples conjuntos de mnemónicos o sintaxis de lenguaje ensamblador para un único conjunto de instrucciones, normalmente instanciadas en diferentes programas ensambladores. En estos casos, el más popular suele ser el suministrado por el fabricante de la CPU y utilizado en su documentación.
Dos ejemplos de CPU que tienen dos conjuntos diferentes de mnemónicos son la familia Intel 8080 y la Intel 8086/8088. Debido a que Intel reclamó los derechos de autor sobre sus mnemotécnicos en lenguaje ensamblador (en cada página de su documentación publicada en la década de 1970 y principios de 1980, al menos), algunas empresas que produjeron CPU de forma independiente compatibles con los conjuntos de instrucciones de Intel inventaron sus propios mnemotécnicos. La CPU Zilog Z80, una mejora de Intel 8080A, admite todas las instrucciones 8080A y muchas más; Zilog inventó un lenguaje ensamblador completamente nuevo, no solo para las nuevas instrucciones sino también para todas las instrucciones del 8080A. Por ejemplo, cuando Intel utiliza los mnemotécnicos MOV, MVI, LDA, STA, LXI, LDAX, STAX, LHLD y SHLD para varias instrucciones de transferencia de datos, el lenguaje ensamblador Z80 utiliza el mnemotécnico LD para todos ellos. Un caso similar son las CPUs NEC V20 y V30, copias mejoradas de las Intel 8086 y 8088, respectivamente. Al igual que Zilog con el Z80, NEC inventó nuevos mnemotécnicos para todas las instrucciones 8086 y 8088, para evitar acusaciones de infracción de los derechos de autor de Intel. (Es cuestionable si dichos derechos de autor pueden ser válidos, y las compañías de CPU posteriores, como AMD y Cyrix, volvieron a publicar los mnemónicos de instrucción x86/IA-32 de Intel exactamente sin permiso ni sanción legal). Es dudoso que en la práctica muchas personas que programaron el V20 y el V30 en realidad escribieron en el lenguaje ensamblador de NEC en lugar del de Intel; dado que dos lenguajes ensambladores para la misma arquitectura de conjunto de instrucciones son isomorfos (algo así como el inglés y el latín pig), no es necesario usar el lenguaje ensamblador publicado por el fabricante con los productos de ese fabricante.
Diseño de lenguaje
Elementos básicos
Existe un alto grado de diversidad en la forma en que los autores de los ensambladores categorizan las declaraciones y en la nomenclatura que utilizan. En particular, algunos describen cualquier cosa que no sea una mnemotecnia de máquina o una mnemotécnica extendida como una pseudooperación (pseudo-op). Un lenguaje ensamblador típico consta de 3 tipos de instrucciones que se utilizan para definir las operaciones del programa:
- Opcode mnemonics
- Definiciones de datos
- Directrices de la Asamblea
Mnemónicos de código de operación y mnemónicos extendidos
Las instrucciones (declaraciones) en lenguaje ensamblador son generalmente muy simples, a diferencia de las de los lenguajes de alto nivel. En general, una regla mnemotécnica es un nombre simbólico para una única instrucción de lenguaje de máquina ejecutable (un código de operación), y hay al menos una regla mnemotécnica de código de operación definida para cada instrucción de lenguaje de máquina. Cada instrucción normalmente consta de una operación o opcode más cero o más operandos. La mayoría de las instrucciones se refieren a un solo valor o a un par de valores. Los operandos pueden ser inmediatos (valor codificado en la instrucción misma), registros especificados en la instrucción o implícitos, o direcciones de datos ubicados en otra parte del almacenamiento. Esto está determinado por la arquitectura del procesador subyacente: el ensamblador simplemente refleja cómo funciona esta arquitectura. Mnemónicos extendidos a menudo se usan para especificar una combinación de un código de operación con un operando específico, por ejemplo, los ensambladores de System/360 usan B
como mnemónico extendido para BC
con una máscara de 15 y NOP
("NO OPERACIÓN" – no hacer nada en un paso) para BC
con una máscara de 0.
Mnemónicos extendidos a menudo se utilizan para apoyar usos especializados de instrucciones, a menudo para propósitos que no son obvios a partir del nombre de la instrucción. Por ejemplo, muchas CPU no tienen una instrucción NOP explícita, pero tienen instrucciones que se pueden usar para ese propósito. En las CPU 8086, la instrucción xchg hacha,hacha
se usa para nop
, con nop
siendo un pseudo-opcode para codificar la instrucción xchg ax, ax
. Algunos desensambladores reconocen esto y decodificarán el xchg hacha,ax
instrucción como nop. De manera similar, los ensambladores de IBM para System/360 y System/370 utilizan los mnemónicos extendidos
NOP
y NOPR
para BC
y BCR
con cero máscaras. Para la arquitectura SPARC, se conocen como instrucciones sintéticas.
Algunos ensambladores también admiten macroinstrucciones integradas simples que generan dos o más instrucciones de máquina. Por ejemplo, con algunos ensambladores Z80, la instrucción ld hl,bc
se reconoce para generar ld l,c
seguido por ld h,b
. Estos a veces se conocen como pseudo-opcodes.
Los mnemotécnicos son símbolos arbitrarios; en 1985, el IEEE publicó el estándar 694 para un conjunto uniforme de mnemotécnicos para ser utilizado por todos los ensambladores. Desde entonces, el estándar ha sido retirado.
Directivas de datos
Hay instrucciones que se utilizan para definir elementos de datos para contener datos y variables. Definen el tipo de datos, la longitud y la alineación de los datos. Estas instrucciones también pueden definir si los datos están disponibles para programas externos (programas ensamblados por separado) o solo para el programa en el que se define la sección de datos. Algunos ensambladores los clasifican como pseudooperaciones.
Directivas de ensamblado
Las directivas de ensamblaje, también denominadas pseudocódigos de operación, pseudooperaciones o pseudooperaciones, son comandos que se dan a un ensamblador "que le indican que realice operaciones distintas de las instrucciones de ensamblaje". Las directivas afectan la forma en que opera el ensamblador y "pueden afectar el código objeto, la tabla de símbolos, el archivo de listado y los valores de los parámetros internos del ensamblador". A veces, el término pseudo-opcode se reserva para las directivas que generan código objeto, como las que generan datos.
Los nombres de las pseudooperaciones a menudo comienzan con un punto para distinguirlas de las instrucciones de máquina. Las pseudooperaciones pueden hacer que el ensamblaje del programa dependa de los parámetros ingresados por un programador, de modo que un programa se pueda ensamblar de diferentes maneras, quizás para diferentes aplicaciones. O bien, se puede usar una pseudo-operación para manipular la presentación de un programa para que sea más fácil de leer y mantener. Otro uso común de pseudo-ops es reservar áreas de almacenamiento para datos en tiempo de ejecución y, opcionalmente, inicializar su contenido a valores conocidos.
Los ensambladores simbólicos permiten a los programadores asociar nombres arbitrarios (etiquetas o símbolos) con ubicaciones de memoria y varias constantes. Por lo general, a cada constante y variable se le da un nombre para que las instrucciones puedan hacer referencia a esas ubicaciones por nombre, promoviendo así el código autodocumentado. En el código ejecutable, el nombre de cada subrutina está asociado con su punto de entrada, por lo que cualquier llamada a una subrutina puede usar su nombre. Dentro de las subrutinas, los destinos GOTO reciben etiquetas. Algunos ensambladores admiten símbolos locales que a menudo son léxicamente distintos de los símbolos normales (por ejemplo, el uso de "10$" como destino GOTO).
Algunos ensambladores, como NASM, brindan una administración de símbolos flexible, lo que permite a los programadores administrar diferentes espacios de nombres, calcular automáticamente las compensaciones dentro de las estructuras de datos y asignar etiquetas que se refieren a valores literales o al resultado de cálculos simples realizados por el ensamblador. Las etiquetas también se pueden usar para inicializar constantes y variables con direcciones reubicables.
Los lenguajes ensambladores, como la mayoría de los demás lenguajes informáticos, permiten agregar comentarios al código fuente del programa que se ignorarán durante el ensamblado. Los comentarios juiciosos son esenciales en los programas en lenguaje ensamblador, ya que el significado y el propósito de una secuencia de instrucciones binarias de máquina pueden ser difíciles de determinar. El "crudo" El lenguaje ensamblador (sin comentarios) generado por compiladores o desensambladores es bastante difícil de leer cuando se deben realizar cambios.
Macros
Muchos ensambladores admiten macros predefinidos, y otros admiten macros definidas por el programador (y repetidamente redefinibles) que involucran secuencias de líneas de texto en las que se incrustan variables y constantes. La definición de macro es más comúnmente una mezcla de declaraciones de ensamblador, por ejemplo, directivas, instrucciones de máquina simbólicas y plantillas para declaraciones de ensamblador. Esta secuencia de líneas de texto puede incluir códigos de operación o directivas. Una vez que se ha definido una macro, se puede usar su nombre en lugar de un mnemotécnico. Cuando el ensamblador procesa tal declaración, reemplaza la declaración con las líneas de texto asociadas con esa macro, luego las procesa como si existieran en el archivo de código fuente (incluyendo, en algunos ensambladores, la expansión de cualquier macro existente en el texto de reemplazo). Las macros en este sentido datan de los autocodificadores de IBM de la década de 1950.
Los ensambladores de macros suelen tener directivas para, por ejemplo, definir macros, definir variables, establecer variables en el resultado de una expresión aritmética, lógica o de cadena, iterar, generar código de forma condicional. Algunas de esas directivas pueden estar restringidas para usar dentro de una definición de macro, por ejemplo, MEXIT en HLASM, mientras que otras pueden estar permitidas dentro de código abierto (fuera de las definiciones de macro), por ejemplo, AIF y COPIAR en HLASM.
En lenguaje ensamblador, el término "macro" representa un concepto más completo que en algunos otros contextos, como el preprocesador en el lenguaje de programación C, donde su directiva #define generalmente se usa para crear macros cortas de una sola línea. Las instrucciones de macros del ensamblador, como las macros en PL/I y algunos otros lenguajes, pueden ser "programas" por sí mismos, ejecutados por interpretación del ensamblador durante el montaje.
Dado que las macros pueden tener 'cortas' nombres pero se expanden a varias o, de hecho, muchas líneas de código, se pueden usar para hacer que los programas en lenguaje ensamblador parezcan mucho más cortos, requiriendo menos líneas de código fuente, como con los lenguajes de nivel superior. También se pueden usar para agregar niveles más altos de estructura a los programas de ensamblaje, opcionalmente introducir código de depuración incrustado a través de parámetros y otras características similares.
Los ensambladores de macros a menudo permiten que las macros tomen parámetros. Algunos ensambladores incluyen lenguajes de macro bastante sofisticados, que incorporan elementos de lenguaje de alto nivel como parámetros opcionales, variables simbólicas, condicionales, manipulación de cadenas y operaciones aritméticas, todos utilizables durante la ejecución de una macro determinada y que permiten que las macros guarden contexto o intercambien información.. Por lo tanto, una macro puede generar numerosas instrucciones en lenguaje ensamblador o definiciones de datos, según los argumentos de la macro. Esto podría usarse para generar estructuras de datos de estilo de registro o "desenrollado" bucles, por ejemplo, o podría generar algoritmos completos basados en parámetros complejos. Por ejemplo, un "ordenar" macro podría aceptar la especificación de una clave de clasificación compleja y generar código diseñado para esa clave específica, sin necesidad de las pruebas de tiempo de ejecución que serían necesarias para un procedimiento general que interpreta la especificación. Se puede considerar que una organización que utiliza lenguaje ensamblador que se ha ampliado considerablemente utilizando un conjunto de macros de este tipo está trabajando en un lenguaje de nivel superior, ya que dichos programadores no están trabajando con los elementos conceptuales de nivel más bajo de una computadora. Para subrayar este punto, se usaron macros para implementar una de las primeras máquinas virtuales en SNOBOL4 (1967), que se escribió en el lenguaje de implementación de SNOBOL (SIL), un lenguaje ensamblador para una máquina virtual. La máquina de destino traduciría esto a su código nativo usando un ensamblador de macros. Esto permitió un alto grado de portabilidad para la época.
Las macros se usaban para personalizar sistemas de software a gran escala para clientes específicos en la era del mainframe y también las usaba el personal del cliente para satisfacer las necesidades de sus empleadores. necesidades haciendo versiones específicas de los sistemas operativos del fabricante. Esto lo hicieron, por ejemplo, los programadores de sistemas que trabajan con el sistema de monitorización conversacional/máquina virtual (VM/CMS) de IBM y con el sistema de procesamiento de transacciones en tiempo real de IBM. complementos, el Sistema de control de información del cliente CICS y ACP/TPF, el sistema financiero/de líneas aéreas que comenzó en la década de 1970 y todavía ejecuta muchos sistemas de reserva por computadora (CRS) y sistemas de tarjetas de crédito de gran tamaño en la actualidad.
También es posible usar únicamente las habilidades de procesamiento de macros de un ensamblador para generar código escrito en lenguajes completamente diferentes, por ejemplo, para generar una versión de un programa en COBOL usando un programa ensamblador de macros puro que contiene líneas de código COBOL en su interior. Operadores de tiempo de ensamblaje que instruyen al ensamblador para generar código arbitrario. IBM OS/360 utiliza macros para realizar la generación del sistema. El usuario especifica las opciones codificando una serie de macros de ensamblador. El ensamblaje de estas macros genera un flujo de trabajo para construir el sistema, incluido el lenguaje de control de trabajo y las declaraciones de control de utilidad.
Esto se debe a que, como se descubrió en la década de 1960, el concepto de "procesamiento de macros" es independiente del concepto de "ensamblado", siendo el primero en términos modernos más procesamiento de textos, procesamiento de texto, que generación de código objeto. El concepto de procesamiento de macros apareció, y aparece, en el lenguaje de programación C, que admite "instrucciones de preprocesador" establecer variables y hacer pruebas condicionales sobre sus valores. A diferencia de ciertos macroprocesadores anteriores dentro de los ensambladores, el preprocesador C no es Turing-completo porque carece de la capacidad de realizar un bucle o 'ir a', lo que permite que los programas realicen un bucle.
A pesar de la potencia del procesamiento de macros, cayó en desuso en muchos lenguajes de alto nivel (las principales excepciones son C, C++ y PL/I) y permaneció perenne para los ensambladores.
La sustitución de parámetros de macro es estrictamente por nombre: en el momento del procesamiento de la macro, el valor de un parámetro se sustituye textualmente por su nombre. La clase más famosa de errores resultantes fue el uso de un parámetro que en sí mismo era una expresión y no un simple nombre cuando el escritor de macros esperaba un nombre. En la macro:
foo: macro a cargar a*b
la intención era que la persona que llama proporcionara el nombre de una variable y el "global" la variable o constante b se usaría para multiplicar "a". Si se llama a foo con el parámetro a-c
, se produce la expansión de la macro de load a-c*b
. Para evitar cualquier posible ambigüedad, los usuarios de macroprocesadores pueden poner entre paréntesis los parámetros formales dentro de las definiciones de macro, o los llamantes pueden poner entre paréntesis los parámetros de entrada.
Soporte para programación estructurada
Se han escrito paquetes de macros que proporcionan elementos de programación estructurados para codificar el flujo de ejecución. El primer ejemplo de este enfoque fue el conjunto de macros Concept-14, propuesto originalmente por Harlan Mills (marzo de 1970) e implementado por Marvin Kessler en la División de Sistemas Federales de IBM, que proporcionó IF/ELSE/ENDIF y control similar. bloques de flujo para programas ensambladores de OS/360. Esta fue una forma de reducir o eliminar el uso de operaciones GOTO en el código ensamblador, uno de los principales factores que causan el código espagueti en el lenguaje ensamblador. Este enfoque fue ampliamente aceptado a principios de la década de 1980 (los últimos días del uso del lenguaje ensamblador a gran escala). El High Level Assembler Toolkit de IBM incluye un paquete de macros de este tipo.
Un diseño curioso fue A-natural, un "orientado a la corriente" ensamblador para 8080/Z80, procesadores de Whitesmiths Ltd. (desarrolladores del sistema operativo Idris similar a Unix, y lo que se informó fue el primer compilador comercial de C). El lenguaje se clasificó como ensamblador porque trabajaba con elementos de máquina sin procesar, como códigos de operación, registros y referencias de memoria; pero incorporó una sintaxis de expresión para indicar el orden de ejecución. Los paréntesis y otros símbolos especiales, junto con construcciones de programación estructurada orientada a bloques, controlaban la secuencia de las instrucciones generadas. A-natural se creó como el lenguaje objeto de un compilador de C, en lugar de para la codificación manual, pero su sintaxis lógica ganó algunos seguidores.
Ha habido poca demanda aparente de ensambladores más sofisticados desde el declive del desarrollo del lenguaje ensamblador a gran escala. A pesar de eso, todavía se están desarrollando y aplicando en los casos en que las limitaciones de recursos o las peculiaridades en la arquitectura del sistema de destino impiden el uso efectivo de lenguajes de nivel superior.
Los ensambladores con un potente motor de macros permiten la programación estructurada a través de macros, como la macro de cambio proporcionada con el paquete Masm32 (este código es un programa completo):
incluir masm32incluirmasm32rt.inc; utilizar la biblioteca Masm32.codedemomain: REPEAT 20interruptor rv()náutica, 9); generar un número entre 0 y 8mov ecx, 7Caso 0impresión "caso 0"Caso ecx; en contraste con la mayoría de los demás idiomas de programación,impresión "caso 7"; el interruptor Masm32 permite "casos variables"Caso 1 .. 3.if eax==1impresión "caso 1".elseif eax==2impresión "caso 2".elseimpresión "casos 1 a 3: otros".endifCaso 4, 6, 8impresión "casos 4, 6 o 8"por defectomov ebx, 19 ; imprimir 20 estrellas. Repitoimpresión "*dec ebx. Hasta luego ¿Firmar? ; bucle hasta que se establezca la bandera de señalTerminawimpresión chr$()13, 10) ENDM Salidafinal demomain
Uso de lenguaje ensamblador
Perspectiva histórica
Los lenguajes ensambladores no estaban disponibles en el momento en que se introdujo la computadora con programa almacenado. A Kathleen Booth "se le atribuye la invención del lenguaje ensamblador" basándose en el trabajo teórico, comenzó en 1947, mientras trabajaba en el ARC2 en Birkbeck, Universidad de Londres, luego de la consulta de Andrew Booth (más tarde su esposo) con el matemático John von Neumann y el físico Herman Goldstine en el Instituto de Estudios Avanzados.
A fines de 1948, la Calculadora automática de almacenamiento de retardo electrónico (EDSAC) tenía un ensamblador (denominado "pedidos iniciales") integrado en su programa de arranque. Utilizaba mnemotécnicos de una letra desarrollados por David Wheeler, a quien la IEEE Computer Society acredita como el creador del primer 'ensamblador'. Los informes sobre el EDSAC introdujeron el término "asamblea" para el proceso de combinar campos en una palabra de instrucción. SOAP (Symbolic Optimal Assembly Program) fue un lenguaje ensamblador para la computadora IBM 650 escrito por Stan Poley en 1955.
Los lenguajes ensambladores eliminan gran parte de la programación de primera generación propensa a errores, tediosa y que consume mucho tiempo necesaria con las primeras computadoras, liberando a los programadores del tedio, como recordar códigos numéricos y calcular direcciones. Alguna vez fueron ampliamente utilizados para todo tipo de programación. Sin embargo, a fines de la década de 1950, su uso había sido reemplazado en gran medida por lenguajes de alto nivel, en la búsqueda de una mejor productividad de programación. Hoy en día, el lenguaje ensamblador todavía se usa para la manipulación directa del hardware, el acceso a instrucciones especializadas del procesador o para abordar problemas críticos de rendimiento. Los usos típicos son controladores de dispositivos, sistemas integrados de bajo nivel y sistemas en tiempo real (ver § Uso actual).
Históricamente, numerosos programas se han escrito completamente en lenguaje ensamblador. El Burroughs MCP (1961) fue la primera computadora para la cual no se desarrolló un sistema operativo completamente en lenguaje ensamblador; fue escrito en Lenguaje Orientado a Problemas de Sistemas Ejecutivos (ESPOL), un dialecto de Algol. Muchas aplicaciones comerciales también se escribieron en lenguaje ensamblador, incluida una gran cantidad de software de mainframe de IBM escrito por grandes corporaciones. COBOL, FORTRAN y algunos PL/I finalmente desplazaron gran parte de este trabajo, aunque varias organizaciones grandes mantuvieron infraestructuras de aplicaciones en lenguaje ensamblador hasta bien entrada la década de 1990.
La mayoría de las primeras microcomputadoras se basaban en lenguaje ensamblador codificado a mano, incluidos la mayoría de los sistemas operativos y aplicaciones grandes. Esto se debió a que estos sistemas tenían severas restricciones de recursos, impusieron arquitecturas de visualización y memoria idiosincrásicas, y proporcionaron servicios de sistema limitados y con errores. Quizás más importante fue la falta de compiladores de lenguaje de alto nivel de primera clase adecuados para el uso de microcomputadoras. Un factor psicológico también puede haber influido: la primera generación de programadores de microcomputadoras retuvo a un aficionado, "alambres y alicates" actitud.
En un contexto más comercial, las razones más importantes para usar el lenguaje ensamblador fueron la mínima expansión (tamaño), la mínima sobrecarga, la mayor velocidad y la confiabilidad.
Ejemplos típicos de grandes programas en lenguaje ensamblador de esta época son los sistemas operativos IBM PC DOS, el compilador Turbo Pascal y las primeras aplicaciones como el programa de hoja de cálculo Lotus 1-2-3. El lenguaje ensamblador se utilizó para obtener el mejor rendimiento de Sega Saturn, una consola que era notoriamente difícil de desarrollar y programar juegos. El juego de arcade de 1993 NBA Jam es otro ejemplo.
El lenguaje ensamblador ha sido durante mucho tiempo el principal lenguaje de desarrollo para muchas computadoras domésticas populares de las décadas de 1980 y 1990 (como MSX, Sinclair ZX Spectrum, Commodore 64, Commodore Amiga y Atari ST). Esto se debió en gran parte a que los dialectos BASIC interpretados en estos sistemas ofrecían una velocidad de ejecución insuficiente, así como instalaciones insuficientes para aprovechar al máximo el hardware disponible en estos sistemas. Algunos sistemas incluso tienen un entorno de desarrollo integrado (IDE) con funciones de macro y depuración muy avanzadas. Algunos compiladores disponibles para Radio Shack TRS-80 y sus sucesores tenían la capacidad de combinar la fuente de ensamblaje en línea con declaraciones de programa de alto nivel. Tras la compilación, un ensamblador incorporado produjo un código de máquina en línea.
Uso actual
Ha habido debate sobre la utilidad y el rendimiento del lenguaje ensamblador en relación con los lenguajes de alto nivel.
Aunque el lenguaje ensamblador tiene usos de nicho específicos donde es importante (ver más abajo), existen otras herramientas para la optimización.
En julio de 2017, el índice TIOBE de popularidad de lenguajes de programación clasifica al lenguaje ensamblador en el puesto 11, por delante de Visual Basic, por ejemplo. El ensamblador se puede utilizar para optimizar la velocidad o el tamaño. En el caso de la optimización de la velocidad, se afirma que los compiladores de optimización modernos convierten los lenguajes de alto nivel en un código que puede ejecutarse tan rápido como un ensamblaje escrito a mano, a pesar de los contraejemplos que se pueden encontrar. La complejidad de los procesadores modernos y los subsistemas de memoria hace que la optimización efectiva sea cada vez más difícil para los compiladores, así como para los programadores de ensamblaje. Además, el aumento del rendimiento del procesador ha significado que la mayoría de las CPU permanezcan inactivas la mayor parte del tiempo, con demoras causadas por cuellos de botella predecibles, como errores de caché, operaciones de E/S y paginación. Esto ha hecho que la velocidad de ejecución del código sin procesar no sea un problema para muchos programadores.
Existen algunas situaciones en las que los desarrolladores pueden optar por utilizar el lenguaje ensamblador:
- Código de escritura para sistemas con procesadores antiguos que tienen opciones limitadas de idiomas de alto nivel, como los Atari 2600, Commodore 64, y calculadoras de gráficos. Los programas para estos ordenadores de los años 70 y 1980 se escriben a menudo en el contexto de subculturas de demoscenas o retrogaming.
- Código que debe interactuar directamente con el hardware, por ejemplo en los controladores de dispositivo e interrumpir los controladores.
- En un procesador integrado o DSP, las interrupciones de alta repetición requieren el menor número de ciclos por interrupción, como una interrupción que ocurre 1000 o 10000 veces por segundo.
- Programas que necesitan usar instrucciones específicas del procesador no implementadas en un compilador. Un ejemplo común es la instrucción de rotación bitwise en el núcleo de muchos algoritmos de encriptación, así como la consulta de la paridad de un byte o la carga de 4 bits de una adición.
- Se requiere un ejecutable independiente de tamaño compacto que debe ejecutarse sin recurrir a los componentes de tiempo de ejecución o a las bibliotecas asociadas con un lenguaje de alto nivel. Ejemplos han incluido firmware para teléfonos, sistemas de encendido y combustible de automóviles, sistemas de control de aire acondicionado, sistemas de seguridad y sensores.
- Programas con bucles interiores sensibles al rendimiento, donde el lenguaje de montaje proporciona oportunidades de optimización que son difíciles de alcanzar en un lenguaje de alto nivel. Por ejemplo, álgebra lineal con BLAS o transformación cosina discreta (por ejemplo, versión de montaje SIMD de x264).
- Programas que crean funciones vectorizadas para programas en idiomas de alto nivel como C. En el lenguaje de alto nivel esto es a veces ayudado por el compilador funciones intrínsecas que mapean directamente a la mnemonía SIMD, pero sin embargo resulta en una conversión de montaje uno a uno específica para el procesador vector dado.
- Programas en tiempo real como simulaciones, sistemas de navegación de vuelo y equipos médicos. Por ejemplo, en un sistema de vuelo por cable, la telemetría debe interpretarse y actuar dentro de limitaciones de tiempo estrictas. Esos sistemas deben eliminar las fuentes de retrasos impredecibles, que pueden crearse por (algunos) idiomas interpretados, recogida automática de basura, operaciones de pavimentación o multitarea preventiva. Sin embargo, algunos idiomas de alto nivel incorporan componentes de tiempo de ejecución y interfaces de sistema operativo que pueden introducir tales demoras. Elegir lenguajes de montaje o nivel inferior para dichos sistemas da a los programadores mayor visibilidad y control sobre los detalles del procesamiento.
- algoritmos criptográficos que siempre deben tomar estrictamente el mismo tiempo para ejecutar, evitando ataques de tiempo.
- Modificar y ampliar el código hereditario escrito para ordenadores IBM mainframe.
- Situaciones donde se requiere el control completo del medio ambiente, en situaciones de extrema alta seguridad donde no se puede dar por sentado nada.
- virus informáticos, cargadores de arranque, ciertos controladores de dispositivo u otros elementos muy cercanos al hardware o sistema operativo de bajo nivel.
- simuladores de conjunto de instrucciones para monitorear, rastrear y depurar donde se mantiene una sobrecarga adicional al mínimo.
- Situaciones donde no existe un lenguaje de alto nivel, sobre un procesador nuevo o especializado para el que no se dispone de un compilador cruzado.
- Configuración inversa y modificación de archivos de programas tales como:
- binarios existentes que pueden o no haberse escrito originalmente en un lenguaje de alto nivel, por ejemplo cuando intentan recrear programas para los cuales el código fuente no está disponible o se ha perdido, o proteger copia de cracking del software propietario.
- Videojuegos (también denominado piratería ROM), que es posible a través de varios métodos. El método más empleado es alterar el código de programa a nivel de montaje.
El lenguaje ensamblador todavía se enseña en la mayoría de los programas de informática e ingeniería electrónica. Aunque hoy en día pocos programadores trabajan regularmente con el lenguaje ensamblador como herramienta, los conceptos subyacentes siguen siendo importantes. Temas fundamentales como la aritmética binaria, la asignación de memoria, el procesamiento de pilas, la codificación de conjuntos de caracteres, el procesamiento de interrupciones y el diseño del compilador serían difíciles de estudiar en detalle sin comprender cómo funciona una computadora a nivel de hardware. Dado que el comportamiento de una computadora se define fundamentalmente por su conjunto de instrucciones, la forma lógica de aprender dichos conceptos es estudiar un lenguaje ensamblador. La mayoría de las computadoras modernas tienen conjuntos de instrucciones similares. Por lo tanto, estudiar un solo lenguaje ensamblador es suficiente para aprender: I) los conceptos básicos; II) reconocer situaciones donde el uso del lenguaje ensamblador puede ser apropiado; y III) para ver cómo se puede crear un código ejecutable eficiente a partir de lenguajes de alto nivel.
Aplicaciones típicas
- El lenguaje de la Asamblea se utiliza típicamente en el código de arranque del sistema, el código de bajo nivel que inicializa y prueba el hardware del sistema antes de arrancar el sistema operativo y a menudo se almacena en ROM. (BIOS on IBM-compatible PC systems and CP/M is an example.)
- El lenguaje de la Asamblea se utiliza a menudo para códigos de bajo nivel, por ejemplo para los núcleos del sistema operativo, que no pueden depender de la disponibilidad de llamadas del sistema preexistente y deben implementarlas para la arquitectura procesadora particular en la que se ejecutará el sistema.
- Algunos compiladores traducen idiomas de alto nivel en asamblea primero antes de compilar completamente, permitiendo que el código de montaje sea visto para propósitos de depuración y optimización.
- Algunos compiladores para lenguajes relativamente bajos, como Pascal o C, permiten que el programador incruste el lenguaje de montaje directamente en el código fuente (también llamado montaje en línea). Los programas que utilizan tales instalaciones pueden construir abstracciones utilizando diferentes lenguajes de montaje en cada plataforma de hardware. El código portátil del sistema puede utilizar estos componentes específicos del procesador a través de una interfaz uniforme.
- El lenguaje de la Asamblea es útil en ingeniería inversa. Muchos programas se distribuyen sólo en forma de código de máquina que es directo para traducir en lenguaje de ensamblaje por un desmontador, pero más difícil de traducir en un lenguaje de alto nivel a través de un descompilador. Herramientas como el Disassembler Interactivo hacen uso amplio de la desmontaje para tal propósito. Esta técnica es utilizada por los hackers para romper software comercial, y los competidores para producir software con resultados similares de empresas competidoras.
- El lenguaje de la Asamblea se utiliza para mejorar la velocidad de ejecución, especialmente en computadoras personales tempranas con un poder de procesamiento limitado y RAM.
- Los ensambladores se pueden utilizar para generar bloques de datos, sin sobrecabezamiento de lenguaje de alto nivel, desde el código fuente formateado y comentado, para ser utilizado por otro código.
Contenido relacionado
Formato de punto flotante de doble precisión
Árbol rojo-negro
Transferencia de estado representacional (REST)