Los modos de direccionamiento son un aspecto de la arquitectura del conjunto de instrucciones en la mayoría de los diseños de unidades centrales de procesamiento (CPU). Los diversos modos de direccionamiento que se definen en una arquitectura de conjunto de instrucciones determinada definen cómo las instrucciones de lenguaje de máquina en esa arquitectura identifican el o los operandos de cada instrucción. Un modo de direccionamiento especifica cómo calcular la dirección de memoria efectiva de un operando mediante el uso de información almacenada en registros o constantes contenidas dentro de una instrucción de máquina o en otro lugar.
En programación informática , los modos de direccionamiento son de interés principalmente para quienes escriben en lenguajes ensambladores y para los programadores de compiladores . Para un concepto relacionado, véase el conjunto de instrucciones ortogonales , que trata de la capacidad de cualquier instrucción para utilizar cualquier modo de direccionamiento.
No existen nombres generalmente aceptados para los modos de direccionamiento: diferentes autores y fabricantes de computadoras pueden dar nombres diferentes al mismo modo de direccionamiento, o los mismos nombres a diferentes modos de direccionamiento. Además, un modo de direccionamiento que, en una arquitectura dada, se trata como un modo de direccionamiento único puede representar una funcionalidad que, en otra arquitectura, está cubierta por dos o más modos de direccionamiento. Por ejemplo, algunas arquitecturas de computadoras de conjunto de instrucciones complejas (CISC), como la VAX de Digital Equipment Corporation (DEC) , tratan los registros y las constantes literales o inmediatas como otro modo de direccionamiento más. Otras, como el IBM System/360 y sus sucesores, y la mayoría de los diseños de computadoras de conjunto de instrucciones reducidas (RISC), codifican esta información dentro de la instrucción. Por lo tanto, estas últimas máquinas tienen tres códigos de instrucción distintos para copiar un registro a otro, copiar una constante literal en un registro y copiar el contenido de una ubicación de memoria en un registro, mientras que la VAX tiene solo una única instrucción "MOV".
El término "modo de direccionamiento" está sujeto a diferentes interpretaciones: "modo de cálculo de dirección de memoria" o "modo de acceso a operandos". Según la primera interpretación, se considera que las instrucciones que no leen de la memoria ni escriben en la memoria (como "agregar literal a registro") no tienen un "modo de direccionamiento". La segunda interpretación permite que existan máquinas como VAX que usan bits de modo de operando para permitir un registro o un operando literal. Solo la primera interpretación se aplica a instrucciones como "cargar dirección efectiva", que carga la dirección del operando, no el operando en sí.
Los modos de direccionamiento que se enumeran a continuación se dividen en direccionamiento de código y direccionamiento de datos. La mayoría de las arquitecturas informáticas mantienen esta distinción, pero existen (o han existido) algunas arquitecturas que permiten utilizar (casi) todos los modos de direccionamiento en cualquier contexto.
Las instrucciones que se muestran a continuación son puramente representativas para ilustrar los modos de direccionamiento y no reflejan necesariamente los mnemónicos utilizados por ninguna computadora en particular.
Algunas computadoras, por ejemplo, IBM 709 , RCA 3301, [1] no tienen un solo campo de modo de dirección sino que tienen campos separados para direccionamiento indirecto e indexación.
Las arquitecturas de las computadoras varían mucho en cuanto a la cantidad de modos de direccionamiento que proporcionan en el hardware. Existen algunas ventajas en eliminar los modos de direccionamiento complejos y usar solo uno o unos pocos modos de direccionamiento más simples, aunque requiera algunas instrucciones adicionales y quizás un registro adicional. [2] [3] Se ha demostrado [4] [5] [6] que es mucho más fácil diseñar CPU segmentadas si los únicos modos de direccionamiento disponibles son los simples.
La mayoría de las arquitecturas RISC tienen sólo unos cinco modos de direccionamiento simples, mientras que las arquitecturas CISC como DEC VAX tienen más de una docena de modos de direccionamiento, algunos de los cuales son bastante complicados. La arquitectura IBM System/360 tiene sólo cuatro modos de direccionamiento; se han añadido algunos más para la arquitectura ESA/390 .
Cuando sólo hay unos pocos modos de direccionamiento, el modo de direccionamiento particular requerido suele estar codificado dentro del código de instrucción (por ejemplo, IBM System/360 y sucesores, la mayoría de RISC). Pero cuando hay muchos modos de direccionamiento, a menudo se reserva un campo específico en la instrucción para especificar el modo de direccionamiento. El DEC VAX permitía múltiples operandos de memoria para casi todas las instrucciones, y por eso reservaba los primeros bits de cada especificador de operando para indicar el modo de direccionamiento para ese operando en particular. Mantener los bits del especificador de modo de direccionamiento separados de los bits de operación del código de operación produce un conjunto de instrucciones ortogonal .
Incluso en una computadora con muchos modos de direccionamiento, las mediciones de programas reales [7] indican que los modos de direccionamiento simples que se enumeran a continuación representan aproximadamente el 90% o más de todos los modos de direccionamiento utilizados. Dado que la mayoría de estas mediciones se basan en código generado a partir de lenguajes de alto nivel por compiladores, esto refleja en cierta medida las limitaciones de los compiladores que se utilizan. [8] [7] [9]
Algunas arquitecturas de conjuntos de instrucciones, como Intel x86 e IBM/360 y sus sucesores, tienen una instrucción de carga de dirección efectiva . [10] [11] Esta calcula la dirección efectiva del operando y la carga en un registro, sin acceder a la memoria a la que hace referencia. Esto puede ser útil cuando se pasa la dirección de un elemento de matriz a una subrutina. También puede ser una forma inteligente de hacer más cálculos de lo normal en una instrucción; por ejemplo, usar una instrucción de este tipo con el modo de direccionamiento "base+índice+desplazamiento" (que se detalla a continuación) permite sumar dos registros y una constante en una instrucción y almacenar el resultado en un tercer registro.
A continuación se muestran algunos modos de direccionamiento de código simples. La nomenclatura puede variar según la plataforma.
+----+------------------------------+ |saltar| dirección | +----+------------------------------+ (Dirección de PC efectiva = dirección)
La dirección efectiva para una dirección de instrucción absoluta es el parámetro de dirección en sí sin modificaciones.
+----+------------------------------+ |saltar| desplazamiento | salto relativo +----+------------------------------+ (Dirección de PC efectiva = siguiente dirección de instrucción + desplazamiento, el desplazamiento puede ser negativo)
La dirección efectiva para una instrucción relativa a una PC es el parámetro de desplazamiento que se agrega a la dirección de la siguiente instrucción. Este desplazamiento suele estar firmado para permitir la referencia al código tanto antes como después de la instrucción. [12]
Esto es particularmente útil en conexión con instrucciones de salto , porque los saltos típicos son a instrucciones cercanas (en un lenguaje de alto nivel, la mayoría de las instrucciones if o while son razonablemente cortas). Las mediciones de programas reales sugieren que un desplazamiento de 8 o 10 bits es lo suficientemente grande para aproximadamente el 90% de los saltos condicionales (aproximadamente ±128 o ±512 bytes). [13] Para saltos a instrucciones que no están cercanas, se utilizan otros modos de direccionamiento.
Otra ventaja del direccionamiento relativo a PC es que el código puede ser independiente de la posición , es decir, puede cargarse en cualquier lugar de la memoria sin necesidad de ajustar ninguna dirección.
+-------+-----+ |saltarVía| reg | +-------+-----+ (Dirección de PC efectiva = contenido del registro 'reg')
La dirección efectiva para una instrucción indirecta de registro es la dirección en el registro especificado. Por ejemplo, (A7) para acceder al contenido del registro de dirección A7.
El efecto es transferir el control a la instrucción cuya dirección está en el registro especificado.
Muchas máquinas RISC, así como el CISC IBM System/360 y sus sucesores, tienen instrucciones de llamada de subrutina que colocan la dirección de retorno en un registro de direcciones (el modo de direccionamiento indirecto de registro se utiliza para regresar de esa llamada de subrutina).
+------+ | nop | ejecuta la siguiente instrucción +------+ (Dirección de PC efectiva = dirección de la siguiente instrucción)
La CPU, después de ejecutar una instrucción secuencial, ejecuta inmediatamente la siguiente instrucción.
La ejecución secuencial no se considera un modo de direccionamiento en algunas computadoras.
La mayoría de las instrucciones en la mayoría de las arquitecturas de CPU son instrucciones secuenciales. Debido a que la mayoría de las instrucciones son instrucciones secuenciales, los diseñadores de CPU a menudo agregan características que sacrifican deliberadamente el rendimiento de las otras instrucciones (instrucciones de bifurcación) para que estas instrucciones secuenciales se ejecuten más rápido.
Las ramas condicionales cargan la PC con uno de dos resultados posibles, dependiendo de la condición; la mayoría de las arquitecturas de CPU utilizan otro modo de direccionamiento para la rama "tomada" y ejecución secuencial para la rama "no tomada".
Muchas características de las CPU modernas (precarga de instrucciones y canalización más compleja , ejecución fuera de orden , etc.) mantienen la ilusión de que cada instrucción termina antes de que comience la siguiente, lo que arroja los mismos resultados finales, aunque eso no es exactamente lo que sucede internamente.
Cada " bloque básico " de dichas instrucciones secuenciales exhibe localidad de referencia tanto temporal como espacial .
Las CPU que no utilizan la ejecución secuencial con un contador de programa son extremadamente raras. En algunas CPU, cada instrucción siempre especifica la dirección de la siguiente instrucción. Dichas CPU tienen un puntero de instrucción que contiene esa dirección especificada; no es un contador de programa porque no hay ninguna disposición para incrementarlo. Dichas CPU incluyen algunas computadoras con memoria de tambor como la IBM 650 , la máquina SECD , Librascope RPC 4000 y la RTX 32P. [14]
En los procesadores implementados con microcódigo horizontal , la microinstrucción puede contener los bits de orden superior de la siguiente dirección de instrucción.
Otras arquitecturas informáticas van mucho más allá e intentan evitar el cuello de botella de von Neumann utilizando una variedad de alternativas al contador de programa .
Algunas arquitecturas informáticas tienen instrucciones condicionales (como ARM , pero ya no para todas las instrucciones en modo de 64 bits) o instrucciones de carga condicional (como x86) que, en algunos casos, pueden hacer innecesarias las ramificaciones condicionales y evitar que se vacíe la secuencia de instrucciones . Una instrucción como "comparar" se utiliza para establecer un código de condición y las instrucciones posteriores incluyen una prueba de ese código de condición para ver si se obedecen o se ignoran.
+------+-----+-----+ |skipEQ| reg1| reg2| omitir la siguiente instrucción si reg1=reg2 +------+-----+-----+ (Dirección de PC efectiva = dirección de la siguiente instrucción + 1)
El direccionamiento por omisión puede considerarse un tipo especial de modo de direccionamiento relativo a la PC con un desplazamiento fijo "+1". Al igual que el direccionamiento relativo a la PC, algunas CPU tienen versiones de este modo de direccionamiento que solo hacen referencia a un registro ("omitir si reg1=0") o a ningún registro, haciendo referencia implícita a algún bit previamente establecido en el registro de estado . Otras CPU tienen una versión que selecciona un bit específico en un byte específico para probar ("omitir si el bit 7 de reg12 es 0").
A diferencia de todas las demás ramas condicionales, una instrucción "skip" nunca necesita vaciar la secuencia de instrucciones , aunque puede necesitar hacer que se ignore la siguiente instrucción.
A continuación se muestran algunos modos de direccionamiento de datos simples. La nomenclatura puede variar según la plataforma.
+------+-----+-----+-----+-----+ | mul | reg1| reg2| reg3| reg1 := reg2 * reg3; +------+-----+-----+-----+-----+
Este "modo de direccionamiento" no tiene una dirección efectiva y no se considera un modo de direccionamiento en algunas computadoras.
En este ejemplo, todos los operandos están en registros y el resultado se coloca en un registro.
A esto a veces se le llama "base más desplazamiento".
+------+-----+-----+----------------+ | carga | reg | base | desplazamiento | reg := RAM[base + desplazamiento] +------+-----+-----+----------------+ (Dirección efectiva = desplazamiento + contenido del registro base especificado)
El desplazamiento suele ser un valor con signo de 16 bits (aunque el 80386 lo amplió a 32 bits).
Si el desplazamiento es cero, esto se convierte en un ejemplo de direccionamiento indirecto de registro ; la dirección efectiva es simplemente el valor en el registro base.
En muchas máquinas RISC, el registro 0 está fijado en el valor cero. Si se utiliza el registro 0 como registro base, se convierte en un ejemplo de direccionamiento absoluto . Sin embargo, solo se puede acceder a una pequeña parte de la memoria (64 kilobytes , si el desplazamiento es de 16 bits).
El desplazamiento de 16 bits puede parecer muy pequeño en relación con el tamaño de las memorias de las computadoras actuales (razón por la cual el 80386 lo amplió a 32 bits). Podría ser peor: los mainframes IBM System/360 solo tienen un desplazamiento de 12 bits sin signo. Sin embargo, se aplica el principio de localidad de referencia : en un corto período de tiempo, la mayoría de los elementos de datos a los que un programa desea acceder están bastante cerca unos de otros.
Este modo de direccionamiento está estrechamente relacionado con el modo de direccionamiento absoluto indexado.
Ejemplo 1 : Dentro de una subrutina, el programador estará principalmente interesado en los parámetros y las variables locales, que rara vez superarán los 64 KB , para lo cual basta un registro base (el puntero de marco ). Si esta rutina es un método de clase en un lenguaje orientado a objetos, entonces se necesita un segundo registro base que apunte a los atributos del objeto actual ( this o self en algunos lenguajes de alto nivel).
Ejemplo 2 : Si el registro base contiene la dirección de un tipo compuesto (un registro o estructura), el desplazamiento se puede utilizar para seleccionar un campo de ese registro (la mayoría de los registros/estructuras tienen un tamaño inferior a 32 kB).
+------+-----+-----+----------------+ | añadir | reg1| reg2| constante | reg1 := reg2 + constante; +------+-----+-----+----------------+
Este "modo de direccionamiento" no tiene una dirección efectiva y no se considera un modo de direccionamiento en algunas computadoras.
La constante puede tener signo o no. Por ejemplo, move.l #$FEEDABBA, D0
para mover el valor hexadecimal inmediato de "FEEDABBA" al registro D0.
En lugar de utilizar un operando de la memoria, el valor del operando se guarda dentro de la propia instrucción. En la máquina DEC VAX, los tamaños de los operandos literales pueden ser de 6, 8, 16 o 32 bits.
Andrew Tanenbaum demostró que el 98% de todas las constantes de un programa cabrían en 13 bits (véase filosofía de diseño RISC ).
+-----------------+ | bit de transporte transparente | +-----------------+ +-------------------+ | Limpiar acumulador | +-------------------+
El modo de direccionamiento implícito, también llamado modo de direccionamiento implícito ( lenguaje ensamblador x86 ), no especifica explícitamente una dirección efectiva ni para el origen ni para el destino (o, a veces, para ambos).
El código de operación implica la dirección efectiva de origen (si la hay) o de destino (o, a veces, ambas).
El direccionamiento implícito era bastante común en las computadoras más antiguas (hasta mediados de la década de 1970). Estas computadoras generalmente tenían un solo registro en el que se podían realizar operaciones aritméticas: el acumulador. Estas máquinas de acumuladores hacen referencia implícita a ese acumulador en casi todas las instrucciones. Por ejemplo, la operación < a := b + c; > se puede realizar utilizando la secuencia < load b; add c; store a; > -- el destino (el acumulador) está implícito en cada instrucción de "carga" y "adición"; la fuente (el acumulador) está implícita en cada instrucción de "almacenamiento".
Las computadoras posteriores generalmente tenían más de un registro de propósito general o ubicación de RAM que podía ser la fuente o el destino o ambos para la aritmética, por lo que las computadoras posteriores necesitan algún otro modo de direccionamiento para especificar la fuente y el destino de la aritmética.
Entre las instrucciones x86, algunas utilizan registros implícitos para uno de los operandos o resultados (multiplicación, división, conteo, salto condicional).
Muchas computadoras (como x86 y AVR) tienen un registro de propósito especial llamado puntero de pila , que se incrementa o decrementa implícitamente al insertar o extraer datos de la pila, y la dirección efectiva de origen o destino es (implícitamente) la dirección almacenada en ese puntero de pila.
Muchas computadoras de 32 bits (como 68000, ARM o PowerPC) tienen más de un registro que podría usarse como puntero de pila y, por lo tanto, utilizan el modo de direccionamiento "indirecto de autoincremento de registro" para especificar cuál de esos registros se debe usar al insertar o extraer datos de una pila.
Algunas arquitecturas informáticas actuales (por ejemplo, IBM/390 e Intel Pentium) contienen algunas instrucciones con operandos implícitos para mantener la compatibilidad con diseños anteriores.
En muchas computadoras, las instrucciones que invierten el bit de modo de usuario/sistema, el bit de habilitación de interrupciones, etc., especifican implícitamente el registro especial que contiene esos bits. Esto simplifica el hardware necesario para atrapar esas instrucciones a fin de cumplir con los requisitos de virtualización de Popek y Goldberg : en un sistema de este tipo, la lógica de trampa no necesita mirar ningún operando (o la dirección efectiva final), sino solo el código de operación.
Se han diseñado algunas CPU en las que cada operando siempre se especifica implícitamente en cada instrucción: CPU con operando cero .
+------+-----+------------------------------------ --+ | carga | reg | dirección | +------+-----+------------------------------------ --+ (Dirección efectiva = dirección tal como figura en las instrucciones)
Esto requiere espacio en una instrucción para una dirección bastante grande. Suele estar disponible en máquinas CISC que tienen instrucciones de longitud variable, como x86 .
Algunas máquinas RISC tienen una instrucción especial de carga literal superior que coloca una constante de 16 o 20 bits en la mitad superior de un registro. Esta puede utilizarse como registro base en un modo de direccionamiento de base más desplazamiento que proporciona los 16 o 12 bits de orden inferior. La combinación permite una dirección completa de 32 bits.
+------+-----+-----+------------------------------ --+ |cargar |reg |índice|dirección | +------+-----+-----+------------------------------ --+ (Dirección efectiva = dirección + contenido del registro de índice especificado)
Esto también requiere espacio en una instrucción para una dirección bastante grande. La dirección podría ser el comienzo de una matriz o un vector, y el índice podría seleccionar el elemento de matriz particular requerido. El procesador puede escalar el registro de índice para permitir el tamaño de cada elemento de la matriz .
Tenga en cuenta que esto es más o menos lo mismo que el modo de direccionamiento base más desplazamiento, excepto que el desplazamiento en este caso es lo suficientemente grande como para direccionar cualquier ubicación de memoria.
Ejemplo 1 : Dentro de una subrutina, un programador puede definir una cadena como una constante local o una variable estática . La dirección de la cadena se almacena en la dirección literal de la instrucción. El desplazamiento (qué carácter de la cadena se utilizará en esta iteración de un bucle) se almacena en el registro de índice.
Ejemplo 2 : Un programador puede definir varias matrices grandes como variables globales o de clase . El inicio de la matriz se almacena en la dirección literal (quizás modificada en el momento de la carga del programa por un cargador que la reubica ) de la instrucción que la referencia. El desplazamiento (qué elemento de la matriz se utilizará en esta iteración de un bucle) se almacena en el registro de índice. A menudo, las instrucciones de un bucle reutilizan el mismo registro para el contador del bucle y los desplazamientos de varias matrices.
+------+-----+-----+-----+-----+ | carga | reg | base|índice| +------+-----+-----+-----+-----+ (Dirección efectiva = contenido del registro base especificado + contenido del registro de índice especificado)
El registro base podría contener la dirección de inicio de una matriz o un vector, y el índice podría seleccionar el elemento de matriz particular requerido. El procesador puede escalar el registro de índice para permitir el tamaño de cada elemento de la matriz . Esto podría usarse para acceder a los elementos de una matriz que se pasa como parámetro.
+------+-----+-----+-----+----------------+ | carga | reg | base | índice | desplazamiento | +------+-----+-----+-----+----------------+ (Dirección efectiva = desplazamiento + contenido del registro base especificado + contenido del registro de índice especificado)
El registro base podría contener la dirección de inicio de una matriz o vector de registros, el índice podría seleccionar el registro particular requerido y el desplazamiento podría seleccionar un campo dentro de ese registro. El procesador puede escalar el registro de índice para permitir el tamaño de cada elemento de la matriz .
+------+-----+-----+-----+-----+ | carga | reg | base|índice| +------+-----+-----+-----+-----+ (Dirección efectiva = contenido del registro base especificado + contenido escalado del registro de índice especificado)
El registro base podría contener la dirección de inicio de una matriz o una estructura de datos vectorial , y el índice podría contener el desplazamiento de un elemento de matriz particular requerido.
Este modo de direccionamiento escala dinámicamente el valor en el registro de índice para permitir el tamaño de cada elemento de la matriz, por ejemplo, si los elementos de la matriz son números de punto flotante de doble precisión que ocupan 8 bytes cada uno, entonces el valor en el registro de índice se multiplica por 8 antes de usarse en el cálculo de la dirección efectiva. El factor de escala normalmente se limita a ser una potencia de dos , de modo que se puede utilizar el desplazamiento en lugar de la multiplicación.
+------+------+-----+ | carga | reg1 | base| +------+------+-----+ (Dirección efectiva = contenido del registro base)
Algunas computadoras tienen este como un modo de direccionamiento específico. Muchas computadoras solo usan la base más el desplazamiento con un valor de desplazamiento de 0. Por ejemplo, (A7)
+------+-----+-------+ | carga | reg | base | +------+-----+-------+ (Dirección efectiva = contenido del registro base)
Después de determinar la dirección efectiva, el valor en el registro base se incrementa según el tamaño del elemento de datos al que se va a acceder. Por ejemplo, (A7)+ accedería al contenido del registro de dirección A7 y luego aumentaría el puntero de dirección de A7 en 1 (normalmente 1 palabra). Dentro de un bucle, este modo de direccionamiento se puede utilizar para recorrer todos los elementos de una matriz o un vector.
En lenguajes de alto nivel, se suele pensar que es una buena idea que las funciones que devuelven un resultado no tengan efectos secundarios (la falta de efectos secundarios hace que la comprensión y validación del programa sea mucho más fácil). Este modo de direccionamiento tiene un efecto secundario, ya que se altera el registro base. Si el acceso a la memoria posterior provoca un error (por ejemplo, un error de página, un error de bus, un error de dirección) que conduce a una interrupción, reiniciar la instrucción se vuelve mucho más problemático, ya que es posible que sea necesario volver a establecer uno o más registros al estado en el que se encontraban antes de que se iniciara la instrucción originalmente.
Ha habido al menos tres arquitecturas de computadoras que han tenido problemas de implementación con respecto a la recuperación de fallas cuando se utiliza este modo de direccionamiento:
+------+-----+-----+ | carga | reg | base| +------+-----+-----+ (Dirección efectiva = nuevo contenido del registro base)
Antes de determinar la dirección efectiva, el valor en el registro base se reduce según el tamaño del elemento de datos al que se va a acceder.
Dentro de un bucle, este modo de direccionamiento se puede utilizar para recorrer hacia atrás todos los elementos de una matriz o un vector. Se puede implementar una pila utilizando este modo junto con el modo de direccionamiento anterior (autoincremento).
Consulte la discusión de los efectos secundarios en el modo de direccionamiento de autoincremento.
Cualquiera de los modos de direccionamiento mencionados en este artículo podría tener un bit adicional para indicar direccionamiento indirecto, es decir, la dirección calculada utilizando algún modo es de hecho la dirección de una ubicación (normalmente una palabra completa ) que contiene la dirección efectiva real.
El direccionamiento indirecto se puede utilizar para código o datos. Puede facilitar la implementación de punteros , referencias o identificadores , y también puede facilitar la llamada a subrutinas que de otro modo no serían direccionables. El direccionamiento indirecto conlleva una penalización en el rendimiento debido al acceso adicional a la memoria que implica.
Algunas de las primeras minicomputadoras (por ejemplo, DEC PDP-8 , Data General Nova ) tenían solo unos pocos registros y un rango de direccionamiento directo limitado (8 bits). Por lo tanto, el uso del direccionamiento indirecto de memoria era casi la única forma de hacer referencia a una cantidad significativa de memoria.
La mitad de los ocho modos de direccionamiento del DEC PDP-11 son diferidos. El modo diferido de registro @Rn es el mismo que el modo indirecto de registro, como se definió anteriormente. Los modos diferido de predecremento @-(Rn), diferido de postincremento @(Rn)+ y diferido indexado @nn(Rn) apuntan a direcciones en la memoria que se leen para encontrar la dirección del parámetro. El modo diferido del PDP-11, cuando se combina con el contador de programa, proporciona su modo de direccionamiento absoluto.
+------+------+---------+----------------+ | carga | reg1 | base=PC | desplazamiento | +------+------+---------+----------------+ reg1 := RAM[PC + desplazamiento] (Dirección efectiva = PC + offset)
El modo de direccionamiento relativo al PC se puede utilizar para cargar un registro con un valor almacenado en la memoria del programa a poca distancia de la instrucción actual. Puede considerarse un caso especial del modo de direccionamiento "base más desplazamiento", que selecciona el contador de programa (PC) como el "registro base".
Hay algunas CPU que admiten referencias de datos relativas a PC. Estas CPU incluyen:
La arquitectura x86-64 y la arquitectura ARMv8-A de 64 bits [23] tienen modos de direccionamiento relativos a la PC, denominados "RIP-relative" en x86-64 y "literal" en ARMv8-A. El Motorola 6809 también admite un modo de direccionamiento relativo a la PC.
La arquitectura PDP-11 , la arquitectura VAX y las arquitecturas ARM de 32 bits admiten el direccionamiento relativo a la PC al tener la PC en el archivo de registro.
IBM z/Architecture incluye instrucciones específicas, por ejemplo, Cargar largo relativo, con direccionamiento relativo a PC si la función de extensión de instrucciones generales está activa.
Cuando se utiliza este modo de direccionamiento, el compilador normalmente coloca las constantes en un grupo literal inmediatamente antes o inmediatamente después de la subrutina que las utiliza, para evitar ejecutar accidentalmente esas constantes como instrucciones.
Este modo de direccionamiento, que siempre obtiene datos de la memoria o almacena datos en la memoria y luego pasa secuencialmente a ejecutar la siguiente instrucción (la dirección efectiva apunta a los datos), no debe confundirse con la "bifurcación relativa de PC", que no obtiene datos de la memoria ni los almacena en ella, sino que se bifurca a alguna otra instrucción en el desplazamiento dado (la dirección efectiva apunta a una instrucción ejecutable).
Los modos de direccionamiento que se enumeran aquí se utilizaron en el período 1950-1980, pero ya no están disponibles en la mayoría de las computadoras actuales. Esta lista no está completa; se han utilizado muchos otros modos de direccionamiento interesantes y peculiares de vez en cuando, por ejemplo, el OR absoluto menos lógico de dos o tres registros de índice. [24] [25]
Si el tamaño de la palabra es mayor que la dirección, entonces la palabra a la que se hace referencia para el direccionamiento indirecto de memoria podría tener una bandera indirecta establecida para indicar otro ciclo indirecto de memoria. Esta bandera se conoce como bit de indirección y el puntero resultante es un puntero etiquetado , el bit de indirección etiqueta si es un puntero directo o un puntero indirecto. Se debe tener cuidado para garantizar que una cadena de direcciones indirectas no se refiera a sí misma; si lo hace, se puede obtener un bucle infinito al intentar resolver una dirección.
El IBM 1620 , el Data General Nova , la serie HP 2100 y el NAR 2 tienen cada uno un modo de direccionamiento indirecto de memoria de varios niveles y podían entrar en un bucle infinito de cálculo de direcciones. El modo de direccionamiento indirecto de memoria del Nova influyó en la invención del código indirecto enhebrado .
La computadora DEC PDP-10 con direcciones de 18 bits y palabras de 36 bits permitía el direccionamiento indirecto de múltiples niveles con la posibilidad de utilizar también un registro de índice en cada etapa. El sistema de interrupción de prioridad se consultaba antes de decodificar cada palabra de dirección. [26] Por lo tanto, un bucle de dirección indirecta no impediría la ejecución de rutinas de servicio del dispositivo, incluido cualquier controlador de expiración de intervalo de tiempo del programador multitarea preventivo . Una instrucción de bucle se trataría como cualquier otro trabajo limitado por el cálculo.
En algunas computadoras, había direcciones que hacían referencia a registros en lugar de al almacenamiento primario, o a la memoria primaria utilizada para implementar esos registros. Aunque en algunas computadoras tempranas había direcciones de registro en el extremo superior del rango de direcciones, por ejemplo, IBM 650 , [27] [a] IBM 7070 , [28] [c] la tendencia ha sido usar solo direcciones de registro en el extremo inferior y usar solo las primeras 8 o 16 palabras de memoria (por ejemplo, ICL 1900 , DEC PDP-10). Esto significaba que no había necesidad de una instrucción separada de "agregar registro a registro"; uno podía simplemente usar la instrucción "agregar memoria a registro".
En el caso de los primeros modelos del PDP-10, que no tenían memoria caché, un bucle interno ajustado cargado en las primeras palabras de la memoria (donde los registros rápidos eran direccionables si estaban instalados) funcionaba mucho más rápido de lo que lo hubiera hecho en una memoria de núcleo magnético.
Los modelos posteriores de la serie DEC PDP-11 asignaron los registros a direcciones en el área de entrada/salida, pero esto se hizo principalmente para permitir diagnósticos remotos. Para confusión, los registros de 16 bits se asignaron a direcciones de bytes consecutivos de 8 bits.
La minicomputadora DEC PDP-8 tenía ocho ubicaciones especiales (en las direcciones 8 a 15). Cuando se accedía a ellas mediante direccionamiento indirecto de memoria, estas ubicaciones se incrementaban automáticamente antes de su uso. [29] Esto facilitaba recorrer la memoria en un bucle sin necesidad de usar el acumulador para incrementar la dirección.
La minicomputadora Data General Nova tenía 16 ubicaciones de memoria especiales en las direcciones 16 a 31. [30] Cuando se accedía a ellas a través del direccionamiento indirecto de memoria, las direcciones 16 a 23 se incrementaban automáticamente antes del uso, y las direcciones 24 a 31 se decrementaban automáticamente antes del uso.
Los procesadores Data General Nova , Motorola 6800 y MOS Technology 6502 tenían muy pocos registros internos. Las instrucciones aritméticas y lógicas se ejecutaban principalmente con valores en la memoria, en lugar de registros internos. Como resultado, muchas instrucciones requerían una ubicación de dos bytes (16 bits) en la memoria. Dado que los códigos de operación en estos procesadores tenían solo un byte (8 bits) de longitud, las direcciones de memoria podían representar una parte significativa del tamaño del código.
Los diseñadores de estos procesadores incluyeron una solución parcial conocida como direccionamiento de "página cero". Se podía acceder a los 256 bytes iniciales de memoria ($0000 – $00FF; también conocida como página "0") utilizando una dirección de memoria absoluta o indexada de un byte. Esto reducía el tiempo de ejecución de las instrucciones en un ciclo de reloj y la longitud de las instrucciones en un byte. Al almacenar datos de uso frecuente en esta región, los programas podían hacerse más pequeños y más rápidos.
Como resultado, la página cero se utilizaba de forma similar a un archivo de registro. Sin embargo, en muchos sistemas, esto daba como resultado una alta utilización del área de memoria de la página cero por parte del sistema operativo y los programas de usuario, lo que limitaba su uso ya que el espacio libre era limitado.
El modo de dirección de página cero se mejoró en varios procesadores de 8 bits de modelos recientes, incluidos el WDC 65816 , el CSG 65CE02 y el Motorola 6809. El nuevo modo, conocido como direccionamiento de "página directa", agregó la capacidad de mover la ventana de memoria de página cero de 256 bytes desde el inicio de la memoria (dirección de desplazamiento $0000) a una nueva ubicación dentro de los primeros 64 KB de memoria.
El CSG 65CE02 permitía mover la página directa a cualquier límite de 256 bytes dentro de los primeros 64 KB de memoria almacenando un valor de desplazamiento de 8 bits en el nuevo registro de página base (B). El Motorola 6809 podía hacer lo mismo con su registro de página directa (DP). El WDC 65816 fue un paso más allá y permitió mover la página directa a cualquier ubicación dentro de los primeros 64 KB de memoria almacenando un valor de desplazamiento de 16 bits en el nuevo registro directo (D).
Como resultado, una mayor cantidad de programas pudieron utilizar el modo de direccionamiento de página directa mejorado en comparación con los procesadores tradicionales que solo incluían el modo de direccionamiento de página cero.
Esto es similar al direccionamiento de índice escalado, excepto que la instrucción tiene dos operandos adicionales (normalmente constantes) y el hardware verifica que el valor del índice esté entre estos límites.
Otra variación utiliza descriptores vectoriales para contener los límites; esto facilita la implementación de matrices asignadas dinámicamente y aún así tener una verificación de límites completa.
Algunas computadoras tenían modos especiales de direccionamiento indirecto para subcampos dentro de las palabras.
La palabra indirecta de direccionamiento de caracteres de la serie 600 de GE/Honeywell especificaba campos de caracteres de 6 o 9 bits dentro de su palabra de 36 bits .
El DEC PDP-10 , también de 36 bits, tenía instrucciones especiales que permitían tratar la memoria como una secuencia de campos de bits de tamaño fijo o bytes de cualquier tamaño desde 1 bit hasta 36 bits. Un descriptor de secuencia de una palabra en la memoria, llamado "puntero de byte", contenía la dirección de palabra actual dentro de la secuencia, una posición de bit dentro de una palabra y el tamaño de cada byte.
Existían instrucciones para cargar y almacenar bytes a través de este descriptor, y para incrementar el descriptor para apuntar al siguiente byte (los bytes no se dividían entre los límites de las palabras). Gran parte del software DEC utilizaba cinco bytes de 7 bits por palabra (caracteres ASCII simples), con un bit por palabra sin usar. Las implementaciones de C tenían que utilizar cuatro bytes de 9 bits por palabra, ya que la función 'malloc' en C supone que el tamaño de un int es un múltiplo del tamaño de un char ; [31] el múltiplo real está determinado por el operador de tiempo de compilación dependiente del sistema sizeof .
El Elliott 503 , [32] el Elliott 803 , [32] [33] y el Apollo Guidance Computer solo usaban direccionamiento absoluto y no tenían registros de índice. Por lo tanto, los saltos indirectos, o saltos a través de registros, no eran compatibles con el conjunto de instrucciones. En su lugar, se le podía indicar que añadiera el contenido de la palabra de memoria actual a la siguiente instrucción . Añadir un valor pequeño a la siguiente instrucción que se va a ejecutar podría, por ejemplo, cambiar a JUMP 0
en a JUMP 20
, creando así el efecto de un salto indexado. Nótese que la instrucción se modifica sobre la marcha y permanece sin cambios en la memoria, es decir, no es un código automodificable . Si el valor que se añade a la siguiente instrucción fuera lo suficientemente grande, podría modificar el código de operación de esa instrucción así como o en lugar de la dirección.
*p++
en el lenguaje de programación C , se utiliza para las operaciones de extracción de pila .*--p
en el lenguaje de programación C , utilizado para operaciones de inserción en la pila .... MIPS-X utiliza un único modo de direccionamiento: registro base más desplazamiento. Este modo de direccionamiento simple permite que el cálculo de la dirección efectiva comience muy pronto ...
autodecremento, que se encuentran en algunas arquitecturas RISC, representan otro 25% del uso. Estos datos se recopilaron a partir de una medición de instrucciones estáticas para la biblioteca de 54 rutinas DSP invocables en C codificadas en lenguaje ensamblador.
3 programas medidos en la máquina con todos los modos de direccionamiento (VAX) ... 75% de desplazamiento e inmediato
El 79% de todas las instrucciones ejecutadas podrían reemplazarse por instrucciones RISC o sintetizarse en instrucciones RISC utilizando solo una combinación básica de instrucciones de bloque.
Figura 2-9: Cálculo de dirección efectiva: prueba "PI RQ ?"