miércoles, 13 de abril de 2011

traductores de bajo nivel

ensambladores

El Nivel Del Lenguaje Ensamblador
Dicho nivel difiere considerablemente de los niveles de microprogramación, de maquina convencional y del sistema operativo, debido a que se implanta por traducción y no por interpretación(1)

Funciones De Un Ensamblador
La tarea fundamental de un ensamblador es traducir un programa en lenguaje de ensamblador al código correspondiente en lenguaje de máquina. En esencia, el ensamblador debe realizar las siguientes tareas:
1. Convertir los códigos ("opcode" o mnemónicos: ej. LDA, STX) a su código equivalente en lenguaje de máquina
2. Convertir los operandos simbólicos (las etiquetas o variables) en las direcciones correspondientes de los operandos
3. Construir las instrucciones en lenguaje de máquina en el formato apropiado
4. Convertir las constantes definidas en el programa en su verdadera representación
5. Escribir el "listing" en lenguaje de ensamblador con el código en lenguaje de máquina correspondiente
6. Procesar las directrices al ensamblador (las directrices son instrucciones o comandos dirigidos al ensamblador, que éste procesa y ejecuta al hallarlos en el programa en lenguaje de ensamblador; estas directrices no se traducen a lenguaje de máquina, si no que el ensamblador ejecuta alguna acción, como reservar memoria para variables, entre otras)
(Org. De Computadoras Un Enfoque Estructurado, Tanenbaum, P.445)
En general, la mayoría de las tareas se puede realizar si se recorre el programa línea por línea. Pero surge una dificultad si se trata de traducir una instrucción cuyo operando está definido más abajo en el programa. Si esto ocurre, el ensamblador desconoce la dirección del operando y la instrucción no se podría ensamblar(1).

 Tipos De Ensambladores
Aunque todos los ensambladores realizan básicamente las mismas tareas, podemos clasificarlos de acuerdo a características.
Así podemos clasificarlos en:
Ensambladores Cruzados (Cross-Assembler).
Se denominan así 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áquinaspotentes para desarrollar programas que luego los van a ejecutar sistemas muy especializados en determinados tipos de tareas.

Ensambladores Residentes.
Son aquellos que permanecen en la memoria principal de la computadora y cargan, para su ejecución, al programa objeto producido. Este tipo de ensamblador tiene la ventaja de que se puede comprobar inmediatamente el programa sin necesidad de transportarlo de un lugar a otro, como se hacía en cross-assembler, y sin necesidad de programas simuladores.
Sin embargo, puede presentar problemas de espacio de memoria, ya que el traductor ocupa espacio que no puede ser utilizado por el programador. Asimismo, también ocupará memoria el programa fuente y el programa objeto. Esto obliga a tener un espacio de memoria relativamente amplio. Es el indicado para desarrollos de pequeños sistemas de control y sencillos automatismo empleando microprocesadores(1).

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.
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(1).
Ensambladores de dos fases.
Los 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.

pasadas del texto fuente


El proceso de traducción se compone internamente de varias etapas o fases, que realizan distintas operaciones lógicas. Es útil pensar en estas fases como en piezas separadas dentro del traductor, y pueden en realidad escribirse como operaciones codificadas separadamente aunque en la práctica a menudo se integren juntas.


Fase de análisis


Análisis léxico


El análisis léxico constituye la primera fase, aquí se lee el programa fuente de izquierda a derecha y se agrupa en componentes léxicos (tokens), que son secuencias de caracteres que tienen un significado. Además, todos los espacios en blanco, líneas en blanco, comentarios y demás información innecesaria se elimina del programa fuente. También se comprueba que los símbolos del lenguaje (palabras claveoperadores,...) se han escrito correctamente.
Como la tarea que realiza el analizador léxico es un caso especial de coincidencia de patrones, se necesitan los métodos de especificación y reconocimiento de patrones, se usan principalmente los autómatas finitos que acepten expresiones regulares. Sin embargo, un analizador léxico también es la parte del traductor que maneja la entrada del código fuente, y puesto que esta entrada a menudo involucra un importante gasto de tiempo, el analizador léxico debe funcionar de manera tan eficiente como sea posible.


Análisis sintáctico


En esta fase los caracteres o componentes léxicos se agrupan jerárquicamente en frases gramaticales que el compilador utiliza para sintetizar la salida. Se comprueba si lo obtenido de la fase anterior es sintácticamente correcto (obedece a la gramática del lenguaje). Por lo general, las frases gramaticales del programa fuente se representan mediante un árbol de análisis sintáctico.
La estructura jerárquica de un programa normalmente se expresa utilizando reglas recursivas. Por ejemplo, se pueden dar las siguientes reglas como parte de la definición de expresiones:
  1. Cualquier identificador es una expresión.
  2. Cualquier número es una expresión.
  3. Si expresión1 y expresión2 son expresiones, entonces también lo son:
    • expresión1 + expresión2
    • expresión1 * expresión2
    • expresión1 )
Las reglas 1 y 2 son reglas básicas (no recursivas), en tanto que la regla 3 define expresiones en función de operadores aplicados a otras expresiones.
La división entre análisis léxico y análisis sintáctico es algo arbitraria. Un factor para determinar la división es si una construcción del lenguaje fuente es inherentemente recursiva o no. Las construcciones léxicas no requieren recursión, mientras que las construcciones sintácticas suelen requerirla. No se requiere recursión para reconocer los identificadores, que suelen ser cadenas de letras y dígitos que comienzan con una letra. Normalmente, se reconocen los identificadores por el simple examen del flujo de entrada, esperando hasta encontrar un carácter que no sea ni letra ni dígito, y agrupando después todas las letras y dígitos encontrados hasta ese punto en un componente léxico llamado identificador. Por otra parte, esta clase de análisis no es suficientemente poderoso para analizar expresiones o proposiciones. Por ejemplo, no podemos emparejar de manera apropiada los paréntesis de las expresiones, o las palabras begin y end en proposiciones sin imponer alguna clase de estructura jerárquica o de anidamiento a la entrada.

Análisis semántico

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. Revisa que los arreglos tengan definido el tamaño correcto.


Fase de síntesis

Consiste en generar el código objeto equivalente al programa fuente. Sólo 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

Después de los análisis sintáctico y semántico, algunos compiladores generan una representación intermedia explícita del programa fuente. Se puede considerar esta representación intermedia como un programa para una máquina abstracta. Esta representación intermedia debe tener dos propiedades importantes; debe ser fácil de producir y fácil de traducir al programa objeto.
La representación intermedia puede tener diversas formas. Existe una forma intermedia llamada "código de tres direcciones" que es como el lenguaje ensamblador de una máquina en la que cada posición de memoria puede actuar como un registro. El código de tres direcciones consiste en una secuencia de instrucciones, cada una de las cuales tiene como máximo tres operandos. Esta representación intermedia tiene varias propiedades:
  • Primera.- Cada instrucción de tres direcciones tiene a lo sumo un operador, además de la asignación, por tanto, cuando se generan estas instrucciones, el traductor tiene que decidir el orden en que deben efectuarse las operaciones.
  • Segunda.- El traductor debe generar un nombre temporal para guardar los valores calculados por cada instrucción.
  • Tercera.- Algunas instrucciones de "tres direcciones" tienen menos de tres operandos, por ejemplo, la asignación.


Optimización de código

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

tratamiento de operandos y modos de direccionamiento en la máquina objeto


DIRECCIONAMIENTO INMEDIATO
- La instrucción contiene al propio objeto.
- Se emplea cuando la instrucción contiene un valor constante.
- El tamaño del operando viene determinado por el espacio reservado para él en el
formato de instrucción.
- Subcampos del campo de operando
- Esquema de funcionamiento
- Ejemplo M68000: MOVE.B #5,D0
Mdir CD (operando inmediato)
Estructura y Tecnología de Computadores I
Modos de direccionamiento


 DIRECCIONAMIENTO DIRECTO
Direccionamiento mediante registro
- El operando se encuentra contenido en un registro.
- Subcampos del campo de operando
- Esquema de funcionamiento
- Ejemplos M68000: MOVE.B #5,D0
MOVE.W #$23A0,A3
Mdir CR
Estructura y Tecnología de Computadores I
Modos de direccionamiento -4-
Direccionamiento directo absoluto
- La instrucción contiene la dirección de memoria exacta donde se encuentra el
operando.
- El operando se encuentra en memoria.
- Subcampos del campo de operando
• El rango de CD debe ser capaz de direccionar toda la memoria.
• Si no es capaz: direccionamiento de página base.
- Esquema de funcionamiento
- Ejemplo M68000: MOVE.B D0,$A61350
Mdir CD (dirección)
Estructura y Tecnología de Computadores I
Modos de direccionamiento -5-

 DIRECCIONAMIENTO RELATIVO A REGISTRO

- El operando se encuentra en memoria.
- La dirección del objeto ha de ser calculada a partir de la suma del contenido de un
cierto registro (que funciona como un puntero) y un desplazamiento (contenido en la
instrucción).
- Subcampos del campo de operando
Direccionamiento relativo a registro base
- Se emplea como puntero un registro base, que puede ser un registro específico o uno
cualquiera de los del banco.
- El registro base utilizado se indica en el subcampo CR.
- Esquema de funcionamiento
- Ejemplo M68000: MOVE.W 5(A0),D1
Mdir CR CD (desplazamiento)
Estructura y Tecnología de Computadores I
Modos de direccionamiento -6-

Direccionamiento relativo a contador de programa
- Como puntero se emplea el contador de programa.
- Este direccionamiento se emplea para acceder a instrucciones (saltos relativos o
bucles) o para referenciar datos cercanos al código.
- No es preciso introducir el subcampo CR.
- Esquema de funcionamiento
- Ejemplo M68000: MOVE.L 24(PC),D0

Direccionamiento relativo a puntero de pila
- Pila: estructura de tipo LIFO marcada por
• el fondo de la pila
• el puntero de pila (*SP), registro que apunta a la última posición ocupada de la
pila
- Como puntero del direccionamiento se emplea el puntero de pila.
- No es preciso incluir el subcampo CR, y a veces no lleva CD.
- Ejemplo M68000: MOVE.B D0,24(SP) MOVE.B D0,24(A7)
Estructura y Tecnología de Computadores I
Modos de direccionamiento -7-


DIRECCIONAMIENTO INDEXADO
- Similar al direccionamiento relativo a registro base.
El operando se encuentra en memoria.
Registro índice: se modifica a menudo en la ejecución del programa.
- Subcampos del campo de operando
- El registro índice puede permitir incrementos o decrementos antes o después de
obtenerse la dirección del objeto:
Preincremento
Predecremento
Posincremento
Posdecremento
- El incremento o decremento puede depender del tamaño del objeto referenciado.
- Ejemplos M68000 (sólo predecremento y posincremento):
MOVE.B D0,(A0)+ incrementa A0 en 1
MOVE.W D0,-(A2) decrementa A2 en 2
MOVE.L (A5)+,D3 incrementa A5 en 4
- Utilizado para recorrer tablas o vectores.

DIRECCIONAMIENTO INDIRECTO
- El operando se encuentra en memoria.
- La instrucción contiene una dirección que se emplea para leer en memoria una
dirección intermedia que será la verdadera dirección del objeto buscado.
- Subcampos del campo de operando
- Esquema de funcionamiento
Mdir CR CD (desplazamiento)
Mdir CD (dirección indirecta)
Estructura y Tecnología de Computadores I
Modos de direccionamiento -8-

 COMBINACIONES DE MODOS BÁSICOS DE DIRECCIONAMIENTO
Direccionamiento indirecto a registro
- Es similar al direccionamiento indirecto, pero la dirección intermedia está contenida en
un registro, no en una posición de memoria.
- La instrucción contendrá la referencia al registro.
- Subcampos del campo de operando
- Coincide con el direccionamiento relativo a registro base sin usar desplazamiento.
- Ejemplo M68000: MOVE.L D0,(A4)
Combinación de modos indirecto e indexado
- Subcampos del campo de operando
- Preindexado: El contenido del registro índice se suma al de la dirección indirecta en el
primer paso de la indirección.
Mdir CR
Mdir CR CD (dirección indirecta)
Estructura y Tecnología de Computadores I
Modos de direccionamiento -9-
- Posindexado: el contenido del registro índice se suma a la dirección intermedia en el
segundo paso de la indirección.
- Puede haber preincremento o predecremento (total: 8 posibilidades).
Direccionamiento relativo a registro con índice y desplazamiento
- La dirección del objeto se obtiene sumando el contenido de un registro base, un
registro índice y un desplazamiento.
- Subcampos del campo de operando
- Ejemplo M68000: MOVE.L D0,12(A0,D2)
Mdir CR1 CR2 CD (desplazamiento)
Estructura y Tecnología de Computadores I
Modos de direccionamiento -10-

MODOS DE DIRECCIONAMIENTO DEL MICROPROCESADOR M68000
- Inmediato (sólo operandos fuente):
MOVE.B #$48,D3
- Mediante registro
De datos: MOVE.B #$48,D3
De direcciones: MOVE.W #$4AE8,A5
- Absoluto directo: MOVE.W D2,$4AB12F0
- Relativo a registro de direcciones (indirecto a registro):
MOVE.L $345A0A5F,(A6)
- Relativo a registro de direcciones con desplazamiento:
MOVE.L $345A0A5F,3(A6)
- Relativo a registro de direcciones con desplazamiento e índice:
MOVE.L $345A0A5F,4(A1,A2)
MOVE.L $345A0A5F,4(A4,D7)
- Relativo a registro de direcciones con predecremento:
MOVE.W D0,-(A7)
- Relativo a registro de direcciones con posincremento:
MOVE.W D0,(A7)+
- Relativo a contador de programa con desplazamiento (sólo operando fuente):
MOVE.L 4(PC),$345A0A5F
- Relativo a contador de programa con desplazamiento e índice (sólo operando fuente):
MOVE.L 4(PC,A2),$345A0A5F
MOVE.L 4(PC,D7),$345A0A5F
Estructura y Tecnología de Computadores I
Modos de direccionamiento -11-


DIRECCIONAMIENTO PAGINADO Y DIRECCIONAMIENTO
SEGMENTADO
Direccionamiento paginado
- La memoria se encuentra dividida en páginas (bloques de igual longitud).
- Las direcciones se componen de dos partes:
Indicador de página (IP): en un registro específico o de propósito general de
la máquina.
Dirección de palabra (DP): en el campo CD de la instrucción.
- Concatenando IP y DP se obtiene la dirección completa.
- Esquema de funcionamiento
Direccionamiento segmentado
- La memoria se divide en porciones de tamaño variable llamadas segmentos.
- Tabla de segmentos: contiene la dirección del comienzo y del final de cada segmento
en memoria.
- Proble