• No se han encontrado resultados

Unidad 1-Lenguaje Maquina, Ensamblador y Z80 Parte A

N/A
N/A
Protected

Academic year: 2021

Share "Unidad 1-Lenguaje Maquina, Ensamblador y Z80 Parte A"

Copied!
15
0
0

Texto completo

(1)

ESTRUCTURA Y FUNCIONAMIENTO DE

ESTRUCTURA Y FUNCIONAMIENTO DE

COMPUTADORAS II

COMPUTADORAS II

UNIDAD 1: LENGUAJE DE MÁQUINA Y

UNIDAD 1: LENGUAJE DE MÁQUINA Y

ENSAMBLAD

ENSAMBLADOR. P

OR. PROCESADOR Z80.

ROCESADOR Z80.

(PARTE A)

(PARTE A)

Licenciatura en Ciencias de

Licenciatura en Ciencias de la Computación

la Computación

Departamento de Informática

Departamento de Informática

Facultad de Ciencias Exactas, Físicas y Naturales

Facultad de Ciencias Exactas, Físicas y Naturales

Universidad Naciona

(2)

1.1. LENGUAJE DE MAQUINA

1.1.1.

Conjunto (set) de instrucciones

Si un programador utiliza un lenguaje de alto nivel, tal como Pascal o Ada, muy poco de la arquitectura de la máquina es visible.

Un límite donde el diseñador de la computadora y el programador de ésta pueden ver la misma máquina es en el conjunto de instrucciones de la máquina. Desde el punto de vista del diseñador, el conjunto de instrucciones de la máquina proporciona los requerimientos funcionales para la CPU: la implantación de la CPU es una tarea que en gran parte involucra establecer el conjunto de instrucciones de la máquina. Desde el lado de usuario, aquel que elige programar en lenguaje de máquina (o más probablemente en lenguaje ensamblador; como se verá más adelante) es quien se llega a enterar de los registros y estructura de la memoria, los tipos de datos soportados directamente por la máquina y el funcionamiento de la ALU.

Una descripción del conjunto de instrucciones de máquina permite explicar acabadamente la CPU de la computadora y cada una de las operaciones que ella puede realizar.

Conjunto (Set) de Instrucciones

Una computadora sólo puede reconocer, interpretar y procesar un grupo definido y limitado de órdenes, que corresponden a las operaciones que puede realizar.

Esto se lleva a cabo a través de la ejecución de instrucciones, que determinan la operación de la CPU. A estas instrucciones se hace referencia como instrucciones de máquina o instrucciones de la computadora. La CPU puede ejecutar una variedad de funciones y éstas se reflejan en la variedad de instrucciones definidas en ella. La colección de diferentes instrucciones que la CPU puede ejecutar es referida como el conjunto, repertorio o set de instrucciones y constituye el llamado LENGUAJE DE MAQUINA o CODIGO ABSOLUTO de esa computadora.

Cada fabricante establece el SET correspondiente a una máquina en particular, de modo que ello constituye una característica de diseño. Esa máquina está hecha para ejecutar esas instrucciones y ninguna otra.

Por ejemplo:

• SUMAR al valor actual del registro Acumulador de la CPU, un número entero codificado en

binario de punto fijo en 8 bits, almacenado en la memoria principal, almacenando el resultado en el Acumulador.

• SUMAR dos números enteros codificados en binario de punto fijo en 16 bits, almacenados en

registros de 16 bits de la CPU, almacenando el resultado en el primero de los registros que se indiquen como sumandos.

• TRANSFERIR un número entero de 8 bits, almacenado en registro de la CPU, a una posición

de almacenamiento en la memoria principal.

• COMPARAR dos cantidades enteras de 8 bits .... • ETC.

Los ejemplos muestran que cada instrucción requiere que se especifique con todo detalle no sólo la operación a realizar, sino otros datos sin los cuales no podría llevarse a cabo. Los dos primeros ejemplos lo ilustran claramente. En ambos casos se trata de la operación de SUMAR, pero las dos instrucciones tienen diferencias sustanciales. Por lo tanto, dos computadoras diferentes contarán ambas con instrucciones de SUMA, pero pueden diferir enormemente en cuanto a los distintos tipos de esta operación incluidos en su SET.

El número de instrucciones que componen el SET es distinto para distintas máquinas. Sin embargo, existe un grupo básico de operaciones que todas incluyen, aunque con características propias para cada máquina.

1.1.2.

Representación de instrucciones

Dado que la representación de la información dentro de la computadora es binaria, cada instrucción es un código binario que puede ser interpretado por los circuitos de la CPU.

(3)

diseñador, con ajuste a un formato determinado.

Por ejemplo, la siguiente configuración corresponde a la operación de Sumar los contenidos de los registros R12 y R11 de la CPU, con almacenamiento del resultado en R12, en una CPU IBM S/370

0001101011001011 (Fig. 1)

y la siguiente a una instrucción de salto incondicional a la dirección 9A33 (hexadecimal) de la memoria principal, en un microprocesador Z80

110000111001101000110011 (Fig. 2)

1.1.3.

Elementos de una instrucción de máquina

Cada instrucción debe contener la información requerida por la CPU para su ejecución. La figura 8.1 muestra los pasos involucrados en la ejecución de instrucciones y, por implicación, define los elementos de una instrucción de máquina. Estos elementos son:

• Código de operación: Especifica la operación a ser realizada (por ejemplo, SUMAR, E/S,

TRANFERIR, ETC). La operación está especificada por una configuración de bits, conocido como el código de operación u opcode, que es parte del código binario de la instrucción.

• Referencia del operando fuente: La operación puede involucrar uno o más operandos fuente,

esto es, operandos que son entradas para la operación.

• Referencia del operando resultado: La operación puede producir un resultado.

• Referencia de la siguiente instrucción: Ésta le dice a la CPU dónde buscar la siguiente

instrucción después de que la ejecución de esta instrucción se haya completado.

La siguiente instrucción a ser buscada se localiza en la memoria principal o, en el caso de un sistema de memoria virtual, en la memoria principal o en la memoria secundaria (disco). En la mayoría de los casos, la siguiente instrucción a ser buscada sigue de inmediato a la instrucción actual. En esos casos, no hay referencia explícita a la siguiente instrucción. Cuando es necesaria una referencia explícita, entonces se debe suministrar la dirección de la memoria principal o virtual.

Los operandos fuente y resultado pueden estar en una de tres áreas:

•  Memoria: En este caso, la instrucción debe suministrar las correspondientes direcciones.

•  Registro de la CPU : Con raras excepciones, una CPU contiene uno o más registros que pueden

ser referenciados mediante instrucciones de máquina. Si sólo existe un registro, la referencia a él puede ser implícita. Si existe más de un registro, entonces a cada registro se le asigna un número único y la instrucción debe contener el número del registro deseado.

•  Dispositivo de E-S: La instrucción debe especificar el módulo de E-S y el dispositivo para la

operación. Si se utiliza E-S mapeada a memoria, ésta es otra dirección de la memoria principal o virtual.

1.1.4.

Formato de instrucciones

Una instrucción se divide en campos, en correspondencia a los elementos constituyentes de la instrucción. Esto se conoce como el formato de instrucción.

(4)

guarda estrecha relación con las características físicas (estructura, organización) del procesador. Esquemáticamente, podemos representarlo como muestra la fig. 8.2.:

En el esquema, el código de operación ocupa los primeros 4 bits del formato.

Es común que el código de operación ocupe los ocho primeros bits (primer byte u octeto) de la instrucción. Dado que 8 bits permiten 28 combinaciones distintas, es posible disponer, en este caso, de hasta 256 instrucciones. Si el SET de instrucciones contuviera un número mayor de ellas, debería entonces destinarse un número mayor de bits para el código de operación.

La extensión del campo o zona de referencia de operandos depende del tipo de operación y de características de la máquina. Por ejemplo, en el caso de la Fig. 2, el operando es la dirección del salto (bifurcación). Como en el microprocesador Z80 las direcciones son informaciones de 16 bits, la extensión del campo de operando será de 16 bits. Sin embargo, una máquina con una estructura de direcciones de mayor capacidad requerirá, para una operación idéntica, un campo de mayor extensión para el operando.

Por otro lado, en una misma máquina, distintas instrucciones tendrán distintas extensiones del campo de operandos. Por ejemplo, en el Z80 la siguiente instrucción corresponde a la suma de una constante de 8 bits (el número decimal 25), al registro acumulador de la UAL:

1100011000011001 (Fig. 3)

Esta instrucción consta de sólo un octeto en el campo de operando, empleado para representar la constante de 8 bits, tal como está definido en la operación.

Existen, incluso, instrucciones que no contienen campo de operandos, o sea que solamente constan del código de operación. La siguiente instrucción, por ejemplo, opera la inversa booleana (NOT) sobre el contenido del registro acumulador, en un Z80:

00101111 (Fig. 4)

Como esta operación, dentro del set o repertorio, está definida exclusivamente para el acumulador, no es necesario consignar el operando (el acumulador) explícitamente. Es, entonces, una instrucción con operando implícito.

Otros casos en que la instrucción no especifica operando corresponden a ciertas operaciones de control (operaciones de pausa, detención, etc).

En síntesis, el formato de las instrucciones y el código de operación son asignados por el fabricante al diseñar la computadora. Distintas CPU tienen, en general, distintos formatos de instrucciones y en la mayoría de los casos, para una misma CPU el conjunto de instrucciones utiliza más de un formato. La CPU debe estar habilitada para extraer los datos de los varios campos de instrucción a fin de realizar la operación requerida.

Es importante destacar que el código de operación no sólo informa a la Unidad de Control la operación que se va a realizar sino que brinda, además, información acerca de:

El tipo de los operandos (numéricos o no-numéricos, el convenio de codificación en que están representados: binario punto fijo, punto flotante, BCD, ASCII, EBCDIC. etc.).

La ubicación de los datos: en un registro, en la memoria (ya sea en una ubicación propia o bién, como se muestra en el ejemplo de la Fig. 3, dentro de la misma instrucción)

1.1.5.

Tipos de instrucciones

Considere una instrucción en lenguaje de alto nivel que podría estar expresada en un lenguaje tal como BASIC o FORTRAN. Por ejemplo:

X=X+Y

Esta declaración instruye a la computadora para sumar el valor almacenado en Y al valor almacenado en X y poner el resultado en X. ¿Cómo podría esto llevarse a cabo con instrucciones de máquina? Vamos a suponer que las variables X y Y corresponden a las localidades 513 y 514. Si suponemos un conjunto de instrucciones de máquina simple, esta operación podría llevarse a cabo con tres

(5)

instrucciones:

1. Cargar un registro con el contenido de la localidad 513 de memoria. 2. Sumar el contenido de la localidad 514 de memoria al registro. 3. Almacenar el contenido del registro en la localidad 513 de memoria.

Como se puede ver, la simple instrucción de BASIC puede requerir tres instrucciones de la máquina. Esto es típico de la relación entre un lenguaje de alto nivel y un lenguaje de máquina. Un lenguaje de alto nivel expresa las operaciones en una forma algebraica concisa, usando variables. Un lenguaje de máquina expresa las operaciones en una forma básica e involucra el movimiento de datos hacia o desde registros.

Con este ejemplo simple como guía, vamos a considerar los tipos de instrucciones que deben estar incluidos en una computadora práctica. Una computadora debe tener un conjunto de instrucciones que le permitan al usuario formular cualquier mecanismo de procesamiento de datos. Otra manera de ver esto es considerar las capacidades de un lenguaje de programación de alto nivel. Cualquier programa escrito en un lenguaje de alto nivel se debe convertir a un lenguaje de máquina para poder ser ejecutado. Por consiguiente, el conjunto de instrucciones de la máquina tiene que ser suficiente para poder expresar cualquiera de las instrucciones desde un lenguaje de alto nivel. Con esto en mente, podemos categorizar los tipos de instrucciones como sigue:

Procesamiento de datos: Instrucciones aritméticas y lógicas.  Almacenamiento de datos: Instrucciones de memoria.

 Movimiento de datos: Instrucciones de E-S. Control: Instrucciones de prueba y bifurcación.

Las instrucciones aritméticas proporcionan capacidades computacionales para el procesamiento de datos numéricos. Las instrucciones lógicas (Booleanas) operan sobre los bits de una palabra como bits en vez de como números; de esta manera, proporcionan capacidades para el procesamiento de cualquier otro tipo de datos que el usuario pueda desear emplear. Estas operaciones se ejecutan, principalmente, sobre datos en los registros de la CPU. Por consiguiente, deben existir instrucciones de memoria para mover datos entre la memoria y los registros. Las instrucciones de E-S son necesarias para transferir programas y datos a la memoria y que los resultados de cálculos regresen al usuario. Las instrucciones de prueba se emplean para verificar el valor de una palabra de datos o el estado de un cálculo. Las instrucciones de bifurcación se usan entonces para saltar a un conjunto diferente de instrucciones dependiendo de la decisión hecha.

1.1.6.

Número de direcciones

Una de las maneras tradicionales de describir la arquitectura del procesador es en términos del número de direcciones contenidas en cada instrucción. Esta dimensión se ha vuelto menos significativa con el incremento de la complejidad del diseño de la CPU. No obstante, es útil este punto para traer y analizar tal distinción.

¿Cuál es el número máximo de direcciones que se podría necesitar en una instrucción? Es evidente que las instrucciones lógicas y aritméticas requerirán la mayoría de los operandos. De hecho, casi todas las operaciones lógicas y aritméticas son unitarias (un operando) o binarias (dos operandos). Por lo tanto, necesitaríamos un máximo de dos direcciones para hacer referencia a los operandos. El resultado de una operación se debe almacenar, sugiriendo una tercera dirección. Por último, una vez que se ha finalizado una instrucción, la siguiente instrucción se debe buscar, y se requiere también su dirección.

La línea de razonamiento de arriba sugiere que una instrucción podría, con cierta razón, ser requerida para contener cuatro referencias de dirección: dos operandos, un resultado y la dirección de la siguiente instrucción. En la práctica, las instrucciones de cuatro direcciones son en extremo raras. La mayoría de las CPUs es de la variedad de una, dos o tres direcciones, con la dirección de la siguiente instrucción implícita (obtenida del contador del programa).

En la figura 8.3 se comparan las instrucciones típicas de una, dos, y tres direcciones que se podrían usar para calcular Y = (A - B) / (C + D * E). Con tres direcciones, cada instrucción especifica localidades de dos operandos y una localidad de resultado. Dado que nos gustaría no alterar el valor de cualquiera de las localidades de los operandos, una localidad temporal, T, se usa para almacenar algunos resultados intermedios. Observe que existen cuatro instrucciones y que la expresión original tuvo cinco operandos. Cada instrucción se muestra en forma esquemática indicando la operación que lleva a cabo cada una.

(6)

Y ← A – B T ← D * E Tres direcciones T ← T + C Y ← Y / T Y ← A Y ← Y – B T ← D Dos direcciones T ← T * E T ← T + C Y ← Y / T AC ← D AC ← AC * E AC ← AC + C Y ← AC Una dirección AC ← A AC ← AC – B AC ← AC / Y Y ← AC

Los formatos de instrucción de tres direcciones no son comunes debido a que requieren un formato de instrucción relativamente largo para almacenar las referencias de tres direcciones. Con las instrucciones de dos direcciones, nuestro programa de muestra se expande a seis instrucciones.

Más simple es la instrucción de una dirección. Para que esto funcione, una segunda dirección debe ser implícita. Esto era común en las primeras máquinas, donde la dirección implícita era un registro de la CPU conocido como el acumulador (AC o A). El acumulador contiene uno de los operandos y se utiliza para almacenar el resultado. En nuestro ejemplo, son necesarias ocho instrucciones para llevar a cabo el procedimiento.

De hecho, es posible trabajar con cero direcciones para algunas instrucciones. Las instrucciones de cero direcciones son aplicables a una organización especial de memoria, llamada stack (pila). Un stack es un conjunto de localidades del tipo último-en-entrar-primero-en-salir (LIFO). El stack se encuentra en una ubicación conocida y, con frecuencia, al menos los dos elementos superiores están en registros de la CPU. Por lo tanto, las instrucciones de cero direcciones harían referencia a los dos elementos superiores del stack. Los stacks se describen en el apéndice de este capítulo. Su uso se explora más adelante en éste y en el capítulo 9.

En la tabla 8.1 se resumen las interpretaciones que se van a poner sobre las instrucciones con 0, 1, 2 o 3 direcciones. En cada caso en la tabla, se supone que la dirección de la siguiente instrucción está implícita, y que se realiza una operación con dos operandos fuente y un operando resultado.

El número de direcciones por instrucción es una decisión básica de diseño. Pocas direcciones por instrucción devienen en instrucciones más primitivas, lo cual requiere una CPU menos compleja. Esto también deriva en instrucciones de longitud más corta. Por otra parte, los programas contienen más instrucciones totales, lo cual resulta, en general, en tiempos de ejecución más largos y programas más complejos y largos. También, existe un umbral importante entre las instrucciones de una dirección y las

(7)

de múltiples direcciones. Con las instrucciones de una dirección, el programador casi siempre tiene disponible sólo un registro de propósito general, el acumulador. Con las instrucciones de múltiples direcciones, es común tener múltiples registros de propósito general. Esto permite que algunas operaciones se lleven a cabo de manera exclusiva en los registros. Ya que las referencias de registro son más rápidas que las referencias de memoria, esto acelera la ejecución. Por razones de flexibilidad y habilidad para utilizar múltiples registros, la mayoría de las máquinas contemporáneas emplea una mezcla de instrucciones de dos y tres direcciones.

Los compromisos de diseño involucrados en escoger el número de direcciones por instrucción se complican por otros factores. Existe el problema de si una dirección hace referencia a una localidad de memoria o a un registro. Puesto que existen pocos registros, se necesitan pocos bits para una referencia de registro. También, como veremos en el siguiente capítulo, una máquina puede ofrecer una variedad de modos de direccionamiento y la especificación de los modos toma uno o más bits. El resultado es que la mayoría de los diseños de la CPU involucra una variedad de formatos de instrucción.

1.1.7.

Diseño del conjunto de instrucciones

Uno de los aspectos más interesantes, y más analizados, del diseño de computadoras es el diseño del conjunto de instrucciones. El diseño de un conjunto de instrucciones es muy complejo, ya que afecta muchos de los aspectos del sistema computacional. El conjunto de instrucciones define muchas de las funciones llevadas a cabo por la CPU y, por consiguiente, tiene un efecto significante en la implantación de ésta. El conjunto de instrucciones es el medio del programador para controlar la CPU. Por lo tanto, los requerimientos del programador deben ser considerados en el diseño del conjunto de instrucciones.

Puede sorprenderle saber que algunos de los temas más fundamentales relacionados con el diseño del conjunto de instrucciones permanecen en disputa. En realidad, en años recientes, el nivel de desacuerdo concerniente a estos fundamentos ha crecido. Los más importantes de tales temas de diseño fundamentales incluyen:

•  Repertorio de operaciones: Cuántas y cuáles operaciones proporcionar, y cómo deberían ser las

operaciones complejas.

• Tipos de datos: Los varios tipos de datos sobre los cuales las operaciones se realizan.

• Formato de las instrucciones: Longitud de la instrucción (en bits), número de direcciones,

tamaño de varios campos, etc.

•  Registros: Número de registros de la CPU que se pueden referenciar mediante instrucciones, y

su uso.

•  Direccionamiento: El modo o modos por el cual la dirección de un operando se especifica.

Estos temas presentan alta interrelación y se deben considerar en el diseño de un conjunto de instrucciones.

En la tabla siguiente (8.2.- basada en [HAYE88]) se listan los tipos de instrucciones comunes en las distintas categorías.

(8)
(9)

1.1.8.

Tipos de operandos

Las instrucciones de máquina operan sobre los datos. Las categorías generales más importantes de datos son: direcciones, números, caracteres y datos lógicos.

Direcciones

Las direcciones son, de hecho, una forma de datos. En muchos casos, algún cálculo se debe ejecutar sobre la referencia de operando en una instrucción para determinar la dirección de la memoria principal o virtual. En este contexto, las direcciones se pueden considerar como enteros sin signo.

Otros tipos comunes de datos son los números, caracteres y datos lógicos, y cada uno de éstos se analiza, de manera breve, en esta sección. Más allá de eso, algunas máquinas definen tipos de datos o estructuras de datos especializados. Por ejemplo, existen operadores de máquina que operan en forma directa sobre una lista o un conjunto de caracteres.

Números

Todos los lenguajes de máquina incluyen tipos de datos numéricos. Incluso en el procesamiento de datos no numéricos, hay una necesidad de los números para que actúen como contadores, anchuras de campo, y cosas por el estilo. Una distinción importante entre los números utilizados en la matemática ordinaria y los números almacenados en una computadora es que estos últimos están limitados. Esto es cierto en dos sentidos. Primero, existe un límite para la magnitud de los números representables en una máquina y, segundo, en el caso de los números de punto flotante, hay un límite para su precisión. Siendo así, el programador tiene que vérselas con la comprensión de las consecuencias del redondeo, desbordamiento positivo (overflow) y desbordamiento negativo (underflow).

En las computadoras, son tres los tipos de datos numéricos comunes (ya conocidos por el alumno de esta asignatura):

• Enteros o de punto fijo. • Punto flotante.

• Decimales (codificados en binario: BCD, ASCII, EBCDIC, empaquetados (packed) o

desempaquetados (unpacked))

Caracteres

Una forma común de datos es el texto o cadena de caracteres. Mientras que los datos textuales son los más convenientes para la comprensión humana, no pueden en forma de caracteres, almacenarse o transmitirse con facilidad mediante sistemas de procesamiento de datos y de comunicaciones. Tales

(10)

sistemas están diseñados para los datos binarios. Por lo tanto, se ha ideado un número de códigos mediante el cual los caracteres se representan por una secuencia de bits. Quizá el primer ejemplo más común de esto es el código Morse. En la actualidad, el código de caracteres de uso más común en Estados Unidos es el código ASCII (American - Standard Code for Information Interchange), véase la tabla 5. 1, promulgado por el American National Standards Institute (ANSI). ASCII también se utiliza en gran medida fuera de Estados Unidos.

Otro código utilizado para codificar caracteres es el Extended Binary Coded Decimal Interchange Code (EBCDIC). Éste se emplea en las máquinas IBM S/370.

Datos lógicos

Por lo general, cada palabra u otra unidad direccionable (byte, media palabra, etc.) se trata como una unidad de datos simple. Algunas veces es útil, sin embargo, considerar una unidad de n bits como consistiendo de n elementos de datos de 1 bit, cada elemento debe tener el valor de 0 o 1. Cuando los datos se ven de esta manera, se consideran como datos lógicos.

Existen dos ventajas del esquema orientado a bits. Primero, algunas veces podemos desear almacenar un arreglo de elementos de datos binarios o Booleanos, en los cuales cada elemento puede tomar sólo los valores 1 (cierto) y 0 (falso). Con los datos lógicos, la memoria se puede utilizar de manera más eficiente para este almacenamiento. Segundo, hay ocasiones en que deseamos manipular los bits de un elemento de datos. Por ejemplo, si las operaciones de punto flotante se implementan en software, necesitamos estar habilitados para recorrer los bits significativos en algunas operaciones. Otro ejemplo: para convertir de ASCII en decimal empaquetado, necesitamos extraer los 4 bits de más a la derecha de cada byte.

Observe que, en los ejemplos anteriores, los mismos datos se tratan algunas veces como lógicos y otras ocasiones como numéricos o texto. El “tipo” de una unidad de datos está determinado por la operación que se está llevando a cabo sobre él. Mientras éste no es el caso en los lenguajes de alto nivel (por ejemplo, Pascal), es casi siempre el caso en el lenguaje de máquina.

1.1.9.

Tipos de operaciones

El número de diferentes códigos de operación varía en gran medida de máquina a máquina. Sin embargo, los mismos tipos generales de operaciones se encuentran en todas las máquinas. Una categorización típica y útil es la siguiente:

• Transferencia de datos. • Aritméticas.

• Lógicas. • Conversión. • E-S.

• Control del sistema. • Transferencia de control.

En la tabla 8.2. (anterior) se listan los tipos de instrucciones comunes en las distintas categorías.

1.1.10. Representación simbólica de las instrucciones de máquina

Es difícil tanto para el programador como para el lector de libros de texto tratar con las representaciones binarias de las instrucciones de máquina. Por lo tanto, se ha convertido en una práctica común el usar una representación simbólica de las instrucciones de la máquina.

Los códigos de operación se representan por medio de abreviaturas, llamadas mnemónicos, que indican la operación. Los ejemplos comunes incluyen:

ADD Adición (suma). SUB Sustracción (resta). MPY Multiplicación. DIV División.

LOAD Cargar datos desde la memoria. STOR Almacenar datos en la memoria.

(11)

Los operandos también se pueden representar de manera simbólica. Por ejemplo, la instrucción: ADD R, Y

puede significar la suma del valor contenido en la localidad de datos Y al contenido del registro R, almacenando el resultado en R. En este ejemplo, Y se refiere a la dirección de una localidad en la memoria, y R se refiere a un registro particular. Observe que la operación se lleva a cabo sobre el contenido de una localidad, no sobre su dirección.

Siendo así, es posible escribir un programa en lenguaje de máquina en forma simbólica. Cada código de operación tiene una representación binaria fija, y el programador especifica la localidad de cada operando simbólico. Por ejemplo, el programador debe comenzar con una lista de definiciones:

X = 513 Y = 514

y así en forma sucesiva. Un programa simple aceptaría esta entrada simbólica, convertiría los códigos de operación y las referencias de operandos en una forma binaria, y construiría instrucciones de máquina binarias.

1.1.11. Operaciones de transferencia de control

Para todos los tipos de operación analizados hasta este momento, la instrucción especifica la operación a ser realizada y los operandos. De manera implícita, la siguiente instrucción a ser ejecutada es aquella que sigue inmediatamente, en la memoria, a la instrucción actual. Por lo tanto, en el curso normal de eventos, las instrucciones se ejecutan en forma consecutiva desde la memoria. La CPU tan sólo incrementa el contador del programa antes de empezar a buscar la siguiente instrucción.

Sin embargo, una fracción significativa de las instrucciones en cualquier programa tiene como función cambiar la secuencia de ejecución de las instrucciones. Para estas instrucciones, la operación llevada a cabo por la CPU consiste en actualizar el contador del programa para que contenga la dirección de alguna instrucción en la memoria.

Existe un número de razones de por qué las operaciones de transferencia de control son necesarias. Entre las más importantes están:

1. En el uso práctico de las computadoras, es esencial habilitar la ejecución de cada instrucción más de una vez y quizá muchas miles de veces. Esto puede requerir miles o quizás millones de instrucciones para implantar una aplicación. Esto sería impensable si cada instrucción tuviera que escribirse por separado. Si una tabla o una lista de elementos va a ser procesada, se requiere un lazo de programa. Una secuencia de instrucciones se ejecuta de manera repetida para procesar todos los datos.

2. Casi todos los programas involucran la toma de alguna decisión. Nos agradaría que la computadora realizara alguna acción si una condición se cumple y otra si una condición distinta se cumple. Por ejemplo, una secuencia de instrucciones calcula la raíz cuadrada de un número. Al inicio de la secuencia, el signo del número se verifica. Si el número es negativo, el cálculo no se realiza, pero se reporta una condición de error.

3. Componer en forma correcta un programa de computadora grande o aun de tamaño medio es una tarea difícil en exceso. Ayuda si existen mecanismos para la división de la tarea hasta en piezas más pequeñas que se puedan trabajar una a la vez.

Ahora, volvamos a centrar nuestro interés en un examen detallado de las operaciones de transferencia de control más comunes que podernos encontrar en los conjuntos de instrucciones:

• Bifurcación. • Brinco.

• Llamada a la subrutina.

Instrucciones de bifurcación

Una instrucción de bifurcación, también llamada instrucción de transferencia, tiene como uno de sus operandos la dirección de la siguiente instrucción a ser ejecutada. Con mayor frecuencia, la instrucción es una instrucción de bifurcación condicional. Esto es, la bifurcación se efectúa (el contador del programa se actualiza con la dirección especificada en el operando) sólo si una cierta condición se cumple. De otra manera, la siguiente instrucción en secuencia se ejecuta (por lo general, el contador del programa se incrementa).

(12)

Existen dos maneras comunes de generar la condición a ser verificada en una instrucción de bifurcación condicional. Primero, la mayoría de las máquinas proporcionan un código de condición de 1 bit o múltiples bits que se establece como el resultado de algunas operaciones. Este código puede ser visto como un registro corto visible al usuario. Por ejemplo, una operación aritmética (SUMA, RESTA, etc.) podría establecer un código de condición de 2 bits con uno de los siguientes cuatro valores: 0, positivo, negativo, desbordamiento. En tal máquina podrían existir cuatro instrucciones de bifurcación condicional diferentes:

BRP X Bifurcar a la localidad X si el resultado es positivo. BRN X Bifurcar a la localidad X si el resultado es negativo. BRZ X Bifurcar a la localidad X si el resultado es cero.

BRO X Bifurcar a la localidad X si ocurre desbordamiento (overflow).

En todos estos casos, el resultado referido se deriva de la operación más reciente que puso el código de condición.

Otra propuesta que se puede utilizar con un formato de instrucción de tres direcciones es llevar a cabo una comparación y especificar una bifurcación en la misma instrucción. Por ejemplo,

BRE Rl, R2, X (Bifurcar a X si el contenido de Rl = contenido de R2).

Una bifurcación puede ser ya sea hacia adelante (una instrucción con una dirección mayor), o hacia atrás (una dirección más baja). Una bifurcación incondicional y una condicional se pueden utilizar para crear un lazo para repetición de instrucciones.

Códigos de condición

Hemos mencionado que los códigos de condición son bits en registros especiales que pueden ser puestos por ciertas operaciones y utilizados en instrucciones de bifurcación condicional. Tales condiciones están puestas por operaciones aritméticas y de comparación. La operación de comparación en la mayoría de los lenguajes resta dos operandos, como lo hace una operación de resta. La diferencia es que la operación de comparación sólo pone los códigos de condición mientras que una operación de resta común también almacena el resultado de la diferencia en el operando destino.

Como un ejemplo, en la tabla 8.8 se listan los códigos de condición utilizados en las máquinas 80386/80486.

Instrucciones de salto o brinco

Otra forma común de instrucciones de transferencia de control es la instrucción de salto. Ésta incluye una dirección implícita. Por lo común, el brinco implica que una instrucción sea brincada; por consiguiente, la dirección implícita es igual a la dirección de la siguiente instrucción más una longitud de instrucción.

(13)

para hacer otras cosas. Un ejemplo típico es la instrucción de incremento-y-brinco-si-cero o bien decremento-y-brinco-si-no-cero. Se verá un ejemplo al tratar el set de instrucciones de un procesador en particular, más adelante.

Instrucciones de llamada a subrutina

Quizá la innovación más importante en el desarrollo de los lenguajes de programación es la subrutina. Una subrutina es un programa de computadora autocontenido que está incorporado en un programa más grande. En cualquier punto del programa es factible invocar o llamar a la subrutina. Esto es, en ese punto, la computadora es instruida para ir y ejecutar la subrutina entera y entonces retornar al punto desde el cual el llamado tuvo lugar.

Las dos razones principales para el uso de subrutinas son la economía y la modularidad. Una subrutina permite que la misma pieza de código se pueda emplear en muchas ocasiones. Esto es importante para la economía en un esfuerzo de programación, y para hacer uso más eficiente del espacio de almacenamiento en el sistema (el programa se debe almacenar). Las subrutinas también permiten que grandes tareas de programación sean subdivididas en unidades más pequeñas. Este uso de la modularidad facilita en gran medida la tarea de programación.

El mecanismo de subrutinas involucra dos instrucciones básicas: una instrucción de llamado que bifurca desde la localidad presente a la subrutina, y así como una instrucción de retorno que regresa desde la subrutina al lugar desde el cual fue llamada. Ambas son formas de instrucciones de bifurcación.

En la figura 8.6 se ilustra el uso de subrutinas para construir un programa.

En este ejemplo, existe un programa principal que inicia en la localidad 4000. Este programa incluye un llamado a la subrutina SUB 1, que inicia en la localidad 4500. Cuando esta instrucción de llamado se encuentra, la CPU suspende la ejecución del programa principal y comienza la ejecución de SUB 1 en busca de la siguiente instrucción de la localidad 4500. Dentro de SUB 1, existen dos llamados a SUB2 en la localidad 4800. En cada caso, la ejecución de SUB 1 se suspende y SUB2 se ejecuta. La instrucción RETURN ocasiona que la CPU regrese al programa donde ocurrió la llamada y continúe la ejecución en la instrucción después de la correspondiente instrucción CALL. Este comportamiento se muestra en la figura 8.7.

Existen varios puntos dignos de mencionar:

1. Una subrutina puede ser llamada desde más de una localidad.

2. Un llamado de subrutina puede aparecer en una subrutina, lo cual permite el anidamiento de subrutinas a una profundidad arbitraria.

3. Cada llamado de subrutina se aparea con un retorno en el programa llamado

Ya que nos gustaría habilitar la llamada a una subrutina desde una variedad de puntos, la CPU debe saber cómo guardar la dirección de retorno para que ésta tome lugar de manera apropiada. Existen tres

(14)

lugares comunes para almacenar la dirección de retorno: • Registro.

• Inicio de la subrutina. • Tope del stack.

1.1.12. Programación en Lenguaje de Máquina

El lenguaje de máquina es el que realmente interpreta el hardware del computador. Está formado por el conjunto básico (SET) de instrucciones de máquina.

Una CPU puede entender y ejecutar sólo instrucciones de máquina. Tales instrucciones tan sólo son números binarios almacenados en la computadora. Si un programador desea programar de manera directa en lenguaje de máquina, entonces sería necesario introducir el programa como datos binarios, codificando todas las instrucciones combinando ceros y unos.

Por otra parte hay que recordar que un procesador es simplemente un dispositivo capaz de realizar un gran número de tareas a una gran velocidad. Si una computadora es un buen ajedrecista no es debido a la inteligencia del microprocesador sino a la del programador, que ha sabido dividir el problema en un gran número de tareas elementales que son ejecutadas por el microprocesador. Estas tareas son, esencialmente, cálculos.

Un procesador es, por lo tanto, un individuo que puede ejecutar un cierto número de cálculos sucesivos rápidamente sin preocuparse en saber para qué van a servir. Esto es el problema del programador. Veamos un ejemplo para entrar más en detalle. Se quiere que el microprocesador calcule el importe que queda sabiendo que teníamos $57 y hemos gastado $23. Para entregarle el problema al procesador, deberemos descomponerlo de una manera como la siguiente:

1 ) ponga el número 57 en la casilla 1; 2) ponga el número 23 en la casilla 2; 3) tome el contenido de la casilla 1; 4) efectúe una sustracción con el contenido de la casilla 2; 5) ponga el resultado en la casilla 3. Ahora el procesador puede efectuar las operaciones 1, 2, 3, 4 y 5 dando el resultado 34 en la casilla 3. Puede verse que es un procedimiento nada fácil.

Suele decirse que cuando se programa empleando el lenguaje absoluto de máquina la herramienta más valiosa que tiene el programador es la goma de borrar. Debe atender un enorme volumen de datos administrativos y recordar docenas de códigos numéricos para las operaciones de la computadora, tratando de no cometer errores cuando los usa.

Además, tiene que llevar la cuenta del espacio de almacenamiento que usa para las instrucciones, los datos y las áreas de trabajo y hacer el cálculo real de cualquier dirección que necesita para referencia en su programa.

Considere esta sentencia simple de BASIC:

N = 1 + J + K

Suponga que deseamos programar esta sentencia en lenguaje de máquina e inicializar I, J, y K a 2, 3 y 4, respectivamente. Esto se muestra en la figura siguiente.

El programa inicia en la localidad 101 (hexadecimal). La memoria reservada para las cuatro variables inicia en la localidad 201. El programa consiste de cuatro instrucciones:

1. Cargar el contenido de la localidad 201 en el AC (acumulador). 2. Sumar el contenido de la localidad 202 al del AC.

3. Sumar el contenido de la localidad 203 al del AC. 4. Almacenar el contenido del AC en la localidad 204. Es claro que es proceso tedioso y muy propenso a errores.

(15)

Se ilustra en la figura siguiente:

Se escribiría el programa como una serie de líneas. Cada línea contiene la dirección de una localidad de la memoria y el código hexadecimal del valor binario almacenado en esa localidad. Entonces, necesitamos un programa (traductor) que acepte esta entrada, convierta cada línea a un número binario y la almacene en la localidad especificada.

Esta es sólo una ligera mejoría. Para hacer mucho más, podemos hacer uso del nombre simbólico o mnemónico de cada instrucción. Vamos a emplear eso en vez del código de operación actual. Esto resulta en el programa simbólico mostrado en la figura siguiente

Cada línea de entrada aún representa una localidad de la memoria. Cada línea consiste de tres campos, separados por espacios. El primer campo contiene la dirección de una localidad. Para una instrucción, el segundo campo contiene el símbolo de tres letras para el código de operación. Si es una instrucción de referencia a la memoria, entonces un tercer campo contiene la dirección. Para almacenar los datos arbitrarios en una localidad, inventamos una pseudoinstrucción con el símbolo DAT. Esto es tan sólo una indicación de que el tercer campo en la línea contiene un número hexadecimal para ser almacenado en la localidad especificada en el primer campo.

Para este tipo de entrada necesitamos un programa un poco más complejo. El programa acepta cada línea de entrada, genera un número binario basado en el segundo y tercer (si existe) campo, y lo almacena en la localidad especificada por el primer campo.

El uso de un programa simbólico hace la vida mucho más fácil pero es aún torpe. En particular, debemos dar una dirección absoluta para cada palabra. Esto significa que el programa y los datos pueden ser cargados únicamente en un lugar en la memoria, y debemos conocer ese lugar en cualquier momento.

En cualquiera de estas tres variantes de la programación en lenguaje de máquina, lo peor es que la revisión de un programa (cosa que ocurría con mucha frecuencia, como actualmente), lo cual en general implica agregar o borrar una o más líneas, cambiará las direcciones de todas las palabras subsecuentes. Todos estos detalles suponían muchos errores y demasiado tiempo dedicado a verificar, calcular, llevar tablas y otros trabajos de tipo administrativo.

Referencias

Documento similar

Porque, cuando decimos que no podemos escrutar la referencia del lenguaje aborigen en términos absolutos, sino sólo en términos relativos a nuestro lenguaje,

Si para Wittgenstein «las palabras de este lenguaje deben referirse a lo que sólo puede ser conocido por el hablante, a sus sensaciones inmediatas, privadas –de modo que– otro no

Puede, en cambio, chocar con la razón.» Toda esta serie de cosas, nos indica, acertadamente, Hermann Fu ner, hacen que el estudio 1 del Gobierno sea un deber y una fascinación,

Aunque ambas guerras responden a contextos distintos, y el escenario descrito por Gruzinski incide en el papel de la imagen (o la destrucción de las mismas) como elemento

Nota 1 a la entrada: Las partes de un sistema de gestión que pueden estar involucradas en una auditoría combinada pueden identificarse por las normas de sistemas de

− La función de prohibición de llamadas está activada (sólo se puede llamar a números de teléfono guardados en la unidad como números de emergencia).. L No abra la unidad base,

Volviendo a la jurisprudencia del Tribunal de Justicia, conviene recor- dar que, con el tiempo, este órgano se vio en la necesidad de determinar si los actos de los Estados

Salían débiles no sólo por los largos años de prisión, y lo que conllevaba en cuanto a enfermedades y dolencias de diverso tipo, resultado tanto de los interrogatorios como de las