Load y Store
Como se ha dicho en artículos anteriores, ARM utiliza un modelo de acceso LOAD/STORE. Esto significa que para procesar cualquier dato que se encuentre en la memoria, primero tiene que cargarse a un registro del procesador con una instrucción de tipo LOAD y al terminar debe guardarse en de nuevo en la memoria con una instrucción de tipo STORE.
Existen tres variaciones para las instrucciones LOAD y STORE dependiendo de cuál sea su operando, y a su vez cada una de estas tiene tres variaciones que indica qué sucede con el operando al terminar la operación.
La primera clasificación es por su tipo de offset:
- Por valor inmediato
- Por registro
- Por registro escalado
La segunda clasificación que puede ser cada unade las anteriores es por tipo de direccionamiento:
- Offset
- Pre-indexado
- Post-indexado
Véamos algunos ejemplos.
El ejemplo más simple de instrucciones LOAD y STORE es el siguiente:
LDR R0, [R1] ; LOAD
STR R2, [R3] ; STORE
En la instrucción LOAD tenemos que el registro destino es R0, y el operando es [R1]. Los corchetes alrededor del R1 indican que no se refiere al valor de R1 en sí, sino al contenido de la dirección de memoria a la que apunta R1. La instrucción LDR R0, [R1]
nos está diciendo que el contenido de la dirección de memoria R1 será transferido al registro R0.
De forma inversa, la instrucción STR R2, [R3]
nos indica que el contenido del registro R2 se guardará en la dirección de memoria a la que apunta R3.
Variación por tipo de offset
Por valor inmediato
Cuando hacemos offset por valor inmediato, el operando es modificado por una variable que se le suma a este. Se representa de la siguiente manera en la instrucción:
LDR R0, [R1, #2]
STR R2, [R3, #4]
El #
antes de los números indican que este es un valor inmediato, es decir, se trata directamente de un número 2 y un número 4. Todo escrito entre corchetes nos indica que estamos haciendo una operación de LOAD con la dirección de memoria a la que apunta R1 + 2
y una operación de STORE con la dirección a la que apunta R3 + 4
.
Por ejemplo, para la operación LOAD, si R1
apunta a la dirección 1000h, entonces el valor que se cargará al registro R0
será el valor de la dirección de memoria 1002h. Para la operación STORE, si R3
apunta a la dirección 2000h, entonces el valor contenido en la variable R2
se almacenará en la dirección de memoria 1004h.
Por registro
Cuando hacemos offset con registro, de forma similar al offset por valor inmediato, se le suma un valor al registro del operando. La diferencia es que el valor que modifica al operando se encuentra dentro de un registro. Se representa de la siguiente forma:
LDR R0, [R1, R2]
STR R3, [R4, R5]
La primera instrucción indica que se cargara el registro R0
el valor ubicado en la dirección R1 + R2
, es decir, el valor contenido en la dirección de memoria R1
si se le suma el valor contenido en R2
. De forma similar, la segunda instrucción indica que guardaremos el valor contenido en R3
en la dirección de memoria R4 + R5
.
Por ejemplo, para la operación LOAD, si el registro R1
apunta a la dirección 1000h, y el registro R2
contiene el valor FEh, entonces se cargará al registro R0
el contenido de la dirección de memoria 10FEh.
Por registro escalado
El offset por registro escalado es similar al offset por registro, con la diferencia de que el valor del registro que se suma es modificado por una operación de corrimiento. Se representa de la siguiente forma:
LDR R0, [R1, R2, LSL#2]
STR R3, [R4, R5, LSL#4]
En la primera instrucción, antes de sumar el valor del registro R2
al registro R1
, primero se le aplica un corrimiento lógico a la izquierda de dos bits.
Por ejemplo, para la instrucción de STORE, si R4
apunta a la dirección 1000h, y R5
contiene el valor 2h, primero es necesario modificar este último con un corrimiento a la izquierda. Un corrimiento a la izquierda del byte 2h resulta en 40h (64, en decimal, el equivalente a haberlo duplicado 4 veces). Este 40h es lo que se suma al registro R4
, con lo que obtenemos que el contenido del registro R3
se guardará en la dirección 1040h.
Por modo de direccionamiento
Además de clasificarse por el tipo de offset, tenemos la clasificación por direccionamiento, que puede tenerse en cualquiera de los casos anteriormente discutidos. Por default hemos estado viendo el modo de direccionamiento offset, pero existen dos variantes: Prefijo y posfijo. Estos se utilizan para cambiar el registro modificado, y cada uno indica en qué momento se hace.
En los casos anteriores, el registro del segundo operando se modificaba con un offset (inmediato, por registro o por registro escalado), pero este registro se mantenía igual al terminar la operación; esto no ocurre en los modos direccionamientos prefijo y sufijo.
Prefijo
En el modo de direccionamiento prefijo, el operando del segundo registro se modifica antes de realizar la instrucción. Se representa colocando un signo de exclamación después de los corchetes de los operandos, de la siguiente forma:
LDR R0, [R1, #2]!
STR R3, [R4, R5]!
La operación de LOAD es un offset inmediato con modo de direccionamiento prefijo. Por ejemplo, si R1
apunta la dirección 1000h, entonces se cargará al registro R0
el valor contenido en la dirección a la que apunta R1 + 2
, es decir, 1002h. A diferencia del modo de direccionamiento por offset, esta operación también modifica a R1
, pues ahora es 1002h.
La operación de STORE es un offset por registro: si R4
apunta a la dirección 2000h, y R5 contiene el valor FEh, entonces se guardará el valor del registro R3
en la dirección de memoria 10FEh, y el valor de R4
ahora será 10FEh.
Sufijo
En el direccionamiento posfijo, primero se realiza la instrucción con el valor inicial del operando, y despues se modifica. Se representa colocando el primer operando dentro de corchetes, separados con coma de su modificador, de la siguiente manera:
LDR R3, [R4], R5
STR R0, [R1], R2, LSL#2
La operación de LOAD indica que se cargará el valor al que apunta R4
al registro R3
, y después se le sumará el valor de R5
a R4
. Como en el ejemplo anterior, si R4
apunta a la dirección 2000h y R5
contiene el valor FEh, entonces al registro R3
se le cargará el valor contenido en la dirección de memoria 2000h, y al terminar el valor del registro R4
se volverá 20FEh.
La operación STORE indica que primero se guardará el valor de R0
en R1
, y después se modificará R1
. Si R1
apunta a la dirección 1000h y R2
tiene el valor de 2h, significa que el valor del registro R0
se guardará en la dirección 1000h, y al terminar el valor del registro R1
se volverá 1000h + 2 « 5 = 1040h.
Apunte sobre valores inmediatos en ARM.
El valor inmediato que se utiliza para hacer operaciones aritméticas o cargar datos directamente a un registro debe cumplir ciertas características. No se puede cargar cualquier valor de 32 bits a un registro, pues para representarlo se tendría que ocupar todo el espacio de la instrucción en modo ARM (y el doble de una instrucción en modo Thumb). La forma en la que ARM soluciona esto es que dedica 12 bits de una instrucción al valor inmediato. La forma en que este se representa es como un número de 8 bits (0-255) rotado un número de 4 bits multiplicado por 2 (0, 2, 4,…, 28, 30). XXXX XXXX << 2*YYYY