Ensamblador ARM: Conjunto de instrucciones

blog-image

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 y STR 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.

imagen

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

imagen

Formato de instrucciones en modo Thumb

imagen

En el siguiente artículo, veremos más a detalle cómo funcionan las instrucciones de LOAD y STORE, y sus variedades.