Está en la página 1de 75

Conociendo el lenguaje de programación

Lenguaje de programación
Un lenguaje de programación es un conjunto
de sintaxis o palabras ordenadas y
combinadas de tal forma que, formar
instrucciones bajo determinadas reglas de
semántica.
Los lenguajes de programación son utilizados
por los programadores o codificadores para
generar soluciones que sean interpretadas por
una coputadora.

Algoritmo
Ir a la navegaciónIr a la búsqueda
No debe confundirse con Logaritmo.

Los diagramas de flujo sirven para representar algoritmos de manera gráfica


Características del lenguaje de programación
Simplicidad
Eficiencia
Estructuración
Abstraccion
Naturalidad

Clasificación de lenguaje de programación


Lenguaje maquina
Lenguaje de bajo nivel (ensamblador)
Lenguaje de alto nivel
Algoriatmo. Es el conjunto de pasos ordenados y consecutivos o secuenciales que
seguimos para llevar acabo determinada acción.
Las instrucciones del algoritmo deben se expresadas en lenguaje natural
Un algoritmo tiene tres propiedades
Preciso definido finito
Partes de un algoritmo
Inicio secuencia fin

En matemáticas, lógica, ciencias de la computación y disciplinas relacionadas,


un algoritmo (del latín, dixit algorithmus y este del griego arithmos, que significa
«número», quizá también con influencia del nombre del matemático persa Al-
Juarismi)1 es un conjunto de instrucciones o reglas definidas y no-ambiguas,
ordenadas y finitas que permite, típicamente, solucionar un problema, realizar un
cómputo, procesar datos y llevar a cabo otras tareas o actividades. 2 Dados un
estado inicial y una entrada, siguiendo los pasos sucesivos se llega a un estado
final y se obtiene una solución. Los algoritmos son el objeto de estudio de
la algoritmia.1
En la vida cotidiana, se emplean algoritmos frecuentemente para resolver
problemas determinados. Algunos ejemplos son los manuales de usuario, que
muestran algoritmos para usar un aparato, o las instrucciones que recibe un
trabajador de su patrón. Algunos ejemplos en matemática son el algoritmo de
multiplicación, para calcular el producto, el algoritmo de la división para calcular el
cociente de dos números, el algoritmo de Euclides para obtener el máximo común
divisor de dos enteros positivos, o el método de Gauss para resolver un sistema
de ecuaciones lineales.
En términos de programación, un algoritmo es una secuencia de pasos lógicos
que permiten solucionar un problema.
Índice

 1Definición
 2Medios de expresión de un algoritmo
o 2.1Diagrama de flujo
o 2.2Pseudocódigo
o 2.3Sistemas formales
o 2.4Implementación
o 2.5Variables
o 2.6Estructuras secuenciales
 3Algoritmos como funciones
 4Análisis de algoritmos
 5Ejemplo de algoritmo
o 5.1Descripción de alto nivel
o 5.2Descripción formal
o 5.3Implementación
 6Véase también
o 6.1Tipos de algoritmos según su función
o 6.2Técnicas de diseño de algoritmos
o 6.3Temas relacionados
o 6.4Disciplinas relacionadas
 7Referencias
 8Bibliografía
 9Enlaces externos

Definición[editar]
En general, no existe ningún consenso definitivo en cuanto a la definición formal
de algoritmo. Muchos autores los señalan como listas de instrucciones para
resolver un cálculo o un problema abstracto, es decir, que un número finito de
pasos convierten los datos de un problema (entrada) en una solución (salida). 123456
Sin embargo cabe notar que algunos algoritmos no necesariamente tienen que
terminar o resolver un problema en particular. Por ejemplo, una versión modificada
de la criba de Eratóstenes que nunca termine de calcular números primos no deja
de ser un algoritmo.7
A lo largo de la historia varios autores han tratado de definir formalmente a los
algoritmos utilizando modelos matemáticos. Esto fue realizado por Alonzo
Church en 1936 con el concepto de "calculabilidad efectiva" basada en su cálculo
lambda y por Alan Turing basándose en la máquina de Turing. Los dos enfoques
son equivalentes, en el sentido en que se pueden resolver exactamente los
mismos problemas con ambos enfoques.89 Sin embargo, estos modelos están
sujetos a un tipo particular de datos como son números, símbolos
o gráficas mientras que, en general, los algoritmos funcionan sobre una vasta
cantidad de estructuras de datos.31 En general, la parte común en todas las
definiciones se puede resumir en las siguientes tres propiedades siempre y
cuando no consideremos algoritmos paralelos:7

 Tiempo secuencial. Un algoritmo funciona en tiempo discretizado –paso a


paso–, definiendo así una secuencia de estados computacionales por cada
entrada válida (la entrada son los datos que se le suministran al algoritmo
antes de comenzar).
 Estado abstracto. Cada estado computacional puede ser descrito formalmente
utilizando una estructura de primer orden y cada algoritmo es independiente de
su implementación (los algoritmos son objetos abstractos) de manera que en
un algoritmo las estructuras de primer orden son invariantes bajo isomorfismo.
 Exploración acotada. La transición de un estado al siguiente queda
completamente determinada por una descripción fija y finita; es decir, entre
cada estado y el siguiente solamente se puede tomar en cuenta una cantidad
fija y limitada de términos del estado actual.
En resumen, un algoritmo es cualquier cosa que funcione paso a paso, donde
cada paso se pueda describir sin ambigüedad y sin hacer referencia a una
computadora en particular, y además tiene un límite fijo en cuanto a la cantidad de
datos que se pueden leer/escribir en un solo paso. Esta amplia definición abarca
tanto a algoritmos prácticos como aquellos que solo funcionan en teoría, por
ejemplo el método de Newton y la eliminación de Gauss-Jordan funcionan, al
menos en principio, con números de precisión infinita; sin embargo no es posible
programar la precisión infinita en una computadora, y no por ello dejan de ser
algoritmos.10 En particular es posible considerar una cuarta propiedad que puede
ser usada para validar la tesis de Church-Turing de que toda función calculable se
puede programar en una máquina de Turing (o equivalentemente, en un lenguaje
de programación suficientemente general): 10

 Aritmetizabilidad. Solamente operaciones innegablemente calculables están


disponibles en el paso inicial.

Medios de expresión de un algoritmo[editar]


Los algoritmos pueden ser expresados de muchas maneras, incluyendo
al lenguaje natural, pseudocódigo, diagramas de flujo y lenguajes de
programación entre otros. Las descripciones en lenguaje natural tienden a ser
ambiguas y extensas. El usar pseudocódigo y diagramas de flujo evita muchas
ambigüedades del lenguaje natural. Dichas expresiones son formas más
estructuradas para representar algoritmos; no obstante, se mantienen
independientes de un lenguaje de programación específico.
La descripción de un algoritmo usualmente se hace en tres niveles:

1. Descripción de alto nivel. Se establece el problema, se selecciona un


modelo matemático y se explica el algoritmo de manera verbal,
posiblemente con ilustraciones y omitiendo detalles.
2. Descripción formal. Se usa pseudocódigo para describir la secuencia de
pasos que encuentran la solución.
3. Implementación. Se muestra el algoritmo expresado en un lenguaje de
programación específico o algún objeto capaz de llevar a cabo
instrucciones.
También es posible incluir un teorema que demuestre que el algoritmo es correcto,
un análisis de complejidad o ambos.
Diagrama de flujo[editar]

Diagrama de flujo que expresa un algoritmo para calcular la raíz cuadrada de un número 

Artículo principal: Diagrama de flujo


Los diagramas de flujo son descripciones gráficas de algoritmos; usan símbolos
conectados con flechas para indicar la secuencia de instrucciones y están regidos
por ISO.
Los diagramas de flujo son usados para representar algoritmos pequeños, ya que
abarcan mucho espacio y su construcción es laboriosa. Por su facilidad de lectura
son usados como introducción a los algoritmos, descripción de un lenguaje y
descripción de procesos a personas ajenas a la computación.
Pseudocódigo[editar]
Artículo principal: Pseudocódigo

El pseudocódigo (falso lenguaje, el prefijo pseudo significa falso) es una


descripción de alto nivel de un algoritmo que emplea una mezcla de lenguaje
natural con algunas convenciones sintácticas propias de lenguajes de
programación, como asignaciones, ciclos y condicionales, aunque no está regido
por ningún estándar.
El pseudocódigo está pensado para facilitar a las personas el entendimiento de un
algoritmo, y por lo tanto puede omitir detalles irrelevantes que son necesarios en
una implementación. Programadores diferentes suelen utilizar convenciones
distintas, que pueden estar basadas en la sintaxis de lenguajes de programación
concretos. Sin embargo, el pseudocódigo, en general, es comprensible sin
necesidad de conocer o utilizar un entorno de programación específico, y es a la
vez suficientemente estructurado para que su implementación se pueda hacer
directamente a partir de él.
Así el pseudocódigo cumple con las funciones antes mencionadas para
representar algo abstracto los protocolos son los lenguajes para la programación.
Busque fuentes más precisas para tener mayor comprensión del tema.
Sistemas formales[editar]
La teoría de autómatas y la teoría de funciones recursivas proveen modelos
matemáticos que formalizan el concepto de algoritmo. Los modelos más comunes
son la máquina de Turing, máquina de registro y funciones μ-recursivas. Estos
modelos son tan precisos como un lenguaje máquina, careciendo de expresiones
coloquiales o ambigüedad, sin embargo se mantienen independientes de cualquier
computadora y de cualquier implementación.
Implementación[editar]
Muchos algoritmos son ideados para implementarse en un programa. Sin
embargo, los algoritmos pueden ser implementados en otros medios, como
una red neuronal, un circuito eléctrico o un aparato mecánico y eléctrico. Algunos
algoritmos inclusive se diseñan especialmente para implementarse usando lápiz y
papel. El algoritmo de multiplicación tradicional, el algoritmo de Euclides, la criba
de Eratóstenes y muchas formas de resolver la raíz cuadrada son solo algunos
ejemplos.
Variables[editar]
Son elementos que toman valores específicos de un tipo de datos concreto. La
declaración de una variable puede realizarse comenzando con var.
Principalmente, existen dos maneras de otorgar valores iniciales a variables:

1. Mediante una sentencia de asignación.


2. Mediante un procedimiento de entrada de datos (por ejemplo: 'read').
Ejemplo:

...
i:=1;
read(n);
while i < n do begin
(* cuerpo del bucle *)
i := i + 1
end;
...
Estructuras secuenciales[editar]
La estructura secuencial es aquella en la que una acción sigue a otra en
secuencia. Las operaciones se suceden de tal modo que la salida de una es la
entrada de la siguiente y así sucesivamente hasta el fin del proceso. La asignación
de esto consiste, en el paso de valores o resultados a una zona de la memoria.
Dicha zona será reconocida con el nombre de la variable que recibe el valor. La
asignación se puede clasificar de la siguiente forma:

1. Simples: Consiste en pasar un valor constante a una variable (a ← 15)


2. Contador: Consiste en usarla como un verificador del número de veces que
se realiza un proceso (a ← a + 1)
3. Acumulador: Consiste en usarla como un sumador en un proceso (a ← a +
b)
4. De trabajo: Donde puede recibir el resultado de una operación matemática
que involucre muchas variables (a ← c + b*1/2).
Un ejemplo de estructura secuencial, como obtener el área de un triángulo:

Inicio
...
float b, h, a;
printf("Diga la base");
scanf("%f", &b);
printf("Diga la altura");
scanf("%f", &h);
a = (b*h)/2;
printf("El área del triángulo es %f", a)
...
Fin

Algoritmos como funciones[editar]


Artículo principal: Teoría de la computabilidad

Esquemática de un algoritmo que soluciona un problema de ciclo hamiltoniano

Un algoritmo se puede concebir como una función que transforma los datos de


un problema (entrada) en los datos de una solución (salida). Más aún, los datos se
pueden representar a su vez como secuencias de bits, y en general, de símbolos
cualesquiera.1911 Como cada secuencia de bits representa a un número
natural (véase Sistema binario), entonces los algoritmos son en esencia funciones
de los números naturales en los números naturales que sí se pueden calcular. Es
decir que todo algoritmo calcula una función  donde cada número natural es
la codificación de un problema o de una solución.
En ocasiones los algoritmos son susceptibles de nunca terminar, por ejemplo,
cuando entran a un bucle infinito. Cuando esto ocurre, el algoritmo nunca devuelve
ningún valor de salida, y podemos decir que la función queda indefinida para ese
valor de entrada. Por esta razón se considera que los algoritmos son funciones
parciales, es decir, no necesariamente definidas en todo su dominio de definición.
Cuando una función puede ser calculada por medios algorítmicos, sin importar la
cantidad de memoria que ocupe o el tiempo que se tarde, se dice que dicha
función es computable. No todas las funciones entre secuencias datos son
computables. El problema de la parada es un ejemplo.

Análisis de algoritmos[editar]
Artículo principal: Análisis de algoritmos
Como medida de la eficiencia de un algoritmo, se suelen estudiar los recursos
(memoria y tiempo) que consume el algoritmo. El análisis de algoritmos se ha
desarrollado para obtener valores que de alguna forma indiquen (o especifiquen)
la evolución del gasto de tiempo y memoria en función del tamaño de los valores
de entrada.
El análisis y estudio de los algoritmos es una disciplina de las ciencias de la
computación y, en la mayoría de los casos, su estudio es completamente
abstracto sin usar ningún tipo de lenguaje de programación ni cualquier otra
implementación; por eso, en ese sentido, comparte las características de las
disciplinas matemáticas. Así, el análisis de los algoritmos se centra en los
principios básicos del algoritmo, no en los de la implementación particular. Una
forma de plasmar (o algunas veces "codificar") un algoritmo es escribirlo
en pseudocódigo o utilizar un lenguaje muy simple tal como Lexico, cuyos códigos
pueden estar en el idioma del programador.
Algunos escritores restringen la definición de algoritmo a procedimientos que
deben acabar en algún momento, mientras que otros consideran procedimientos
que podrían ejecutarse eternamente sin pararse, suponiendo el caso en el que
existiera algún dispositivo físico que fuera capaz de funcionar eternamente. En
este último caso, la finalización con éxito del algoritmo no se podría definir como la
terminación de este con una salida satisfactoria, sino que el éxito estaría definido
en función de las secuencias de salidas dadas durante un periodo de vida de la
ejecución del algoritmo. Por ejemplo, un algoritmo que verifica que hay más ceros
que unos en una secuencia binaria infinita debe ejecutarse siempre para que
pueda devolver un valor útil. Si se implementa correctamente, el valor devuelto por
el algoritmo será válido, hasta que evalúe el siguiente dígito binario. De esta
forma, mientras evalúa la siguiente secuencia podrán leerse dos tipos de señales:
una señal positiva (en el caso de que el número de ceros sea mayor que el de
unos) y una negativa en caso contrario. Finalmente, la salida de este algoritmo se
define como la devolución de valores exclusivamente positivos si hay más ceros
que unos en la secuencia y, en cualquier otro caso, devolverá una mezcla de
señales positivas y negativas.
Ejemplo de algoritmo[editar]
El problema consiste en encontrar el máximo de un conjunto de números. Para un
ejemplo más complejo véase Algoritmo de Euclides.
Descripción de alto nivel[editar]
Dado un conjunto finito  de números, se tiene el problema de encontrar el número
más grande. Sin pérdida de generalidad se puede asumir que dicho conjunto no
es vacío y que sus elementos están numerados como .
Es decir, dado un conjunto  se pide encontrar  tal que  para todo elemento  que
pertenece al conjunto .
Para encontrar el elemento máximo, se asume que el primer elemento () es el
máximo; luego, se recorre el conjunto y se compara cada valor con el valor del
máximo número encontrado hasta ese momento. En el caso que un elemento sea
mayor que el máximo, se asigna su valor al máximo. Cuando se termina de
recorrer la lista, el máximo número que se ha encontrado es el máximo de todo el
conjunto.
Descripción formal[editar]
El algoritmo puede ser escrito de una manera más formal en el
siguiente pseudocódigo:

Algoritmo Encontrar el máximo de un conjunto

función max()
// es un conjunto no vacío de números//
 ←  // es el número de elementos de //
 ← 
para  ←  hasta  hacer
si  entonces
 ← 
devolver 

Sobre la notación:
 "←" representa una asignación:  ←  significa que la variable  toma el valor de ;
 "devolver" termina el algoritmo y devuelve el valor a su derecha (en este caso,
el máximo de ).
Implementación[editar]
En lenguaje C++:

int max(int c[], int n)


{
int i, m = c[0];
for (i = 1; i < n; i++)
if (c[i] > m) m = c[i];
return m;
}

Compilador
Ir a la navegaciónIr a la búsqueda

Este artículo o sección necesita referencias que aparezcan en una publicación


acreditada.

Este aviso fue puesto el 22 de diciembre de 2017.

«Compilación» redirige aquí. Para otras acepciones, véase recopilación.

Diagrama a bloques de la operación de un buen compilador.

En informática, un compilador es un tipo de traductor que transforma


un programa entero de un lenguaje de programación (llamado código fuente) a
otro.1 Usualmente el lenguaje objetivo es código máquina, aunque también puede
ser traducido a un código intermedio (bytecode) o a texto. A diferencia de
los intérpretes, los compiladores reúnen diversos elementos o fragmentos en una
misma unidad (un programa ejecutable o una biblioteca), que puede ser
almacenada y reutilizada. Este proceso de traducción se conoce
como compilación.2
La construcción de un compilador involucra la división del proceso en una serie de
fases que variará con su complejidad. Generalmente estas fases se agrupan en
dos tareas: el análisis del programa fuente y la síntesis del programa objeto.

 Análisis: se trata de la comprobación de la corrección del programa fuente, según la


definición del lenguaje en términos de teoría de lenguajes formales. Incluye las fases
correspondientes al análisis léxico (que consiste en la descomposición del programa
fuente en componentes léxicos), análisis sintáctico (agrupación de los componentes
léxicos en frases gramaticales ) y análisis semántico (comprobación de la validez
semántica de las sentencias aceptadas en la fase de análisis sintáctico).
 Síntesis: su objetivo es la generación de la salida expresada en el lenguaje objeto y
suele estar formado por una o varias combinaciones de fases de generación de
código (normalmente se trata de código intermedio o de código objeto) y
de optimización de código (en las que se busca obtener un programa objetivo lo más
eficiente posible, según su complejidad computacional o complejidad de Kolmogórov:
tiempo de ejecución, espacio durante ejecución, espacio para ser almacenado fuera
de ejecución, etc).
Alternativamente, las fases descritas para las tareas de análisis y síntesis se
pueden agrupar en:

 Analizador o front-end: es la parte que analiza el código fuente, comprueba su validez,


genera el árbol de derivación y rellena los valores de la tabla de símbolos. Esta parte
suele ser independiente de la plataforma o sistema para el cual se vaya a compilar, y
está compuesta por las fases comprendidas entre el análisis léxico y la generación de
código intermedio.
 Generador o back-end: es la parte que genera el código máquina, específico de una
plataforma, a partir de los resultados de la fase de análisis, realizada por este
generador.
Esta división permite que el mismo generador se utilice para crear el código
máquina de varios lenguajes de programación distintos y que el mismo analizador
que sirve para examinar el código fuente de un lenguaje de programación concreto
sirva para producir código máquina en varias plataformas. Suele incluir la
generación y optimización del código dependiente de la máquina.

Índice

 1Historia
 2Tipos de compiladores
 3Proceso de compilación
 4Etapas del proceso
o 4.1Fase de análisis
 4.1.1Análisis léxico
 4.1.2Análisis sintáctico
 4.1.3Análisis semántico
o 4.2Fase de síntesis
 4.2.1Generación de código intermedio
o 4.3Optimización de código
 5Estructura de datos principales
o 5.1Componentes léxicos o tókenes
o 5.2Árbol sintáctico
o 5.3Tabla de símbolos
o 5.4Tabla de literales
o 5.5Código intermedio
o 5.6Archivos temporales
 6Véase también
 7Referencias
 8Enlaces externos

Historia[editar]
Artículo principal: Historia de la construcción de los compiladores

En 1938, Konrad Zuse desarrolló la primera computadora digital electromecánica,


denominada Z1 en Alemania, y posteriormente, en 1946, se desarrolló la primera
computadora totalmente electrónica ENIAC, sucedida principalmente por
la EDVAC (1951), primera computadora electrónica digital. En un principio, estas
máquinas ejecutaban instrucciones consistentes en códigos numéricos que
señalaban a los circuitos de la máquina los estados correspondientes a cada
operación, lo que se denominó lenguaje máquina.
Pronto los primeros usuarios de estos ordenadores descubrieron la ventaja de
escribir sus programas mediante claves más fáciles de recordar que esos códigos;
al final, todas esas claves juntas se traducían manualmente a lenguaje máquina.
Estas claves constituyen los llamados lenguajes ensambladores.
Pese a todo, el lenguaje ensamblador seguía siendo el de una máquina, pero más
fácil de manejar (las instrucciones de máquina se reemplazan por mnemónicos.
Los trabajos de investigación se orientaron hacia la creación de un lenguaje que
expresara las distintas acciones a realizar de una manera lo más sencilla posible
para una persona. El primer compilador fue escrito por Grace Hopper, en 1952
para el lenguaje de programación A-0. En 1950 John Backus dirigió una
investigación en IBM sobre un lenguaje algebraico. En 1954 se empezó a
desarrollar un lenguaje que permitía escribir fórmulas matemáticas de manera
traducible por un ordenador; le llamaron FORTRAN (FORmulae TRANslator). Fue
el primer lenguaje de alto nivel y se introdujo en 1957 para el uso de la
computadora IBM modelo 704.
Surgió así por primera vez el concepto de un traductor como un programa que
traducía un lenguaje a otro lenguaje. En el caso particular de que el lenguaje a
traducir es un lenguaje de alto nivel y el lenguaje traducido de bajo nivel, se
emplea el término compilador.
El trabajo de realizar un compilador fue complicado de realizar. El primer
compilador de FORTRAN tardó 18 años-persona en realizarse y era muy sencillo.
Este desarrollo de FORTRAN estaba muy influenciado por la máquina objeto en la
que iba a ser implementado. Como un ejemplo de ello tenemos el hecho de que
los espacios en blanco fuesen ignorados, debido a que el periférico que se
utilizaba como entrada de programas (una lectora de tarjetas perforadas) no
contaba correctamente los espacios en blanco.
El primer compilador autocontenido, es decir, capaz de compilar su propio código
fuente fue el creado para Lisp por Hart y Levin en el MIT en 1962. Desde 1970 se
ha convertido en una práctica común escribir el compilador en el mismo lenguaje
que este compila, aunque PASCAL y C han sido alternativas muy usadas.
Crear un compilador autocontenido genera un problema llamado bootstrapping, es
decir el primer compilador creado para un lenguaje tiene que o bien ser compilado
por un compilador escrito en otro lenguaje o bien compilado al ejecutar el
compilador en un intérprete.

Tipos de compiladores[editar]
Esta taxonomía de los tipos de compiladores no es excluyente, por lo que puede
haber compiladores que se adscriban a varias categorías:

 Compiladores cruzados: generan código para un sistema distinto del que están
funcionando.
 Compiladores optimizadores: realizan cambios en el código para mejorar su eficiencia,
pero manteniendo la funcionalidad del programa original.
 Compiladores de una sola pasada: generan el código máquina a partir de una única
lectura del código fuente.
 Compiladores de varias pasadas: necesitan leer el código fuente varias veces antes
de poder producir el código máquina.
 Compiladores JIT (just in time): forman parte de un intérprete y compilan partes del
código según se necesitan.
En las primeras épocas de la informática, los compiladores eran considerados
un software de los más complejos existentes.
Los primeros compiladores se realizaron programándolos directamente
en lenguaje máquina o en ensamblador. Una vez que se dispone de un
compilador, se pueden escribir nuevas versiones del compilador (u otros
compiladores distintos) en el lenguaje que compila ese compilador.
Existen herramientas que facilitan la tarea de escribir compiladores
o intérpretes informáticos. Estas herramientas permiten generar el esqueleto
del analizador sintáctico a partir de una definición formal del lenguaje de partida,
especificada normalmente mediante una gramática formal y barata, dejando
únicamente al programador del compilador la tarea de programar las acciones
semánticas asociadas.

Proceso de compilación[editar]
Es el proceso por el cual se traducen las instrucciones escritas en un determinado
lenguaje de programación a lenguaje máquina. Además de un traductor, se
pueden necesitar otros programas para crear un programa objeto ejecutable. Un
programa fuente se puede dividir en módulos almacenados en archivos distintos.
La tarea de reunir el programa fuente a menudo se confía a un programa distinto,
llamado preprocesador. El preprocesador también puede expandir abreviaturas,
llamadas a macros, a proposiciones del lenguaje fuente.
Normalmente la creación de un programa ejecutable (un típico
archivo .exe para Windows o DOS) conlleva dos pasos. El primer paso se
llama compilación (propiamente dicho) y traduce el código fuente escrito en
un lenguaje de programación almacenado en un archivo a código en bajo nivel
(normalmente en código objeto, no directamente a lenguaje máquina). El segundo
paso se llama enlazado en el cual se enlaza el código de bajo nivel generado de
todos los ficheros y subprogramas que se han mandado a compilar y se añade el
código de las funciones que hay en las bibliotecas del compilador para que el
ejecutable pueda comunicarse directamente con el sistema operativo, traduciendo
así finalmente el código objeto a código máquina, y generando un módulo
ejecutable.
Estos dos pasos se pueden hacer por separado, almacenando el resultado de la
fase de compilación en archivos objetos (un típico.obj para Microsoft Windows,
DOS o para Unix); para enlazarlos en fases posteriores, o crear directamente el
ejecutable; con lo que la fase de compilación se almacena solo temporalmente. Un
programa podría tener partes escritas en varios lenguajes (por ejemplo C, C+
+ y Asm), que se podrían compilar de forma independiente y luego enlazar juntas
para formar un único módulo ejecutable.

Etapas del proceso[editar]


El proceso de traducción se compone internamente de varias etapas o fases, que
realizan distintas operaciones lógicas. Es útil pensar en estas fases como en
piezas separadas dentro del traductor, y pueden en realidad escribirse como
operaciones codificadas separadamente aunque en la práctica a menudo se
integren juntas.
Fase de análisis[editar]
Análisis léxico[editar]
Artículo principal: Analizador léxico

El análisis léxico constituye la primera fase, aquí se lee el programa fuente de


izquierda a derecha y se agrupa en componentes léxicos (tókenes), que son
secuencias de caracteres que tienen un significado. Además, todos los espacios
en blanco, líneas en blanco, comentarios y demás información innecesaria se
elimina del programa fuente. También se comprueba que los símbolos del
lenguaje (palabras clave, operadores, etc.) se han escrito correctamente.
Como la tarea que realiza el analizador léxico es un caso especial de coincidencia
de patrones, se necesitan los métodos de especificación y reconocimiento de
patrones, se usan principalmente los autómatas finitos que acepten expresiones
regulares. Sin embargo, un analizador léxico también es la parte del traductor que
maneja la entrada del código fuente, y puesto que esta entrada a menudo
involucra un importante gasto de tiempo, el analizador léxico debe funcionar de
manera tan eficiente como sea posible.
Análisis sintáctico[editar]
Artículo principal: Analizador sintáctico

En esta fase los caracteres o componentes léxicos se agrupan jerárquicamente en


frases gramaticales que el compilador utiliza para sintetizar la salida. Se
comprueba si lo obtenido de la fase anterior es sintácticamente correcto (obedece
a la gramática del lenguaje). Por lo general, las frases gramaticales del programa
fuente se representan mediante un árbol de análisis sintáctico.
La estructura jerárquica de un programa normalmente se expresa utilizando
reglas recursivas. Por ejemplo, se pueden dar las siguientes reglas como parte de
la definición de expresiones:

1. Un identificador puede ser una expresión.


2. Un número puede ser una expresión.
3. Si expresión1 y expresión2 son expresiones, entonces también lo son:
o expresión1 + expresión2
o expresión1 * expresión2
o ( expresión1 )

Las reglas 1 y 2 son reglas básicas (no recursivas), en tanto que la regla 3 define
expresiones en función de operadores aplicados a otras expresiones.
La división entre análisis léxico y análisis sintáctico es algo arbitraria. Un factor
para determinar la división es si una construcción del lenguaje fuente es
inherentemente recursiva o no. Las construcciones léxicas no requieren recursión,
mientras que las construcciones sintácticas suelen requerirla. No se requiere
recursión para reconocer los identificadores, que suelen ser cadenas de letras y
dígitos que comienzan con una letra. Normalmente, se reconocen los
identificadores por el simple examen del flujo de entrada, esperando hasta
encontrar un carácter que no sea ni letra ni dígito, y agrupando después todas las
letras y dígitos encontrados hasta ese punto en un componente léxico
llamado identificador. Por otra parte, esta clase de análisis no es suficientemente
poderoso para analizar expresiones o proposiciones. Por ejemplo, no podemos
emparejar de manera apropiada los paréntesis de las expresiones, o las
palabras begin y end en proposiciones sin imponer alguna clase de estructura
jerárquica o de anidamiento a la entrada.
Análisis semántico[editar]
La fase de análisis semántico revisa el programa fuente para tratar de encontrar
errores semánticos y reúne la información sobre los tipos para la fase posterior de
generación de código. En ella se utiliza la estructura jerárquica determinada por la
fase de análisis sintáctico para identificar los operadores y operandos de
expresiones y proposiciones.
Un componente importante del análisis semántico es la verificación de tipos. Aquí,
el compilador verifica si cada operador tiene operandos permitidos por la
especificación del lenguaje fuente. Por ejemplo, las definiciones de muchos
lenguajes de programación requieren que el compilador indique un error cada vez
que se use un número real como índice de una matriz. Sin embargo, la
especificación del lenguaje puede imponer restricciones a los operandos, por
ejemplo, cuando un operador aritmético binario se aplica a un número entero y a
un número real.3 Revisa que los arreglos tengan definido el tamaño correcto.
Fase de síntesis[editar]
Consiste en generar el código objeto equivalente al programa fuente. Solo se
genera código objeto cuando el programa fuente está libre de errores de análisis,
lo cual no quiere decir que el programa se ejecute correctamente, ya que un
programa puede tener errores de concepto o expresiones mal calculadas. Por lo
general el código objeto es código de máquina relocalizable o código
ensamblador. Las posiciones de memoria se seleccionan para cada una de las
variables usadas por el programa. Después, cada una de las instrucciones
intermedias se traduce a una secuencia de instrucciones de máquina que ejecuta
la misma tarea. Un aspecto decisivo es la asignación de variables a registros.
Generación de código intermedio[editar]
Después de los análisis sintáctico y semántico, algunos compiladores generan una
representación intermedia explícita del programa fuente. Se puede considerar esta
representación intermedia como un programa para una máquina abstracta. Esta
representación intermedia debe tener dos propiedades importantes; debe ser fácil
de producir y fácil de traducir al programa objeto.
La representación intermedia puede tener diversas formas. Existe una forma
intermedia llamada «código de tres direcciones» que es como el lenguaje
ensamblador de una máquina en la que cada posición de memoria puede actuar
como un registro. El código de tres direcciones consiste en una secuencia de
instrucciones, cada una de las cuales tiene como máximo tres operandos. Esta
representación intermedia tiene varias propiedades:

 Primera: cada instrucción de tres direcciones tiene a lo sumo un operador, además de


la asignación, por tanto, cuando se generan estas instrucciones, el traductor tiene que
decidir el orden en que deben efectuarse las operaciones.
 Segunda: el traductor debe generar un nombre temporal para guardar los valores
calculados por cada instrucción.
 Tercera: algunas instrucciones de «tres direcciones» tienen menos de tres operandos,
por ejemplo, la asignación.
Optimización de código[editar]
Artículo principal: Compilador optimizador

La fase de optimización de código consiste en mejorar el código intermedio, de


modo que resulte un código máquina más rápido de ejecutar. Esta fase de la
etapa de síntesis es posible sobre todo si el traductor es un compilador
(difícilmente un intérprete puede optimizar el código objeto). Hay mucha variación
en la cantidad de optimización de código que ejecutan los distintos compiladores.
En los que hacen mucha optimización, llamados «compiladores optimizadores»,
una parte significativa del tiempo del compilador se ocupa en esta fase. Sin
embargo, hay optimizaciones sencillas que mejoran sensiblemente el tiempo de
ejecución del programa objeto sin retardar demasiado la compilación. 3

Estructura de datos principales[editar]


La interacción entre los algoritmos utilizados por las fases del compilador y
las estructuras de datos que soportan estas fases es, naturalmente, muy fuerte. El
escritor del compilador se esfuerza por implementar estos algoritmos de una
manera tan eficaz como sea posible, sin aumentar demasiado la complejidad. De
manera ideal, un compilador debería poder compilar un programa en un tiempo
proporcional al tamaño del mismo.
Componentes léxicos o tókenes[editar]
Cuando un analizador léxico reúne los caracteres en un token, generalmente
representa el token de manera simbólica, es decir, como un valor de un tipo de
datos enumerado que representa el conjunto de tokens del lenguaje fuente. En
ocasiones también es necesario mantener la cadena de caracteres misma u otra
información derivada de ella, tal como el nombre asociado con un token
identificador o el valor de un token de número.
En la mayoría de los lenguajes el analizador léxico solo necesita generar un token
a la vez. En este caso se puede utilizar una variable global simple para mantener
la información del token. En otros casos (cuyo ejemplo más notable
es FORTRAN), puede ser necesario un arreglo (o vector) de tókenes.
Árbol sintáctico[editar]
Si el analizador sintáctico genera un árbol sintáctico, por lo regular se construye
como una estructura estándar basada en un puntero que se asigna de manera
dinámica a medida que se efectúa el análisis sintáctico. El árbol entero puede
entonces conservarse como una variable simple que apunta al nodo raíz. Cada
nodo en la estructura es un registro cuyos campos representan la información
recolectada tanto por el analizador sintáctico como, posteriormente, por el
analizador semántico. Por ejemplo, el tipo de datos de una expresión puede
conservarse como un campo en el nodo del árbol sintáctico para la expresión.
En ocasiones, para ahorrar espacio, estos campos se asignan de manera
dinámica, o se almacenan en otras estructuras de datos, tales como la tabla de
símbolos, que permiten una asignación y desasignación selectivas. En realidad,
cada nodo del árbol sintáctico por sí mismo puede requerir de atributos diferentes
para ser almacenado, de acuerdo con la clase de estructura del lenguaje que
represente. En este caso, cada nodo en el árbol sintáctico puede estar
representado por un registro variable, con cada clase de nodo conteniendo
solamente la información necesaria para ese caso.
Tabla de símbolos[editar]
Esta estructura de datos mantiene la información asociada con los
identificadores: funciones, variables, constantes y tipos de datos. La tabla de
símbolos interactúa con casi todas las fases del compilador: el analizador léxico, el
analizador sintáctico o el analizador semántico pueden introducir identificadores
dentro de la tabla; el analizador semántico agregará tipos de datos y otra
información; y las fases de optimización y generación de código utilizarán la
información proporcionada por la tabla de símbolos para efectuar selecciones
apropiadas de código objeto.
Puesto que la tabla de símbolos tendrá solicitudes de acceso con tanta frecuencia,
las operaciones de inserción, eliminación y acceso necesitan ser eficientes,
preferiblemente operaciones de tiempo constante. Una estructura de datos
estándar para este propósito es la tabla de dispersión o de cálculo de dirección,
aunque también se pueden utilizar diversas estructuras de árbol. En ocasiones se
utilizan varias tablas y se mantienen en una lista o pila.
Tabla de literales[editar]
La búsqueda y la inserción rápida son esenciales también para la tabla de
literales, la cual almacena constantes y cadenas utilizadas en el programa. Sin
embargo, una tabla de literales necesita impedir las eliminaciones porque sus
datos se aplican globalmente al programa y una constante o cadena aparecerá
solo una vez en esta tabla. La tabla de literales es importante en la reducción del
tamaño de un programa en la memoria al permitir la reutilización de constantes y
cadenas. También es necesaria para que el generador de código construya
direcciones simbólicas para las literales y para introducir definiciones de datos en
el archivo de código objeto.
Código intermedio[editar]
De acuerdo con la clase de código intermedio (por ejemplo, código de tres
direcciones o código P) y de las clases de optimizaciones realizadas, este código
puede conservarse como un arreglo de cadenas de texto, un archivo de texto
temporal o bien una lista de estructuras ligadas. En los compiladores que realizan
optimizaciones complejas debe ponerse particular atención a la selección de
representaciones que permitan una fácil reorganización.
Generación de código intermedio
Después de los análisis sintáctico y semántico, algunos compiladores generan una
representación intermedia explícita del programa fuente. Se puede considerar esta
representación intermedia como un programa para una máquina abstracta. Esta
representación intermedia debe tener dos propiedades importantes; debe ser fácil
de producir y fácil de traducir al programa objeto.
La representación intermedia puede tener diversas formas. Existe una forma
intermedia llamada «código de tres direcciones», que es como el lenguaje
ensamblador para una máquina en la que cada posición de memoria puede actuar
como un registro. El código de tres direcciones consiste en una secuencia de
instrucciones, cada una de las cuales tiene como máximo tres operandos. El
programa fuente de (1) puede aparecer en código de tres direcciones como
temp1 := entreal(60)
temp2 := id3 * temp1 ===> (2)
temp3 := id2 + temp2
id1 := temp3

Esta representación intermedia tiene varias propiedades. Primera, cada


instrucción de tres direcciones tiene a lo sumo un operador, además de la
asignación. Por tanto, cuando se generan esas instrucciones el compilador tiene
que decidir el orden en que deben efectuarse, las operaciones; la multiplicación
precede a la adición al programa fuente de. Segunda, el compilador debe generar
un nombre temporal para guardar los valores calculados por cada instrucción.
Tercera, algunas instrucciones de «tres direcciones» tienen menos de tres
operadores, por ejemplo la primera y la última instrucciones de asignación.
Optimización de código
La fase de optimización de código trata de mejorar el código intermedio de modo
que resulte un código de máquina más rápido de ejecutar. Algunas optimizaciones
son triviales. Por ejemplo, un algoritmo natural genera el código intermedio (2)
utilizando una instrucción para cada operador de la representación del árbol
después del análisis semántico, aunque hay una forma mejor de realizar los
mismos cálculos usando las dos instrucciones

temp1 := id3 * 60.0 ===> (3)


id1 := id2 + temp1

Este sencillo algoritmo no tiene nada de malo, puesto que el problema se puede
solucionar en la fase de optimización de código. Esto es, el compilador puede
deducir que la conversión de 60 de entero a real se puede hacer de una vez por
todas en el momento de la compilación, de modo que la operación "entreal( )" se
puede eliminar. Además, temp3 se usa solo una vez, para transmitir su valor a id1.
Entonces resulta seguro sustituir a id1 por temp3, a partir de lo cual la última
proposición de (2) no se necesita y se obtiene el código de (3).
Hay muchas variaciones en la cantidad de optimización de código que ejecutan los
distintos compiladores. En lo que hacen mucha optimización llamados
«compiladores optimizadores», una parte significativa del tiempo del compilador se
ocupa en esta fase. Sin embargo, hay optimizaciones sencillas que mejoran
sensiblemente el tiempo de ejecución del programa objeto sin retardar demasiado
la compilación.
Archivos temporales[editar]
Al principio las computadoras no tenían la suficiente memoria para guardar un
programa completo durante la compilación. Este problema se resolvió mediante el
uso de archivos temporales para mantener los productos de los pasos intermedios
durante la traducción o bien al compilar «al vuelo», es decir, manteniendo solo la
información suficiente de las partes anteriores del programa fuente que permita
proceder a la traducción.
Las limitaciones de memoria son ahora un problema mucho menor, y es posible
requerir que una unidad de compilación entera se mantenga en memoria, en
especial si se dispone de la compilación por separado en el lenguaje. Con todo,
los compiladores ocasionalmente encuentran útil generar archivos intermedios
durante alguna de las etapas del procesamiento. Algo típico de estos es la
necesidad de direcciones de corrección hacia atrás durante la generación de
código

Lenguaje de bajo nivel


Ir a la navegaciónIr a la búsqueda
Este artículo o sección necesita referencias que aparezcan en una publicación
acreditada.
Este aviso fue puesto el 17 de octubre de 2015.

Un lenguaje de programación de características de bajo nivel o de primera


generación, es aquel en el que sus instrucciones ejercen un control directo sobre
el hardware y están condicionados por la estructura física de las computadoras
que lo soportan. El uso de la palabra bajo en su denominación no implica que el
lenguaje sea menos potente que un lenguaje de alto nivel, sino que se refiere a la
reducida abstracción entre el lenguaje y el hardware. Por ejemplo, este tipo de
lenguajes se utiliza para programar tareas críticas de los sistemas operativos, de
aplicaciones en tiempo real o controladores de dispositivos.

Índice

 1Complejidad vs dificultad
 2Interacción máquina
 3Primera generación
 4Segunda generación
 5Véase también

Complejidad vs dificultad[editar]
Los lenguajes de bajo nivel tales como el lenguaje ensamblador pueden ser más
difíciles de programar que los lenguajes de alto nivel debido a que están más
íntimamente relacionados con las características técnicas del hardware. Además
estos suelen carecer de abstracciones de mayor nivel. No es posible una
abstracción fuera de lo estipulado para el conjunto
del microcódigos del microprocesador de un ordenador. Por otro lado, es más
fácilmente traducible a lenguaje de máquina, tarea que realiza esencialmente
un compilador.
De menor a mayor nivel de abstracción respecto del hardware es posible clasificar
los lenguajes de programación de la siguiente manera:

1. Lenguaje de máquina: Está formado por los unos (1) y ceros (0) que
ejecutará directamente la unidad central de procesamiento (CPU). Al
visualizar este lenguaje en un editor de texto plano parecerá sin sentido
(caracteres basura). Muchos de ellos serán caracteres no imprimibles.
Estos ceros y unos representan literalmente instrucciones y datos a ser
procesados.
2. Lenguajes ensambladores (en inglés assembler): También denominados
nemotécnicos o nemónicos, son un primer nivel de abstracción. No son ya
programas ejecutables directamente por el ordenador, sino textos de
código fuente inteligibles por humanos que necesitan de alguna
herramienta (esencialmente un compilador) para su traducción a lenguaje
de máquina que el CPU pueda ejecutar. Sus instrucciones suelen ser una
denominación abreviada de la instrucción máquina que simbolizan, y tienen
una correspondencia casi directa (uno a uno) a las instrucciones de
máquina que representan. El código resultante de la compilación del
lenguaje ensamblador genera un código de máquina binario ejecutable.
Son instrucciones que ensamblan los grupos de conmutadores necesarios
para expresar una mínima lógica aritmética. Están íntimamente vinculados
al hardware. Algunas de estas instrucciones pueden ser por
ejemplo  MOV  para mover un dato de un lugar a otro, o  ADD  para sumar dos
valores. Por norma general están disponibles a nivel firmware, cmos o chip
set. Estos lenguajes están orientados a procesos. Los procesos se
componen de tareas. Contienen tantas instrucciones como la arquitectura
del hardware así haya sido diseñada. La arquitectura CISC contiene
muchas más instrucciones a este nivel, que la RISC.

o Por ejemplo: Assembler x86, JMP y MULT.


Los otros lenguajes que completan la clasificación no serían ya lenguajes
de bajo nivel.
3. Lenguajes de medio nivel: Son aquellos que, basándose en los juegos de
instrucciones disponibles (chip set), permiten el uso de funciones a nivel
aritmético, pero a nivel lógico dependen de literales en ensamblador. Estos
lenguajes están orientados a procedimientos. Los procedimientos se
componen de procesos.
o Ejemplos: C, Basic. De mayor nivel pero aun así considerables de nivel
medio: C++, Fortran, Cobol, Lisp.

Interacción máquina[editar]
En este tipo de lenguajes se trabaja a nivel de instrucciones, es decir, su
programación es al más fino detalle, además, está completamente orientado a la
máquina.

 Adaptación - Máxima entre programación y aprovechamiento del recurso de la


máquina.
 Velocidad - Máxima al contar con un acceso directo a los recursos, sin capas
intermedias.
 Portabilidad - Mínima por estar restringido a las especificaciones del fabricante.
 Abstracción - Mínima por depender completamente de la técnica del hardware.
 Uso - Requiere de la máxima atención y de una organización estructurada
sobre la base de los planos del hardware y del objetivo del software.
 isomorfismo - Conceptualización de los datos de la información, que se
complementaran

Primera generación[editar]
El lenguaje de programación de primera generación (por sus siglas en
inglés: 1GL), es el lenguaje de código máquina. Es el único lenguaje que un
microprocesador entiende de forma nativa. El lenguaje máquina al constar de unos
y ceros, es de difícil lectura y gran probabilidad de equivocación al ingresarlo, por
lo tanto es raro que una persona lo use directamente.
Las instrucciones en lenguaje máquina, constan de una o dos memorias de
instrucción, y otras memorias con datos (ver artículo modos de direccionamiento)

Ejemplo, una instrucción en lenguaje máquina acorde al microprocesador Z80

Índice de
Binario Hexadecimal Significado
memoria

Agrega lo que esté en la siguiente memoria a la


0 10000111 87
memoria acumulador

1 01110111 77 Dato. Número 119 en decimal y 77 en hexadecimal.

Segunda generación[editar]
El lenguaje de programación de segunda generación (por sus siglas en
inglés: 2GL), es el lenguaje ensamblador. El avance respecto al anterior, es que
en vez de llanos unos y ceros, se tienen mnemónicos ya escritos como un texto, y
datos visibles a un lado del mnemónico. En este se requiere de un programa que
traduzca los mnemónicos a lenguaje máquina.
Un programador de lenguaje ensamblador debe conocer la arquitectura del
procesador (como por ejemplo las particularidades de sus registros o su conjunto
de instrucciones).

Ejemplo de código ensamblador en el recuadro morado. Abajo, en el recuadro rojo se muestra un texto
codificado en hexadecimal en código ASCII.

Lenguaje ensamblador
Ir a la navegaciónIr a la búsqueda
Para otros usos de este término, véase Ensamblador (desambiguación).

Lenguaje ensamblador
Motorola MC6800 Assembly listing

Información general

Extensiones comunes .asm

Paradigma Imperative, unstructured

Apareció en 1949

[editar datos en Wikidata]

Lenguaje de máquina del Intel 8088. El código de máquina en hexadecimal se resalta en rojo, el


equivalente en lenguaje ensamblador en magenta, y las direcciones de memoria donde se encuentra el
código, en azul. Abajo se ve un texto en hexadecimal y ASCII.

El lenguaje ensamblador o assembly (en inglés: assembly language y la


abreviación asm) es un lenguaje de programación de bajo nivel. Consiste en un
conjunto de mnemónicos que representan instrucciones básicas para
los computadores, microprocesadores, microcontroladores y otros circuitos
integrados programables. Implementa una representación simbólica de los códigos
de máquina binarios y otras constantes necesarias para programar
una arquitectura de procesador y constituye la representación más directa
del código máquina específico para cada arquitectura legible por un programador.
Cada arquitectura de procesador tiene su propio lenguaje ensamblador que
usualmente es 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 portables.
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 se llaman 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.
También es utilizado en el desarrollo de controladores de dispositivo (en
inglés, device drivers) y en el desarrollo de sistemas operativos, debido a la
necesidad del acceso directo a las instrucciones de la máquina. Muchos
dispositivos programables (como los microcontroladores) aún cuentan con el
ensamblador como la única manera de ser manipulados.

Índice

 1Características
 2Programa ensamblador
o 2.1Número de pasos
o 2.2Ensambladores de alto nivel
o 2.3Uso del término
 3Lenguaje
o 3.1Instrucciones de CPU
o 3.2Ensamblado
o 3.3Ejemplos
 4Diseño del lenguaje
o 4.1Elementos básicos
 4.1.1Mnemónicos de opcode y mnemónicos extendidos
 4.1.2Secciones de datos
 4.1.3Directivas del ensamblador
o 4.2Macros
o 4.3Soporte para programación estructurada
 5Uso del lenguaje ensamblador
o 5.1Perspectiva histórica
o 5.2Uso actual
o 5.3Aplicaciones típicas
 6Detalles adicionales
 7Ejemplos de lenguaje ensamblador
o 7.1Ejemplo para la arquitectura x86
o 7.2Ejemplo para el computador virtual (POCA)
o 7.3Ejemplo para el computador virtual (ARC)
o 7.4Ejemplo para el µC Intel 8051
o 7.5Ejemplo para el Microchip PIC16F84
 8Véase también
 9Referencias
 10Bibliografía
 11Enlaces externos

Características[editar]
 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
pueden ser más rápidos y consumir menos recursos del sistema (ej: memoria
RAM) 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. Conforme han evolucionado tanto los
procesadores como los compiladores de lenguajes de alto nivel, esta
característica del lenguaje ensamblador se ha vuelto cada vez menos
significativa. Es decir, un compilador moderno de lenguaje de alto nivel puede
generar código casi tan eficiente como su equivalente en lenguaje
ensamblador.1
 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.

Programa ensamblador[editar]
Artículo principal: Ensamblador

Generalmente, 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. 2 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[editar]
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 pasos más fáciles
de leer y mantener.3
Ensambladores de alto nivel[editar]
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 de los años 1960 para el IBM S/360,
entre otras máquinas)
 Características de programación orientada a objetos
Uso del término[editar]
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":

Lenguaje[editar]
El lenguaje ensamblador refleja directamente la arquitectura y
las instrucciones en lenguaje de máquina de la CPU, y pueden ser muy diferentes
de una arquitectura de CPU a otra.
Cada arquitectura de microprocesador tiene su propio lenguaje de máquina, y en
consecuencia su propio lenguaje ensamblador ya que este se encuentra muy
ligado a la estructura del hardware para el cual se programa. Los
microprocesadores difieren en el tipo y número de operaciones que soportan;
también pueden tener diferente cantidad de registros, y distinta representación de
los tipos de datos en memoria. Aunque la mayoría de los microprocesadores son
capaces de cumplir esencialmente las mismas funciones, la forma en que lo hacen
difiere y los respectivos lenguajes ensamblador reflejan tal diferencia.
Instrucciones de CPU[editar]
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 o shift lógico y rotaciones
u Operadores a nivel 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
Nota: Un valor inmediato es una constante que se especifica en la misma
microinstrucción.
Operaciones de pila (stack, en inglés):

 PUSH (escribe datos hacia el tope de la pila)


 POP (lee datos desde el tope de la pila)
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 una comparación
 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
trigonométricas. Seno, coseno, tangente, arcot
angente
 Operaciones con logaritmos, potencias y
raíces
 Otras
El lenguaje ensamblador tiene mnemónicos para
cada una de las instrucciones de la CPU en
adición a otros mnemónicos a ser procesados por
el programa ensamblador (como por ejemplo
macros y otras sentencias en tiempo de
ensamblado).
Ensamblado[editar]
La transformación del lenguaje ensamblador en
código máquina la realiza un
programa ensamblador, y la traducción inversa la
puede efectuar un desensamblador. A diferencia
de los lenguajes de alto nivel, aquí hay
usualmente una correspondencia 1 a 1 entre las
instrucciones simples del ensamblador y el
lenguaje de máquina. Sin embargo, en algunos
casos, un ensamblador puede proveer "pseudo
instrucciones" que se expanden en un código de
máquina más extenso a fin de proveer la
funcionalidad necesaria y simplificar la
programación. Por ejemplo, para un código
máquina condicional como "si X mayor o igual
que", un ensamblador puede utilizar una
pseudoinstrucción al grupo "haga si menor que", y
"si = 0" sobre el resultado de la condición anterior.
Los Ensambladores más completos también
proveen un rico lenguaje de macros que se utiliza
para generar código más complejo y secuencias
de datos.
Para el mismo procesador y el mismo conjunto de
instrucciones de CPU, diferentes programas
ensambladores pueden tener, cada uno de ellos,
variaciones y diferencias en el conjunto de
mnemónicos o en la sintaxis de su lenguaje
ensamblador. Por ejemplo, en un lenguaje
ensamblador para la arquitectura x86, se puede
expresar la instrucción para mover  5  al
registro  AL  de la siguiente manera:  MOV AL, 5 ,
mientras que para otro ensamblador para la
misma arquitectura se expresaría al revés:  MOV
5, AL . Ambos lenguajes ensambladores harían
exactamente lo mismo, solo que está expresado
de manera diferente. El primero usa la sintaxis
de Intel, mientras que el segundo usa la sintaxis
de AT&T.
El uso del ensamblador no resuelve
definitivamente el problema de cómo programar
un sistema basado en microprocesador de modo
sencillo ya que para hacer un uso eficiente del
mismo, hay que conocer a fondo
el microprocesador, los registros de trabajo de
que dispone, la estructura de la memoria, y
muchas cosas más referentes a su estructura
básica de funcionamiento.
Ejemplos[editar]
Un programa escrito en lenguaje ensamblador
consiste en una serie de instrucciones que
corresponden al flujo de órdenes ejecutables por
un microprocesador.
Por ejemplo, en el lenguaje ensamblador para un
procesador x86:
La sentencia

 MOV AL, 61h

Asigna el valor hexadecimal  61  (97 decimal) al


registro " AL ".
El programa ensamblador lee la sentencia de
arriba y produce su
equivalente binario en lenguaje de máquina.

 Binario:  10110000
01100001  (hexadecimal:  B061 )

El mnemónico  MOV  es un código de operación u


"opcode". El opcode es seguido por una lista de
argumentos o parámetros, completando una típica
instrucción de ensamblador. En el ejemplo,  AL  es
un registro de 8 bits del procesador, al cual se le
asignará el valor hexadecimal 61 especificado.
El código de máquina generado por el
ensamblador consiste de 2 bytes. El primer byte
contiene empaquetado la instrucción MOV y el
código del registro hacia donde se va a mover el
dato:

1011 0000 01100001


| | |
| | +---- Número 61h en binario
| |
| +--- Registro AL
+-------- Instrucción MOV

En el segundo byte se especifica el número 61h,


escrito en binario como  01100001 , que se
asignará al registro  AL , quedando la sentencia
ejecutable como:

 10110000 01100001

La cual puede ser entendida y ejecutada


directamente por el procesador.

Diseño del lenguaje[editar]


Elementos básicos[editar]
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[editar]
A diferencia de las instrucciones (sentencias) de
los lenguajes de alto nivel, las instrucciones en el
lenguaje ensamblador son generalmente muy
simples. Generalmente, un 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.
Los mnemónicos extendidos son frecuentemente
usados para soportar usos especializados de
instrucciones, a menudo para propósitos no
obvios con respecto al nombre de la instrucción.
Por ejemplo, muchos CPU no tienen una
instrucción explícita de  NOP  (No Operación), pero
tienen instrucciones que puedan ser usadas para
tal propósito. En el CPU 8086, la instrucción  XCHG
AX,AX  (intercambia el registro AX consigo mismo)
es usada para el  NOP , con  NOP  siendo un pseudo-
opcode para codificar la instrucción  XCHG AX,AX .
Algunos desensambladores reconocen esto y
decodificarán la instrucción  XCHG
AX,AX  como  NOP . Similarmente, los
ensambladores de IBM para el System/360 usan
los mnemónicos extendidos  NOP  y  NOPR  con las
máscaras cero para  BC  y  BCR .
Algunos ensambladores también soportan simples
macroinstrucciones incorporadas que generan
dos o más instrucciones de máquina. Por ejemplo,
con algunos ensambladores para el Z80, la
instrucción
LD HL, BC

genera las instrucciones


LD L, C
LD H, B .4

LD HL, BC  es un pseudo-opcode, que


en este caso simula ser una instrucción
de 16 bits. Cuando se expande se
producen dos instrucciones de 8 bits
que equivalen a la simulada de 16 bits.
Secciones de datos[editar]
Hay instrucciones usadas para definir
elementos de datos para manejar datos
y variables. Definen el tipo de dato, la
longitud y la alineación de los datos.
Estas instrucciones también pueden
definir si los datos están disponibles
para programas exteriores (programas
ensamblados separadamente) o
solamente para el programa en el cual
la sección de datos está definida.
Algunos ensambladores clasifican
estas instrucción como pseudo-
instrucciones.
Directivas del ensamblador[editar]
Las directivas del ensamblador,
también llamadas pseudo opcodes,
pseudo-operaciones o pseudo-ops, son
instrucciones que son ejecutadas por
un ensamblador en el tiempo de
ensamblado, no por la CPU en tiempo
de ejecución. Pueden hacer al
ensamblado del programa dependiente
de parámetros especificados por el
programador, de modo que un
programa pueda ser ensamblado de
diferentes maneras, quizás para
diversas aplicaciones. También pueden
ser usadas para manipular la
presentación de un programa para
hacerlo más fácil de leer y mantener.
Por ejemplo, las directivas pudieran ser
usadas para reservar áreas de
almacenamiento y opcionalmente para
asignar su contenido inicial. Los
nombres de las directivas a menudo
comienzan con un punto para
distinguirlas de las instrucciones de
máquina.
Los ensambladores simbólicos le
permiten a los programadores asociar
nombres arbitrarios (etiquetas o
símbolos) a posiciones de memoria.
Usualmente, cada constante y variable
tiene un nombre para que las
instrucciones puedan referir a esas
ubicaciones por nombre, así
promoviendo el código
autodocumentado. En el código
ejecutable, el nombre de cada
subprograma es asociado a su punto
de entrada, así que cualquier llamada a
un subprograma puede usar su
nombre. Dentro de subprogramas, a
los destinos GOTO se le dan etiquetas.
Algunos ensambladores soportan
símbolos locales que son léxicamente
distintos de los símbolos normales (ej,
el uso de "10$" como un destino
GOTO).
La mayoría de los ensambladores
proporcionan un manejo flexible de
símbolos, permitiendo a los
programadores manejar
diversos espacios de nombres, calcular
automáticamente offsets dentro
de estructuras de datos, y asignar
etiquetas que refieren a valores
literales o al resultado de cálculos
simples realizados por el ensamblador.
Las etiquetas también pueden ser
usadas para inicializar constantes y
variables con direcciones
relocalizables.
Los lenguajes ensambladores, como la
mayoría de los otros lenguajes de
computador, permiten que sean
añadidos comentarios al código fuente,
que son ignorados por el programa
ensamblador. El buen uso de los
comentarios es aún más importante
con código ensamblador que con
lenguajes de alto nivel, pues el
significado y el propósito de una
secuencia de instrucciones puede ser
más difícil de entender a partir del
código en sí mismo.
El uso sabio de estas facilidades puede
simplificar significativamente los
problemas de codificar y mantener el
código de bajo nivel. El código fuente
de lenguaje ensamblador crudo
generado por compiladores o
desensambladores - código sin ningún
comentario, ni símbolos con algún
sentido, ni definiciones de datos - es
muy difícil de leer cuando deben
hacerse cambios.
Macros[editar]
Muchos ensambladores soportan
macros predefinidas, y otros soportan
macros definidas (y repetidamente
redefinibles) por el programador que
implican secuencias de líneas del texto,
en las cuales las variables y las
constantes están embebidas. Esta
secuencia de líneas de texto puede
incluir opcodes o directivas. Una vez
una macro se define, su nombre se
puede usar en lugar de un mnemónico.
Cuando el ensamblador procesa tal
sentencia, reemplaza la sentencia por
las líneas del texto asociadas a esa
macro. Entonces, las procesa como si
hubieran existido en el archivo del
código fuente original (incluyendo, en
algunos ensambladores, la expansión
de cualquier macro que exista en el
texto de reemplazo).
Puesto que las macros pueden tener
nombres cortos pero se expanden a
varias, o de hecho, muchas líneas de
código, pueden usarse para hacer que
los programas en lenguaje
ensamblador parezcan ser mucho más
cortos, requiriendo menos líneas de
código fuente, como sucede con los
lenguajes de alto nivel. También se
pueden usar para añadir niveles de
estructura más altos a los programas
ensamblador; opcionalmente
introducen código de
depuración embebido vía parámetros y
otras características similares.
Muchos ensambladores tienen macros
incorporadas (o predefinidas) para las
llamadas de sistema y otras
secuencias especiales de código, tales
como la generación y el
almacenamiento de los datos
realizados a través de avanzadas
operaciones bitwise y operaciones
booleanas usadas en juegos, software
de seguridad, gestión de datos y
criptografía.
Los macro ensambladores a menudo
permiten a las macros
tomar parámetros. Algunos
ensambladores incluyen lenguajes
macro sofisticados, incorporando
elementos de lenguajes de alto nivel
tales como parámetros opcionales,
variables simbólicas, condiciones,
manipulaciones de strings, operaciones
aritméticas, todos usables durante la
ejecución de una macro dada, y
permitiendo a las macros guardar el
contexto o intercambiar información.
Así una macro puede generar un gran
número de instrucciones o definiciones
de datos en lenguaje ensamblador,
basadas en los argumentos de la
macro. Esto se podría usar para
generar, por ejemplo, estructuras de
datos de estilo de récord o bucles
"desenrollados", o podría generar
algoritmos enteros basados en
parámetros complejos. Una
organización, usando lenguaje
ensamblador, que ha sido fuertemente
extendido usando tal suite de macros,
puede ser considerada que se está
trabajando en un lenguaje de alto nivel,
puesto que tales programadores no
están trabajando con los elementos
conceptuales de más bajo nivel del
computador.
Las macros se usaron para
personalizar sistemas de software de
gran escala para clientes específicos
en la era del mainframe. También se
emplearon macros por los usuarios
mismos para satisfacer las
necesidades de sus organizaciones
haciendo versiones específicas de los
sistemas operativos del fabricante.
Esto fue hecho, por ejemplo, por los
programadores de sistema que
trabajaban con el Conversational
Monitor System / Virtual Machine
(CMS/VM) de IBM y con los
complementos real time transaction
processing de IBM, CICS, Customer
Information Control System,
el airline/financial system que comenzó
en los años 1970 y todavía corre con
muchos sistemas de reservaciones
computarizados (CRS) y sistemas de
tarjeta de crédito de hoy.
También es posible usar solamente 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 macro ensamblador puro
conteniendo líneas de código COBOL
dentro de operadores de tiempo
ensamblaje dando instrucciones al
ensamblador para generar código
arbitrario.
Esto era porque, como en los años
1970 fue observado, el concepto de
"procesamiento de macro" es
independiente del concepto de
"ensamblaje", siendo el anterior, en
términos modernos, más un
procesamiento de textos, que una
generación de código objeto. El
concepto de procesamiento de macro
apareció, y aparece, en el lenguaje de
programación C, que soporta
"instrucciones de preprocesador" de
fijar variables, y hace pruebas
condicionales en sus valores. Observe
que a diferencia de ciertos
macroprocesadores previos dentro de
los ensambladores, el preprocesador
de C no es Turing-completo porque
carecía de la capacidad de bucle o go
to, esto último permitiendo a los
programas hacer bucles.
A pesar del poder del procesamiento
macro, este dejó de usarse en muchos
lenguajes de alto nivel (una importante
excepción es C/C++), pero persistió en
los ensambladores. Esto era porque
muchos programadores estaban
bastante confundidos por la sustitución
de parámetros macro y no distinguían
la diferencia entre procesamiento
macro, el ensamblaje y la ejecución.
La sustitución de parámetros macro es
estrictamente por nombre: en el tiempo
de procesamiento macro, el valor de un
parámetro es sustituido textualmente
por su nombre. La clase más famosa
de Error de software resultantes era el
uso de un parámetro que en sí mismo
era una expresión y no un nombre
primario cuando el escritor macro
esperaba un nombre. En el macro:
foo: macro a
load a*b
la intención era que la rutina
que llama proporcionaría el
nombre de una variable, y la
variable o constante "global b"
sería usada para multiplicar a
"a". Si foo se llama con el
parámetro  a-c , ocurre la
expansión macro  load a-c*b .
Para evitar cualquier posible
ambigüedad, los usuarios de
macro procesadores pueden
encerrar en paréntesis los
parámetros formales dentro de
las definiciones de macros, o
las rutinas que llaman pueden
envolver en paréntesis los
parámetos de entrada.5 Así, el
macro correcto, con los
paréntesis, sería:
foo: macro a
load (a)*b
y su expansión, daría
como resultado:  load
(a-c)*b

El PL/I y el C/C++
ofrecen macros, pero
esta facilidad solo puede
manipular texto. Por otra
parte, los
lenguajes homoicónicos,
tales como Lisp, Prolog,
y Forth, retienen el poder
de los macros de
lenguaje ensamblador
porque pueden
manipular su propio
código como datos.
Soporte para
programación
estructurada[editar]
Algunos ensambladores
han incorporado
elementos
de programación
estructurada para
codificar el flujo de la
ejecución. El ejemplo
más temprano de este
acercamiento estaba en
el Concept-14 macro set,
originalmente propuesto
por el Dr. H.D. Mills
(marzo de 1970), e
implementado por
Marvin Kessler en
la Federal Systems
Division de IBM, que
extendió el macro
ensamblador del S/360
con bloques de control
de flujo IF/ELSE/ENDIF
y similares.6 Esto era
una manera de reducir o
eliminar el uso de
operaciones GOTO en el
código en lenguaje
ensamblador, uno de los
principales factores que
causaban código
espagueti en el lenguaje
ensamblador. Este
acercamiento fue
ampliamente aceptado a
principios de los años
1980 (los últimos días
del uso de lenguaje
ensamblador en gran
escala).
Un curioso diseño fue A-
natural, un ensamblador
"orientado a la corriente"
(stream-oriented) para
los
procesadores 8080/Z80[c
ita  requerida]
 de Whitesmiths
Ltd. (desarrolladores del
sistema operativo Idris,
similar al Unix), y lo que
fue reportado como el
primer compilador C
comercial). El lenguaje
fue clasificado como un
ensamblador, porque
trabajaba con elementos
de máquina crudos tales
como opcodes, registros,
y referencias de
memoria; pero
incorporaba 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 orientadas
a bloques, controlaban la
secuencia de las
instrucciones generadas.
A-natural fue construido
como el lenguaje objeto
de un compilador C, en
vez de la codificación
manual, pero su sintaxis
lógica ganó algunos
seguidores.
Ha habido poca
demanda aparente para
ensambladores más
sofisticados debido a la
declinación del
desarrollo de lenguaje
ensamblador de larga
escala.7 A pesar de eso,
todavía se están
desarrollando y
aplicando en casos
donde las limitaciones
de recursos o las
particularidades en la
arquitectura de sistema
objetivo previenen el
efectivo uso de
lenguajes de alto nivel.8

Uso del lenguaje


ensamblador[edita
r]
Perspectiva
histórica[editar]
Los lenguajes
ensambladores fueron
primero desarrollados en
los años 1950, cuando
fueron referidos como
lenguajes de
programación de
segunda generación. Por
ejemplo, el SOAP
(Symbolic Optimal
Assembly Program) era
un lenguaje
ensamblador de 1957
para el computador IBM
650. Los lenguajes
ensambladores
eliminaron mucha de la
propensión a errores y
del consumo de tiempo
de la programación de
los lenguajes de primera
generación, que se
necesitaba con los
primeros computadores,
liberando a los
programadores del tedio
tal como recordar
códigos numéricos y
cálculo de direcciones.
Una vez fueron
ampliamente usados
para todo tipo de
programación. Sin
embargo, por los años
1980 (1990 en
los microcomputadores),
su uso había sido en
gran parte suplantado
por los lenguajes de alto
nivel,[cita  requerida] en la
búsqueda de una
mejorada productividad
en programación. Hoy
en día, aunque el
lenguaje ensamblador es
casi siempre manejado y
generado por
los compiladores,
todavía se usa para la
manipulación directa del
hardware, acceso a
instrucciones
especializadas del
procesador, o para
resolver problemas de
desempeño crítico. Los
usos típicos son
controladores/manejador
es (drivers) de
dispositivo,
sistemas embebidos de
bajo nivel, y sistemas
de tiempo real.
Históricamente, un gran
número de programas
han sido escritos
enteramente en lenguaje
ensamblador. Los
sistemas operativos
fueron casi
exclusivamente escritos
en lenguaje
ensamblador hasta la
aceptación amplia del
lenguaje de
programación C en los
años 1970 y principios
de los 1980. También,
muchas aplicaciones
comerciales fueron
escritas en lenguaje
ensamblador, incluyendo
una gran cantidad del
software escrito por
grandes corporaciones
para mainframes de
IBM. Los
lenguajes COBOL y FO
RTRAN eventualmente
desplazaron mucho de
este trabajo, aunque un
número de
organizaciones grandes
conservaran las
infraestructuras de
aplicaciones en lenguaje
ensamblador hasta bien
entrados los años 1990.
La mayoría de los
primeros
microcomputadores
confiaron en el lenguaje
ensamblador codificado
a mano, incluyendo la
mayoría de los sistemas
operativos y de las
aplicaciones grandes.
Esto era porque estos
sistemas tenían
limitaciones severas de
recursos, impusieron
idiosincráticas
arquitecturas de
memoria y de pantalla, y
proporcionaron servicios
de sistema limitados y
con errores. Quizás más
importante era la falta de
compiladores de primera
clase de lenguajes de
alto nivel adecuados
para el uso en el
microcomputador. Un
factor psicológico
también pudo haber
jugado un papel: la
primera generación de
programadores de los
microcomputadores
conservó una actitud de
aficionado de "alambres
y alicates".
En un contexto más
comercial, las más
grandes razones para
usar el lenguaje
ensamblador era hacer
programas con mínimo
tamaño, mínima
sobrecarga, mayor
velocidad y confiabilidad.
Los típicos ejemplos de
programas grandes en
lenguaje ensamblador
de ese tiempo son los
sistemas
operativos IBM PC
DOS y aplicaciones
tempranas tales como
la hoja de cálculo Lotus
1-2-3, y casi todos los
juegos populares para la
familia Atari 800 de
computadores
personales. Incluso en
los años 1990, la
mayoría de los
videojuegos de consola
fueron escritos en
ensamblador, incluyendo
la mayoría de los juegos
para la Mega
Drive/Genesis y el Super
Nintendo Entertainment
System.[cita  requerida] Según
algunos insiders de la
industria, el lenguaje
ensamblador era el
mejor lenguaje de
programación a usar
para obtener el mejor
desempeño del Sega
Saturn, una consola para
la cual era notoriamente
desafiante desarrollar y
programar juegos.9 El
popular juego
de arcade NBA
Jam (1993) es otro
ejemplo. El ensamblador
ha sido por largo trecho,
el lenguaje de desarrollo
primario en los
computadores
hogareños Commodore
64, Atari ST, así como
el ZX Spectrum. Esto fue
así en gran parte porque
los dialectos
del BASIC en estos
sistemas ofrecieron
insuficiente velocidad de
ejecución, así como
insuficientes
características para
aprovechar
completamente el
hardware disponible.
Algunos sistemas, más
notablemente el Amiga,
incluso tienen IDEs con
características de
depuración y macros
altamente avanzados,
tales como el
freeware ASM-One
assembler, comparable
a las del Microsoft Visual
Studio (el ASM-Uno
precede al Microsoft
Visual Studio).
El ensamblador para
el VIC-20 fue escrito por
Don French y publicado
por French Silk. Con
1639 bytes de longitud,
su autor cree que es el
más pequeño
ensamblador simbólico
jamás escrito. El
ensamblador soportaba
el direccionamiento
simbólico usual y la
definición de cadenas de
caracteres o cadenas
hexadecimales. También
permitía expresiones de
direcciones que podían
combinarse con las
operaciones
de adición, sustracción, 
multiplicación, división, A
ND lógico, OR lógico,
y exponenciación.10
Uso actual[editar]
Siempre ha habido
debates sobre la utilidad
y el desempeño del
lenguaje ensamblador
relativo a lenguajes de
alto nivel. El lenguaje
ensamblador tiene
nichos específicos
donde es importante (ver
abajo). Pero, en general,
los modernos
compiladores de
optimización para
traducir lenguajes de alto
nivel en código pueden
correr tan rápidamente
como el lenguaje
ensamblador escrito a
mano, a pesar de los
contraejemplos que
pueden ser
encontrados.111213 La
complejidad de los
procesadores modernos
y del subsistema de
memoria hace la
optimización efectiva
cada vez más difícil para
los compiladores, así
como para los
programadores en
ensamblador.1415
Adicionalmente, y para
la consternación de los
amantes de la eficiencia,
el desempeño cada vez
mayor del procesador ha
significado que la
mayoría de
los CPU estén
desocupados la mayor
parte del tiempo, con
retardos causados por
embotellamientos
predecibles tales como
operaciones
de entrada/salida y pagin
ación de memoria. Esto
ha hecho que la
velocidad de ejecución
cruda del código no sea
un problema para
muchos programadores.
Hay algunas situaciones
en las cuales los
profesionales pudieran
elegir utilizar el lenguaje
ensamblador. Por
ejemplo cuando:

 Es requerido un
ejecutable binario
independiente
(stand-alone), es
decir uno que deba
ejecutarse sin
recursos a
componentes
de tiempo de
ejecución o
a bibliotecas asociad
as con un lenguaje
de alto nivel; ésta es
quizás la situación
más común. Son
programas
empotrados que solo
almacenan una
pequeña cantidad de
memoria y el
dispositivo está
dirigido para hacer
tareas para un simple
propósito. Ejemplos
consisten en
teléfonos, sistemas
de combustible e
ignición para
automóviles,
sistemas de control
del aire
acondicionado,
sistemas de
seguridad, y
sensores.
 Interactuando
directamente con el
hardware, por
ejemplo en
controladores
(drivers) de
dispositivo y
manejadores de
interrupción.
 Usando instrucciones
específicas del
procesador no
explotadas o
disponibles por el
compilador. Un
ejemplo común es la
instrucción de
rotación bitwise en el
núcleo de muchos
algoritmos de cifrado.
 Creando funciones
vectorizadas para
programas en
lenguajes de alto
nivel como C. En el
lenguaje de alto nivel
esto es a veces
ayudado por
funciones intrínsecas
del compilador que
mapean
directamente a los
mnemónicos del
SIMD, pero sin
embargo resulta en
una conversión de
ensamblador de uno
a uno para un
procesador de vector
asociado.
 Es requerida la
optimización
extrema, ej, en
un bucle interno en
un algoritmo
intensivo en el uso
del procesador. Los
programadores de
juegos toman ventaja
de las habilidades de
las características del
hardware en los
sistemas,
permitiendo a los
juegos correr más
rápidamente.
También las grandes
simulaciones
científicas requieren
algoritmos altamente
optimizados,
ej, álgebra lineal con
BLAS o
la transformada de
coseno discreta (ej,
la versión SIMD en
ensamblador
del x264,16 (una
biblioteca para
codificar streams de
video).
 Un sistema con
severas limitaciones
de recursos (ej,
un sistema
empotrado) debe ser
codificado a mano
para maximizar el
uso de los limitados
recursos; pero esto
está llegando a ser
menos común a
medida que el precio
del procesador
decrece y el
desempeño mejora.
 No existe ningún
lenguaje de alto
nivel, en un
procesador nuevo o
especializado.
 Escribiendo
programas de tiempo
real que necesitan
sincronización y
respuestas precisas,
tales como sistemas
de navegación de
vuelo, y equipo
médico. Por ejemplo,
en un sistema fly-by-
wire (vuelo por
mandos eléctricos),
la telemetría debe
ser interpretada y
hay que actuar
dentro de
limitaciones estrictas
de tiempo. Tales
sistemas deben
eliminar fuentes de
retrasos
impredecibles, que
pueden ser creados
por (algunos)
lenguajes
interpretados, recolec
ción de
basura automática,
operaciones de
paginación,
o multitarea
apropiativa. Sin
embargo, algunos
lenguajes de alto
nivel incorporan
componentes
de tiempo de
ejecución e
interfaces de sistema
operativo que
pueden introducir
tales retrasos. Elegir
el ensamblador o
lenguajes de bajo
nivel para tales
sistemas da a los
programadores
mayor visibilidad y
control sobre los
detalles del
procesamiento.
 Es requerido control
total sobre el
ambiente, en
situaciones de
seguridad
extremadamente alta
donde nada puede
darse por sentado.
 Se escriben virus de
computadora, bootlo
aders, ciertos
controladores/maneja
dores de dispositivo,
u otros elementos
muy cerca del
hardware o al
sistema operativo de
bajo nivel.
 Se escriben
simuladores del
conjunto de
instrucciones para
monitoreo, trazado
y depuración de
errores donde la
sobrecarga adicional
es mantenida al
mínimo.
 Se hace ingeniería
inversa en binarios e
xistentes que pueden
o no haber sido
escritos
originalmente en un
lenguaje de alto
nivel, por ejemplo al
crackear
la protección
anticopia del
software propietario.
 Se hace ingeniería
inversa y
modificación de video
juegos (también
denominado ROM
hacking), que es
posible por medio de
varios métodos. El
más ampliamente
implementado es
alterando el código
del programa a nivel
de lenguaje
ensamblador.
 Se escribe código
automodificable
(también conocido
como polimórfico),
algo para lo que el
lenguaje
ensamblador se
presta bien.
 Se escriben juegos y
otros softwares
para calculadoras
gráficas.17
 Se escribe software
compilador que
genera código
ensamblador, y por lo
tanto los
desarrolladores
deben ser
programadores de
lenguaje
ensamblador.
 Se escriben
algoritmos
criptográficos que
siempre deben tomar
estrictamente el
mismo tiempo para
ejecutar, previniendo
ataques de tiempo.
Sin embargo, el lenguaje
ensamblador es todavía
enseñado en la mayoría
de los programas
de ciencias de la
computación e ingeniería
electrónica. Aunque hoy
en día, pocos
programadores trabajan
regularmente con el
lenguaje ensamblador
como una herramienta,
los conceptos
fundamentales continúan
siendo muy importantes.
Tales tópicos
fundamentales,
como aritmética
binaria, asignación de
memoria, procesamiento
del stack, codificación de
conjunto de caracteres,
procesamiento de
interrupciones, y diseño
de compiladores, serían
duros de estudiar en
detalle sin la
comprensión de cómo el
computador opera a
nivel del hardware.
Puesto que el
comportamiento del
computador es
fundamentalmente
definido por su conjunto
de instrucciones, la
manera lógica de
aprender tales
conceptos es estudiar un
lenguaje ensamblador.
La mayoría de los
computadores modernos
tienen un conjunto 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 de lenguaje
ensamblador puede ser
apropiado; y iii) ver cómo
el código ejecutable
eficiente puede ser
creado por los lenguajes
de alto nivel18
Aplicaciones
típicas[editar]
El lenguaje
ensamblador hard-coded 
es típicamente usado en
el ROM de arranque del
sistema (BIOS en los
sistemas compatible IBM
PC). Este código de bajo
nivel es usado, entre
otras cosas, para
inicializar y probar el
hardware del sistema
antes
de cargar el sistema
operativo, y está
almacenado en el ROM.
Una vez que ha tomado
lugar un cierto nivel de
inicialización del
hardware, la ejecución
se transfiere a otro
código, típicamente
escrito en lenguajes de
alto nivel; pero el código
corriendo
inmediatamente después
de que es aplicada la
energía usualmente está
escrito en lenguaje
ensamblador. Lo mismo
es cierto para los boot
loaders.
Muchos compiladores
traducen lenguajes de
alto nivel a lenguaje
ensamblador primero,
antes de la compilación
completa, permitiendo
que el código en
ensamblador sea visto
para propósitos
de depuración y
optimización. Lenguajes
de relativo bajo nivel,
como C, con frecuencia
proveen sintaxis especial
para empotrar lenguaje
ensamblador en cada
plataforma de hardware.
El código portable del
sistema entonces puede
usar estos componentes
específicos a un
procesador a través de
una interface uniforme.
El lenguaje ensamblador
también es valioso
en ingeniería inversa,
puesto que muchos
programas solamente
son distribuidos en una
forma de código de
máquina. El código de
máquina es usualmente
fácil de trasladar hacia
lenguaje ensamblador
para luego ser
cuidadosamente
examinado en esta
forma, pero es muy difícil
de trasladar hacia un
lenguaje de alto nivel.
Herramientas
como Interactive
Disassembler, hacen
uso extenso
del desensamblador par
a tales propósitos.
Un nicho que hace uso
del lenguaje
ensamblador es
el demoscene. Ciertas
competiciones requieren
a los concursantes
restringir sus creaciones
a un muy pequeño
tamaño (ej, 256 bytes,
1 KB, 4 KB o 64 KB), y el
lenguaje ensamblador es
el lenguaje de
preferencia para
alcanzar este objetivo.19
Cuando los recursos son
una preocupación, es
una necesidad la
codificación en
ensamblador,
especialmente en
sistemas constreñidos
por el procesamiento del
CPU, como los primeros
modelos del Amiga, y
el Commodore 64. El
código optimizado en
ensamblador es escrito
"a mano" por
los programadores en un
intento de minimizar el
número de ciclos de
CPU usados. Las
limitaciones del CPU son
tan grandes que cada
ciclo cuenta. Usar tales
métodos ha habilitado, a
sistemas como el
Commodore 64, para
producir gráficos en
3D en tiempo real con
efectos avanzados, una
hazaña que puede ser
considerada improbable
o incluso imposible para
un sistema con un
procesador de
0.99 MHz.[cita  requerida]

Detalles
adicionales[editar]
Para un determinado
computador personal,
mainframe, sistema
empotrado, y consola de
juegos, tanto del pasado
como del presente, ha
sido escrito al menos
uno, y posiblemente
docenas de
ensambladores. Para
algunos ejemplos, vea
la lista de
ensambladores.
En los sistemas Unix, el
ensamblador es llamado
tradicionalmente as,
aunque no es un simple
cuerpo de código, siendo
típicamente escrito uno
nuevo por cada port. Un
número de variantes de
Unix usan el GAS
Dentro de los grupos de
procesadores, cada
ensamblador tiene su
propio dialecto. A veces,
algunos ensambladores
pueden leer el dialecto
de otro, por
ejemplo, TASM puede
leer el viejo código
del MASM, pero no al
revés. FASM y NASM tie
nen una sintaxis similar,
pero cada uno soporta
diferentes macros que
pueden ser difícil de
trasladar de uno al otro.
Las cosas básicas son
siempre las mismas,
pero las características
avanzadas serán
diferentes20
También, los lenguajes
ensambladores a veces
pueden ser portables a
través de diferentes
sistemas operativos en
el mismo tipo de CPU.
Las convenciones de
llamadas entre los
sistemas operativos con
frecuencia difieren
ligeramente o en nada. y
con cuidado es posible
ganar portabilidad en el
lenguaje ensamblador,
usualmente al enlazar
con una biblioteca de
lenguaje C que no
cambia entre sistemas
operativos. Un simulador
de conjunto de
instrucciones (que
idealmente sería escrito
en lenguaje
ensamblador) puede, en
teoría, procesar
el código
objeto/binario de cualqui
er ensamblador) para
lograr la portabilidad
incluso a través
de plataformas (con una
sobrecargue no mayor
que la de un
interpretador
de bytecode típico). Esto
es esencialmente lo que
logra
el microcódigo cuando
una plataforma de
hardware cambia
internamente.
Por ejemplo, muchas
cosas en libc dependen
del preprocesador para
hacer, al programa antes
de compilar, cosas que
son específicas del
sistema operativo o
específicas del C. De
hecho, algunas
funciones y símbolos ni
siquiera están
garantizados que existan
fuera del preprocesador.
Peor aún, el tamaño y el
orden de los campos de
las estructuras, tanto
como el tamaño de
ciertas typedefs como off
_t, no están disponibles
en lenguaje
ensamblador sin la
ayuda de un script de
configuración, y difieren
incluso entre versiones
de Linux, haciendo
imposible portar
llamadas de funciones
en libc diferentes de los
que toman simples
enteros o punteros como
parámetros. Para
manejar estos
problemas, el proyecto
FASMLIB provee una
biblioteca de lenguaje
ensamblador portable
para las plataformas
Win32 y Linux, pero
todavía está muy
incompleta.21
Algunos lenguajes de
muy alto nivel, como C y
Borland/Pascal, soportan
ensamblado en línea,
donde relativamente
secciones cortas de
código en ensamblador
puede ser empotradas
dentro del código del
lenguaje de alto nivel. El
lenguaje Forth comúnme
nte contiene un
ensamblador usado para
codificar palabras.
La mayoría de la gente
usa
un emulador para depur
ar sus programas en
lenguaje ensamblador.

Ejemplos de
lenguaje
ensamblador[edita
r]
Ejemplo para la
arquitectura
x86[editar]
El siguiente es un
ejemplo del programa
clásico Hola
mundo escrito para la
arquitectura de
procesador x86 (bajo el
sistema operativo DOS).

;
----------------------
----------------------
-
; Programa que imprime
un string en la
pantalla
;
----------------------
----------------------
-
.model small
; modelo de memoria
.stack
; segmento del stack

.data
; segmento de datos
Cadena1 DB 'Hola
Mundo. ; string a
imprimir (finalizado
en $)

.code
; segmento del código

;
----------------------
----------------------
-
; Inicio del programa
;
----------------------
----------------------
-
programa:
;
----------------------
----------------------
----------------------
----------------------
------------
; inicia el
segmento de datos
;
----------------------
----------------------
----------------------
----------------------
------------
MOV AX,
@data ; carga
en AX la dirección del
segmento de datos
MOV DS, AX
; mueve la dirección
al registro de
segmento por medio de
AX

;
----------------------
----------------------
----------------------
----------------------
------------
; Imprime un
string en pantalla
;
----------------------
----------------------
----------------------
----------------------
------------
MOV DX,
offset Cadena1 ; mueve
a DX la dirección del
string a imprimir
MOV AH, 9
; AH = código para
indicar al MS DOS que
imprima en la
pantalla, el string en
DS:DX
INT 21h
; llamada al MS DOS
para ejecutar la
función (en este caso
especificada en AH)

;
----------------------
----------------------
----------------------
----------------------
------------
; Finaliza
el programa
;
----------------------
----------------------
----------------------
----------------------
------------
INT 20h
; llamada al MS DOS
para finalizar el
programa

end programa

Ejemplo para el
computador virtual
(POCA)[editar]
Una selección de
instrucciones para una
computadora virtual22)
con las correspondientes
direcciones de memoria
en las que se ubicarán
las instrucciones. Estas
direcciones NO son
estáticas. Cada
instrucción se acompaña
del código en lenguaje
ensamblador generado
(código objeto) que
coincide con la
arquitectura de
computador virtual, o
conjunto de
instrucciones ISA.

Códi
go
Di Etiq Instruc
máq
r. ueta ción
uina2
3

.begi
n

.org
2048

a_st .equ
art 3000

ld
20 [leng
48 th],
%r1

0000
0010
1000
20 be 0000
64 done 0000
0000
0000
0110
1000
0010
1000
addcc
20 0000
%r1,-
68 0111
4,%r1
1111
1111
1100

1000
1000
addcc 1000
20 %r1,% 0000
72 r2,%r 0100
4 0000
0000
0010

1100
1010
0000
ld
20 0001
%r4,%
76 0000
r5
0000
0000
0000

0001
0000
1011
20 ba 1111
80 loop 1111
1111
1111
1011

1000
0110
addcc 1000
20 %r3,% 0000
84 r5,%r 1100
3 0000
0000
0101

20 done jmpl 1000


88 : %r15+ 0001
1100
0011
1110
4,%r0
0000
0000
0100

0000
0000
0000
20 leng 0000
20
92 th: 0000
0000
0001
0100

0000
0000
0000
20 addr a_sta 0000
96 ess: rt 0000
1011
1011
1000

.org
a_sta
rt

30
a:
00

Ejemplo para el
computador virtual
(ARC)[editar]
ARC es un subconjunto
del modelo de
arquitectura basado en
el procesador SPARC.
ARC ( A RISC
Computer) contiene la
mayoría de las
características
importantes de la
arquitectura SPARC.

Dire Instr Come


Etiq
cció ucció ntario
ueta
n n s

.begi
n

.org
2048

!
Direc
ción
de
memo
a_st .equ
ria del
art: 3000
arregl
o
comie
nza en
3000

! Se
establ
ece la
ld
longit
[leng
2048 ud del
ht],
arregl
%r1
o en el
regist
ro

!
ld Direc
[addr ción
2052
ess], del
%r2 arregl
oa
! Se
establ
andd ece
cc con
2056 %r3, cero
%r0, la
%r3 suma
parcia
l

!
Verifi
andd co si
cc r1
loop
2060 %r1, tiene
:
%r1, eleme
%r0 ntos
restan
tes

be
2064
done

!
Decre
addc
mento
c
el
2068 %r1,
tamañ
-4,
o del
%r1
arregl
o

!
Cargo
ld
el
%r1
próxi
2072 +
mo
%r2,
eleme
%r4
nto en
r4

2076 addc !
c Actua
%r3, lizo la
%r4, suma
parcia
%r3
l en r3

!
Vuelv
ea
verific
ba
2080 ar la
loop
condi
ción
de
corte

!
Retor
jmpl
no a
don %r15
2084 la
e: + 4,
rutina
%r0
princi
pal

!
Tama
ño del
arregl
leng o en
2088 20
ht: 20
bytes,
5
núme
ros

add
a_sta
2092 ress
rt
:

!
Comi
.org
enzo
a_sta
del
rt
arregl
oa
a: 25

15

-20

-35

15

! Fin
del
.end
progr
ama

Ejemplo para el µC
Intel 8051[editar]
Código en lenguaje
ensamblador
para µC Intel 80C51:

ORG 8030H
include
T05SEG:
SETB TR0
JNB uSEG,T05SEG
;esta subrutina es
utilizada
CLR TR0 ;para
realizar una cuenta de
CPL uSEG ;0,5
segundos mediante la
MOV R1,DPL
;interrupción del
timer 0.
INVOKE
MOV R2,DPH
CJNE R2,#07H,T05SEG
CJNE R1,#78H,T05SEG
MOV DPTR,#0
RET

Ejemplo para el
Microchip
PIC16F84[editar]
Código en lenguaje
ensamblador para
el microcontrolador 16F8
4 de Microchip:

ORG 0
Inicio
bcf STATUS,RP0
clrf PORTB
movlw 0xFF
movwf PORTA
bsf STATUS,RP0
Principal
movf PORTA,W

movwf Contador

movf Contador,F
btfsc STATUS,Z
goto PuntoDecimal
sublw d'9'
btfss STATUS,C
END

Lenguaje de alto nivel


Ir a la navegaciónIr a la búsqueda
Un lenguaje de programación de alto nivel se caracteriza por expresar
los algoritmos de una manera adecuada a la capacidad cognitiva humana, en
lugar de la capacidad con que los ejecutan las máquinas. Estos lenguajes
permiten una máxima flexibilidad al programador a la hora de abstraerse o de ser
literal. Permiten un camino bidireccional entre el lenguaje máquina y una expresión
casi oral entre la escritura del programa y su posterior compilación. Por lo general
suelen estar orientados a objetos, a eventos o a funciones, pudiendo estos
combinarse. Asimismo, pueden ser compilados o interpretados. Algunos ejemplos
son: Java, PHP, Python, Javascript, C#.
En los primeros lenguajes, la limitación era que se orientaban a un área específica
y sus instrucciones requerían de una sintaxis predefinida. Se clasifican como
lenguajes procedimentales o lenguajes de bajo nivel. Otra limitación de estos es
que se requiere de ciertos conocimientos de programación para realizar las
secuencias de instrucciones lógicas. Los lenguajes de alto nivel se crearon para
que el usuario común pudiese solucionar un problema de procesamiento de datos
de una manera más fácil y rápida.
Por esta razón, a finales de los años 1950 surgió un nuevo tipo de lenguajes de
programación que evitaba estos inconvenientes, a costa de ceder un poco en las
ventajas. Estos lenguajes se llaman de tercera generación o de nivel alto, en
contraposición a los de bajo nivel o de nivel próximo a la máquina.

Índice

 1Características
 2Ventajas e inconvenientes
 3Principales lenguajes de nivel alto
 4Véase también
 5Referencias

Características[editar]
Lenguaje de alto nivel se refiere al nivel más alto de abstracción de lenguaje de
máquina. En lugar de tratar con registros, direcciones de memoria y las pilas de
llamadas, lenguajes de alto nivel se refieren a las variables, matrices, objetos,
aritmética compleja o expresiones booleanas, subrutinas y funciones, bucles,
hilos, cierres y otros conceptos de informática abstracta, con un enfoque en la
facilidad de uso sobre la eficiencia óptima del programa.

Ventajas e inconvenientes[editar]
Ventajas

 Genera un código más sencillo y comprensible.


 Escribir un código válido para diversas máquinas o sistemas operativos.
 Permite utilizar paradigmas de programación.
 Permite crear programas complejos en relativamente menos líneas de
código.
Inconvenientes

 Reducción de velocidad al ceder el trabajo de bajo nivel a la máquina.


 Algunos requieren que la máquina cliente posea una determinada
plataforma.

Principales lenguajes de nivel alto[editar]


 VB.NET
 Erlang
 Ada
 ALGOL
 BASIC
 C Sharp
 FORTRAN
 C++ Se usa en conjunto con C y deben mencionarse como C/C++

Principales lenguajes considerados de nivel medio


(Debido a que permite programar en nivel bajo)

 C Se usa en conjunto con C++ y deben mencionarse como C/C++


Lenguaje de máquina
Ir a la navegaciónIr a la búsqueda

Este artículo o sección necesita referencias que aparezcan en una publicación


acreditada.

Este aviso fue puesto el 13 de junio de 2011.

Lenguaje de máquina del Intel 8088. El código de máquina en hexadecimal se resalta en rojo, el


equivalente en lenguaje ensamblador en magenta, y las direcciones de memoria donde se encuentra el
código, en azul. Abajo se ve un texto en hexadecimal y ASCII.

El lenguaje de máquina o código máquina es el sistema de códigos


directamente interpretable por un circuito microprogramable, como
el microprocesador de una computadora o el microcontrolador de un autómata.
Este lenguaje está compuesto por un conjunto de instrucciones que determinan
acciones a ser tomadas por la máquina. Un programa consiste en una cadena de
estas instrucciones más un conjunto de datos sobre el cual se trabaja. Estas
instrucciones son normalmente ejecutadas en secuencia, con eventuales cambios
de flujo causados por el propio programa o eventos externos. El lenguaje de
máquina es específico de la arquitectura de la máquina, aunque el conjunto de
instrucciones disponibles pueda ser similar entre arquitecturas distintas.
Los circuitos microprogramables son digitales, lo que significa que trabajan con
dos únicos niveles de tensión. Dichos niveles, por abstracción, se simbolizan con
los números 0 y 1, por eso el lenguaje de máquina solo utiliza dichos signos. Esto
permite el empleo de las teorías del álgebra booleana y del sistema binario en el
diseño de este tipo de circuitos y en su programación.
Claude Elwood Shannon, en su libro Analysis of Relay and Switching Circuits, y
con sus experiencias en redes de conmutación, sentó las bases para la aplicación
del álgebra de Boole a las redes de conmutación. Una red de conmutación es un
circuito de interruptores eléctricos que al cumplir ciertas combinaciones booleanas
con las variables de entrada, define el estado de la salida. Este concepto es el
núcleo de las puertas lógicas, las cuales son, por su parte, los ladrillos con que se
construyen sistemas lógicos cada vez más complejos. Shannon utilizaba
el relé como dispositivo físico de conmutación en sus redes, dado que el relé, a
igual que una lámpara eléctrica, posee dos estados: activado (encendido) o
desactivado (apagado).
El desarrollo tecnológico ha permitido evolucionar desde las redes de relés
electromagnéticos a circuitos con tubos de vacío, luego a redes transistorizadas,
hasta llegar a los modernos circuitos integrados, en cuya cúspide se encuentran
los circuitos microprogramados.

También podría gustarte