Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
LENGUAJE ENSAMBLADOR
Las instrucciones de lenguaje máquina constan de código de operación y uno o más campos
de referencia de operando. El tamaño de la instrucción es la cantidad de bits que ocupa en
memoria, y en el caso de los núcleos Cortex-M, son de 16 bits y 32 bits.
El juego de instrucciones (ISA1) de los núcleos Cortex-M lo definió ARM Limited con el
nombre THUMB-2. Sin embargo, cada núcleo soporta un subconjunto de este juego de
instrucciones. El único que soporta todas las instrucciones es el núcleo Cortex-M4F que
incluye la unidad de punto flotante (FPU), siendo el núcleo Cortex-M0 es el que menos
instrucciones soporta.
En el lenguaje ensamblador las instrucciones de lenguaje máquina se representan con líneas
de texto. Cada línea consta de una o más de las siguientes partes: etiqueta, nemónico,
operandos, comentario. De éstas, es obligatorio el nemónico y también los operandos, si la
instrucción los requiere.
El nemónico representa la operación que realiza la instrucción, y cada operando representa al
dato a emplear en la operación.
Es el fabricante del CPU (en este caso el núcleo) el que define el lenguaje ensamblador: los
nemónicos, operandos y la sintaxis.
El lenguaje ensamblador tiene, además de los nemónicos, a las directivas. Las directivas no
son instrucciones del CPU. Son comandos del lenguaje ensamblador que permiten hacer cosas
como por ejemplo las siguientes: definir dónde comienza el programa escrito (dirección de
memoria), definir constantes a grabar en la memoria de programa.
Aunque los nemónicos y sintaxis de los operandos son estándar (iguales) entre diferentes
fabricantes de herramientas de software (por ejemplo Keil, Texas Instruments), no lo son las
directivas.
ARM Limited tiene dos versiones de la sintaxis del lenguaje ensamblador. La vigente se
denomina UAL (unified assembly language) y a la anterior la denomina pre-UAL. El Code
Composer Studio permite utilizar ambos, pero en el curso sólo emplearemos el UAL.
1
ISA Instruction Set Architecture: El conjunto de instrucciones del CPU que son visibles al programador.
LenguajeEnsamblador_Cortex-M3yM4_simple_Rev5.docx -1- 23/09/2018
Pontificia Universidad Católica del Perú
Curso : Sistemas Digitales Profesor : Ing. Hugo Pratt
R13 (SP). Es el puntero de pila (stack pointer). Se emplea para implementar una
estructura de datos denominada pila (stack).
R14 (LR). Es el registro de enlace (link register). Se usa para implementar funciones.
Contiene la dirección de retorno cuando se llama a una función.
R15 (PC). Es el contador de programa. Siempre apunta a la siguiente instrucción a
procesar (leer).
PSR. Es el registro de estados (program status register), que contiene a las banderas.
Fig. 1.- Modelo del programador (parcial) de los núcleos Cortex-M3 y Cortex-M4
Banderas (flags)
Las banderas son un conjunto de bits cuyos valores son modificados por algunas instrucciones
según el resultado de una operación. Normalmente están agrupados en un registro especial
llamado registro de banderas (flag register) o registro de estados (status register) , que en los
núcleos Cortex-M3 y Cortex-M4 es el registro PSR (program status register).
Los valores de las banderas son utilizados en ciertas instrucciones, como por ejemplo los
saltos condicionales. Gracias a estas banderas es posible implementar estructuras como IF,
WHILE, REPEAT del lenguaje C.
Los núcleos Cortex®-M3 y Cortex®-M4 cuentan con las siguientes banderas:
Bandera de cero (Z). Indica si el resultado de una operación es cero.
Bandera de acarreo (C). Indica si al efectuar una operación hubo acarreo en el bit más
significativo del resultado.
LenguajeEnsamblador_Cortex-M3yM4_simple_Rev5.docx -2- 23/09/2018
Pontificia Universidad Católica del Perú
Curso : Sistemas Digitales Profesor : Ing. Hugo Pratt
Bandera de negativo (N). Indica el valor del bit más significativo del resultado.
Bandera de desborde (V). Indica si hubo desborde al efectuar operaciones con
números con signo.
No todas las instrucciones modifican (actualizan) las banderas, y muchas instrucciones
permiten indicar si queremos que se actualicen las banderas o no. Veremos esto al tratar sobre
la sintaxis de instrucciones en lenguaje ensamblador.
Fig. 2 Ejemplo 1
Si la instrucción con la que se efectúa la suma modifica las banderas, entonces la bandera Z se
pondrá a 1 en este ejemplo, pues los 32 bits del resultado valen cero.
Ejemplo 2:
Si R0=0x76543210, R1=0x8A09920F y se efectúa la operación R0=R1+R2, el resultado
sería el mostrado en la siguiente figura.
Como no todos los bits del resultado valen cero, la bandera Z se pondrá a 0 si la instrucción
de suma actualiza las banderas.
Fig. 3 Ejemplo 2
La etiqueta y comentario son opcionales. Los operandos pueden ser: ninguno, uno, dos o tres.
Si hay más de uno, se separan con comas.
La etiqueta representa la dirección de inicio de la instrucción. Puede ser cualquier palabra que
comience con un carácter alfabético o ‘$’ y las demás un carácter alfanumérico, o ‘ _’
Se presentan algunos ejemplos:
add, movs, mov y ldr son los nemónicos de las instrucciones. main, lazo_1 son
etiquetas. add y ldr tienen tres operandos y los demás dos. Cada operando está relacionado
con cada campo de referencia de operando del código máquina de la instrucción.
En la primera línea “; inicio del programa” es un comentario.
En la siguiente figura se muestra el ejemplo de una instrucción de 16 bits THUMB-2 en
lenguaje máquina y lenguaje ensamblador.
MOVS es el nemónico y Rd, #imm8 son los operandos en la sintaxis de la instrucción. Más
abajo, en la misma figura, está la instrucción usando como operandos a R7 y la constante
0x35.
Fig. 4 Una variante de instrucción MOV en lenguaje máquina y lenguaje ensamblador. Copia
una constante sin signo de 8 bits a un registro de 32 bits y actualiza las banderas.
Comentarios
En lenguaje ensamblador los comentarios se comienzan con un punto y coma y terminan al
final de la línea de texto. Son equivalentes a los comentarios que comienzan con “ //” en
lenguaje C.
main:
movs r0, #7 ; aquí va nuestro programa en lenguaje ensamblador
; una vez hecha la inicialización del sistema
; (start-up) llega aquí
mov r1, r0
ldr r2, puerto
str r0, [r2]
fin: b fin
puerto: .word 0x400053FC
.end ; aquí termina nuestro programa
Es importante mantener ese orden, y que exista la etiqueta main, pues al compilar el
proyecto, se generará automáticamente el código de inicialización (start-up) pero luego que
este se ejecute, se espera encontrar la primera instrucción con la etiqueta main.
En el ejemplo anterior tenemos lo siguiente:
las dos primeras líneas de texto son comentarios, y en las otras hay comentarios al
final.
mov, ldr, b son nemónicos de instrucciones
.word es otra directiva del lenguaje ensamblador
puerto es una etiqueta
MODOS DE DIRECCIONAMIENTO
Son las diferentes maneras como el CPU accede a los datos a utilizarse en las instrucciones.
Los datos son los valores utilizados por la instrucción para las operaciones matemáticas.
Suelen llamarse a estos datos "operandos".
Los operandos pueden ser los siguientes:
el contenido de un registro del CPU
el contenido de un registro de E/S
un registro de la memoria de programa (flash)
un registro de memoria RAM
una constante que está en el código máquina de la instrucción
Dirección efectiva
Es la dirección de memoria (o de un registro de E/S) en la que se encuentra el operando, una
vez realizada cualquier operación aritmética requerida para determinar dicha dirección.
Las operaciones mencionadas se refieren a los cálculos que hace el CPU a partir de los
valores de los campos de referencia de operando de la instrucción en lenguaje máquina.
Por ejemplo, en una instrucción con tres operandos podríamos tener los siguientes casos:
Que hayan dos operandos, uno emplea uno de los campos de referencia de operando, y
el otro dos.
Que hayan tres operandos, cada uno emplea un campo de referencia de operando.
Uno u otro caso depende de cómo ha sido diseñado el CPU.
Veremos los modos de direccionamiento a nivel de lenguaje ensamblador.
MOV
Copia una constante en un registro del CPU o el contenido de un registro del CPU a otro
registro del CPU.
Registro CPU <== Constante
MOV (inmediato)
Modifica Operandos
Sintaxis Operación
Banderas
MOV Rd, #constante X Rd: R0...R12, LR
Rd := constante
MOVS Rd, #constante √ constante: 0 a 65535
Ejemplo 1
R11 <== 2300 sin modificar banderas
Solución:
MOV R11, #2300
Ejemplo 2
R7 <== 214 actualizando banderas
Solución:
MOVS R7, #214
Ejemplo 1
Solución:
MOV R12, R3
Ejemplo 2
Solución:
MOVS R7, R3
LDR
Permite copiar en un registro del CPU el contenido de un registro de memoria o de E/S para
ello lee del espacio de memoria cuatro direcciones consecutivas. La instrucción es más rápida
en ejecutarse si la dirección efectiva es múltiplo de 4.
Registro memoria
Registro CPU <==
(Flash, RAM, E/S)
LDR (inmediato)
Modifica Operandos
Sintaxis Operación
Banderas
LDR Rt, [Rm] X Rd := [Rm] Rt: R0...R12, PC
LDR Rt, [Rm, #constante] X Rd := [Rm+constante] Rm: R0...R12, PC
constante: 0 a 4096
Ejemplo 1
Si se tiene grabado en el espacio de memoria lo
mostrado a la derecha, y dirección Memoria
R7= 0x40000000 0x0000 0000
Y se ejecutan las siguientes instrucciones:
..... .....
0xFFFF FFFF
El contenido de R7 no se modifica.
Ejemplo 2
Usando los mismos valores de R7 y del espacio de memoria del ejemplo 1 ahora se ejecuta la
instrucción siguiente:
0x4000 0008
0x4000 0009
0x4000 000A
0x4000 000B
LDR (literal)
Esta instrucción permite grabar un número de 32 bits en un registro del CPU. El número está
grabado en una dirección cercana a la instrucción (por lo tanto, en este curso, grabado en la
memoria de programa).
Para poder utilizarla, debe emplearse conjuntamente con la directiva .word, la cual también
se describirá en los ejemplos.
Modifica Operandos
Sintaxis Operación
Banderas
Rt: R0...R15
LDR Rt, etiqueta X Rt := [etiqueta] etiqueta: La etiqueta con la dirección
de inicio del número a leer
Ejemplo 1
LDR R0, Num1
....
....
Num1: .word 0x40004780
Num2: .word 0x12345678
La directiva .word permite definir un número de 32 bits que formará parte del código del
programa (ocupará 4 direcciones consecutivas). Al momento de grabar el programa en la
memoria Flash, se grabarán también estos 4 bytes.
En el ejemplo, al ejecutar la instrucción LDR el CPU determinará la dirección de Num1
(dirección efectiva) y leerá el contenido de las direcciones Num1, Num+1, Num+2 y Num+3.
Los valores leídos los grabará en R0.
Finalmente el valor de R0 será el siguiente:
R0 = 0x40004780
Ejemplo 2
Se quiere leer el registro GPIO_PORTB_DIR_R y grabar su contenido en R7.
LenguajeEnsamblador_Cortex-M3yM4_simple_Rev5.docx -11- 23/09/2018
Pontificia Universidad Católica del Perú
Curso : Sistemas Digitales Profesor : Ing. Hugo Pratt
Solución
La porción de programa que resuelve el problema es el siguiente:
Explicación:
LDR R0, PORTB_DIR
La dirección base del puerto B es 0x40005000
LDR R7, [R0] y el desplazamiento (offset) de GPIODIR es
.... 0x400.
.... La dirección efectiva de GPIO_PORTB_DIR es
Dirección base + offset
PORTB_DIR: .word 0x40005400
Dirección = 0x40005000+0x400 = 0x40005400
Ese valor lo definimos con la directiva .word
STR
Esta instrucción permite copiar el contenido de un registro del CPU en un registro de memoria
(4 direcciones consecutivas). La ejecución de la instrucción es más rápida si la dirección
efectiva es múltiplo de 4.
Registro memoria
Registro CPU ==>
(Flash, RAM, E/S)
STR (inmediato)
Modifica Operandos
Sintaxis Operación
Banderas
STR Rt, [Rn] X [Rn] := Rt Rt: R0...R13
STR Rt, [Rn, X [Rn+constante] := Rt constante: 0 a 4095
#constante]
Ejemplo 2
Escribir el registro GPIODATA del puerto B el dirección Memoria
valor 0x7F.
0x0000 0000
Solución:
LDR R0, PORTB_DATA ..... .....