Conjunto de Instrucciones
En la sección anterior, tuvimos un vistazo de las diferentes instrucciones que maneja la arquitectura ARM32, con el ejemplo del código que suma y multiplica a un número.
ldr r2,[n]
add r2,3
str r2,[x]
ldr r3,[x]
mul r3,2
str r3,[y]
ret
Podemos ver instrucciones como LDR
, STR
que sirven para cargar y guardar datos entre registro y memoria, ADD
y MUL
que nos sirven para realizar operaciones aritméticas, y RET
, que nos sirve para indicar que ha terminado una rutina y debemos regresar a la función que la llamó.
Existe una variedad de instrucciones que nos permiten realizar acciones específicas. Al ser ARM una arquitectura RISC, el conjunto de instrucciones es reducido, lo que implica que las instrucciones no pueden ser tan complejas: cada una realiza una tarea simple. Es cuando las combinamos en un programa que pueden realizar tareas tan complejas como las que se hacen en los celulares o los dispositivos IoT.
La estructura general de una instrucción es la siguiente:
MNEM[S][COND] {Rd}, Operando1, Operando2
-
MNEM - Es el mnemónico que indica lo que realiza la instrucción. En los ejemplos anteriores, sería
LDR
,ADD
,RET
. Brindan una descripción breve de lo que hace la instrucción. -
[S] - Sufijo (opcional). Como se vio en el artículo anterior, las instrucciones de
LDR
ySTR
pueden tener un sufijo que indica si el tipo de dato es un byte (-B
) o media palabra (-S
), o si tiene signo (-S
). -
[COND] - Condicion (opcional). En modo ARM, todas las instrucciones tienen la capacidad de ser condicionales, es decir, solo se realizan si ciertas condiciones se cumplen. Estas condiciones se calculan a través de las banderas del registro CPRS mencionado en el artículo anterior. El modo Thumb tiene reglas especiales para aplicar instrucciones condicionales, serán mencionado más adelante.
-
{Rd} - Registro destino. Es el registro en el que se almacenará el resultado de la instrucción.
-
Operando1. Puede ser un registro o un valor inmediato (un valor que se encuentra en la misma instrucción).
-
Operando2. Puede ser un valor inmediato o un registro con un corrimiento opcional.
A continuación se muestra una tabla con las instrucciones más comunes.
Instrucción | Descripción |
---|---|
MOV |
Mover un dato entre registros |
MVN |
Mover y negar |
LDR |
Carga de memoria a registros |
STR |
Guarda de registros a memoria |
LDM |
Carga múltiple |
STM |
Guarda múltiple |
PUSH |
Empuja dato a la pila |
POP |
Saca dato de la pila |
ADD |
Suma |
SUB |
Resta |
MUL |
Multiplicación |
CMP |
Comparar |
AND |
AND con bits |
ORR |
OR con bits |
EOR |
XOR con bits |
LSL |
Corrimiento lógico a la izquierda |
LSR |
Corrimiento lógico a la derecha |
ASR |
Corrimiento aritmético a la derecha |
ROL |
Corrimiento a la izquierda |
ROR |
Corrimiento a la derecha |
B |
Branch |
BL |
Branch with link |
BX |
Branch and exchange |
BLX |
Branch with link and exchange |
SWI/SVC |
Llamada al sistema |
Las instrucciones MOV
y MVN
nos sirven para mover datos entre registros o directamente desde la instrucción hacia ellos. A diferencia de estas, las instrucciones LDR
y STR
, como se ha mencionado antes, mueven datos entre registros y memoria. Tienen su versión múltiple, LDM
y STM
, que sirven para mover varios bytes en una sola instrucción (en el siguiente artículo veremos cómo hacer esto). Una variación especial de estas instrucciones es PUSH
y POP
, que nos permiten mover datos de registros a memoria o de memoria a registros, respectivamente, pero específicamente a la dirección indicada por el apuntador al stack (SP).
Las instrucciones ADD
, SUB
, MUL
son instrucciones aritméticas que hacen lo que indica su nombre. Hacen la operación entre los operandos y lo guardan en el registro destino. En esto último difiere la instrucción CMP
, que realiza la resta entre los operandos, pero no lo guarda en ningún registro, sino que solo es relevante el estado de las banderas después de dicha operación. De forma similar funcionan las instrucciones lógicas AND
, ORR
y EOR
, que realizan la operación de su respectiva compuerta lógica entre cada uno de los 32 bits del registro.
Existen también las operaciones de corrimiento: LSL
, LSR
, ASR
, ROL
y ROR
. Estas recorren los bits de un registro en una dirección, como si estuvieran en una fila que está avanzando. Existen tres variedades de corrimiento: la rotación, el corrimiento lógico y el corrimiento aritmético. La diferencia entre esto es lo que hacen con los bits que salen de su lugar. En la rotación, los bits que salen de un lado, entran en el mismo orden del otro lado. En el corrimiento lógico, los bits que salen, se pierden. En el corrimiento aritmético a la derecha, es igual al corrimiento lógico a la izquierda, pero el bit de signo (el más significativo) se mantiene.
Las instrucciones de Branching son instrucciones de salto, nos permiten cambiar el flujo de ejecución cambiando el valor del PC a otra dirección de memoria. Veremos en un capítulo más adelante las diferentes variedades de branching.
Como se ha mencionado antes, existen dos tipos de modos de ejecución en ARM: el modo ARM y el modo Thumb. Existen varias diferencias entre estos dos modos, la más destacable siendo que en el modo Thumb, cada instrucción toma 2 bytes en la memoria, la mitad de lo del modo ARM. Cada bit de una instrucción, ya sea ARM o Thumb, tiene un significado, como su código de operación que indica el tipo de operación que es, la condición para que se ejecute, el código de su registro destino, los operandos, etc. A continuación se muestra una tabla con el formato de instrucciones ARM y Thumb.
Formato de instrucciones en modo ARM
Formato de instrucciones en modo Thumb
En el siguiente artículo, veremos más a detalle cómo funcionan las instrucciones de LOAD y STORE, y sus variedades.