Ensamblador

LENGUAJE MÁQUINA Los ordenadores sólo entienden un lenguaje específico para cada máquina, que se denomina Código Maquina

Views 309 Downloads 14 File size 1MB

Report DMCA / Copyright

DOWNLOAD FILE

Recommend stories

Citation preview

LENGUAJE MÁQUINA Los ordenadores sólo entienden un lenguaje específico para cada máquina, que se denomina Código Maquina o Lenguaje Máquina. Este lenguaje utiliza un código binario (símbolos "0" y "1"). Las órdenes que se dan a un ordenador han de ir codificadas en instrucciones, y estas forman los programas. Las instrucciones tienen dos partes diferenciadas: código de operación y código(s) de operando(s):

En la primera, se codifica la operación que realiza la instrucción. Este código de operación siempre es único para cada instrucción. En la segunda, se indica(n) la(s) dirección(es) de memoria en la que se encuentra el operando, hasta un máximo de tres, sobre el/(los) que se aplicará la operación. Puesto que cada tipo de ordenador tiene su código máquina específico, para programar en este lenguaje el programador debe conocer la arquitectura física de la computadora con cierto detalle (registros de la CPU, palabras de memoria,...). La estructura del lenguaje máquina está totalmente adaptada a los circuitos de la computadora y muy alejada del lenguaje que empleamos normalmente para expresar y analizar los problemas que hoy día son resolubles con la computadora. Por ejemplo, para hacer cálculos aritméticos disponemos de un "lenguaje" matemático fácil de comprender y claro, que no se parece en nada al código máquina necesario para hacer dichos cálculos. Las ventajas de los lenguajes máquina son: 



Un programa escrito en lenguaje máquina es directamente interpretable por el procesador central. Una vez introducido el programa en la memoria principal de la computadora, no se necesitan transformaciones previas para ser ejecutado (como más adelante veremos que sí ocurre con los programas escritos en lenguajes de alto nivel). Los programas escritos en lenguaje máquina se ejecutan muy eficientemente (con rapidez), debido a que el usuario lo redacta específicamente para los circuitos que lo han de interpretar y ejecutar, y a que desde el código máquina se puede utilizar la totalidad de los recursos de la máquina.

Por contra, los lenguajes máquina tienen los siguientes inconvenientes:  





Las instrucciones son cadenas de ceros y unos, aunque estas cadenas se pueden introducir en la computadora mediante un código intermedio (octal o hexadecimal). Los datos se utilizan por medio de las direcciones de memoria donde se encuentran. En las instrucciones no aparecen nombres de variables (i, j, aux,...), sino que el programador debe asignar las direcciones de memoria para las variables y constantes del programa. Para realizar esta asignación se debe tener en cuenta la zona de memoria que ocupa el programa, para que no se traslape con la zona en la que se almacenan las variables. El repertorio de instrucciones suele ser muy reducido y las instrucciones realizan operaciones muy simples. El lenguaje máquina depende y está ligado íntimamente a la CPU del computador. Si dos computadoras tienen CPU's diferentes, tendrán distintos lenguajes máquina. En particular, dos microcomputadoras con el mismo microprocesador e iguales circuitos de control, tienen igual lenguaje máquina. La dependencia del lenguaje máquina de la configuración de la CPU hace que los programas redactados en este lenguaje de programación sean poco transferibles o transportables de una computadora a otra. En un programa en código máquina, no pueden incluirse comentarios que faciliten la legibilidad del mismo. Además, debido a su representación totalmente numérica, es muy difícil de reconocer o interpretar por el usuario.

LENGUAJE HUMANO El lenguaje humano se basa en la capacidad de los seres humanos para comunicarse por medio de signos(usualmente secuencias sonoras, pero también gestos y señas, así como signos gráficos). Principalmente lo hacemos utilizando el signo lingüístico. Aun así, hay diversos tipos de lenguaje. La complejidad del lenguaje humano se debe sin duda a su potencia. El hombre es un creador de códigos, y el lenguaje es un código característicamente creativo o productivo. Es un código abierto. Además, muchos de los códigos creados por el hombre son códigos secundarios respecto al lenguaje (la escritura, el Morse,...). Con ellos el hombre convierte su código lingüístico no inscripcional (los mensajes desaparecen sin dejar huella) en otro código inscripcional, la escritura, de extraordinaria trascendencia. NECESIDAD DE UN TRADUCTOR Para facilitar el trabajo de programación y hacer los programas más legibles, resolviendo las limitaciones que presentan los lenguajes máquina, se han desarrollado lenguajes de programación que permiten utilizar una simbología y terminología próximas a las tradicionalmente utilizadas en la descripción de problemas. Estos lenguajes se denominan genéricamente lenguajes simbólicos. Como la computadora únicamente puede interpretar y ejecutar código máquina, existen programas traductores, que traducen o trasladan programas desde el lenguaje simbólico al lenguaje máquina. En general existen traductores para convertir programas escritos en un lenguaje a otro. El programa inicial se denomina programa fuente, y el programa obtenido tras el proceso de traducción programa objeto. En el mecanismo el programa fuente es utilizado por el traductor como conjunto de datos que debe procesar, y a partir del mismo genera como resultado el programa objeto. Todo ello con la ayuda del computador. EVOLUCIÓN DE LOS LENGUAJES DE PROGRAMACIÓN Los lenguajes de programación han pasado por 4 diferentes generaciones en las que han evolucionado notablemente. Las 2 primeras son llamadas de bajo nivel, porque son dependientes de la máquina, están diseñados para ejecutarse en una determinada computadora y las 2 más recientes, las de alto nivel son independientes de la máquina y se pueden utilizar en una variedad de computadoras.    

Primera Generación: Lenguaje de Máquina. Segunda Generación: Lenguajes Simbólicos. Tercera Generación: Lenguajes de alto nivel. Cuarta Generación: Programación orientada a objetos.

CARÁCTERÍSTICAS DE UN LENGUAJE SIMBÓLICO:     

Constituye la representación más directa del código máquina específico para cada arquitectura de computadoras legible por un programador. Fue usado principalmente en los inicios del desarrollo de software, cuando aún no se contaba con los potentes lenguajes de alto nivel. Un programa escrito en lenguaje simbólico consiste en una serie de instrucciones que corresponden al flujo de órdenes ejecutables que pueden ser cargados en la memoria de un sistema basado microprocesador. No son directamente interpretados por la circuitería de la máquina. Se codifican mediante símbolos alfanuméricos, de puntuación, paréntesis, separadores, etc.

¿A QUE SE REFIERE UN ENSAMBLADOR? El lenguaje ensamblador, o assembler(assembly language en inglés), es un lenguaje de programación de bajo nivel 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 dada de CPU y constituye la representación más directa del código máquina específico para cada arquitectura legible por un programador. Esta representación es usualmente definida por el fabricante de hardware, y está basada en los mnemónicos que simbolizan los pasos de procesamiento (las instrucciones), los registros del procesador, las posiciones de memoria y otras características del lenguaje. Un lenguaje ensamblador es por lo tanto específico de cierta arquitectura de computador física (o virtual). Esto está en contraste con la mayoría de los lenguajes de programación de alto nivel, que idealmente son portables. ¿CUÁL ES SU FUNCIÓN? El programa ensamblador es un software que se encarga de traducir los mnemónicos y símbolos alfanuméricos del programa escrito en ensamblador por el usuario a código máquina, para que pueda ser interpretado y ejecutado por el microprocesador. El programa escrito en lenguaje ensamblador recibe la denominación de código fuente, archivo fuente o fichero fuente. Suele tener la extensión *.asm. El archivo fuente debe ser traducido a código máquina, de lo cual se encarga el programa ensamblador. La mayoría de los ensambladores proporcionan a su salida un fichero que suele tener la extensión *.hex. Este fichero puede ser grabado en la memoria ROM de un sistema con microprocesador. MOTIVOS DE SU USO 

Un programa escrito en el lenguaje ensamblador requiere considerablemente menos memoria y tiempo de ejecución que un programa escrito en los conocidos lenguajes de alto nivel, como C, C++, java, etc. El lenguaje ensamblador da a un programador la capacidad de realizar tareas muy técnicas que serian difíciles, si no es que imposibles de realizar en un lenguaje de alto nivel.



El conocimiento del lenguaje ensamblador permite una comprensión de la arquitectura de la maquina que ningún lenguaje de alto nivel puede ofrecer. Aunque la mayoría de los especialistas en Software desarrolla aplicaciones en lenguajes de alto nivel, que son más fáciles de escribir y de dar mantenimiento, una práctica común es recodificar en lenguaje ensamblador aquellas rutinas que han causado cuellos de botella en el procesamiento. Los programas residentes y rutinas de servicio de interrupción casi siempre son desarrollados en el lenguaje ensamblador.



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.

MOTIVOS PARA NO USARLO 

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.





Al ser de bajo nivel, el Lenguaje Ensamblador requiere más instrucciones para realizar el mismo proceso, en comparación con un lenguaje de alto nivel. Por otro lado, requiere de más cuidado por parte del programador, pues es propenso a que los errores de lógica se reflejen más fuertemente en la ejecución. Por todo esto, es más lento el desarrollo de programas comparables en Lenguaje Ensamblador que en un lenguaje de alto nivel, pues el programador goza de una menor abstracción. Por las mismas razones que aumenta el tiempo, crecen los programas fuentes; simplemente, requerimos más instrucciones primitivas para describir procesos equivalentes. Esto es una desventaja porque dificulta el mantenimiento de los programas, y nuevamente reduce la productividad de los programadores.

TIPOS DE ENSAMBLADORES. Ensambladores cruzados: Se denominan así a los ensambladores que se utilizan en una computadora que posee un procesador diferente al que tendrán las computadoras donde va a ejecutarse el programa objeto producido. El empleo de este tipo de traductores permite aprovechar el soporte de medios físicos (discos, impresoras, pantallas, etc.) y de programación que ofrecen las máquinas potentes para desarrollar programas que luego los van a ejecutar en sistemas muy especializados en determinados tipos de tareas. Macroensambladores Son ensambladores que permiten el uso de macroinstrucciones (macros). Debido a su potencia, normalmente son programas robustos que no permanecen en memoria una vez generado el programa objeto. Puede variarse complejidad, dependiendo de las posibilidades de definición y manipulación de las macroinstrucciones, pero normalmente son programas bastantes complejos, por lo que suelen ser ensambladores residentes. Ejemplos: Macro Ensamblador IBM.- Está integrado por un ensamblador y un macroensamblador. En gran medida su funcionamiento y forma de invocarlo es sumamente similar al de Microsoft. Su forma de uso consiste en generar un archivo fuente en código ASCII, se procede a generar un programa objeto que es ligado y se genera un programa .EXE. Opcionalmente puede recurrirse a la utilería EXE2BIN de MS-DOS para transformarlo a .COM. Es capaz de generar un listado con información del proceso de ensamble y referencias cruzadas. Macro Ensamblador de Microsoft.- Dependiendo de la versión, este ensamblador es capaz de soportar el juego de instrucciones de distintos tipos de microprocesadores Intel de la serie 80xx/80x86. En su versión 4.0 este soporta desde el 8086 al 80286 y los coprocesadores 8087 y 80287. Requiere 128KB de memoria y sistema operativo MS-DOS v2.0 o superior. Trabaja con un archivo de código fuente creado a partir de un editor y grabado en formato ASCII. Este archivo es usado para el proceso de ensamble y generación de código objeto. Posteriormente, y con un ligador, es creado el código ejecutable en formato .EXE. Microensambladores: Generalmente, los procesadores utilizados en las computadoras tienen un repertorio fijo de instrucciones, es decir, que el intérprete de las mismas interpretaba de igual forma un determinado código de operación. El programa que indica al intérprete de instrucciones de la UCP cómo debe actuar se denomina microprograma. El programa que ayuda a realizar este microprograma se llama microensamblador. Existen procesadores que permiten la modificación de sus microprogramas, para lo cual se utilizan microensambladores. Ensambladores de una fase Estos ensambladores leen una línea del programa fuente y la traducen directamente para producir una instrucción en lenguaje máquina o la ejecuta si se trata de una pseudoinstrucción. También va construyendo la tabla de símbolos a medida que van apareciendo las definiciones de variables, etiquetas, etc.

Debido a su forma de traducción, estos ensambladores obligan a definir los símbolos antes de ser empleados para que, cuando aparezca una referencia a un determinado símbolo en una instrucción, se conozca la dirección de dicho símbolo y se pueda traducir de forma correcta. Estos ensambladores son sencillos, baratos y ocupan poco espacio, pero tiene el inconveniente indicado. Ensambladores de dos fases Se denominan así debido a que realizan la traducción en dos etapas. En la primera fase, leen el programa fuente y construyen una tabla de símbolos; de esta manera, en la segunda fase, vuelven a leer el programa fuente y pueden ir traduciendo totalmente, puesto que conocen la totalidad de los símbolos utilizados y las posiciones que se les ha asignado. Estos ensambladores son los más utilizados en la actualidad. Ejemplo: Ensamblador del 8086. Ensambladores Residentes La ventaja de estos ensambladores es que permiten ejecutar inmediatamente el programa; la desventaja es que deben mantenerse en la memoria principal tanto el ensamblador como el programa fuente y el programa objeto. El polo opuesto del ensamblador cruzado es el ensamblador residente, que se ejecuta sobre una maquina que contiene el mismo procesador que el destinatario del código ensamblado. Un ensamblador residente ofrece al programador la ventaja de utilizar una única máquina para crear, probar, y depurar código. Los ensambladores residentes sobre los primeros microprocesadores fueron algo lentos y restrictivos en características debido al alto costo de memoria y la lentitud del microprocesador, con la disponibilidad de memoria de bajo costo (y consecuentemente grandes memorias disponibles en la mayor parte de los sistemas) y la posibilidad del procesador de direccionar directamente grandes cantidades de memoria, así como de realizar funciones más rápidas, los ensambladores residentes proporcionan ahora una variedad de características y velocidad de ensamblaje que anteriormente solo se encontraban en ensambladores cruzados sobre grandes computadores y microcomputadores.

COMPILADOR. Compilar es el proceso de traducción de un código fuente (escrito en un lenguaje de programación de alto nivel) a lenguaje máquina (código objeto) para que pueda ser ejecutado por la computadora. Un compilador es un programa que permite traducir el código fuente de un programa en lenguaje de alto nivel, a otro lenguaje de nivel inferior (típicamente el lenguaje de máquina, pero también puede ser un código intermedio (bytecode), o simplemente texto). De esta manera un programador puede diseñar un programa en un lenguaje mucho más cercano a cómo piensa un ser humano, para luego compilarlo a un programa más manejable por una computadora.

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, e 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 código lo más eficiente posible). La fase final genera código objeto (en general código de máquina relocalizable o código ensamblador), se seleccionan las posiciones de memoria para las variables usadas por el programa y se traduce cada una de las instrucciones intermedias a una secuencia de instrucciones de máquina. Diagrama que muestra las fases de un compilador:

La tabla de símbolos es una estructura de datos que contiene un registro por cada identificador, con los campos para los atributos: -- Información sobre la memoria asignada -- Tipo -- Si es nombre de procedimiento (número, tipo y método de paso de cada argumento) ⨳ ⨳ ⨳

Además la tabla de símbolos: Permite encontrar rápidamente cada ID y almacenar o consultar datos de ese registro. En el Análisis Léxico se detectan los ID y se introducen en la Tabla de Símbolos. Las fases restantes introducen información sobre los ID y después la utilizan.

La detección e información de errores hace que cada fase puede encontrar errores y debe tratarlo para continuar con la Compilación, permitiendo detectar más errores. Las fases de Análisis Sintáctico y Semántico manejan la mayoría de los errores. En el Análisis Semántico se detectan errores donde la estructura sintáctica es correcta pero no tiene significado la operación (Por. ej. sumar dos ID, donde uno es el nombre de una matriz y el otro un nombre de procedimiento)

Los compiladores se pueden clasificar como: Compiladores de una sola pasada: generan el código máquina a partir de una única lectura del código fuente. Compilador incremental (interactivo o conversacional): Si se descubren errores, luego de modificado el archivo fuente, se compilan sólo las modificaciones.

Compilador con montador: compila módulos independientes y luego los enlaza. Compilador de pasadas múltiples: requieren pasos intermedios para producir un código en otro lenguaje, y una pasada final para producir y optimizar el código producido durante los pasos anteriores. Compilador cruzado: Compilador que toma un lenguaje fuente y genera un código objeto, este objeto es para una plataforma o computador distinto en el que se compila. Ejemplo de uso: fase de desarrollo de nuevos computadores. Autocompilador: Compilador escrito en el propio lenguaje que compila, facilitando la portabilidad, ejemplo, lenguaje C. Metacompilador: Es un programa que tiene como entrada una gramática y genera el “compilador” del lenguaje definido por la misma, en realidad genera el código del autómata, se debe añadir código para lograr un compilador. Ej: Lex,Yacc Descompilador: Es un programa que tiene como entrada código de máquina y lo traduce a un lenguaje de alto nivel. En la práctica es muy difícil “volver atrás”, en la práctica existen “desensambladores”, que sirven para opciones de depuración. INTERPRETE: Un intérprete traduce un código fuente en lenguaje máquina también. El intérprete difiere del compilador en que ejecuta cada orden una vez que se traduce. Este proceso puede hacer más lenta la ejecución del programa, así que los intérpretes se utilizan menos que los compiladores. Un intérprete es otra manera de implementar un lenguaje de programación. La interpretación comparte muchos aspectos con el compilador. Los análisis léxico, sintáctico y verificación de tipos son exactamente los mismos que en un compilador. Pero en lugar de generar código del árbol sintáctico, el árbol sintáctico se procesa directamente para evaluar expresiones y ejecutar sentencias, y así hasta terminar. Un intérprete puede necesitar evaluar la misma parte del árbol sintáctico varias veces (por ejemplo cuando se hacen ciclos), por eso la interpretación puede ser más lenta que la ejecución de un programa compilado. Pero escribir un intérprete es más fácil de mover a una máquina diferente, así que para aplicaciones donde la velocidad no importa, lo mejor es utilizar un intérprete. Ejemplos: Basic, LISP, PROLOG son interpretados. Ventaja: fácil depuración. Desventaja: lentitud y consumo de recursos (pues el intérprete ocupa tiempo y memoria). ¿CÓMO SE CREA UN PROGRAMA? Un programa es una serie de comandos ejecutados por el equipo. Sin embargo, el equipo sólo es capaz de procesar elementos binarios, es decir, una serie de 0s y 1s. Por lo tanto, necesitamos un lenguaje de programación para escribir de manera legible, es decir, con comandos que el ser humano pueda comprender (por ser similares a su propio lenguaje) los comandos que el equipo deberá ejecutar. Estos programas se traducen después a un lenguaje máquina (en binario) a través de un compilador. En términos generales, el programa es un simple archivo de texto (escrito usando un procesador o editor de texto), llamado archivo fuente. El archivo fuente contiene líneas de programa llamadas código fuente. Este archivo fuente, debe compilarse una vez completado. La compilación se realiza en dos pasos:

 El compilador transforma el código fuente en código objeto y lo guarda en un archivo objeto, es decir que traduce el archivo fuente a lenguaje máquina (algunos compiladores también crean un archivo en ensamblador, un lenguaje similar al lenguaje máquina ya que posee las funciones básicas, pero puede ser leído por los seres humanos.  Luego, el compilador llama a un editor de vínculos (o ensamblador) que permite insertar los elementos adicionales (funciones y bibliotecas) a los que hace referencia el programa dentro del archivo final, pero que no se almacenan en el archivo fuente. A continuación, se crea un archivo ejecutable que contiene todos los elementos requeridos por el programa para funcionar de manera independiente (en Microsoft Windows o MS-DOS este archivo tendrá la extensión .exe).

Para la creación de un programa es necesario seguir cinco pasos: Diseño del algoritmo, codificación del mismo, su traducción a lenguaje máquina, la prueba del programa y la depuración. En la etapa de diseño se plantea el problema a resolver y se propone la mejor solución, creando diagramas esquemáticos utilizados para el mejor planteamiento de la solución. La codificación del programa consiste en escribir el programa en algún lenguaje de programación, tomando como base la solución propuesta en el paso anterior. La traducción al lenguaje máquina es la creación del programa objeto, esto es, el programa escrito como una secuencia de ceros y unos que pueda ser interpretado por el procesador. La prueba del programa consiste en verificar que el programa funcione sin errores, o sea, que haga lo que tiene que hacer. La última etapa es la eliminación de las fallas detectadas en el programa durante la fase de prueba. La corrección de una falla normalmente requiere la repetición de los pasos comenzando desde el primero o el segundo.

REGISTROS DEL CPU. Un registro es un grupo de celdas binarias. Como una celda almacena un bit de información, se desprende que un registro de n celdas puede almacenar cualquier cantidad discreta de información que contenga n bits. El estado del registro es un número enésimo de unos o ceros con cada bit indicando el estado de una celda en el registro. Físicamente se podría pensar que el siguiente registro está compuesto de 16 celdas binarias, con cada celda almacenando un 1 ó un 0. 1 1

1 2

0 3

0 4

0 5

1 6

1 7

1 8

0 9

0 10

1 11

0 12

0 13

1 14

0 15

1 16

La UCP o CPU de los procesadores x86, por ejemplo, tiene 14 registros internos, cada uno de ellos de 16 bits (una palabra). Los bits están enumerados de derecha a izquierda, de tal modo que el bit menos significativo es el bit 0. Los registros se pueden clasificar de la siguiente forma: Registros de datos:

AX: Registro acumulador. Es el principal empleado en las operaciones aritméticas. BX: Registro base. Se usa para indicar un desplazamiento. CX: Registro contador. Se usa como contador en los bucles. DX: Registro de datos. También se usa en las operaciones aritméticas. Estos registros son de uso general y también pueden ser utilizados como registros de 8 bits, para utilizarlos como tales es necesario referirse a ellos como por ejemplo: AH y AL, que son los bytes alto (high) y bajo (low) del registro AX. Esta nomenclatura es aplicable también a los registros BX, CX y DX. Registros de segmentos: CS: Registro de segmento de código. Contiene la dirección de las instrucciones del programa. DS: Registro segmento de datos. Contiene la dirección del área de memoria donde se encuentran los datos del programa SS: Registro segmento de pila. Contiene la dirección del segmento de pila. La pila es un espacio de memoria temporal que se usa para almacenar valores de 16 bits (palabras). ES: Registro segmento extra. Contiene la dirección del segmento extra. Se trata de un segmento de datos adicional que se utiliza para superar la limitación de los 64Kb del segmento de datos y para hacer transferencias de datos entre segmentos. Registros punteros de pila: SP: Puntero de la pila. Contiene la dirección relativa al segmento de la pila. BP: Puntero base. Se utiliza para fijar el puntero de pila y así poder acceder a los elementos de la pila. Registros índices: SI: Índice fuente. DI: Índice destino. Puntero de instrucciones: IP: Registro puntero de instrucción o contador de programa (PC). Contiene el desplazamiento de la siguiente instrucción a ejecutar respecto al segmento de código en ejecución. Por lo tanto, la dirección completa de la siguiente instrucción sería CS:IP. La única forma de influir en este registro es de forma indirecta mediante instrucciones de bifurcación. Registro de banderas (flags): Cada bandera es un bit y se usa para registrar la información de estado y de control de las operaciones del microprocesador.

SEGMENTOS Y REGISTROS ASOCIADOS. La arquitectura de los procesadores x86 obliga al uso de segmentos de memoria para manejar la información, el tamaño de estos segmentos es de 64kb. La razón de ser de estos segmentos es que, considerando que el tamaño máximo de un número que puede manejar el procesador está dado por una palabra de 16 bits o registro, no sería posible accesar a más de 65536 localidades de memoria utilizando uno solo de estos registros, ahora, si se divide la memoria de la PC en grupos o segmentos, cada uno de 65536 localidades, y utilizamos una dirección en un registro exclusivo para localizar cada segmento, y entonces cada dirección de una casilla específica la formamos con dos registros, nos es posible acceder a una cantidad de 4294967296 bytes de memoria, lo cual es, en la actualidad, más memoria de la que veremos instalada en una PC. Para que el ensamblador pueda manejar los datos es necesario que cada dato o instrucción se encuentren localizados en el área que corresponde a sus respectivos segmentos. El ensamblador accede a esta información tomando en cuenta la localización del segmento, dada por los registros DS, ES, SS y CS, y dentro de dicho registro la dirección del dato específico. Un programa consta de 4 tipos de segmentos. Cada segmento se direcciona mediante un determinado tipo de registro de segmento: Segmento código: Cada instrucción se direcciona mediante el registro segmento de código y el registro de desplazamiento IP, CS: IP. Segmento de datos: Los datos de direccionan mediante el registro de segmento de dato y un registro de desplazamiento (BX, SI o DI), por ejemplo DS:BX. Segmento de pila: Los datos se direccionan mediante el registro segmento de pila y un registro de desplazamiento (SP o BP), por ejemplo SS:SP. Segmento extra: Igual que el de datos, sustituyendo el registro de segmento, por ejemplo ES: BX. SOFTWARE EMPLEADO. Para poder crear un programa se requieren varias herramientas: Primero un editor para crear el programa fuente. Segundo un compilador que no es más que un programa que "traduce" el programa fuente a un programa objeto. Y tercero un enlazador (Linker), que genere el programa ejecutable a partir del programa objeto. El editor puede ser cualquier editor de textos que se tenga a la mano, como compilador utilizaremos el programa TurboAssembler (TASM), por ejemplo, y como enlazador utilizaremos el Turbo Link (TLINK). La extensión usada para que el TASM reconozca los programas fuente en ensamblador es .ASM, una vez traducido el programa fuente, el TASM crea un archivo con la extensión .OBJ, este archivo contiene un "formato intermedio" del programa, llamado así porque aún no es ejecutable pero tampoco es ya un programa en lenguaje fuente. El enlazador genera, a partir de un archivo .OBJ o la combinación de varios de estos archivos, un programa ejecutable, cuya extensión es .EXE. Otros softwares empleados para la creación de programas son: MPLAB de Microchip y el PIC C compiler empleado en la programación de PICS.

ESTRUCTURA Estas sentencias dirigen el proceso de compilación o construcción del programa ejecutable. No generan código ejecutable. Normalmente se utilizan para aumentar la legibilidad del código fuente. El fichero creado con todas las sentencias que constituyen un programa se denomina fichero fuente. Este tipo de fichero tiene formato ASCII. Se recomienda que el fichero de código fuente tenga por extensión .asm. El fichero fuente se compila utilizando el programa ensamblador. El programa ensamblador generará un nuevo fichero, denominado fichero ejecutable, de igual nombre y de extensión .exe o .hex. Este fichero tiene formato ASCII y contiene el código máquina de las instrucciones que componen el programa, así como cierta información adicional para realizar la carga del programa en el simulador del computador elemental.

Estructura de un fichero en lenguaje ensamblador: Los ficheros de código fuente escritos en lenguaje ensamblador se organizan en líneas. Cada una de las líneas del fichero puede contener una directiva, una instrucción o ambas cosas a la vez en los casos en quesea posible. El carácter separador de líneas es el retorno de carro, por lo que una instrucción no podrá ocupar más de una línea en el fichero fuente. Todos los ficheros fuente tienen que adecuarse a una estructura fija dividida en secciones. Ejemplo: Escala EQU 1000 ; Definición de constantes simbólicas ORIGEN 7F40h ; Definición del origen de carga del programa INICIO int ; Definición de la etiqueta que marca la ; Primera instrucción a ejecutar del programa .PILA 100h ; Definición de la pila .DATOS ; Definición de los datos del programa Dato1 VALOR 12h … .CODIGO ; Definición del código del programa INI: MOV R5, R4 … FIN

TÉCNICAS DE CODIFICACIÓN. En ensamblador los bucles, condicionales, subrutinas y demás elementos se codifica de forma distinta de cómo se hace en otros lenguajes de alto nivel. A continuación describiremos algunas técnicas útiles para codificar las estructuras más usuales. BUCLES: Inicialización de un registro(a modo de contador) al nº de vueltas del bucle. Etiqueta: Primera instrucción del bucle

decrementar el contador saltar a la etiqueta si el contador es mayor que cero

SALTOS CONDICIONALES: Instrucción de comparación. Evalúa la condición y si se cumple saltar a la etiqueta prefijada saltar fuera del condicional MACROS:

Declaración: MACRO

ENDM Llamada:

INTERRUPCIONES Interrupción (también conocida como interrupción de hardware o petición de interrupción) es una señal recibida por el procesador de un ordenador, indicando que debe "interrumpir" el curso de ejecución actual y pasar a ejecutar código específico para tratar esta situación. Una interrupción es una suspensión temporal de la ejecución de un proceso, para pasar a ejecutar una subrutina de servicio de interrupción, la cual, por lo general, no forma parte del programa. Luego de finalizada dicha subrutina, se reanuda la ejecución del programa. Las interrupciones surgen de las necesidades que tienen los dispositivos periféricos de enviar información al procesador principal de un sistema de computación. La primera técnica que se empleó fue que el propio procesador se encargara de sondear (polling) los dispositivos cada cierto tiempo para averiguar si tenía pendiente alguna comunicación para él. Este método presentaba el inconveniente de ser muy ineficiente, ya que el procesador constantemente consumía tiempo en realizar todas las instrucciones de sondeo. El mecanismo de interrupciones fue la solución que permitió al procesador desentenderse de esta problemática, y delegar en el dispositivo la responsabilidad de comunicarse con el procesador cuando lo necesitara. El procesador, en este caso, no sondea a ningún dispositivo, sino que queda a la espera de que estos le avisen (le "interrumpan") cuando tengan algo que comunicarle (ya sea un evento, una transferencia de información, una condición de error, etc.). Funcionamiento del mecanismo de interrupciones Cada dispositivo que desea comunicarse con el procesador por medio de interrupciones debe tener asignada una línea única capaz de avisar al CPU que le requiere para una operación. Esta línea es la llamada IRQ ("Interrupt ReQuest", petición de interrupción). Las IRQ son líneas que llegan al controlador de interrupciones un componente de hardware dedicado a la gestión de las interrupciones, y que puede estar integrado en el procesador principal o ser un circuito separado conectado al procesador principal. El controlador de interrupciones debe ser capaz de habilitar o inhibir líneas de interrupción (operación llamada comúnmente enmascarar por la utilización de una máscara), y establecer prioridades entre las distintas interrupciones habilitadas. Cuando varias líneas de petición de interrupción se activan a la vez, el controlador de interrupciones utilizará estas prioridades para escoger la interrupción sobre la que informará al procesador principal. También puede darse el caso de que una rutina de tratamiento de interrupción sea interrumpida para realizar otra rutina de tratamiento de una interrupción de mayor prioridad a la que se estaba ejecutando. Sin embargo hay interrupciones que no se pueden enmascarar o deshabilitar, las conocidas como interrupciones no enmascarables o NMI. Un procesador principal que no tenga un controlador de interrupciones integrado, suele tener una única línea de interrupción llamada habitualmente INT. Esta línea es activada por el controlador de interrupciones cuando tiene una interrupción que servir. Al activarse esta línea, el procesador consulta los registros del controlador de interrupciones para averiguar cual IRQ hay que atender. A partir del número del IRQ busca en la tabla de vectores

de interrupción la dirección de la rutina que debe llamar para atender una petición del dispositivo asociado a dicha IRQ. Las rutinas de interrupción generalmente toman un pequeño tiempo de ejecución. Pasos para el procesamiento de una IRQ: 1. Terminar la ejecución de la instrucción de máquina en curso. 2. Salva el valor de contador de programa, IP, en la pila, de manera que en la CPU, al terminar el proceso, pueda seguir ejecutando el programa a partir de la última instrucción. 3. La CPU salta a la dirección donde está almacenada la rutina de servicio de interrupción (Interrupt Service Routine (ISR)) y ejecuta esa rutina que tiene como objetivo atender al dispositivo que generó la interrupción. 4. Una vez que la rutina de la interrupción termina, el procesador restaura el estado que había guardado en la pila en el paso 2 y retorna al programa que se estaba usando anteriormente. Tipos de interrupciones En este subapartado vamos a hacer una clasificación de las distintas clases de interrupciones que nos podemos encontrar atendiendo a la fuente que las produce. Por un lado distinguiremos si se producen por causas internas o externas al procesador y remarcaremos que este hecho está íntimamente ligado con que las interrupciones sean síncronas o asíncronas: 

 

Interrupciones de hardware. Estas son asíncronas a la ejecución del procesador, es decir, se pueden producir en cualquier momento independientemente de lo que esté haciendo el CPU en ese momento. Las causas que lo producen son externas al procesador y a menudo suelen estar ligadas con distintos dispositivos de E/S. Traps. Normalmente son causadas al realizarse operaciones no permitidas tales como la división por 0, el desbordamiento, el acceso a una posición de memoria no permitida, etc. Interrupciones por software. Las interrupciones por software son generadas por el programa en ejecución. Para generarla, existen distintas instrucciones en el código máquina que permiten al programador producir una interrupción, suelen tener nemotécnicos tales como INT. Suelen ser de vital importancia ya que a partir de estas interrupciones se solicita al sistema operativo realizar determinadas funciones, para ello. Por ejemplo, en DOS se realiza la instrucción INT 0x21 y en Unix se utiliza INT 0x80 para hacer llamadas de sistema.

SALTOS La ejecución de los programas no suele ser lineal ejecutándose una lista de instrucciones una tras otras. En puntos determinados, esta secuencia tiene que romperse por una toma de decisión o por cualquier otro motivo. En este caso nos referiremos a las instrucciones de salto que posee el microcontrolador PIC16F84A. SALTO INCONDICIONAL. En casi todos los programas se usa la instrucción de salto incondicional que produce un salto a la dirección del programa indicada por “k”. La constante literal “k” es la dirección de destino del salto, es decir, la nueva dirección de memoria de programa a partir de la cual comenzarán a leerse las instrucciones después de ejecutar la instrucción goto. Así pues, esta instrucción simplemente carga la constante k en el contador de programa (PC). SALTOS CONDICIONALES Además de las instrucciones de salto incondicional se dispone de de instrucciones de salto condicional, que son aquéllas que producen un salto en función de que se cumpla o no una condición. Estas instrucciones son el único medio para realizar bifurcaciones en un programas.

El repertorio de instrucciones del PIC16F84 incluye cuatro instrucciones de salto condicional clasificadas en dos grupos:  

Aquéllas que pueden producir el salto en función del estado de un bit. Son btfsc y btfss. Aquéllas que pueden producir el salto en función del contenido de un registro distinto de cero. Son decfsz y incfsz.

Saltos en función de un BIT. Son muy poderosos ya que permiten al programa tomar decisiones en función del estado de un bit de cualquier registro o puerto de entrada/salida. Hay dos instrucciones de este tipo: Instrucción “btfsc f,b” (Bit test f, skip if clear). Esta instrucción puede actuar de dos formas:  

Si el bit número ‘b’ del registro ‘f’ es “1” la instrucción que sigue a ésta se ejecuta normalmente. Si el bit número ‘b’ del registro ‘f’ es “0” la instrucción que sigue a ésta se ignora y se salta.

Instrucción “btfss f,b” (Bit test f, skip if Set). Esta instrucción actúa de forma contraria a la instrucción anterior.  

Si el bit número ‘b’ del registro ‘f’ es “0” la instrucción que sigue a ésta se ejecuta normalmente. Si el bit número ‘b’ del registro ‘f’ es “1” la instrucción que sigue a ésta se ignora y se salta.

SALTOS EN FUNCIÓN DE UN REGISTRO Las instrucciones de salto condicional “decfsz” e “incfsz” pueden producir el salto en función del contenido de un registro distinto de cero y son casos especiales de las instrucciones de incremento y decremento de un registro. Estas instrucciones podrían categorizarse dentro del grupo de instrucciones aritméticas ya que efectivamente operan de forma aritmética (decrementando o incrementando) sobre los registros. Pero, a diferencia de las otras, además pueden alterar el flujo lineal del programa y por eso se las incluye en este grupo. Su forma de actuar se describe a continuación: Instrucción “decfsz f,d”

(Decrement f, Skip if 0). Esta instrucción decrementa en una unidad el contenido del registro ‘f’. Almacena el resultado en W si ‘d’ = 0 (en cuyo caso ‘f’ no varía) y en el registro ‘f’ si ‘d’ = 1. Después de decrementar, pueden ocurrir dos casos:  

Si el resultado es distinto de cero la instrucción que sigue a ésta se ejecuta normalmente. Si el resultado es cero la instrucción que sigue a esta se ignora y se salta.

Instrucción “incfsz f,d” (Increment f, Skip if 0). Esta instrucción incrementa en una unidad el contenido del registro ‘f’. Almacena el resultado en W si ‘d’ = 0 (en cuyo caso ‘f’ no varía) y en el registro ‘f’ si ‘d’ = 1. Después de incrementar, pueden ocurrir dos casos:  

Si el resultado es distinto de cero la instrucción que sigue a ésta se ejecuta normalmente. Si el resultado es cero (porque al incrementarse se ha desbordado y ha pasado del número b’11111111’ al b’00000000’) la instrucción que sigue a esta se ignora y se salta.

CICLOS, LAZOS O BUCLES. Una aplicación muy importante de los saltos condicionales son los lazos o bucles. Estos son fragmentos de programa que se repiten un número finito de veces. Los principales tipos están descritos en la siguiente figura.

Lazo de repetición finita. Es un salto incondicional a una posición anterior del programa conformando un lazo de repetición infinita, sin posibilidad de tomar otro camino. Utiliza la instrucción goto. Ejemplo:

Lazo con condición de testeo. Se utiliza una instrucción de testeo para controlar la ejecución del bucle. Para este caso la repetición de lazo es finita, pero no se puede precisar el número de veces que se repite.

Lazo que se repite un número conocido de veces. Las instrucciones decfsz e incfsz se utilizan generalmente en combinación con una instrucción de salto goto, para el diseño de lazos de instrucciones que deben repetirse una cantidad determinada de veces. Se hace de manera tal que un registro se decrementa o incrementa hasta que tome un determinado valor. En este caso, si se conoce el número de veces que se repite el bucle.

PROCEDIMIENTOS O SUBRUTINAS Como sabemos, el lenguaje ensamblador ofrece además, la posibilidad de crear partes del programa independientes, denominadas subrutinas, que podrán ser llamadas posteriormente desde cualquier parte del programa principal e incluso desde otra subrutina, mediante la instrucción CALL. De esta forma el programa completo estará constituido esencialmente por una serie de pequeños programas llamados subrutinas, que pueden ser llamadas en repetidas ocasiones, evitándonos por tanto, escribir código repetido. Como se ha mencionado anteriormente, las subrutinas son pequeños programas y como tal debemos aportar a cada una de ellas la documentación o información suficiente para que puedan ser tratadas independientemente, e incluso puedan ser empleadas en programas distintos. Por esto, las subrutinas llevarán el encabezamiento propio de un programa y requerirán el estudio detallado de todas las condiciones en que pueden ser ejecutadas, es decir, la estructura inicial de toda subrutina será:

A continuación comenzará el cuerpo de la subrutina propiamente dicho, que llevará al principio una etiqueta con el nombre de la subrutina, es decir, el parámetro que deberemos especificar en la instrucción de llamada a subrutina CALL. Normalmente el nombre asignado a las subrutinas es suficientemente significativo como para darnos una idea o sugerir la función que desarrollarán las mismas. A continuación de la etiqueta se sitúan las instrucciones que componen la subrutina y finalmente es imprescindible concluir la subrutina con una instrucción RET que devuelve el control del programa a la posición desde donde se efectuó la llamada CALL a subrutina. Debemos recordar que además de la llamada CALL a subrutinas, disponemos también de otro tipo de llamadas a subrutinas con condiciones como CC LABEL, CNC LABEL, CZ LABEL, etc. (dependiendo del microprocesador o microcontrolador usado) que efectuarán la llamada a la subrutina cuyo nombre venga especificado en LABEL si se cumple la condición que lleva implícita la instrucción que en cada caso se emplee. En definitiva, una subrutina tendrá para nosotros la siguiente estructura:

Las subrutinas deben ser vistas desde el programa principal como un bloque o una caja a la que se accede proporcionándole unos datos de entrada que llamaremos “parámetros de entrada” y después de ser ejecutada nos proporciona uno o más resultados. Hay varias formas de comunicarse con las subrutinas pasando y recibiendo datos, entre las que podemos destacar: El empleo de registros.- Su empleo está basado en la utilización de los registros del microprocesador libres para su uso en ese momento. Se almacenarán en ellos los datos que precise la subrutina para desarrollar su función y una vez ejecutada la subrutina, nos devolverá los resultados en los registros precisos. Este método está limitado por el bajo número de registros de que disponemos en el microprocesador 8085. Además se producen modificaciones en el contenido de los registros que deben ser tenidas en cuenta y en caso de que no nos interese modificarlos, tendremos que guardar previamente su contenido en la pila y recuperarlo al finalizar la subrutina. Empleo de memoria RAM.- En este caso el programa debe colocar en posiciones consecutivas de la memoria RAM del sistema, todos los parámetros que necesite la subrutina para desarrollar su función. A continuación se llama a la subrutina, la cual busca en la RAM los parámetros introducidos para trabajar con ellos. Es importante tener en cuenta el orden en que se situaron dichos parámetros para no cometer errores en el tratamiento de los datos. Ejecutadas las tareas precisas por parte de la subrutina, nos devolverá los resultados en las mismas posiciones de

memoria en que le pasamos los datos. El programa principal buscará ahora los resultados en la RAM para proseguir con su desarrollo normal. MOVIMIENTO DE LOS DATOS En todo programa es necesario mover datos en la memoria y en los registros de la UCP; existen diversas formas de hacer esto: puede copiar datos de la memoria a algún registro, de registro a registro, de un registro a una pila, de la pila a un registro, transmitir datos hacia dispositivos externos así como recibir datos de dichos dispositivos. Este movimiento de datos está sujeto a reglas y restricciones. Algunas de ellas son lasque se citan a continuación. No es posible mover datos de una localidad de memoria a otra directamente, es necesario primero mover los datos de la localidad origen hacia un registro y luego del registro a la localidad destino. No se puede mover una constante directamente a un registro de segmentos, primero se debe mover a un registro de la UCP. Aunque la instrucción se llama "mover", en el 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

INSTRUCCIÓN MOV Mov es una instrucción en el lenguaje ensamblador de la mayoría de procesadores, cuyo propósito es la transferencia de datos entre registros de procesador o registro y memoria. Adicionalmente mov también permite el uso de datos absolutos, como por ejemplo mover el número 10 a un registro del procesador.

Implementaciones Está disponible en procesadores intel pentium, amd y sparc entre muchos otros, es a la práctica, una instrucción de ensamblador básica en cualquier procesador. Sintaxis La sintaxis en ensamblador es variable en dependencia del procesador utilizado. La sintaxis de mov se describe de la siguiente manera en los procesadoresx86 y compatibles: "mov destino, fuente" La instrucción mov, seguido del destino de los datos, ya sea un registro del procesador o una posición de memoria, una coma que actúa como separador y a continuación, la fuente de los datos o lo datos en sí, de forma absoluta. En la sintaxis at&t de ensamblador la sintaxis sería distinta: "mov fuente, destino"

En este caso, la fuente va en primer lugar, y a continuación y también separado por una coma, se especifica el destino de los datos.

OPERACIONES LÓGICAS Y ARITMÉTICAS. Las instrucciones aritmético y/o lógicas, permiten realizar operaciones entre registros. La mayoría de la lógica usada para escribir programas en lenguaje ensamblador se basa en las operaciones aritméticas y/o lógicas que se realicen entre los registros. Por ejemplo la instrucción ADD Rd,Rr realiza la suma entre el registro fuente Rd y el registro destino Rr. El resultado de la operación se almacena en el registro destino. Las demás instrucciones tiene el mismo comportamiento que ADD, a diferencia solo de los parámetros que puedan tener. Por ejemplo para el 8085 tenemos las siguientes instrucciones lógicas y aritméticas:

BIBLIOGRAFÍA http://es.kioskea.net/contents/langages/programme.php3 http://es.scribd.com/doc/50746990/23/TECNICAS-DE-CODIFICACION-EN-ENSAMBLADOR http://wwwdi.ujaen.es/~mcdiaz/docencia/cur04_05/fi/teoria/03_Lenguajes.pdf http://signapuntes.8forum.info/t121-el-lenguaje-humano http://www.slideshare.net/neldom/generaciones-de-los-lenguajes-de-programacion http://es.scribd.com/doc/53024489/Ventajas-y-desventajas-del-Lenguaje-Ensamblador http://www.slideshare.net/SpiderHal/tipos-de-ensambladores http://es.scribd.com/doc/97566966/16/Movimiento-de-datos http://ucsystem.blogspot.mx/2011/11/instrucciones-aritmetico-logicas.html “Microcontrolador PIC16F84, Desarrollo de Proyectos”. Palacios, Enrique. Editorial RA-MA. http://es.wikipedia.org/wiki/Lenguaje_ensamblador