Cuando se escribe código ensamblador, digamos R1 = R2 + R3, es bastante fácil entender el procedimiento de direccionamiento porque todos los registros están cerca de la ALU. Pero me resulta difícil ver cómo va la ALU a la memoria en R2 en instrucciones como R1 = R1 + M[R2]. Podría estar muy lejos, y no veo que vaya a comprobar y hacer coincidir las direcciones. ¿Cómo traduce el número hexadecimal en R2 a una posición de memoria?
Respuestas
¿Demasiados anuncios?El número hexadecimal es una conveniencia basada en el hardware del procesador y la memoria. Un número hexadecimal de 4 dígitos como FA3B representa 16 dígitos en binario (1111 1010 0011 1011), que son las 16 líneas de dirección de la memoria del sistema. Existe una relación física de 1 a 1 entre los números binarios y los "cables" del hardware. Son 1 o 0 que es en o fuera de . Hay algo más de 65.000 combinaciones posibles de esos 16 dígitos ( 2 a la 16ª potencia que todo el mundo llama 64K).
La decodificación para llegar a un único conjunto de bits de memoria se realiza en paralelo. En su idea de distancia (o alguna norma que mida lo difícil que es llegar a una ubicación de memoria) desde la ALU, todas están a la misma distancia y tardan lo mismo en encontrarse. Las líneas de dirección van a circuitos de decodificación y las salidas son todas 0 excepto la combinación que coincide con los bits de dirección. Dividir esto en una matriz de 256 filas y 256 columnas para cada bit de datos. Si se trata de un byte, como en un AVR, hay 8 conjuntos. Esto cabe en el plano de un trozo de silicio. Los decodificadores hacen cosas simples. Pueden tener 4 entradas y 16 salidas como los chips lógicos de la serie 7400. Pueden mantenerse así de simples y usar un montón de ellos o en chips modernos, más bien un gran circuito decodificador. Puedes ver cómo se ramifican como un árbol a lo largo de un bloque de memoria en algunas imágenes microscópicas de chips de memoria.
Consulta alguna información tipo wiki sobre cómo funciona la RAM o, más fácil de imaginar, la ROM, porque no tienes que escribir en ella. Mirando un ejemplo simple, como una ROM de 16 bytes debería quedar claro. -- Una búsqueda rápida no ha encontrado ningún diagrama bueno. Consulta la hoja de datos del 74HC138 para ver un simple circuito de la etapa de decodificación.
Para tener una imagen mental que sea lógicamente válida, vea cada bit de memoria como si tuviera dieciséis comparadores que comparan los valores en el bus de direcciones con su dirección particular de los 64K valores posibles. Todos ellos hacen la comparación simultáneamente y sólo uno responde.
Si eso suena como si fuera a usar mucha energía, así es. En realidad es una estrategia de divide y vencerás. Toma el dígito más alto. En el caso de FA3B el bit binario más significativo es 1. Eso significa que la ubicación está en los 32K superiores de la memoria, por lo que los 32K inferiores ni siquiera necesitan ser encendidos. El siguiente bit es un 1, por lo que está en los 16K superiores de los 32K superiores. Luego los 8K superiores y los 4K superiores y los 2K superiores. Finalmente un 0. Así que está en el 1K inferior de ese último 2K, luego en el 512 superior de ese y en el 256 inferior de ese y en el 128 inferior de ese y en el 64 superior, luego en el 32 superior, en el 16 superior, en el 8 inferior, en el 4 superior, en el 2 y finalmente en el superior de las dos posibilidades finales. Creo que he contado bien.
Como puedes ver, si organizas la memoria en pequeños bloques ni siquiera tienes que encender la mayor parte de ella para un acceso concreto. Puedes utilizar la lógica de direcciones para encender también la parte del chip que se necesita sobre la marcha, por así decirlo.
Todo ello para emular el modelo de dirección lineal de la ALU (o la cinta de una máquina de Turing).
En mi opinión: R1 = R1 + M[R2] no es una de las instrucciones más básicas que la CPU puede ejecutar en un solo ciclo de reloj.
Si la CPU no soporta esta instrucción, el compilador debe traducirla en 2 más simples: la primera carga datos en un registro temporal, la segunda añade esos valores a R1. Tenga en cuenta que, el registro temporal puede o no estar disponible desde la perspectiva del programador.
De lo contrario, la CPU tarda al menos 2 ciclos de reloj en realizar dos acciones separadas.
Puedes comprobar mi opinión buscando los ciclos de reloj necesarios para esa instrucción en la hoja de datos de la CPU.
Existen dos tipos principales de ISA (Arquitecturas de conjuntos de instrucciones): RISC y CISC. RISC significa Instrucción reducida Configurar el ordenador. No significa que haya menos instrucciones, sino que cada instrucción tiene menos capacidad. En un ISA de este tipo si se quiere hacer esto:
ADD R1 R1 M[R2]
Tendrás que codificarla así:
LOAD TEMP M[R2]
ADD R1 R1 TEMP
Ahora tenemos arquitecturas como la omnipresente ISA x86. Son ISAs CISC, lo que significa que cada instrucción es muy potente. De hecho, puedes copiar una cadena entera en ensamblador x86 así:
REP MOVSB
Y puedes tener instrucciones de registro de memoria como la que has descrito:
MOV EAX [EBX]
Lo que significa tomar EAX = EAX + M(EBX).
El chip x86 original estaba microcodificado, lo que significa que la instrucción anterior se convertía en dos instrucciones más sencillas de tipo RISC, que se ejecutaban secuencialmente. Luego se consiguieron procesadores con pipelines, en los que había etapas de memoria para acceder a la misma. Ahora bien, tal y como has descrito, era complicado y lento.
En la actualidad, el decodificador de instrucciones x86 descodifica estas instrucciones en un ISA interno similar al de RISC, que luego se ejecuta. Puedes buscar la microarquitectura Haswell para ver cómo funciona su última microarquitectura.