Informe Instrucciones
Informe Instrucciones
2020
FACULTAD DE INGENIERÍA DE
SISTEMAS Y MECÁNICA ELÉCTRICA
1. Introducción
En este capítulo estudiaremos los distintos tipos de instrucciones de que constan los
juegos de instrucciones de los ordenadores. Se verán con especial interés las instrucciones
de control del flujo del programa (bifurcaciones, bucles, procedimientos, etc.).
También se estudiarán los principios y filosofía de diseño de los ordenadores con conjunto
reducido de instrucciones (RISC).
Los conjuntos de instrucciones de las máquinas deben tender a poseer una serie de propiedades,
bastante ideales e imprecisas, que pueden resumirse en las siguientes:
Los juegos de instrucciones también tienen que ser eficientes, esto significa que las
funciones más necesarias deben poder realizarse usando pocas instrucciones.
El conjunto de instrucciones de una máquina debe ser regular, es decir debe ser simétrico
(por ejemplo, si existe una instrucción de desplazamiento a la izquierda, debe haber otra
de desplazamiento a la derecha, etc.) y ortogonal, es decir, deben poder combinarse, en
la medida de lo posible, todas las operaciones con todos los tipos de datos y modos de
direccionamiento.
3. Tipos de instrucciones
Una máquina puede llegar a funcionar con un juego de instrucciones muy limitado
(recuérdese, por ejemplo, la máquina de Turing que sólo tiene 4 instrucciones, incluso se
han diseñado máquinas teóricas con menos instrucciones), esto simplificaría mucho los
circuitos de la máquina. Sin embargo, un conjunto de instrucciones demasiado simplificado
origina, como consecuencia, unos programas demasiado complejos e ineficientes. Es
necesario encontrar un compromiso entre la simplicidad del hardware y del software. Un
mínimo para llegar a ese compromiso se consigue con los tipos de instrucciones siguientes:
En los apartados siguientes iremos viendo con detalle algunos de estos tipos de
instrucciones.
FACULTAD DE INGENIERÍA DE
SISTEMAS Y MECÁNICA ELÉCTRICA
Si bien es cierto que el conjunto de instrucciones debe de cumplir unos mínimos para
conseguir una mínima eficiencia en los programas, también se verá que ésta no se aumenta
indefinidamente al incrementar el número de instrucciones de la máquina.
En general, el dato a transferir podríamos definirlo como una terna con las siguientes
componentes:
Dirección
Tipo
Valor
Normalmente la componente del dato que se transfiere es el valor pero existen instrucciones
especiales para transferir las demás componentes, en especial la dirección. La extracción de la
dirección de un dato se hace necesaria para facilitar la relocalización de los programas. Para
aplicar muchos modos de direccionamiento (indexados, autoindexados, etc.) es necesaria la
transferencia de una dirección a un registro. En muchos ordenadores esta dirección no se
conoce a la hora de compilar el programa (precisamente porque el programa es relocalizable),
por tanto, son necesarias instrucciones que calculen la dirección de un dato para transferirla a
un registro, actualmente la mayoría de las máquinas poseen este tipo de instrucción bajo el
nombre de move address.
FACULTAD DE INGENIERÍA DE
SISTEMAS Y MECÁNICA ELÉCTRICA
Dependiendo del nivel de ortogonalidad de la máquina, podemos tener más o menos instrucciones
de transferencia. Por ejemplo el IBM-370 tiene más de 20 instrucciones de transferencia por su
falta de ortogonalidad; sin embargo, el PDP-11 sólo tiene 2 para datos enteros (MOV y MOVB) y 4
más para datos en punto flotante, ya que en las instrucciones de punto flotante se prescindió
bastante de la ortogonalidad por falta de espacio para la codificación. El MC68000 está en un
punto intermedio, tiene varios formatos distintos dependiendo del direccionamiento utilizado. El
Z-80 en cuanto a instrucciones de transferencia entre registros es bastante ortogonal, pero esa
ortogonalidad se pierde cuando se trata de otros direccionamientos.
En primer lugar, veremos cómo se emplean las instrucciones lógicas para realizar operaciones de
bit individual. La utilidad de estas operaciones es muy variada, probablemente su aplicación más
importante sea empaquetar en un byte varias variables booleanas, ocupando cada una de ellas un
solo bit.
Para analizar un determinado bit se debe tener en cuenta las siguientes propiedades de la
operación AND (. o ∧):
0 ... 0 1
0 x ... x a x ... x ...
0 ... 0 a 0 ... Máscara
0
Dato
Resultado
Análisis de un bit.
Dato
Puesta a 0 de un bit.
FACULTAD DE INGENIERÍA DE
SISTEMAS Y MECÁNICA ELÉCTRICA
1 ∧ x = x, ∀x
0 ∧ x = 0, ∀x
Para aplicarlas, se realiza una operación AND del dato que se quiere analizar con una máscara que
tenga un 1 en el lugar cuyo bit se quiere probar con el resto de la máscara a 0. El resultado de la
operación será 0 si el bit en cuestión es 0, o distinto de 0 si el bit que se quiere probar es 1; por
tanto, el resultado de la prueba quedará en el flag Z en forma complementada como se muestra
en la figura 4.1. Muchas máquinas tienen esta operación bajo el nombre de BIT TEST.
Para poner a 0 un bit de un dato, se aprovechan las mismas propiedades del operador AND.
Para aplicarlas, se construye una máscara que tenga a 1 todos los bits excepto el
correspondiente al lugar que se quiere borrar que se pone a 0 y se hace un AND de esta
máscara con el dato que se quiere tratar; como resultado de la operación tendremos el mismo
dato pero con el bit puesto a cero. El proceso se muestra en la figura 4.2. Esta operación puede
realizarse con varios bits a la vez, basta poner en la máscara 0 todos los bits que se quieran
borrar. Muchos ordenadores poseen esta instrucción con el nombre de BIT CLEAR o BIT RESET.
Para poner un bit a 1 en un dato, aplicaremos las propiedades de la operación OR (+ o ∨):
1 ∨ x = x, ∀x
2 ∨ x = 1, ∀x
En este caso, se construye una máscara con un 1 en el lugar del bit que se quiera poner a 1 y 0
en el resto. Se realiza la operación OR entre esta máscara y el dato y el resultado será el mismo
dato pero con el bit en cuestión puesto a 1 (figura 4.3). Esta operación puede realizarse con
varios bits a la vez poniendo a 1 en la máscara todos los bits que se quieran poner a 1. Muchos
ordenadores tienen esta operación con el nombre de BIT SET.
Para complementar bits en un dato aplicaremos las propiedades de la operación OR exclusivo o
XOR (⊕):
Para ello se construye una máscara con un 1 en el bit que se quiere complementar y 0 en el resto
de las posiciones. Después de hacer un XOR de la máscara con el dato se tendrá el mismo dato con
el bit complementado (figura 4.4). Esta operación también se puede hacer con varios bits a la vez
poniendo a 1 en la máscara todos los bits que se quieran complementar. Algunas máquinas tienen
esta operación en su juego de instrucciones como BIT CHANGE.
Otra utilidad de la operación XOR es la determinación de los bits diferentes que hay entre dos
datos, para ello se hace un XOR entre ellos y los lugares que queden a 1 son los bits diferentes
entre los dos datos. Esto es debido a que la tabla de verdad de XOR es la complementaria a la de la
igualdad entre dos bits. Esta propiedad también es útil para determinar cuándo dos datos son
iguales en todos sus bits: en este caso el OR exclusivo entre ellos nos dará 0 y, en caso contrario,
tendremos un número no nulo; este resultado puede luego ser analizado mediante el flag Z. De
FACULTAD DE INGENIERÍA DE
SISTEMAS Y MECÁNICA ELÉCTRICA
aquí también se puede deducir que haciendo la operación XOR de un operando consigo mismo el
resultado da 0. Esta es una forma muy rápida de poner a 0 los registros en algunos procesadores.
Otra propiedad muy útil del operador XOR es que si se opera dos veces con el mismo operando se
regenera el dato inicial, es decir,
(x ⊕ y) ⊕ y = x,∀x,y[4.1]
Esta propiedad es útil en aplicaciones gráficas para mover cursores por pantalla.
Una utilidad directa de las operaciones lógicas es el manejo de conjuntos tal y como se hace en el
lenguaje Pascal. Supongamos el conjunto universal,
U = {x0,x1,x2,...,xn−1}
an−1an−2 ...a1a0
en que el valor del bit i indicará la pertenencia del elemento xi al conjunto A. En otras palabras,
⎧
1 si x
⎪
ai = ⎨ i∈ A
⎪
⎩0 si xi ∈/ A
A partir de lo anterior resulta bastante evidente que la unión e intersección de dos conjuntos
vendrá representada respectivamente por las operaciones OR y AND sobre sus cadenas de bits. De
la misma forma el complemento de un conjunto se representa mediante la negación de la cadena
de bits que le corresponde.
A ⊂ B ⇔ A ∪ B = B, A⊂B⇔A∩B=A o A⊂B⇔A∩B=∅
di ← di+k
donde di representa a cada uno de los bits del operando y k es el número de lugares que se
desplaza. En función del signo de k, el desplazamiento puede ser a la izquierda (k negativo) o a la
derecha (k positivo), donde se está suponiendo que los bits se numeran de derecha a izquierda.
Los desplazamientos pueden ser de tres tipos: lógicos, aritméticos y rotaciones o desplazamientos
circulares dependiendo del bit entrante. El desplazamiento lógico a la derecha difiere del
aritmético en que el bit entrante es, en éste último, el mismo bit de mayor peso (bit de R −→ x y
z t
S −→ 0 0 0 a
x y z t ←− R
∧1 1 0 1 ←− Máscara
desplazado
Empaquetamiento de caracteres.
x y z t ←− R
FACULTAD DE INGENIERÍA DE
SISTEMAS Y MECÁNICA ELÉCTRICA
Máscara
0 0 0 y ←−Desplazado
Desempaquetamiento de caracteres.
signo), mientras que en aquél es siempre un 0; Así se consigue que los desplazamientos
aritméticos sean equivalentes a multiplicaciones (izquierda) y divisiones (derecha) por 2. En las
rotaciones el valor del bit entrante es el mismo que el del saliente.
Todas las instrucciones que modifican el flujo de la ejecución manejan el contador de programa.
También, si la modificación del flujo de instrucciones se hace de forma condicional, esta condición
vendrá dada por los bits de estado (N, Z, V y C). Vemos, por tanto, que para las instrucciones de
control de flujo hay dos variables fundamentales: el contador de programa y los bits de condición.
Bifurcación condicional.
(Condicion´ )
PC ← Direccion de bifurcaci´ on´ ;
PC + +;
En esta representación, Condicion´ simboliza una expresión que involucra a uno o más bits de
condición. Los bits de condición más usuales son:
también cabrá. Estas observaciones nos llevan a dar valor al bit V por una de las siguientes
ecuaciones lógicas:
[4.2]
Tabla 4.1. Tabla de verdad de la suma para el bit de signo.
V = xn−1yn−1cn−1 + xn−1yn−1cn−1
Esto podría ser una forma de determinar por hardware la existencia de desbordamiento; sin
embargo, a la vista de la tabla, puede deducirse que se produce desbordamiento si la llevada
entrante al bit de signo es diferente de la saliente, es decir:
V = cn−1 ⊕ cn
Esta ecuación es más simple que la anterior para detectar el desbordamiento por hardware. Este
método tiene, además, la ventaja de que también es válida para otras operaciones como, por
ejemplo, los desplazamientos.
En cuanto al posicionamiento de los demás bits de estado, es bastante simple: N y C se detectan
directamente a partir de la unidad aritmética y Z se consigue mediante una puerta NOR cuyas
entradas sean todos los bits del resultado.
FACULTAD DE INGENIERÍA DE
SISTEMAS Y MECÁNICA ELÉCTRICA
Los conjuntos de instrucciones de las máquinas suelen facilitar instrucciones auxiliares para
posicionar los bits de estado sin alterar el resto de los registros de la máquina, estas instrucciones
son (compara con 0) y (compara dos números restándolos). Suele ser conveniente utilizar estas
instrucciones inmediatamente antes de las instrucciones de bifurcación condicional, ya que si,
entre las instrucciones de comparación y las de bifurcación, se insertan otras instrucciones los bits
de estado cambiarán y la comparación quedará sin efecto. Hay que señalar que en algunos
procesadores RISC (por ejemplo, SPARC) la mayoría de las instrucciones
Iteraciones.
no cambian los flags, sólo los cambian instrucciones específicas. Esto es así porque, en este tipo de
procesadores, es frecuente tener que cambiar el orden de ejecución de las instrucciones. En estos
procesadores no es necesario que las instrucciones de comparación estén inmediatamente antes
que las de bifurcación.
(Condicion´ )
PC ← PC + Desplazamiento;
PC + +;
4.2. Iteraciones
Es muy frecuente que se necesite ejecutar un grupo de instrucciones cierto número de veces
(figura 4.10), por ello, la mayoría de las máquinas tienen instrucciones específicas para ello. Un
método para realizar iteraciones se basa en poner un valor inicial en un registro para luego pasar a
ejecutar el código de la iteración, la última instrucción del bucle actualizará el valor del registro y
comprobará si se cumple la condición de terminación, si es así se ejecutará la siguiente instrucción
y si no se comienza una nueva iteración. Esta forma de actuar se caracteriza por hacer la
comprobación de la condición al final del bucle por lo que éste se ejecuta una vez como mínimo en
todos los casos, incluso aunque la condición de terminación se cumpla antes de
Llamada a un procedimiento.
entrar. Se puede analizar también la condición al principio del bucle pero esto emplea algunas
instrucciones más. En lenguaje máquina lo que se suele hacer es poner en un registro el número
de veces que se debe iterar, antes del comienzo del bucle. Al final del bucle se decrementa el
registro y se compara con 0: si es 0 se continúa con la siguiente instrucción y si no, se ejecuta otra
vez el bucle desde el comienzo. Normalmente, la mayoría de las máquinas tienen alguna
instrucción para realizar estas dos últimas operaciones con una sola instrucción (
en el PDP-11, y otras en el VAX, en el Z-80, en el 8086, etc.). Muchas máquinas, además de las
instrucciones para implementar bucles, tienen también instrucciones para realizar operaciones
con bloques y cadenas, lo que evita la ejecución de muchos bucles.
4.3. Procedimientos
La técnica principal para estructurar programas es el uso de procedimientos que, según el
lenguaje, también se llaman subprogramas, subrutinas o funciones. Desde el punto de vista de la
máquina, una llamada a un procedimiento altera el flujo de instrucciones como un salto con la
diferencia importante de que el procedimiento devuelve el control a la instrucción siguiente a la
llamada una vez que se ha concluido (figura 4.11). Sin embargo, desde el punto de vista de un
programador de lenguaje de alto nivel, la llamada a un procedimiento puede considerarse como la
FACULTAD DE INGENIERÍA DE
SISTEMAS Y MECÁNICA ELÉCTRICA
ejecución de una instrucción de otro nivel; desde este punto de vista, una llamada a un
procedimiento puede considerarse como una simple instrucción, aunque pueda ser bastante
complicada.
Definiremos un procedimiento como una secuencia de instrucciones que realiza una tarea y a
la que se puede llamar desde diversos puntos del programa.
Uno de los problemas planteados por los procedimientos es el retorno a la siguiente instrucción a
la llamada: debe tenerse previsto un lugar seguro para guardar la dirección de la siguiente
instrucción (dirección de retorno) ya que, al pasar el control al procedimiento, esta dirección
desaparece del PC. Puede haber diversas soluciones: la más elemental es reservar un registro o
dirección de memoria para guardar la dirección de retorno; este método tiene el inconveniente de
que, si el procedimiento llamara a otro (anidamiento), esta llamada haría que se perdiera la
dirección de retorno de la primera. Una mejora de este método consiste en que la instrucción de
llamada a procedimiento almacene la dirección de retorno en la primera palabra del
procedimiento, estando la primera instrucción ejecutable en la palabra siguiente; el procedimiento
podría retornar mediante una instrucción de salto con direccionamiento indirecto a la primera
palabra del procedimiento. Con este método, el procedimiento podría llamar a otros, ya que cada
uno tiene sitio para una dirección de retorno. Si el procedimiento se llamara a sí mismo
(recursión), este esquema fallaría ya que la segunda dirección de retorno destruiría a la primera.
La recursión es una característica muy importante por lo que este método no se emplea. Tampoco
sería válido este esquema para la recursión indirecta, es decir en el caso de que un procedimiento
A llamara a otro B y éste llamara a su vez a A.
La mejor solución al problema de la dirección de retorno, y por otra parte la más frecuente, es
guardar la dirección de retorno en una pila antes de efectuar el salto al procedimiento; cuando el
procedimiento concluya sacará la dirección de retorno de la pila y la pondrá en el registro
contador de programa. Este método no plantea ningún problema a la hora de la recursión ya que
salva la dirección de retorno por encima de las anteriores, evitando su destrucción. Un problema
similar se plantea para guardar el estado del procesador que está materializado en sus registros;
éstos también deben guardarse en la pila, pero existen dos variantes en cuanto al momento de
hacerlo: puede ser el programa que llama el que guarde los registros (caller-saving) o puede ser el
procedimiento llamado quien lo haga (called-saving). Existe una solución intermedia
proporcionada por algunas arquitecturas en que no hay que añadir instrucciones para guardar los
registros puesto que la misma instrucción de llamada lo hace, este es el caso de la arquitectura
VAX en que la primera palabra del procedimiento indica los registros que el procedimiento usa y la
instrucción de llamada los guarda.
y estos métodos pueden emplearse). En los procesadores con ventanas de registros sí que pueden
almacenarse los parámetros y variables del procedimiento en registros, aunque sean recursivos, ya
que las ventanas hacen la función de la pila.
1. El programa que llama al procedimiento reserva en la pila lugar para los argumentos.
Debe observarse que el orden de esta secuencia de acciones no puede alterarse y este orden es el
que causa el aspecto de la trama de pila (figura 4.12). Hay que tener también en cuenta que en
algún momento también habrá que guardar los registros del procesador (esto no está
representado en la figura 4.12).
La estructuración de los programas exige muchas llamadas a procedimientos lo que hace de ellas
uno de los puntos más críticos a la hora de mejorar el rendimiento de la máquina; esto hace que
sea conveniente que las operaciones anteriores se realicen de la forma más eficiente posible.
Una alternativa para mejorar la velocidad en las llamadas a procedimientos consiste en expandir el
código del procedimiento en el lugar donde ocurre la llamada. Este método, que es empleado a
veces por algunos compiladores, se denomina integración del procedimiento o procedimiento en
línea y puede usarse allí donde el tiempo de ejecución sea crítico. Sin embargo el método tiene el
inconveniente de que aumenta el espacio de memoria necesario para el código, por ello puede ser
una buena alternativa para procedimientos cortos.
Fig. 4.13. Flujo de instrucciones en una llamada a procedimiento (a) y entre corrutinas (b).
4.4. Corrutinas
llamado, éste comienza a ejecutarse desde el principio; sin embargo, cuando se produce el
retorno, la ejecución del procedimiento que hizo la llamada sigue en la instrucción siguiente. Si se
llamara más veces al procedimiento, éste comenzaría a ejecutarse nuevamente desde el principio
(situación mostrada en la figura 4.13 (a)). La asimetría se acentúa si analizamos el proceso llevado
a cabo a nivel máquina: en el momento de la llamada el valor del contador de programa pasa a la
pila y cuando el procedimiento retorna el proceso es justamente el contrario; además las
instrucciones que realizan ambas funciones son distintas. En algunas ocasiones es útil tener dos
procedimientos que se llamen el uno al otro como un procedimiento (sin retorno) sin comenzar la
ejecución desde el principio (figura 4.13 (b)). Dos procedimientos que llamen uno a otro de esta
forma se denominan corrutinas (figura 4.14). La forma de llevar a cabo en la práctica una llamada a
una corrutina es intercambiar los contenidos del contador de programa y de la cima de pila, sin
variar el apuntador de pila, ya que se saca un dato de la pila para meter otro; de esta forma se
intercambia el control entre las dos rutinas.
Los desvíos normalmente se deben a condiciones importantes aunque no muy frecuentes. El caso
más usual es la condición de desbordamiento en operaciones aritméticas (TRAPV). Si después de
una operación se produce desbordamiento el microprograma lo detecta y bifurca a una rutina de
tratamiento de ese error. Usar esta técnica es más rápido que hacer una bifurcación explícita en
función de V después de cada instrucción aritmética ya que la dirección de bifurcación es siempre
la misma y el análisis de la condición, en el caso del desvío, lo realiza automáticamente el
microprograma después de cada instrucción aritmética.
consiste en numerar todas las causas de desvío y tener una tabla en memoria con todas las
direcciones de las rutinas de tratamiento; estas direcciones se denominan vectores y a la tabla
anterior se la denomina tabla de vectores de excepción. Si la tabla de vectores comienza en una
dirección A y cada vector ocupa t palabras, la dirección correspondiente a la excepción n vendrá
dada por:
4.6. Interrupciones
Las interrupciones son llamadas automáticas a procedimiento no debidas al programa sino a
una causa exterior.
La diferencia entre las interrupciones y los desvíos es que éstos son provocados por el mismo
programa mientras que las interrupciones son provocadas por causas externas de forma
totalmente asíncrona.
Normalmente las causas de interrupción están relacionadas con las operaciones de entrada y
salida. Una interrupción detiene el programa en curso y transfiere el control al procedimiento de
tratamiento de la interrupción denominado rutina de servicio de interrupción; cuando esta rutina
concluye se debe devolver el control al proceso interrumpido que debe continuar su ejecución en
el mismo estado en el que estaba cuando se produjo la interrupción. Esto significa que se deben
salvar el estado del procesador (es decir el contenido de los registros) antes de comenzar la
ejecución de la rutina de servicio y restaurarse al finalizar ésta.
Las interrupciones son necesarias cuando las entradas o salidas pueden desarrollarse en paralelo
con la ejecución de instrucciones en el procesador. Esto normalmente es así debido a que,
mientras un dispositivo de entrada/salida efectúa una sola operación, el procesador puede
ejecutar muchas instrucciones convencionales. Los sistemas de interrupciones permiten que la
CPU funcione concurrentemente con los dispositivos de entrada y salida, siendo las interrupciones
el sistema de comunicaciones entre ambos procesos para que el procesador sepa cuando el
dispositivo de entrada/salida ha concluido.
Instrucciones de bifurcación
Existe una instrucción de salto incondicional: La diferencia entre esta instrucción y las
bifurcaciones incondicionales reside en que en se puede usar cualquier modo de direccionamiento
para la dirección de destino y sin embargo en las instrucciones de bifurcación esa dirección
especifica siempre mediante un desplazamiento.
a las subrutinas se les llama a veces subrutinas internas ya que se consideran parte del programa
que las llama.
Las instrucciones para llamar a una subrutina son y que se diferencian por el tamaño del
desplazamiento (1 o 2 bytes). También existe la instrucción (return from subroutine, retorno de
subrutina) que recoge la dirección guardada anteriormente en la pila, por o , y bifurca a ella.
En cuanto a las llamadas a procedimientos, en el VAX hay dos instrucciones para efectuarlas: y . La
diferencia entre ambas radica en la forma de pasar los argumentos:
mientras que en se pasan a través de la pila con el fin de facilitar la recursión, en los
argumentos se pueden pasar en otra zona de memoria.
Número de argumentos
Lista de etiquetas de los argumentos
Puede ocurrir que la dirección de alguno de los argumentos no se conozca al escribir el programa
(por ejemplo, porque sea producto de un cálculo), en este caso, se le pone una etiqueta
independiente a esa dirección y, en ejecución, una vez conocido el valor de la dirección se
transfiere mediante una instrucción . Es necesario comentar que no es conveniente poner en lugar
de ya que éste genera código independiente de la posición y aquél no.
1. Alinear la pila a doble palabra. Esto se consigue poniendo a 0 los dos últimos bits
delapuntador de pila. El valor anterior de estos bits se guarda en un almacenamiento
intermedio.
4. Guardar en la pila la PSW (palabra de estado), la máscara de registros salvados, los dos bits
menos significativos del apuntador de pila (SP), antes de alinearlo, y un bit que indica si la
llamada se ha efectuado con (0) o (1).
FACULTAD DE INGENIERÍA DE
SISTEMAS Y MECÁNICA ELÉCTRICA
Locales: son nombres de variable que sólo se reconocen en una parte del programa, normalmente
uno o más procedimientos.
El ámbito de visibilidad de los nombres de las variables es un concepto que maneja solamente el
compilador, concretamente en la tabla de símbolos haciendo que un símbolo sólo sea reconocido
en su ámbito de validez, y no transciende al código ejecutable del programa. Sin embargo, hay
otro concepto similar que es el de vida de la variable que se define como el tiempo que transcurre
desde que una variable se comienza a usar hasta que se la menciona por última vez o, lo que es lo
mismo, el tiempo en que es necesario que la variable tenga espacio reservado en memoria. Según
este criterio las variables pueden ser de tres tipos:
Permanentes: son variables cuya vida es el tiempo total de ejecución del programa. Estas variables
se almacenan en una zona de memoria accesible desde cualquier punto del programa
denominada área global. Se accede a este área con direccionamiento relativo o
direccionamiento por base y desplazamiento, tomando como base un registro que apunte a
la dirección de comienzo del programa. Un tipo especial de variables de este tipo son las
variables estáticas que son variables permanentes cuyo nombre es local a uno o varios
procedimientos. Las variables estáticas conservan su valor aunque el procedimiento retorne
ya que también se almacenan en el área global.
Automáticas: son variables cuya vida es, como máximo, el tiempo de ejecución del procedimiento
donde se han definido. Los nombres de estas variables son locales a ese procedimiento. Los
parámetros de los procedimientos se consideran variables de este tipo. Las variables
automáticas se almacenan en la pila, de esta forma su espacio se reserva cada vez que se
llama al procedimiento y es liberado cuando retorna. El acceso a estas variables se realiza
mediante direccionamiento indexado al apuntador de trama de procedimiento o frame
pointer. En el caso de los parámetros pasados por referencia o de las variables automáticas
que representen apuntadores, se aplicará el mismo direccionamiento pero en su versión
indirecta.
Dinámicas: son variables cuyo espacio se reserva y libera durante la ejecución de un procedimiento.
Este tipo de variables se emplea para almacenar estructuras de datos que pueden crecer o
decrecer en ejecución tales como listas enlazadas, árboles, etc. El lugar de almacenamiento
de estas variables se denomina área dinámica o heap y puede no ser contiguo ya que el
sistema operativo va concediendo memoria según el programa la va solicitando y no
siempre la memoria está disponible de forma contigua.
FACULTAD DE INGENIERÍA DE
SISTEMAS Y MECÁNICA ELÉCTRICA
Las variables escalares de cualquiera de los tipos anteriores pueden almacenarse en registros del
procesador, salvo las variables dinámicas y las variables automáticas en los procedimientos
recursivos, incluso en este caso también es posible en procesadores RISC con ventanas de
registros.
En estas circunstancias el compilador del lenguaje de alto nivel debe producir una secuencia de
instrucciones para calcular la dirección del componente deseado.
El acceso a las estructuras de datos más simples (un vector unidimensional, una estructura o
record, etc) puede requerir un número muy pequeño de instrucciones si se utiliza la potencia de
los modos de direccionamiento. Por ejemplo, supongamos que queremos acceder al elemento k
de un vector cuyos elementos ocupan 2n bytes cada uno y están situados consecutivamente en
memoria; si el registro Rx contiene la dirección del primer elemento y Ry contiene el índice (k), la
dirección del elemento k vendrá dada por
Rx + [Ry << n]
Si la dirección del primer elemento viene dada por un desplazamiento respecto a otro registro,
normalmente el frame pointer o el propio PC, la expresión anterior, llamando genéricamente Rref al
registro de referencia, se convierte en:
Se puede usar direccionamiento indexado para efectuar la suma, lo que nos eliminaría una
operación. En el VAX el direccionamiento indexado es tan potente que realiza toda la operación: Si
A es la dirección del primer elemento, especificada habitualmente mediante direccionamiento
relativo, el elemento k se especifica mediante A[Ry] ya que el direccionamiento indexado del VAX
multiplica el índice por el tamaño del operando.
También puede darse el caso de que la dirección del primer elemento venga dada por un
apuntador, en cuyo caso hay que añadir un nivel de indirección, con lo que queda:
En el VAX el direccionamiento indexado soporta cualquier modo como índice, por lo que también
se puede aplicar sobre direccionamiento indirecto relativo, en este caso, el elementok del vector
se especifica mediante .
En esta expresión, Rref + desplazamientoprimer elemento, representa la dirección del primer elemento y
desplazamientocampo, es el desplazamiento del campo deseado respecto al primer elemento.
Esto puede hacerse así, o, si el compilador está optimizado, éste puede sumar ambos
desplazamientos y utilizar direccionamiento indexado simple. No puede hacerse esta última
simplificación en el caso de un apuntador a un (operador -> del lenguaje C) ya que, en este caso,
los dos primeros términos están afectados de una indirección y la dirección del campo buscado es:
Para este caso se adecúa muy bien el direccionamiento indirecto postindexado del
microprocesador MC68020.
Una técnica similar puede usarse si una estructura es un campo de otra. En este caso, para
obtener la dirección de uno de los campos de la estructura más interna, es necesario sumar tres
desplazamientos: el desplazamiento del primer campo de la estructura principal respecto al
registro de referencia, el desplazamiento del primer campo de la estructura interna respecto al
primer campo de la exterior y el desplazamiento del campo solicitado respecto al primer campo de
la estructura interna:
Rref + desplprimer elemento struct principal + desplprimer elemento struct interno + desplcampo
De la misma forma que en el caso anterior, con un compilador optimizado, se pueden sumar todos
los desplazamientos en compilación y aplicar direccionamiento indexado simple. Si, por ejemplo, a
la estructura externa se accede a través de un apuntador, la dirección anterior se obtendrá
mediante la expresión:
(Rref + desplprimer elemento struct principal) + desplprimer elemento struct interno + desplcampo
De forma similar se puede actuar si alguno de los otros campos es un apuntador. En este caso
también pueden resultar interesantes los direccionamientos indirectos preindexado y
postindexado incorporados al microprocesador MC68020.
FACULTAD DE INGENIERÍA DE
SISTEMAS Y MECÁNICA ELÉCTRICA
De una forma análoga si analizamos el código máquina de los programas también se llega a una
conclusión similar respecto a los modos de direccionamiento utilizados: entre los
direccionamientos inmediato y por desplazamiento se cubre más de un 80% de las referencias a
memoria de la mayoría de los programas (si bien esto depende mucho del compilador utilizado).
Quiere decirse con todo esto que los programas escritos en lenguajes de alto nivel no usan apenas
las instrucciones más complejas, aunque en principio pudiera pensarse los contrario. Por ello cabe
aquí aplicar el principio de diseño "mejorar en lo posible los casos más frecuentes". También hay
que tener en cuenta que muchas máquinas están orientadas a un solo tipo de aplicaciones
(gestión, diseño asistido, cálculo científico, etc.), esto hace que se puedan encontrar las
instrucciones más necesarias para ese tipo de aplicaciones concretas.
Así se llega a los pasos de diseño de las máquinas RISC (Reduced Instruction Set Computer:
Computadores con conjunto reducido de instrucciones):
Analizar las aplicaciones que van a funcionar en esa máquina para encontrar las operaciones
más necesarias para esas aplicaciones (operaciones clave).
Diseñar el conjunto de registros y las comunicaciones entre ellos más apropiados para la
ejecución de las operaciones clave.
Diseñar las instrucciones y modos de direccionamiento para realizar las operaciones clave en
el conjunto de registros anteriormente diseñado.
FACULTAD DE INGENIERÍA DE
SISTEMAS Y MECÁNICA ELÉCTRICA
Estos pasos de diseño tienen la estructura del llamado diseño descendente, esto quiere decir que
se analizan las especificaciones de lo que se necesita y luego se va descendiendo hacia los
elementos de más bajo nivel necesarios para cumplir esas especificaciones.
Ahora introduciremos el concepto de ciclo máquina que es el tiempo necesario para extraer los
operandos de los registros, llevarlos a la ALU para operar con ellos y depositar el resultado en un
registro. Este tiempo de ciclo debe ser tan pequeño como sea posible, esto se consigue
sacrificando todo lo que incremente la duración del ciclo, por ejemplo, deben eliminarse las
instrucciones que tarden en ejecutarse más de un ciclo.
Los principios de diseño de los ordenadores con arquitectura RISC son los siguientes:
Gran cantidad de registros en el procesador y uso de ventanas de registros para las variables
de los procedimientos.
Estas características se resumen en la tabla 4.4 comparándolas con las de las arquitecturas CISC
(Complex Instruction Set Computers: computadores con conjunto de instrucciones complejo).
Los registros globales, que sirven para almacenar las variables globales y son los mismos
para todas las ventanas.
Los registros para los parámetros de llamada del procedimiento que llamó al actual.
Los registros para los parámetros de llamada del procedimiento actual a otros
procedimientos.
Las ventanas se solapan porque los parámetros de llamada de un procedimiento son los
parámetros con que le llamó el procedimiento anterior y también porque las variables globales
son las mismas para todas las ventanas. El conjunto de ventanas actúa como un buffer circular con
dos apuntadores: el de la ventana actual (CWP: current window pointer) y el de la primera ventana
utilizada (OWP: overflow window pointer), si CWP = OWP significa que todas las ventanas están
ocupadas lo que provoca un desvío que salvaría en memoria la ventana correspondiente al
procedimiento con nivel de anidamiento más profundo. El tamaño del banco de registros debe
calcularse para que este desvío se produzca pocas veces; en general sólo debe producirse en casos
de recursividad fuerte. Este mecanismo además de hacer más ágil el paso de parámetros de un
procedimiento a otro también consigue que el número de bits necesarios en los campos de
registro sea menor porque cada procedimiento sólo necesita acceder a los registros de su ventana.
En el caso de la figura 4.19 el número de bits necesarios para direccionar un registro será log2(4n)
siendo n el número de registros de cada una de las zonas de una ventana.
Una forma de optimizar el uso de los registros es tener en cuenta el concepto de vida de las
variables introducido en el apartado 4.5.1. Si el compilador realiza un estudio detallado de la vida
de todas las variables de los procedimientos, puede utilizar el mismo registro para varias variables
cuyo periodo de vida no se superponga.
Otra de las características de los procesadores RISC es la que le da nombre, esto no sólo debe
entenderse como un conjunto de pocas y sencillas instrucciones sino también como un conjunto
especializado de instrucciones para un tipo de aplicación concreto (estaciones de tra-bajo,
sistemas expertos, etc.). También se ha de mencionar que esta reducción no sólo se refiere a las
instrucciones sino también, e incluso con más énfasis, a los modos de direccionamiento. Esto es
debido a que los modos de direccionamiento que exijan cálculos de direcciones deben eliminarse
debido a que introducirían ciclos máquina adicionales.
FACULTAD DE INGENIERÍA DE
SISTEMAS Y MECÁNICA ELÉCTRICA
Bibliografía y referencias
Baase, S. 1983. VAX-11 Assembly Language Programming. Prentice-Hall.
Hennessy, J.L., & Patterson, D.A. 2003. Computer Architecture. A Quantitative Approach. 3 edn.
Morgan Kaufmann Publishers.
Heudin, J.C., & Panetto, C. 1990. Les architectures RISC. Dunod Informatique.