Apuntes UD1.docx

 

Apuntes

Estructura y Tecnología de Computadores II

UD 1. Introducción a los lenguajes de programación

UD1 – Introducción a los lenguajes de programación

Se introducirán los distintos tipos de lenguajes de programación existentes, explicando los lenguajes de bajo nivel, lenguajes ensamblador, lenguajes macro ensambladores y lenguajes de alto nivel. Veremos sus principales diferencias y en qué casos se utilizan unos u otros.

1.1 – Lenguajes de bajo nivel

Un ordenador es una máquina programable. Es una máquina porque es una herramienta que funciona con señales eléctricas y es programable porque podemos usar esas señales eléctricas para que las interprete de una determinada manera y realice la acción que le hayamos asignado previamente.

Un programa de ordenador es una secuencia de instrucciones que el ordenador ejecuta una tras otra. Dichas instrucciones se almacenan en la memoria. El programador escribe el programa en un lenguaje de programación. El ordenador solo entiende información codificada en binario.

El conjunto de instrucciones codificadas que entiende el ordenador se denomina lenguaje máquina. Este lenguaje está muy cerca del ordenador, pero muy lejos de lo que podemos entender los humanos. Se denomina lenguaje de bajo nivel. Los lenguajes más legibles por el ser humano, pero no comprensibles por el ordenador los denominamos lenguajes de alto nivel. Existe una amplia gama de lenguajes de nivel alto, medio y bajo.

1.1.1 - Definiciones

Surgió el problema de encontrar la manera de representar las acciones que iba a llevar a cabo el ordenador, con la restricción de que únicamente se nos permitía almacenar números binarios en memoria. Para solucionar esta limitación se pensó lo siguiente: para cada acción que realice nuestro ordenador se le asigna un número, y dicho número será conocido como su código de operación (opcode). Por tanto, al conjunto de todos los códigos de operación definidos para una máquina se le denomina juego de instrucciones.

El ordenador no entiende directamente el lenguaje ensamblador, por lo que hay que traducirlo al lenguaje máquina ya que el ordenador solo entiende unos y ceros. Esta traducción la lleva a cabo el compilador de lenguajes ensamblador. Los lenguajes ensambladores y lenguajes máquina dependen del hardware, por tanto, para cada CPU tendremos un lenguaje máquina distinto y, por tanto, un lenguaje ensamblador diferente.

1.1.2 – Evolución histórica

Los lenguajes más primitivos fueron los lenguajes máquina.

La primera gran mejora llegó con el uso del lenguaje ensamblador como abstracción del lenguaje máquina, junto con la automatización de la generación de código máquina. Se redujeron en gran medida los errores triviales en la generación de código.

En los años 50 y 60 aparecieron los primeros lenguajes de alto nivel. Esto ocurrió gracias al aumento de potencia del hardware. FORTRAN fue el primer lenguaje de programación.

La principal premisa que existe a la hora de crear un nuevo lenguaje de alto nivel es la de abstraerse lo máximo posible del lenguaje de bajo nivel, y facilitarle al máximo la vida al desarrollador para así aumentar su eficiencia.

Hay lenguajes de alto nivel que se desarrollaron con la finalidad de que se traducción al lenguaje de bajo nivel fuese lo más sencilla posible, como por ejemplo C, que permite trabajar con librerías de lenguaje de alto nivel, pero a su vez está diseñado para que su traducción a lenguaje de bajo nivel sea sencilla.

Ventajas:

El lenguaje ensamblador posee algunas ventajas con respecto a su evolución hacia los lenguajes de alto nivel:

- Velocidad: Los compiladores traducen una única vez el código, y almacenan esta compilación en un fichero. Esto es mucho más eficiente que los intérpretes, los cuales son más lentos en tiempo de ejecución. La traducción sí que sigue siendo más costosa que la interpretación.

- Eficiencia de tamaño: Por norma general, los programas generados por lenguaje ensamblador suelen ser más extensos que los generados por lenguaje interpretado para la resolución de un mismo problema. Por ejemplo, los drivers se programan en lenguaje ensamblador ya que es beneficioso para la velocidad y la cantidad de memoria que ocupan.

- Flexibilidad: Mientras que el lenguaje ensamblador tiene exceso de instrucciones máquina, los lenguajes de alto nivel tienen menos capacidad de explotar al máximo estos recursos.

Desventajas:

- Tiempo de programación: El tiempo que tarda el programador en implementar la solución con un lenguaje de alto nivel es bastante inferior a la que tardaría en implementarla con lenguaje de bajo nivel.

- Programas fuente de gran tamaño: Los programas fuente en lenguaje ensamblador son de mayor tamaño que los programas fuente en alto nivel, ya que requieren un mayor número de instrucciones.

- Peligro de afectar a recursos de forma inesperada: En el lenguaje ensamblador se puede realizar lo mismo que en el lenguaje de alto nivel. La flexibilidad es útil pero peligrosa, ya que las consecuencias de los errores son mayores.

- Falta de portabilidad: El lenguaje ensamblador es únicos para cada arquitectura y no es posible utilizarlo para realizar programas que se vayan a utilizar en otros sistemas.

1.2 – Lenguajes macroensambladores

A la hora de aumentar la eficiencia de la programación se recurre a la creación de instrucciones que agrupen varias instrucciones de código máquina, y que reciben el nombre de macroinstrucciones. Los lenguajes que se utilizan para la ejecución de macroinstrucciones reciben el nombre de macroensambladores.

1.3 – Lenguaje de alto nivel

Los lenguajes ensambladores, así como los macroensambladores son demasiado primitivos para ser totalmente comprensibles por los desarrolladores debido a los siguientes problemas:

- Por un lado, el programador en lugar de estar ocupado esencialmente en el problema que desea resolver, está dedicado a solventar multitud de detalles relacionados con la arquitectura del ordenador.

- Como las instrucciones en ensamblador y macroensamblador son poco potentes, se requieren gran cantidad de ellas para ejecutar cualquier tarea que tenga significado en el contexto humano.

- El programa desarrollado solo funcionará en el ordenador en el que se ha desarrollado o en un hardware similar.

- Toda esta complejidad hace que, si se produce algún error, sea muy difícil localizarlo y arreglarlo.

1.3.1 – Clasificación por generaciones

- Primera generación: Lenguajes de código máquina

- Segunda generación: Ensambladores y macroensambladores

- Tercera generación: Son los lenguajes imperativos más utilizados en la actualidad como C, PASCAL, Modula, ADA

- Cuarta generación: Son lenguajes acompañados de facilidades para la programación.

- Quinta generación: Se elimina la necesidad de escribir instrucciones. Son lenguajes orientados a la inteligencia artificial. Entre estos lenguajes se encuentra MAGIC.

1.3.2 – Clasificación por filosofía de programación

- Imperativos o procedurales: El programa indica qué pasos debe seguir el ordenador para realizar una tarea dada. El programa se divide en bloques de código llamados funciones o procedimientos. Las instrucciones operan sobre variables o datos que se encuentran en la memoria. C, PASCAL, FORTAN, etc.

- Lenguajes orientados a objetos: Permiten aplicar la filosofía de orientación a objetos. Un objeto es una entidad que consta de métodos y propiedades. En programación los métodos son funciones y procedimientos, y las propiedades son atributos y variables. C++, C#, Python, etc.

- Lenguajes declarativos: El proceso que sigue el ordenador para realizar la tarea deseada no aparece explícitamente en el programa. El programa describe el problema que se desea resolver mediante relaciones entre funciones o entre estructuras de datos. LISP y Prolog.

- Lenguajes orientados al problema: Han sido diseñados para resolver con eficiencia determinados tipos de problemas. Cabe destacar los lenguajes de gestión de bases de datos que permiten desarrollar de forma eficiente programas especializados en trabajar con gran cantidad de información. COBOL.

1.3.3 – Clasificación según el tipo de aplicación

- Lenguajes para aplicaciones científicas y técnicas: Están orientados al cálculo matemático. Los programas desarrollados son rápidos, pudiendo realizar muchas operaciones por segundo y ocupan poco espacio en memoria. Suelen dar pocas facilidades al programador para crear una interfaz amigable con el usuario.

- Lenguajes para aplicaciones de gestión: Son lenguajes especializados en manejar una gran cantidad de información que se encuentra almacenada en el disco. SQL.

- Lenguajes para aplicaciones de software de base y utilidades: Sistemas operativos, compiladores, ensambladores, programas de comunicaciones, etc. Los cuales realizan tareas como el control primario del ordenador y la gestión del hardware de este. C, PASCAL, Modula 2.

- Lenguajes para aplicaciones de inteligencia artificial: Son lenguajes de programación que facilitan la programación de aplicaciones tales como sistemas expertos. Útiles para la inteligencia artificial. Prolog y LISP.

1.4 – Arquitectura de Von Neumann

Este modelo sigue vigente a día de hoy en los sistemas informáticos que no requieren de grandes capacidades de procesamiento, como portátiles u ordenadores de escritorio.

CPU formada por la ALU y la CPU, la unidad de control CU, que posee registros específicos como el registro de instrucciones IR, el contador de programa PC, memoria que se utiliza para almacenar datos e instrucciones, almacenamiento masivo externo y sistemas de entrada/salida.

1.4.1 – Elementos de la arquitectura de Von Neumann

Unidad central de procesamiento (Central Processing Unit, CPU)

Es el cerebro del ordenador. Su función en la de ejecutar programas almacenados en la memoria principal buscando sus instrucciones y examinándolas para, posteriormente, ejecutarlas una tras otra.

La CPU se compone de varias partes:

- Unidad de control (CU): Es la unidad encargada de buscar instrucciones en la memoria principal y de determinar su tipo.

- Unidad aritmético-lógica (ALU): Unidad encargada de hacer las operaciones aritméticas y lógicas.

- Registros: Las CPU poseen una memoria pequeña y de alta velocidad. Esta memoria está formada por varios registros los cuales tienen un determinado tamaño y una cierta función. Algunos de estos registros son:

o Contador de programa (CP)

o Registro de instrucciones (IR)

o Registro de estado (Status register)

o Acumulador (Accumulator)

Memoria

Es el dispositivo en el que se almacenan el programa principal y los datos que se van a ejecutar en la CPU.

Sistemas de entrada/salida

Periféricos

Sistemas de interconexión (buses)

La conexión entre los distintos componentes de un ordenador se realiza a través de los buses. Definimos un bus como enlace de comunicación compartido que utiliza múltiples cables para conectar subsistemas.

1.5 – Ejecución de instrucciones y ciclo de instrucción

Un ciclo de instrucción es el periodo que tarda la CPU en ejecutar una instrucción de lenguaje máquina. El número de ciclos máquina para la ejecución de cada instrucción del juego de instrucciones de una CPU puede ser diferente. Por tanto, uno más ciclos máquina forman un ciclo instrucción.

Para que cualquier sistema de procesos basado en microprocesador o microcontrolador realice una tarea primero debe buscar cada instrucción en la memoria principal, decodificarla y ejecutarla. Habitualmente son 4 los pasos que se llevan a cabo en cada ciclo de instrucción.

1.5.1 – Proceso de ejecución de instrucciones

1. Buscar la instrucción en la memoria principal: El valor del contador de programa se copia en el bus de direcciones de manera que se pueda leer la instrucción almacenada en esa dirección de memoria. La CPU carga la instrucción desde la memoria principal al registro de datos de memoria a través del bus de datos.

2. Decodificar la instrucción: En este paso se identifica la instrucción, es decir, se identifica a qué operación corresponde. El registro de instrucción siempre tiene información sobre la instrucción en curso, mientras que el contador de programa guarda la dirección de memoria de la siguiente instrucción. En este paso se calcula la dirección efectiva de memoria en caso de que la dirección apuntada tenga otra dirección indirecta.

3. Ejecutar la instrucción: En este punto la dirección ya ha sido decodificada y todos los operandos necesarios se encuentran en el registro de datos. Ahora entra en juego la unidad de control, que traduce la operación que se va a llevar a cabo a una secuencia de señales de control, enviadas a las unidades funcionales relevantes de la CPU, que realizan la operación requerida por la instrucción.

4. Almacenar resultados: El resultado de la operación se guarda en la memoria principal o se envía a un dispositivo de salida.