Documentos de Académico
Documentos de Profesional
Documentos de Cultura
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.
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
...
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:
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
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:
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++:
Compilador
Ir a la navegaciónIr a la búsqueda
Í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
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.
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:
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
Í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.
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.
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)
Índice de
Binario Hexadecimal Significado
memoria
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
Apareció en 1949
Í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
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:
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:
Binario: 10110000
01100001 (hexadecimal: B061 )
10110000 01100001
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
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
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
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.
.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
Í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