Sei sulla pagina 1di 19

1.1 Que estudia la programación de sistemas.

El trabajo de un programador de sistemas es seleccionar, modificar y mantener el


complejo software del sistema operativo. Por lo tanto, los programadores de
sistemas desempeñan una función de apoyo al mantener el ambiente del software
del sistema operativo en el que trabajan los programadores de aplicaciones y los
operadores de las computadoras.
Se entiende por programación de sistemas el conjunto de programas necesario
para que una computadora de una imagen coherente y monolítica ante sus
usuarios.

El estudio de la programación de sistemas


En esta área se estudia la teoría de máquinas y su aplicación en el diseño de
sistemas digitales y de arquitectura de computadora

3.4.- Creación de la Tabla de Símbolos.


La tabla de símbolos es una estructura de datos muy importante en casi todo el
proceso de compilación. En ella se guarda durante las primeras fases de
compilación los nombres de los identificadores (símbolos) usados en el programa
fuente, además de los atributos de cada uno de estos identificadores. Estos
identificadores y símbolos junto con sus atributos serán usados posteriormente
para realizar funciones como el chequeo de tipos, la asignación de memoria,
generación de código objeto etc.

Ejemplo.

Programa X1;
Var X, Y: Integer;
Z: Real;
Arreglo: Array [1…100] of int
Procedure compara (a, b: Integer)
Var n, m: integer;
Begin
----
End
Begin
----
End

Manejo de Errores de Léxico


Errores posibles detectados en el análisis de léxico son:

• Patrones de tokens que no coincidan con algún patrón válido. Por ejemplo
el token #### sería inválido en algunos L.P.
• Caracteres inválidos para el alfabeto del el lenguaje.
• Longitud de ciertos tokens demasiado larga.
• Generadores de Código Léxico
• La construcción de un scanner puede automatizarse por medio de un
generador de analizadores de léxico.
• El primer programa de este tipo que se hizo popular fue Lex (Lesk, 1975)
que generaba un scanner en lenguaje C.

Hoy en día existen muchos programas de este tipo: Flex, Zlex, YooLex, JavaCC,
SableCC, etc.

Ejemplo: Javacc
Javacc (Java Compiler Compiler) es un generador de scanners y parsers
(https://javacc.dev.java.net/).

Toma como entrada especificaciones de léxico (expresiones regulares) y sintaxis


(gramática de contexto libre) y produce como salida un analizador de léxico y un
parser recursivo descendente.

Se puede usar como pre-procesador de Javacc, a jjtree y a JTB para la


construcción del árbol sintáctico.

4.1.- Introducción a las gramáticas Libres de Contexto y Árboles de


derivación

El tipo de gramáticas usadas en los LP son llamadas gramáticas de contexto libre,


las cuáles, junto con árboles de derivación, fueron estudiadas en el curso de
Lenguajes y Autómatas.
Un ejemplo de una gramática para un LP simple es la siguiente:
1) SàS;S 2) Sàid := E 3) Sàprint (L)
4) Eà id 5) Eà num 6) Eà E + E 7) Eà(S,E)
8) Là E 9) Là L , E
(ver ejemplo de derivaciones y árboles de parsing)
A continuación veremos un ejemplo de una gramática para Java en formato BNF
Gramática Ambigua. Una gramática es ambigua si puede derivar una oración
(cadena) con dos diferentes árboles de parsing. La sig. Gramática es ambigüa:

Eàid Eànum EàE*E EàE/E


EàE+E EàE-E Eà(E)
ya que tiene dos árboles de parsing para la misma oración (árboles para
id:=id+id+id con primera gramática y árboles para 1-2-3 con segunda en sig
“slice”).

4.2.- Diagramas de Sintaxis


Un método alternativo al BNF para desplegar las producciones de ciertas
gramáticas es el diagrama de sintaxis. Ésta es una imagen de la producciones que
permite al usuario ver las sustituciones en forma dinámica, es decir, verlas como
un movimiento a través del diagrama.

4.3 Precedencia de Operadores

En los dos ejemplos vistos en sección 4.1, los dos árboles de parsing para 1-2-3
significaba diferentes cosas: (1-2)-3=-4 versus 1-(2-3)=2. Similarmente, (1+2)x3 no
es lo mismo que 1+(2x3). Es por eso que gramáticas ambiguas son problemáticas
para un compilador. Afortunadamente, una gramática ambigua puede convertirse
en una no ambigua. Por ejemplo, si queremos encontrar una gramática no
ambigua para el lenguaje de la segunda gramática de sección 4.1, lo podemos
hacer por medio de dos operaciones: primero, aplicar orden de precedencia a los
operadores y segundo aplicar asociatividad por la izquierda. Con esto, tenemos la
sig. gramática:

Operador Propósito Asociatividad


:: Scope (unario) De derecha a izquierda
:: Scope (binario) De izquierda a derecha
-> . Selección de miembros De izquierda a derecha
[] Índices De izquierda a derecha
() Llamada a función De izquierda a derecha
++ Postincremento De izquierda a derecha
-- Postdecremento De izquierda a derecha
sizeof Tamaño de un objeto De derecha a izquierda
++ Preincremento De derecha a izquierda
-- Predecremento De derecha a izquierda
*&+-!~ Operadores unarios De derecha a izquierda
new Crea un objeto De derecha a izquierda
delete Borra un objeto De derecha a izquierda
() Conversión de tipo (type cast) De derecha a izquierda
->* .* Puntero a un miembro De izquierda a derecha
*/% Operadores multiplicativos De izquierda a derecha
+- Operadores aditivos De izquierda a derecha
<< >> Operadores bitwise De izquierda a derecha
< > <= >= Operadores de relación De izquierda a derecha
== != Operadores de igualdad De izquierda a derecha
& Y bitwise De izquierda a derecha
^ bitwise O exclusivo De izquierda a derecha
| bitwise O inclusivo De izquierda a derecha
&& Y lógico De izquierda a derecha
|| O lógico De izquierda a derecha
?: Operador condicional De derecha a izquierda
= *= /= += -= >*gt;=
Operadores de asignación De derecha a izquierda
&= ^= |= %= <<=
, Operador coma De derecha a izquierda

4.4 Analizador Sintáctico

En esta fase se analiza la estructura de la frase del programa.

El parser es el programa que funciona como núcleo del compilador. Alrededor del
parser funcionan los demás programas como el scanner, el analizador semántico y
el generador de código intermedio. De hecho se podría decir que el parser
comienza el proceso de compilación y su primer tarea es pedir al escáner que
envíe los tokens necesarios para llevar a cabo el análisis sintáctico, del estatuto,
expresión o declaración dentro de un programa.

También el parser llama rutinas del análisis semántico para revisar si el significado
del programa es el correcto.

Por ultimo el parser genera código intermedio para los estatutos y expresiones si
no se encontraron errores en ellos.
Existen diferentes técnicas o métodos para realizar un análisis sintáctico “Parsing”.
Estas técnicas se dividen en dos tipos:

 Descendentes

 Ascendentes

Las técnicas descendentes realizan su análisis partiendo desde el símbolo inicial


de la gramática y realizando derivaciones hasta llegar a producir las hojas o
tokens.

Por otra parte las técnicas ascendentes realizan su análisis partiendo de las hojas
o tokens y mediante una serie de operaciones llamadas reducciones terminan en
la raíz o símbolo inicial de la gramática.

Por lo general las técnicas descendentes son mas sencillas de implementar que
las ascendentes, pero por otra parte son menos eficientes

4.4.1 Analizador descendente (LL).

Algunas gramáticas son sencillas de analizarse sintácticamente usando un


algoritmo o método cuyo nombre es recursivo descendente. En esta técnica cada
producción de la gramática se convierte en una cláusula de una función recursiva.

Un parser Predictivo es aquel que “ predice ” que camino tomar al estar realizando
el análisis sintáctico. Esto quiere decir que el parser nunca regresara a tomar otra
decisión ( back tracking ) para irse por otro camino, como sucede en las
derivaciones. Para poder contar con un parser predictivo la gramática debe de
tener ciertas características, entre ellas la mas importante es que el primer
símbolo de la parte derecha de cada producción le proporcione suficiente
información al parser para que este escoja cual producción usar. Normalmente el
primer símbolo mencionado es un Terminal o token.

Esta técnica se utilizó o popularizó en los años 70 a partir del primer compilador de
pascal implementado con ella. A continuación ilustraremos esto escribiendo un
parser recursivo descendente para la siguiente gramática:

S à if E then S else S

S àbegin S L

S àprint E

L àend

L à; S L
E ànum = num

4.4.4 Analizador ascendente(LR LALR)

La debilidad de las técnicas descendentes LL(k) es que deben predecir que


producciones usar, después de haber mirado los primeros k tokens de la cadena
de entrada.

Una técnica ascendente mas poderosa es la técnica LR(K), la cual pospone la


decisión hasta que ha visto los tokens de la entrada correspondientes a la parte
derecha de la producción (además de k mas tokens también).

LR(K) quiere decir parser de izquierda a derecha, derivación por la derecha y


“lookahead(k)”. Esta técnica fue introducida por primera vez en 1965 por Knuth.

4.5 Administración de tablas de símbolos.

Como se estudió en la unidad anterior, durante el análisis de léxico se inicia la


construcción de la tabla de símbolos.

Esto ocurre al detectarse las declaraciones en el programa fuente.

Sin embargo también el parser ayuda a realizar esta tarea pues el es quien llama
a las respectivas rutinas semánticas para que realicen funciones relacionada con
la tabla de símbolos

4.6 Manejo de errores sintácticos y su recuperacion

Dentro del código del parser predictivo estudiado en clase se puede observar que
el parser llama un metodo “error” para el manejo de errores sintácticos, que es
cuando el parser obtiene un token no esperado.
¿Cómo se maneja el error para esta clase de parsers? Una forma simple es
ejecutar una excepción y parar la compilación. Esto como que no es muy amigable
par el usuario.

La mejor técnica es desplegar un mensaje de error y recuperarse de este, para


que otros posibles errores sintácticos puedan ser encontrados en la misma
compilación.

Un error sintáctico ocurre cuando la cadena de tokens de entrada no es una


oración en el lenguaje. La recuperación del error es una forma de encontrar alguna
oración correcta, similar a la cadena de tokens.

Esto se puede realizar por medio de borrar, reemplazar o insertar tokens.

Por ejemplo la recuperación de un error en S, podría ser al insertar un token


if,begin o print (o pretender que existe en la cadena), desplegar el mensaje del
error y continuar la compilación.

Ejemplo

void S ( ) { switch ( tok ) {

case If: eat ( if ); E ( ); eat ( then ); S ( );

eat ( else ); S ( ); break;

case begin: eat ( begin ); S ( ); L ( ); break;

case print: eat ( print ); E ( ); break;

default: print(“se esperaba if, begin o print”);

}}

Un problema que puede ocurrir al insertar un token faltante es que el programa


caiga en un ciclo infinito, por eso a veces es preferible y mas seguro borrar el
token, ya que el ciclo terminará cuando el EOF sea encontrado. Esta técnica
trabaja muy cercanamente con la tabla de parsing (cuando el parser se
implementa con tablas).
En un parser del tipo LR o LALR, la tabla de parsing tiene 4 acciones: shift,
reduce, accept y error (entrada nula). Cuando el parser encuentra una acción
error, se para el proceso de análisis y se reporta la falla.

4.7 Generadores de código para analizadores sintacticos

Como se vio al final del capítulo 3, Javacc es un generador de scanners y de


parsers

Javacc produce un parser del tipo descendente (recursivo) o LL(k) donde k


(lookahead) puede ser cualquier número entero positivo.

El parser producido puede correr en cualquier plataforma que cuente con el


compilador de Java.

En el ejemplo siguiente del uso de Javacc, utilizamos de nuevo la gramática


presentada anteriormente (sección 4.4.1) para un parser predictivo. Ejecutamos
Javacc con la anterior gramática y obtendremos un parser recursivo descendente
para dicha gramática.

5.1 Analizador semántico

Un compilador no solo tiene que revisar la sintaxis de código fuente, si no también


la semántica de este.

Al igual que en los lenguajes naturales (español, ingles, etc.) en los lenguajes de
programación existen reglas semánticas para definir el significado de los
programas, estatutos, expresiones, etc.

Por ejemplo un error semántico es usar (en pascal ó java) un identificador que no
fue anteriormente declarado.

Otro ejemplo de error semántico en un programa es cuando este es compilado y y


no se detectan errores pero el momento de ser ejecutado este programa no
funciona correctamente.

5.2 Verificación de tipos en expresiones.

Cuando mezclamos diferentes tipos en una misma expresión o que llamamos una
rutina que no existe existe un error semántico.

Una de las funciones del analizador smántico es verificar que los tipos de una
expresión sean compatibles entre si.
Para hacer lo anterior el compilador cuenta con información de los atributos (tipos,
tamaño, número de argumento, etc.) de los identificadores en una tabla de
símbolos

5.3 Conversión de tipos.

Algunas veces los tipos de una expresión o estatuto son diferente.

Por ejemplo en la asignación,

a = b * c;

El tipo del resultado de evaluar b*c es diferente al de el identificador a.

El compilador algunas veces con ciertos diferentes tipos puede hacer una
conversión interna en forma implícita para solucionar el problema. Otras veces el
programador explícitamente es el que hace la conversión (casting).

Ejemplo:

float dinero;

int cambio;

dinero = (float) cambio;

5.4 Acciones agregadas en un Analizador sintáctico descendente(top-down)

En un parser recursivo-descendente, el código de las acciones semánticas es


mezclado dentro del flujo de control de las acciones del parser. En un parser
especificado en javaCC, las acciones semánticas son fragmentos de código de
programa en java unido a las producciones gramáticales.

Cada símbolo terminal y noterminal puede asociarse con su propio tipo de valor
semántico.

5.5 Pila semántica en un analizador sintáctico ascendente(botton-up)

Como fue visto en el capitulo anterior (4), un parser ascendente utiliza durante el
análisis una pila. En esta va guardando datos que le permiten ir haciendo las
operaciones de reducción que necesita.

Para incorporar acciones semánticas como lo es construir el árbol sintáctico, es


necesario incorporar a la pila del parser otra columna que guarde los atributos de
los símbolos que se van analizando.

Estos atributos estarían ligados a la correspondiente producción en la tabla de


parsing
5.6 Administracion de la tabla de simbolos

El análisis semántico conecta las definiciones de las variables con sus usos, checa
que cada expresión tenga un tipo correcto y traduce la sintaxis abstracta a una
representación mas simple para generar código máquina.

Esta fase es caracterizada por el mantener la tabla de símbolos (también llamada


“environment”) la cual mapea identificadores con sus tipos y localidades.

Cada variable local en un programa tiene un ámbito (scope) dentro del cual es
visible. Por ejemplo, en un método MiniJava m, todos los parámetros formales y
variables locales declarados en m son visibles solo hasta que finalice m.

5.7 Manejo de errores semánticos.

Cuando el checador de tipos detecta un error de tipos o un identificador no


declarado, debe imprimir el mensaje de error y continuar.

Esto debido a que normalmente el programador prefiere que le describan todos los
errores posibles del programa fuente.

Esto quiere decir, que si un error de tipos es encontrado, no debe producirse un


programa objeto por parte del compilador.

Así, las siguientes fases no deben ejecutarse.

Hasta esta etapa (chequeo de tipos), la parte del compilador se conoce con el
nombre de “front End”.

VI Generación de código intermedio


Al diseñar la parte de generación de código de un compilador debemos de decidir
si genera código directamente para una maquina real o indirectamente a través de
un código intermedio
Es mucho mas que la separación y generación de código intermedio para la
maquina destino

Lenguajes intermedios
Los arboles sintácticos ascendentes o descendentes y la notación posfija son dos
clases de representación de código intermedio.

Notación infija
Es la notación habitual. El orden de su recorrido es el siguiente
1er operando, operador, segundo operando, cuando se realiza el recorrido de una
expresión con este tipo de notación, lo único que cambia es la eliminación de
paréntesis

Expresión: (2+(3*4))

Recorrido: 2+ 3*4

Notación posfija
La notación posfija E se puede definir como:
1.- Si E es una variable o una cte. Entonces la notación posfija de E es también E
2.- Si E es una expresión de la forma:
E1 op E2, Donde op es cualquier operando binario, entonces la notación
posfija de E es E1’ E2’ op
3.- Si E es una expresión de la forma (E1) entonces la notación posfija de E
Otra forma de definir una expresión escrita en notación posfija seria escribiendo la
siguiente gramática:

Exp: var Donde:


|NUM
|exp ou ou = operador unario (--,++,!,~)
|exp ob ob = operador binario (+,-,*,/)
|exp ot ot = operador ternario (engloba más de un operador)

Posfija = 1er operando, 2do operando, operador

Ejemplo:
Exp= (4*X)+3 = 4X*3+

Exp= (9-5)+2 = 95-5+


La notación posfija no necesita paréntesis por que la posición y la variedad
(numero de argumentos), de los operadores que permiten solo la descomposición
de una expresión
Esta notación tiene ventajas con respecto a la notación infija, la cual es más
sencilla; para como ya dijimos la notación posfija no necesita paréntesis y la
evaluación de este tipo de tipo de expresiones es bastantemente sencilla y como
inconveniente principal tiene que es muy difícil expresar en ella las estructuras de
control del lenguaje.

Notación prefija
Esta notación es similar a la anterior pero ahora es descrita por la siguiente
gramática:

Exp: var Donde:


|NUM
|ou exp ou = operador unario (--,++,!,~)
|ob exp exp ob = operador binario (+,-,*,/)
|ot exp exp exp ot = operador ternario (engloba más de un operador)

Prefija = operador, 1er operando, 2do operando


Ejemplo:

Exp= 2+(5+7) = +*572

Notación Cuartetos
Los cuartetos son la forma de código intermedio mas usado, este lenguaje
consiste de una secuencia de cuartetos posiblemente precedida de una etiqueta,
donde cada cuarteto puede tener las formas siguientes:

Del x + p; * x := y;
Dep x + p; x [y] :=z;
X := y opb z; goto etiy;
X := opu y; if not v goto etiq;
X := y; param x;
X :=z[y]; call p, n
X:=*y ; return ; return (x;);

Notación tercetos
Es una notación similar a la de los cuartetos pero mas compacta aquí cada terceto
puede tener asociado un resultado temporal. Este resultado temporal puede ser
referenciado por un terceto posterior

(1) / b c
(2) = a (1)
(3) X c d
(4) = d (3)
Notación tercetos indirecta
Es similar a la anterior se añade un arreglo que indica el orden en que se
ejecutaran los tercetos

(1) / b c
(3) = a (1)
(2) X c d
(4) = d (2)

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:
a) debe ser fácil de producir
b) debe ser 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.

Ejemplo:

Exp = -(4*X+6) T1 = 7+4


T1 = 4*X T2 = T1/2
T2 = T1+6 T3 = T2
T3 = -T2
Exp = (7+4/2)

VII Optimización

7.1 Tipos de optimización

La optimización se realiza reestructurando el código de tal forma que el nuevo


código generado tenga mayores beneficios. La mayoría de los compiladores
tienen una optimización baja, se necesita de compiladores especiales para
realmente optimizar el código.
La optimización es la última fase de la construcción de un compilador. Es un
proceso que tiende a minimizar o maximizar alguna variable de rendimiento,
generalmente tiempo espacio en procesador etc., son factores que influyen en la
optimización de un compilador

7.1.1 Optimización local


se realiza sobre módulos del programa. En la mayoría de las ocasiones a través
de funciones, métodos, procedimientos, clases, etc. se ven reflejados en la rapidez
y confiabilidad de un conjunto de instrucciones que se encuentran en un bloque.

7.1.2 Optimización de ciclos (bucles)


Los ciclos son una de las partes más esenciales en el rendimiento de un programa
dado que realizan acciones repetitivas, y si dichas acciones están mal realizadas,
el problema se hace N veces más grandes
La mayoría de las optimizaciones sobre ciclos tratan de encontrar elementos que
no deben repetirse en un ciclo

Ejemplo:

while(a == b)
{
int c = a; c = 5;

}
En este caso es mejor pasar el int c =a; fuera del ciclo de ser posible.

El problema de la optimización en ciclos y en general radica es que muy difícil


saber el uso exacto de algunas instrucciones. Así que no todo código de proceso
puede ser optimizado.

7.1.3 Optimización Global


Selleva a cabo cuando se declaran variables globales para agilizar los procesos
(el proceso de declarar variables y eliminarlas toma su tiempo) pero consume más
memoria. Algunas optimizaciones incluyen utilizar como variables registros del
CPU, utilizar instrucciones en ensamblador.
7.1.4 Optimización de mirilla

trata deestructurar de manera eficiente el flujo del programa, sobre todo en


instrucciones de bifurcación como son las decisiones, ciclos y saltos de rutinas. La
idea es tener los saltos lo más cerca de las llamadas, siendo el salto lo más
pequeño posible.
7.2 Optimización de costos
Los costos son el factor más importante a tomar en cuentaa la hora de optimizar
ya que en ocasiones la mejora obtenida puede verse no reflejada en el programa
finalpero si ser perjudicial para el equipo de desarrollo.
La optimización de una pequeña mejora tal vez tenga una pequeña ganancia en
tiempo o en espacio pero sale muy costosa en tiempo en generarla

7.2.1 Optimización de costos de ejecución


Los costos de ejecución son aquellos que vienen implícitos al ejecutar el
programa. En algunos programas se tiene un mínimo para ejecutar el programa,
por lo que el espacio y la velocidad del microprocesadores son elementos que se
deben optimizar en lo posible.
Las aplicaciones multimedias como los videojuegos tienen un costo de ejecución
alto por lo cual la optimización de su desempeño es crítico, la gran mayoría de las
veces requieren de procesadores rápidos en tarjetas de video o de mucha
memoria.

Los dispositivos móviles tiene recursos más limitados que un dispositivo de


cómputo convencional razón por la cual, el mejor uso de memoriay otros recursos
de hardware tiene mayor rendimiento para ser explotados.

7.2.2 Criterios para mejorar el código

La mejor manera de optimizar el código es hacer ver a los programadores que


optimicen su código desde el inicio, el problema radica en que el costo podría ser
muy grande ya que tendríaque codificar más y/o hacer su código mas legible.

Los criterios de optimización siempre están definidos por el lenguaje de


programación a utilizar, no se comporta igual a todos sus programas.

7.2.3 Herramientas para el análisis de flujo de datos

Existen algunas herramientas que permiten el análisis de los flujos de datos, entre
ellas tenemos los depuradores y ensambladores, que sigue paso a paso todo el
desarrollo del compilador.

Conclusión: La optimización al igual que la programación es un arte y no se ha


podido sistematizar, es decir que indique el mejor camino para la optimización del
compilador.

VIII Generador de código objeto

8.1 Características del lenguaje maquina

Un lenguaje de programación de bajo nivel es el que proporciona poca o ninguna


abstracción del microprocesador de un ordenador. Consecuentemente es
fácilmente trasladado a lenguaje de máquina.
La palabra “bajo” no implica que el lenguaje sea inferior a un lenguaje de alto nivel;
se refiere a la reducida abstracción entre el lenguaje y el hardware.

En general se utiliza este tipo de lenguaje para programar controladores (drivers).

Ventajas de los lenguajes maquina

a) Mayor adaptación al equipo.


b) Posibilidad de obtener la máxima velocidad con mínimo uso de memoria.

Desventajas
a) Imposibilidad de escribir código independiente de la máquina.
b) Mayor dificultad en la programación y en la comprensión de los programas.
c) El programador debe conocer más de un centenar de instrucciones.

8.1.2 Direccionamiento del lenguaje maquina

Es la forma en como se accede a la memoria. Recordar que un programa no


puede ejecutarse sino se encuentra en memoria principal. La forma de acceder a
la memoria depende del microprocesador, pero en general existen dos tipos de
direccionamiento:

a) El direccionamiento directo también recibe el nombre de direccionamiento


absoluto y el acceso a las direcciones se hace de manera directa.
b) El direccionamiento indirecto también recibe el nombre de direccionamiento
relativo y se basa a partir de una dirección genérica, generalmente el inicio
del programa.

8.2 Características del lenguaje ensamblador

Es un tipo de lenguaje de bajo nivel utilizado para escribir programas informáticos,


y constituye la representación más directa del código máquina específico para
cada arquitectura de computadoras legible por un programador.

Los ensambladores son por lo general más fáciles de programar que los
compiladores de lenguajes de alto nivel, y han estado disponibles desde la década
de los 50’s
Los ensambladores de alto nivel ofrecen posibilidades de abstracción que
incluyen:

a) Control avanzado de estructuras.


b) Procedimientos de alto nivel
c) Declaración de funciones
d) Tipos de datos que incluyen estructuras, registros, uniones, clases y
conjuntos
La transformación del lenguaje ensamblador en código máquina la realiza un
programa ensamblador, y la traducción inversa la puede efectuar un
desensamblador.
Cada arquitectura de computadoras tiene su propio lenguaje de máquina, y en
consecuencia su propio lenguaje ensamblador.
Las computadoras 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

El lenguaje maquina está formado por instrucciones sencillas que dependiendo de


la estructura del procesador puede especificar:

a) Registros específicos para operaciones aritméticas


b) Direccionamiento o control de funciones
c) Posiciones de memoria específicas

El código máquina, un simple patrón de bits, es hecho legible reemplazando


valores crudos por símbolos denominados mnemónicos. Se inventó para facilitar la
tarea de los primeros programadores que hasta ese momento tenían que escribir
directamente en código binario.

8.2.2 Almacenamiento del lenguaje ensamblador

Una de las principales ventajas del uso del ensamblador, es que se encarga de
administrar de manera transparente para el usuario la creación de memoria, las
bifurcaciones y el paso de parámetros entre funciones o procedimeinetos.

Además nos permite acceder directamente a los recursos de la máquina para un


mejor desempeño.

8.3 Registros del lenguaje ensamblador

Los registros del procesador se emplean para controlar instrucciones en ejecución,


manejar direccionamiento de memoria y proporcionar capacidad aritmética.
Los registros son espacios físicos dentro del microprocesador con capacidad de 4
bits hasta 64 bits dependiendo del microprocesador que se emplee.
1.- Registro de segmento: se utiliza para alinear en un límite de párrafo o dicho de
otra forma codifica la dirección de inicio de cada segmento y su dirección en un
registro de segmento Los cuales pueden ser los siguientes:

CS = Código
DS = Datos
SS = Pila
ES = E/S de puertos

2.- Registro de apuntadores de instrucción: contiene el desplazamiento de


dirección de la siguiente instrucción que se ejecuta.
3.- Registro de uso general: Se puede dividir en las siguientes categorías:

AX = Registro acumulador, Interviene en las operaciones aritméticas y lógicas,


después de la operación arroja un resultado.
BX = Registro base, Se utiliza en transferencias de datos entre la memoria y el
procesador.
CX = Registro contador, Se utiliza como contador en bucles.
DX = Registro de datos, Se utiliza en operaciones de multiplicación y división junto
con Ax en operaciones de entrada y salida de puertos

4.- Registro índice: están disponibles para direccionamientos indexados y para


sumas y restas. Que son las operaciones principales.

5.- Registro de bandera: sirven parar indicar el estado actual de la maquina y el


resultado del procesamiento, Cuando algunas instrucciones piden comparaciones
o cálculos aritméticos.

8.3.1 Distribución del Lenguaje ensamblador

Es el proceso en el que el programa generado puede ejecutarse en otras


máquinas. Con respecto al ensamblador, la mayoría del direccionamiento se hace
relativo para que el programa sea relocalizable por un programa llamado cargador.

Debido a la complejidad del software actual se necesitan de asistentes para poder


instalar y ejecutar un programa.

8.4 Administración del Lenguaje ensamblador

Se refiere a la administración de la memoria lo cual hoy en día es un proceso muy


importante, de tal modo que su mal o buen uso tiene una acción directa sobre el
desempeño de memoria, y esto tiene que ver con el uso de punteros los cuales
quedaban perdidos en la memoria consumiendo espacio.

Los lenguajes más recientes controlan el uso de punteros y tienen un programa


denominado recolector de basura que se encarga de limpiar la memoria no
utilizada mejorando el desempeño de la memoria.

Potrebbero piacerti anche