Documentos de Académico
Documentos de Profesional
Documentos de Cultura
6
6.1
6.2
Introducción
Lenguaje ensamblador
6.3 Programación
6.6 Retazos*
operación a realizar como los operandos a usar. Los operandos pueden provenir de la
Micro-
memoria, de los registros o de la propia instrucción.
arquitectura
El hardware de la computadora solo comprende 1 ' sy 0 ' s, entonces las instrucciones son
codificados como números binarios en un formato llamado lenguaje de máquina. Así como +
Lógica
usamos letras para codificar el lenguaje humano, las computadoras usan números binarios
para codificar el lenguaje de la máquina. ARMarchitecture representa cada instrucción como Digital
una palabra de 32 bits. Los microprocesadores son sistemas digitales que leen y ejecutan Circuitos
instrucciones en lenguaje de máquina. Sin embargo, los humanos consideran que leer el
Cosa análoga
+
lenguaje de máquina es tedioso, por lo que preferimos representar las instrucciones en un Circuitos -
diferentes dialectos que a diferentes idiomas. Casi todas las arquitecturas definen
instrucciones básicas, como sumar, restar y bifurcar, que operan en memoria o Física
registros. Una vez que haya aprendido un conjunto de instrucciones, comprender
las demás es bastante sencillo.
295
296 CAPITULO SEIS Arquitectura
a = b + c; AÑADIR a, b, c
son usados. Comienzan con un punto y coma (;) y continúan hasta el final de la línea. El
programa en lenguaje ensamblador en el ejemplo de código 6.3 requiere una variable
temporal t para almacenar el resultado intermedio. El uso de múltiples instrucciones en
lenguaje ensamblador para realizar operaciones más complejas es un ejemplo del
segundo principio de diseño de la arquitectura de la computadora:
Registros
Las instrucciones necesitan acceder a los operandos rápidamente para que puedan ejecutarse
rápidamente. Pero los operandos almacenados en la memoria tardan mucho en recuperarse. Por lo
tanto, la mayoría de las arquitecturas especifican una pequeña cantidad de registros que contienen
operandos de uso común. La arquitectura ARM utiliza 16 registros, llamados conjunto de registro
o registro de archivo. Cuanto menos registros, más rápido se puede acceder a ellos. Esto
conduce al tercer principio de diseño:
a = b + c; ; R0 = a, R1 = b, R2 = c
AÑADIR R0, R1, R2 ;a=b+c
a = b + c - D; ; R0 = a, R1 = b, R2 = c, R3 = d; R4 = t
AÑADIR R4, R1, R2; t = b + c SUB R0,
R4, R3; a = t - D
300 CAPITULO SEIS Arquitectura
Traduzca el siguiente código de alto nivel al lenguaje ensamblador ARM. Asume variables a
- C se mantienen en los registros R0 - R2 y F - j están en R3 - R7.
a = b - C;
f = (g + h) - ( i + j);
El conjunto de registro
Cuadro 6.1 enumera el nombre y el uso de cada uno de los 16 registros ARM. R0 - Los R12 se
utilizan para almacenar variables; R0 - R3 también tiene usos especiales durante las llamadas a
procedimientos. R13 - Los R15 también se denominan SP, LR y PC, y se describirán más adelante
en este capítulo.
Constantes / Inmediatos
Además de las operaciones de registro, las instrucciones ARM pueden usar constantes o
inmediato operandos. Estas constantes se denominan inmediatas, porque sus
valores están disponibles inmediatamente en la instrucción y no requieren un
registro o acceso a la memoria. El ejemplo de código 6.6 muestra el AGREGAR instrucción
agregando un inmediato a un registro. En código ensamblador, el inmediato está
precedido por el símbolo # y puede escribirse en decimal o hexadecimal. Las
constantes hexadecimales en lenguaje ensamblador ARM comienzan con 0x, ya que
Nombre Usar
a = a + 4; ; R7 = a, R8 = b
b = a - 12; AÑADIR R7, R7, # 4 ;a=a+4
SUB R8, R7, # 0xC; b = a - 12
i = 0; ; R4 = yo, R5 = x
x = 4080; MOV R4, # 0 ;i=0
MOV R5, # 0xFF0 ; x = 4080
hacer en C.Los inmediatos son números sin signo de 8 a 12 bits con una
codificación peculiar descrita en Sección 6.4 . La instrucción de movimiento ( MOV) es
una forma útil de inicializar valores de registro. El ejemplo de código 6.7 inicializa
las variables I y X a 0 y 4080, respectivamente. MOV también puede tomar un
operando de origen de registro. Por ejemplo, MOV R1, R7 copia el contenido del
registro R7 en R1.
Memoria
Si los registros fueran el único espacio de almacenamiento para operandos, estaríamos
confinados a programas simples con no más de 15 variables. Sin embargo, los datos también se
pueden almacenar en la memoria. Mientras que el archivo de registro es pequeño y rápido, la
memoria es más grande y más lenta. Por esta razón, las variables de uso frecuente se
mantienen en registros. En la arquitectura ARM, las instrucciones operan exclusivamente en
registros, por lo que los datos almacenados en la memoria deben moverse a un registro antes
de que puedan procesarse. Al utilizar una combinación de memoria y registros, un programa
puede acceder a una gran cantidad de datos con bastante rapidez. Recuperar de la sección
5.5 que las memorias están organizadas como una matriz de palabras de datos. La arquitectura
ARM utiliza direcciones de memoria de 32 bits y palabras de datos de 32 bits.
ARM usa un direccionable por byte memoria. Es decir, cada byte de la memoria tiene una
dirección única, como se muestra en Figura 6.1 (a) . Una palabra de 32 bits consta de cuatro
bytes de 8 bits, por lo que cada dirección de palabra es un múltiplo de 4. El byte más
significativo (MSB) está a la izquierda y el byte menos significativo (LSB) está a la derecha. Tanto
la dirección de palabra de 32 bits como el valor de datos en Figura 6.1 (b) se dan en
hexadecimal. Por ejemplo, la palabra de datos 0xF2F1AC07 se almacena en la dirección de
memoria 4. Por convención, la memoria se extrae con direcciones de memoria baja hacia la
parte inferior y direcciones de memoria alta hacia la parte superior.
ARM proporciona el registro de carga instrucción, LDR, para leer una palabra de datos de
la memoria en un registro. El ejemplo de código 6.8 carga la palabra de memoria 2 en
a ( R7). En C, el número dentro de los corchetes es el índice o número de palabra,
302 CAPITULO SEIS Arquitectura
13 12 11 10 00000010 CD 1 9 A 6 5 B Palabra 4
Figura 6.1 Memoria ARM direccionable por
F mi D C 0000000C 4 0 F 3 0 7 8 8 Palabra 3
byte que muestra: (a) dirección de byte y (b)
B A 9 8 00000008 0 1 EE 2 8 4 2 Palabra 2
datos
7 6 5 4 00000004 F 2 F 1 AC 0 7 Palabra 1
a = mem [2]; ; R7 = a
MOV R5, # 0 ; dirección base = 0
LDR R7, [R5, nº 8]; R7 <= datos en la dirección de memoria (R5 + 8)
Big-Endian Little-Endian
Byte Palabra Byte
Dirección Dirección Dirección
Instrucciones lógicas
BRAZO operaciones lógicas incluir Y, ORR ( O), EOR ( XOR) y BIC
(un poco claro). Cada uno de estos opera bit a bit en dos fuentes y escribe el resultado
304 CAPITULO SEIS Arquitectura
Registros de origen
Figura 6.3 Operaciones lógicas Y R3, R1, R2 R3 0100 0110 1010 0001 0000 0000 0000 0000 1111 1111
EOR R5, R1, R2 R5 1011 1001 0101 1110 1111 0001 0000 1011 0111
BIC R6, R1, R2 R6 0000 0000 0000 1111 0001 0000 0000 1011 0111
Instrucciones de turno
Instrucciones de turno desplaza el valor en un registro hacia la izquierda o hacia la derecha, eliminando bits del
final. La instrucción de rotación rota el valor en un registro a la derecha hasta 31 bits. Nos referimos tanto a
shift como a rotar genéricamente como operaciones de cambio. Las operaciones de cambio de ARM son LSL ( desplazamiento
lógico a la izquierda), LSR ( desplazamiento lógico a la derecha),
ASR ( desplazamiento aritmético a la derecha), y ROR ( Gira a la derecha). No hay ROL
instrucción porque la rotación a la izquierda se puede realizar con una rotación a la derecha por
una cantidad complementaria.
Como se discutió en la Sección 5.2.5, los desplazamientos a la izquierda siempre llenan los
bits menos significativos con 0 ' s. Sin embargo, los cambios a la derecha pueden ser lógicos (0 ' s
cambia a los bits más significativos) o aritmética (el bit de signo cambia a los bits más
significativos). La cantidad por la que cambiar puede ser inmediata o registrada.
Figura 6.4 muestra el código ensamblador y los valores de registro resultantes
para LSL, LSR, ASR, y ROR al cambiar por un valor inmediato. R5 se desplaza por la
cantidad inmediata y el resultado se coloca en el registro de destino.
6.3 Programación 305
Registro de origen
LSR R1, R5, n.º 17 R1 0000 0000 0000 0 111 1111 1000 1110
ASR R2, R5, n.º 3 R2 111 1 1111 1110 0011 1000 0010 0001 1100
ROR R3, R5, n.º 21 R3 1110 0000 1000 0111 0011 1 111 1111 1000
Registros de origen
LSL R4, R8, R6 R4 0110 1110 0111 0000 0000 0000 0000 0000
ROR R5, R8, R6 R5 1100 0001 0110 1110 0111 0000 1000 0001
Cambiar un valor dejado por norte es equivalente a multiplicarlo por 2 NORTE. Del mismo modo, cambiar
aritméticamente un valor a la derecha norte es equivalente a dividirlo por 2 NORTE, como se discutió en la
Sección 5.2.5. Los cambios lógicos también se utilizan para extraer o ensamblar campos de bits.
Figura 6.5 muestra el código ensamblador y los valores de registro resultantes para las
operaciones de cambio donde la cantidad de cambio se mantiene en un registro, R6. Esta
instrucción usa el registro de registro desplazado modo de direccionamiento, donde un registro
(R8) se desplaza por la cantidad (20) retenida en un segundo registro (R6).
Cada una de estas instrucciones también tiene una variante de acumulación múltiple,
MLA, SMLAL, y UMLAL, que agrega el producto a una suma corriente de 32 o 64 bits. Estas
instrucciones pueden mejorar el rendimiento matemático en aplicaciones como la
multiplicación de matrices y el procesamiento de señales que consisten en
multiplicaciones y sumas repetidas.
CPSR
31 30 29 28 4 3 2 1 0
Figura 6.6 Registro de estado del Los programas serían aburridos si solo pudieran ejecutarse en el mismo orden cada vez.
programa actual (CPSR) Instrucciones ARM configuradas opcionalmente banderas de condición en función de si el
resultado es negativo, cero, etc. Las instrucciones posteriores se ejecutan condicionalmente, dependiendo
del estado de esos indicadores de condición. Los indicadores de condición ARM, también
Los cinco bits menos
llamados banderas de estado, son negativos NORTE), cero ( Z), llevar ( C),
significativos del CPSR son modo
y desbordamiento V), como se indica en Cuadro 6.2 . Estos indicadores los establece la ALU (consulte la
bits y se describirá en
Sección 5.2.4) y se mantienen en los 4 bits superiores de la configuración de 32 bits. Pro actual
Sección 6.6.3 .
Registro de estado del gramo (CPSR), como se muestra en Figura 6.6 .
La forma más común de establecer los bits de estado es con la comparación ( CMP)
instrucción, que resta el segundo operando fuente del primero y establece los
Otras instrucciones útiles para indicadores de condición según el resultado. Por ejemplo, si los números son
comparar dos valores son CMN, TST, iguales, el resultado será cero y el Z la bandera está puesta. Si el primer
y TEQ. Cada instrucción realiza una número es un valor sin signo mayor o igual que el segundo, la resta producirá
operación, actualiza los indicadores una ejecución y la C la bandera está puesta.
de condición y Las instrucciones posteriores se pueden ejecutar condicionalmente dependiendo
descarta el resultado. CMN del estado de las banderas. La instrucción mnemotécnica va seguida de una condición
(comparar negativo) compara mnemotécnica que indica cuándo ejecutar. Cuadro 6.3 enumera el campo de condición
la primera fuente al negativo de la
de 4 bits ( cond), el mnemónico de condición, el nombre y el estado de los indicadores de
segunda fuente por
condición que dan como resultado la ejecución de la instrucción (CondEx). Por ejemplo,
agregando las dos fuentes. Como
suponga que un programa realiza CMP R4, R5, y luego ADDEQ R1, R2, R3.
se mostrará en Sección 6.4 , Solo
instrucciones ARM
La comparación establece el Z marca si R4 y R5 son iguales, y el ADDEQ
codificar inmediatos positivos. se ejecuta solo si el Z la bandera está puesta. El cond El campo se utilizará en codificaciones de
Entonces, CMN R2, n. ° 20 se usa lenguaje de máquina en Sección 6.4 .
en lugar de CMP R2, # -20.
TST ( test) ANDs los operandos
de origen. Es útil para
comprobar si alguna parte del registro es Cuadro 6.2 Indicadores de condición
Otras instrucciones de procesamiento de datos establecerán los indicadores de condición Sin firmar firmado
cuando la instrucción mnemotécnica va seguida de " S. " Por ejemplo, SUBS R2, R3, R7 A = 0101 2 A=5 A=5
restará R7 de R3, pondrá el resultado en R2 y establecerá los indicadores de condición. La Tabla B = 1101 2 B = 13 B = –3
B.5 en el Apéndice B resume qué indicadores de condición están influenciados por cada A - B: 0101 NZCV = 1001 2
instrucción. Todas las instrucciones de procesamiento de datos afectarán norte y + 0011 HS: FALSO
Z banderas basadas en si el resultado es cero o si tiene el bit más significativo (B) 1000 GE: CIERTO
establecido. AGREGA y SUBS también influye V y C, y cambia la influencia C.
Figura 6.7 Comparación firmada y no
El ejemplo de código 6.10 muestra instrucciones que se ejecutan
firmada: HS frente a GE
condicionalmente. La primera instrucción, CMP R2, R3, se ejecuta incondicionalmente
y establece las banderas de condición. Las instrucciones restantes se ejecutan
condicionalmente, dependiendo de los valores de las banderas de condición.
Suponga que R2 y R3 contienen los valores 0x80000000 y 0x00000001. La
comparación calcula R2 - R3 = 0x80000000 - 0x00000001 = 0x80000000 + 0xFFFFFFFF
= 0x7FFFFFFF con un llevar a cabo ( C = 1). Las fuentes tenían signos opuestos y el
signo del resultado difiere del signo de la primera fuente, por lo que el resultado se
desborda ( V = 1). Las banderas restantes ( norte y Z) son 0. ANDHS ejecuta
308 CAPITULO SEIS Arquitectura
CMP R2, R3
ADDEQ R4, R5, # 78 ANDHS
R7, R8, R9 ORRMI R10, R11,
R12 EORLT R12, R7, R10
6. 3. 3 Derivación
Una ventaja de una computadora sobre una calculadora es su capacidad para tomar decisiones.
Una computadora realiza diferentes tareas dependiendo de la entrada. Por ejemplo, las
declaraciones if / else, las declaraciones switch / case, los bucles while y los bucles for ejecutan
código de forma condicional dependiendo de alguna prueba.
Una forma de tomar decisiones es utilizar la ejecución condicional para ignorar
ciertas instrucciones. Esto funciona bien para sentencias if simples en las que se ignora
una pequeña cantidad de instrucciones, pero es un desperdicio para sentencias if con
muchas instrucciones en el cuerpo, y es insuficiente para manejar bucles. Por lo tanto,
ARM y la mayoría de las otras arquitecturas utilizan instrucciones de rama
para omitir secciones de código o repetir código.
Un programa generalmente se ejecuta en secuencia, con el contador de programa (PC)
incrementándose en 4 después de cada instrucción para apuntar a la siguiente instrucción.
(Recuerde que las instrucciones tienen 4 bytes de longitud y ARM es una arquitectura con
dirección única). Las instrucciones de rama cambian el contador del programa. ARM incluye dos
tipos de ramas: una simple rama ( B) y rama y enlace LICENCIADO EN DERECHO). licenciado en
Derecho se utiliza para llamadas a funciones y se analiza en Sección 6.3.7 . Al igual que otras
instrucciones ARM, las ramas pueden ser incondicionales o condicionales. Las ramas también
se llaman saltos en algunas arquitecturas.
El ejemplo de código 6.11 muestra la bifurcación incondicional usando la
instrucción de bifurcación B. Cuando el código llega al B OBJETIVO instrucción, la
rama es tomado. Es decir, la siguiente instrucción ejecutada es la SUB
instrucción justo después de la etiqueta llamado OBJETIVO.
El código de ensamblaje usa etiquetas para indicar ubicaciones de instrucciones en el
programa. Cuando el código de ensamblaje se traduce en código de máquina, estas etiquetas
se traducen en direcciones de instrucciones (ver Sección 6.4.3 ). Las etiquetas de ensamblaje
ARM no pueden ser palabras reservadas, como mnemotécnicos de instrucción. La mayoría de
los programadores sangran sus instrucciones, pero no las etiquetas, para ayudar
6.3 Programación 309
OBJETIVO
SUB R1, R1, # 78 ; R1 = R1 - 78
ALLÍ
AÑADIR R1, R1, nº 78; R1 = R1 + 78 = 87
hacer que las etiquetas se destaquen. El compilador ARM hace que esto sea un requisito: las
etiquetas no deben tener sangría y las instrucciones deben ir precedidas de espacios en blanco.
Algunos compiladores, incluido GCC, requieren dos puntos después de la etiqueta.
Las instrucciones de bifurcación se pueden ejecutar condicionalmente según los
mnemónicos de condición enumerados en Cuadro 6.3 . El ejemplo de código 6.12 ilustra
el uso de BEQ, ramificación dependiente de la igualdad Z = 1). Cuando el código llega al BEQ instrucción,
la Z el indicador de condición es 0 (es decir, R0 ≠ R1), entonces la rama es no tomado. Es
decir, la siguiente instrucción ejecutada es la
ORR instrucción.
6. 3. 4 Declaraciones condicionales
Las declaraciones if, if / else y switch / case son declaraciones condicionales comúnmente
utilizadas en lenguajes de alto nivel. Cada uno de ellos ejecuta condicionalmente un
cuadra de código que consta de una o más declaraciones. Esta sección muestra
cómo traducir estas construcciones de alto nivel al lenguaje ensamblador ARM.
si declaraciones
Una sentencia if ejecuta un bloque de código, el si bloque, solo cuando se cumple una
condición. El ejemplo de código 6.13 muestra cómo traducir una instrucción if en código
ensamblador ARM.
310 CAPITULO SEIS Arquitectura
declaraciones if / else
Las sentencias if / else ejecutan uno de los dos bloques de código dependiendo de una
condición. Cuando se cumple la condición de la sentencia if, la si bloque es ejecutado. De
lo contrario, el otro bloque es ejecutado. El ejemplo de código 6.14 muestra un ejemplo
de declaración if / else.
Al igual que las declaraciones if, el código ensamblador if / else prueba la condición
opuesta a la del código de alto nivel. En el ejemplo de código 6.14, el código de alto nivel
prueba para manzanas == naranjas, y las pruebas de código ensamblador para
manzanas! = naranjas. Si esa condición opuesta es VERDADERA, BNE omite el
bloque if y ejecuta el bloque else. De lo contrario, el bloque if se ejecuta y
termina con una rama incondicional ( B) pasado el bloque else.
6.3 Programación 311
6. 3. 5 Volviéndose loco
Los bucles ejecutan repetidamente un bloque de código según una condición. Los
bucles while y for son construcciones de bucles comunes utilizadas por los
lenguajes de alto nivel. Esta sección muestra cómo traducirlos al lenguaje
ensamblador ARM, aprovechando la ramificación condicional.
while Loops
Los bucles while ejecutan repetidamente un bloque de código hasta que se cumple una condición. no
El En t El tipo de datos en C se refiere a
reunió. El ciclo while en el ejemplo de código 6.16 determina el valor de X tal
una palabra de datos que representa
que 2 x = 128. Se ejecuta siete veces, hasta pow = 128.
dos ' s complemento entero. ARM usa
Al igual que las declaraciones if / else, el código ensamblador para bucles
palabras de 32 bits, por lo que un
En t representa un número en el
while prueba la condición opuesta a la del código de alto nivel. Si la condición
rango [ - 2 31, 2 31 - 1]. opuesta es VERDADERA (en este caso, R0 == 128), el ciclo while finaliza. Si no
(R0 ≠ 128), la rama no se toma y el cuerpo del bucle se ejecuta.
En el ejemplo de código 6.16, el ciclo while compara pow a 128 y sale del bucle si es igual. De lo
contrario se duplica pow usando un desplazamiento a la izquierda), incrementos X,
y se ramifica de nuevo al inicio del ciclo while.
para bucles
Es muy común inicializar una variable antes de un ciclo while, verificar esa
variable en la condición del ciclo y cambiar esa variable cada vez a través del
ciclo while. Los bucles for son una práctica abreviada que combina la
inicialización, la verificación de condición y el cambio de variable en un solo
lugar. El formato del bucle for es:
6. 3. 6 Memoria
Para facilitar el almacenamiento y el acceso, los datos similares se pueden agrupar en un formación.
Una matriz almacena su contenido en direcciones de datos secuenciales en la memoria.
Cada elemento de la matriz se identifica por un número llamado su índice. El número de
elementos de la matriz se denomina longitud de la matriz.
Figura 6.8 muestra una matriz de 200 elementos de puntuaciones almacenadas en la Dirección Datos
memoria. El ejemplo de código 6.18 es un algoritmo de inflación de calificaciones que agrega 1400031C puntuaciones [199]
10 puntos a cada una de las calificaciones. Tenga en cuenta que no se muestra el código para 14000318 puntuaciones [198]
inicializar la matriz de puntuaciones. El índice de la matriz es una variable ( I) en lugar de una
constante, por lo que debemos multiplicarlo por 4 antes de agregarlo a la dirección base.
ARM puede escala ( multiplique) el índice, agréguelo a la dirección base y 14000004 puntuaciones [1]
cargue desde la memoria en una sola instrucción. En vez de LSL y LDR 14000000 puntuaciones [0]
secuencia de instrucciones en el ejemplo de código 6.18, podemos usar una sola instrucción:
Memoria principal
LDR R3, [R0, R1, LSL # 2]
Figura 6.8 Retención de memoria
R1 se escala (se desplaza a la izquierda en dos) y luego se agrega a la dirección base (R0). Por puntuaciones [200] comenzando en la
CÍRCULO
puntuaciones [i] = puntuaciones [i] + 10; CMP R0, R1 ; alcanzado el final de la matriz?
BGE L3 ; si es así, salir del ciclo; R2 =
LDR R2, [R0] puntuaciones [i]
AGREGAR R2, R2, # 10 ; R2 = puntuaciones [i] + 10
STR R2, [R0], n.º 4 ; puntuaciones [i] = puntuaciones [i] +
10; entonces R0 = R0 + 4
B CÍRCULO ; repetir bucle
L3
Bytes y caracteres
Números en el rango [ - 128, 127] se pueden almacenar en un solo byte en lugar de
una palabra completa. Debido a que hay mucho menos de 256 caracteres en un
teclado en inglés, los caracteres en inglés a menudo se representan mediante
Otros lenguajes de programación,
bytes. El lenguaje C usa el tipo carbonizarse para representar un byte o carácter.
como Java, utilizan diferentes
Memoria Registros
Dirección de byte 3 2 1 0 R1 00 00 00 8C LDRB R1, [R4, # 2]
Datos F7 8C 42 03 Figura 6.9 Instrucciones para cargar y
R2 FF FF FF8C LDRSB R2, [R4, n.º 2] almacenar bytes
Ejemplo 6.2 USANDO LDRB Y STRB PARA ACCEDER A UNA MATRIZ DE CARACTERES
El siguiente código de alto nivel convierte una matriz de caracteres de 10 entradas de minúsculas a mayúsculas
restando 32 de cada entrada de la matriz. Traducirlo al lenguaje ensamblador ARM. Recuerde que la diferencia
de direcciones entre los elementos de la matriz ahora es de 1 byte, no de 4 bytes. Suponga que R0 ya tiene la
dirección base de
chararray.
para (i = 0; i <10; i = i + 1)
chararray [i] = chararray [i] - 32;
Solución:
; Código de ensamblaje ARM
; R0 = dirección base de chararray (inicializada antes), R1 = i
MOV R1, # 0 ;i=0
CÍRCULO CMP R1, n.º 10 ; yo <10?
BGE HECHO ; si (i> = 10), salir del ciclo
LDRB R2, [R0, R1] ; R2 = mem [R0 + R1] = chararray [i]
SUB R2, R2, # 32 ; R2 = chararray [i] - 32
STRB R2, [R0, R1] ; chararray [i] = R2
AÑADIR R1, R1, # 1 ; yo = yo + 1
B CÍRCULO ; repetir bucle
HECHO
y se extiende desde la dirección 0x1522FFF0 hasta 0x1522FFF6. El primer Dirección de palabra Datos
La persona que llama no debe interferir con el comportamiento de la persona que llama. Esto
significa que la persona que llama debe saber a dónde regresar después de que se complete y no debe
pisotear ningún registro o memoria que necesite la persona que llama. La persona que llama almacena
la dirección de retorno en el registro de enlace LR al mismo tiempo que salta al destinatario de la
llamada utilizando la instrucción de bifurcación y enlace ( LICENCIADO EN DERECHO). La persona que
llama no debe sobrescribir ningún estado arquitectónico o memoria del que dependa la persona que
llama. Específicamente, el destinatario debe dejar el registros guardados
(R4 - R11 y LR) y el apilar, una parte de la memoria utilizada para variables
temporales, sin modificar.
Esta sección muestra cómo llamar y regresar desde una función. Muestra cómo las
funciones acceden a los argumentos de entrada y al valor de retorno y cómo usan la pila
para almacenar variables temporales.
funciones usan R0 - R3 para argumentos de entrada y R0 para el valor de retorno. En hacen un retorno de función usando
int main () { ; R4 = y
int y; PRINCIPAL El ejemplo de código 6.21 tiene algunos
... ...
errores sutiles. Ejemplos de código
y = sumas difusas (2, 3, 4, 5); MOV R0, n.º 2 ; argumento 0 = 2;
... MOV R1, n.º 3 argumento 1 = 3; 6.22 - 6.25 espectáculo mejorado
} MOV R2, n.º 4 argumento 2 = 4; versiones del programa.
MOV R3, n.º 5 argumento 3 = 5
BL DIFFOFSUMS; función de llamada MOV
R4, R0 ; y = valor devuelto
...
; R4 = resultado
int diffofsums (int f, int g, int h, int i) { DIFFOFSUMS
resultado int; AÑADIR R8, R0, R1 ; R8 = f + g
AÑADIR R9, R2, R3 ; R9 = h + yo
resultado = (f + g) - ( h + i); devolver SUB R4, R8, R9 ; resultado = (f + g) - ( h + i); poner el
resultado; MOV R0, R4 valor de retorno en R0; volver a la
} MOV PC, LR persona que llama
320 CAPITULO SEIS Arquitectura
(a) La pila es una cola de último en entrar, primero en salir (LIFO). Como una pila de
platos, el último elemento empujado en la pila (el plato superior) es el primero que se
Dirección Datos
puede estalló apagado. Cada función puede asignar espacio de pila para almacenar
variables locales, pero debe desasignarlo antes de regresar. El parte superior de la pila es
BEFFFAE8 AB000001
el espacio asignado más recientemente. Mientras que una pila de platos crece en el
BEFFFAE4 12345678
espacio, la pila ARM crece en la memoria. La pila se expande a direcciones de memoria
BEFFFAE0 FFEEDDCC SP
más bajas cuando un programa necesita más espacio temporal.
BEFFFADC
Figura 6.11 muestra una imagen de la pila. El puntero de pila, SP (R13), es un
registro ARM ordinario que, por convención, puntos hacia parte superior de la pila. Un
(B) puntero es un nombre elegante para una dirección de memoria. SP apunta a (da la
Memoria
dirección de) datos. Por ejemplo, en Figura 6.11 (a) , el puntero de pila, SP, contiene
Figura 6.11 La pila (a) antes de la el valor de dirección 0XBEFFFAE8 y apunta al valor de datos 0xAB000001.
expansión y (b) después de la expansión de
dos palabras El puntero de pila (SP) comienza en una dirección de memoria alta y disminuye para
expandirse según sea necesario. Figura 6.11 (b) muestra la pila expandiéndose para
Por lo general, la pila se almacena boca
permitir dos palabras de datos más de almacenamiento temporal. Para hacerlo, SP
abajo en la memoria, de modo que la
disminuye en ocho para convertirse en 0xBEFFFAE0. Dos palabras de datos adicionales,
parte superior de la pila es en realidad
0x12345678 y 0xFFEEDDCC, se almacenan temporalmente en la pila.
la dirección más baja y la pila crece
hacia las direcciones de memoria más
Uno de los usos importantes de la pila es guardar y restaurar registros
bajas. Esto se llama pila descendente. ARM que utiliza una función. Recuerde que una función debe calcular un valor de
también permite pilas ascendentes que retorno pero no tener otros efectos secundarios no deseados. En particular, no
crecen hacia direcciones de memoria debe modificar ningún registro además de R0, el que contiene el valor de
más altas. El puntero de la pila retorno. El diffofsums La función en el ejemplo de código 6.21 viola esta regla
normalmente apunta al elemento porque modifica R4, R8 y R9. Si principal había estado usando estos registros
superior de la pila; esto se llama completa antes de la llamada a diffosums, su contenido habría sido dañado por la
pila. ARM también permite
llamada a la función.
Para resolver este problema, una función guarda registros en la pila antes
pilas vacías en el que SP apunta una
de modificarlos, luego los restaura de la pila antes de que regrese. En
palabra más allá de la parte superior de la
concreto, realiza los siguientes pasos:
pila. El brazo Interfaz binaria de aplicación
( ABI) define una forma estándar en la que 1. Hace espacio en la pila para almacenar los valores de uno o más registros.
las funciones pasan variables y usan la pila
para que las bibliotecas desarrolladas por 2. Almacena los valores de los registros en la pila.
diferentes compiladores puedan
3. Ejecuta la función usando los registros.
interoperar. Especifica un descendente 4. Restaura los valores originales de los registros de la pila.
completo stack, que usaremos en
este capítulo. 5. Distribuye espacio en la pila.
6.3 Programación 321
; R4 = resultado
DIFFOFSUMS
SUB SP, SP, # 12 ; hacer espacio en la pila para 3 registros; guardar
STR R9, [SP, # 8] R9 en la pila
STR R8, [SP, # 4] ; guardar R8 en la pila;
STR R4, [SP] guardar R4 en la pila
Figura 6.12 La pila: (a) antes, (b) durante y (c) después de la diffofsums Llamada de función
322 CAPITULO SEIS Arquitectura
no hay otros efectos secundarios: R4, R8, R9 y SP tienen los mismos valores que tenían
antes de la llamada a la función.
El espacio de pila que una función se asigna a sí misma se llama su marco de pila. diffofsums
' El marco de la pila tiene tres palabras de profundidad. El principio de modularidad nos
dice que cada función debe acceder solo a su propio marco de pila, no a los marcos que
pertenecen a otras funciones.
; R4 = resultado
DIFFOFSUMS
STMFD SP !, {R4, R8, R9} ; presione R4 / 8/9 en la pila descendente completa
LDMFD SP !, {R4, R8, R9} MOV ; quitar R4 / 8/9 de la pila descendente completa; volver a la
LDM y STM vienen en cuatro sabores para pilas descendentes y ascendentes llenas y
vacías ( FD, ED, FA, EA). El SP! en las instrucciones indica almacenar los datos relativos al
puntero de la pila y actualizar el puntero de la pila después del almacenamiento o la
carga. EMPUJAR y MÚSICA POP son sinónimos de STMFD SP !, {regs}
y LDMFD SP !, {regs}, respectivamente, y son la forma preferida de guardar registros
en la pila descendente completa convencional.
Registros preservados
Los ejemplos de código 6.22 y 6.23 asumen que todos los registros utilizados
(R4, R8 y R9) deben guardarse y restaurarse. Si la función de llamada no usa
esos registros, el esfuerzo para guardarlos y restaurarlos es en vano. Para
evitar este desperdicio, ARM divide los registros en Preservado y
sin conservantes categorías. Los registros conservados incluyen R4 - R11. Los
registros no conservados son R0 - R3 y R12. SP y LR (R13 y R14)
6.3 Programación 323
también debe conservarse. Una función debe guardar y restaurar cualquiera de los
registros preservados que desee usar, pero puede cambiar los registros no preservados
libremente.
El ejemplo de código 6.24 muestra una versión mejorada de diffofsums
eso guarda solo R4 en la pila. También ilustra el preferido EMPUJAR y MÚSICA POP
sinónimos. El código reutiliza los registros de argumentos no preservados R1 y R3 para
contener las sumas intermedias cuando esos argumentos ya no son necesarios.
Ejemplo de código 6.24 REDUCIR EL NÚMERO DE REGISTROS CONSERVADOS EMPUJAR ( y MÚSICA POP) guardar (y
restaurar) registros en la pila en orden de
Código de ensamblaje ARM número de registro de menor a mayor,
MOV PC, LR ; volver a la persona que llama memoria más baja, luego R3 y finalmente
R8 en las siguientes direcciones de
memoria más altas de la pila.
Recuerde que cuando una función llama a otra, la primera es la persona que llama y
la última es el destinatario. El destinatario de la llamada debe guardar y restaurar los
registros conservados que desee utilizar. El destinatario puede cambiar cualquiera de los
registros no conservados. Por lo tanto, si la persona que llama tiene datos activos en un
registro no conservado, la persona que llama necesita guardar ese registro no
preservado antes de realizar la llamada a la función y luego debe restaurarlo. Por estas
razones, los registros conservados también se denominan llamar-guardar, y los registros
no conservados se denominan llamada-guardar.
Cuadro 6.6 resume qué registros se conservan. R4 - Los R11 generalmente se
usan para contener variables locales dentro de una función, por lo que deben
guardarse. LR también debe guardarse, para que la función sepa dónde regresar.
Apilar encima del puntero de la pila Pila debajo del puntero de pila
324 CAPITULO SEIS Arquitectura
Regla para guardar llamadas: Antes de una llamada de función, la persona que llama debe
guardar los registros no conservados (R0 - R3 y R12) que necesita después de la llamada. Después
de la llamada, debe restaurar estos registros antes de usarlos.
Regla para guardar llamadas: Antes de que una persona que llama perturbe cualquiera de los
registros conservados (R4 - R11 y LR), debe guardar los registros. Antes de que regrese, debe
restaurar estos registros.
DIFFOFSUMS
AGREGAR R1, R0, R1 ; R1 = f + g
AGREGAR R3, R2, R3 ; R3 = h + yo
SUB R0, R1, R3 ; volver (f + g) - ( h + i); volver a la
MOV PC, LR persona que llama
6.3 Programación 325
El ejemplo de código 6.26 demuestra una función sin hojas f1 y una función de
hoja f2 incluyendo todo el guardado y conservación de registros necesarios.
Suponer f1 mantiene I en R4 y X en R5. f2 mantiene r en R4. f1 utiliza registros
preservados R4, R5 y LR, por lo que inicialmente los coloca en la pila de acuerdo con Una función no hoja sobrescribe LR
la regla de guardado del destinatario. Utiliza R12 para mantener el resultado cuando llama a otra función usando LICENCIADO
intermedio ( a - B) por lo que no necesita conservar otro registro para este cálculo. EN DERECHO. Por lo tanto, una función
que no sea de hoja siempre debe guardar
Antes de llamar f2, f1 inserta R0 y R1 en la pila de acuerdo con la regla de guardado
LR en su pila y restaurarla antes de
de la persona que llama porque estos son registros no preservados que f2 podría
regresar.
cambiar y eso f1 todavía necesitará después de la llamada. Aunque R12 es también
un registro no preservado que f2 podría sobrescribir,
f1 ya no necesita R12 y no ' t tengo que salvarlo. f1 luego pasa el argumento a f2 en
R0, realiza la llamada a la función y utiliza el resultado en R0. f1 luego restaura
R0 y R1 porque todavía los necesita. Cuándo f1
termina, pone el valor de retorno en R0, restaura los registros preservados R4,
R5 y LR, y regresa. f2 guarda y restaura R4 de acuerdo con la regla de guardar
llamadas.
Ejemplo de código 6.26 LLAMADA A FUNCIÓN NONLEAF En una inspección cuidadosa, uno
podría notar que f2 no modifica R1,
por lo que f1 no necesitaba
Código de alto nivel Código de ensamblaje ARM
guardarlo y restaurarlo.
int f1 (int a, int b) { ; R0 = una, R1 = b, R4 = yo, R5 = x F1
Sin embargo, un compilador no siempre
int i, x;
PULSE {R4, R5, LR} ; guardar los registros conservados utilizados por puede determinar fácilmente qué
x = (a + b) * (a - B); para (i = AÑADIR R5, R0, R1 f1; x = (a + b) registros no conservados se pueden
0; i <a; i ++) SUB R12, R0, R1 ; temp = (a - B)
x = x + f2 (b + i); return alterar durante una llamada a una función.
MUL R5, R5, R12 ; x = x * temp = (a + b) * (a - B) ; i = 0
x; MOV R4, # 0 Por tanto, un compilador simple siempre
} POR
hará que la persona que llama guarde y
CMP R4, R0 ; yo <a?
BGE REGRESO ; no: bucle de salida restaure cualquier
PRESIONE {R0, R1} ; guardar registros no conservados; el registros no conservados que
AGREGAR R0, R1, R4 argumento es b + i
necesita después de la llamada.
F2
licenciado en Derecho ; llamar a f2 (b + i)
AGREGAR R5, R5, R0 ; x = x + f2 (b + i)
MÚSICA POP {R0, R1} ; restaurar registros no conservados; yo ++
AGREGAR R4, R4, n.º 1
B POR ; continuar para bucle
REGRESO
MOV R0, R5 ; el valor de retorno es x
MÚSICA POP {R4, R5, LR} ; restaurar registros conservados; regreso
MOV PC, LR de f1
marco de pila de F1
R5 R5
marco de pila de F1
BEF7FF04 BEF7FF04 BEF7FF04
pila de f2
marco
BEF7FEF4 BEF7FEF4 BEF7FEF4 R4 SP
Figura 6.13 La pila: (a) antes de las llamadas a funciones, (b) durante f1, y (c) durante f2
A función recursiva es una función sin hojas que se llama a sí misma. Las funciones recursivas
se comportan tanto como llamador como como destinatario de la llamada y deben guardar
tanto los registros conservados como los no conservados. Por ejemplo, la función factorial se
puede escribir como una función recursiva. Recordar que factorial (n) = n × ( norte - 1)
× ( norte - 2) × ⋯ × 2 × 1. La función factorial se puede reescribir de forma recursiva como factorial
(n) = n × factorial (n - 1), como se muestra en el ejemplo de código 6.27. El factorial de 1 es
simplemente 1. Para referirnos cómodamente a las direcciones de los programas, mostramos
el programa que comienza en la dirección 0x8500.
De acuerdo con la regla de guardar llamadas, factorial es una función sin
hojas y debe guardar LR. De acuerdo con la regla de guardar llamadas, factorial necesitará
norte después de llamarse a sí mismo, por lo que debe guardar R0. Por lo
tanto, empuja ambos registros a la pila al principio. Luego comprueba si norte ≤
1. Si es así, pone el valor de retorno de 1 en R0, restaura el puntero de la pila y
regresa a la persona que llama. No es necesario recargar LR y R0 en este caso,
porque nunca se modificaron. Si n> 1, la función llama de forma recursiva factorial
(n - 1). Luego restaura el valor de norte y el registro de enlace (LR) de la pila,
realiza la multiplicación y devuelve este resultado. Tenga en cuenta que la
función restaura inteligentemente norte en R1, para no sobrescribir el valor
devuelto. La instrucción de multiplicar ( MUL R0, R1, R0) multiplica n ( R1) y el valor
devuelto (R0) y pone el resultado en R0.
6.3 Programación 327
int factorial (int n) { 0x8500 FACTORIAL PRESIONE {R0, LR} ; presione ny LR en la pila; R0 <=
si (n <= 1) 0x8504 CMP R0, n.º 1 1?
return 1; 0x8508 BGT DEMÁS ; no: rama a otra
0x850C MOV R0, n.º 1 ; de lo contrario, devuelve 1
0x8510 AGREGAR SP, SP, nº 8; restaurar SP MOV
0x8514 PC, LR ; regreso
demás 0x8518 DEMÁS SUB R0, R0, nº 1; n = n - 1 BL
retorno (n * factorial (n - 1)); 0x851C FACTORIAL; llamada recursiva POP
} 0x8520 {R1, LR} ; pop n (en R1) y LR
0x8524 MUL R0, R1, R0; R0 = n * factorial (n - 1)
0x8528 MOV PC, LR ; regreso
0xBEFF0FE8 y LR en 0xBEFF0FEC, como se muestra en Figura 6.14 (b) . La registros al comienzo de una llamada a
Figura 6.14 Apilar: (a) antes, (b) durante y (c) después de la llamada a la función factorial con n = 3
328 CAPITULO SEIS Arquitectura
Figura 6.15 (a) muestra a la persona que llama ' s pila para llamar a una función con más
de cuatro argumentos.
Una función también puede declarar matrices o variables locales. Las variables
locales se declaran dentro de una función y solo se puede acceder a ellas dentro de esa
función. Las variables locales se almacenan en R4 - R11; si hay demasiadas variables
locales, también se pueden almacenar en la función ' s marco de pila. En particular, las
matrices locales se almacenan en la pila.
Figura 6.15 (b) muestra la organización de un destinatario ' s marco de pila. El marco de pila
contiene los registros temporales y el registro de enlace (si es necesario guardarlos debido a
una llamada de función posterior) y cualquiera de los registros guardados.
(si es necesario)
(b) después de la llamada
Variables locales o
matrices
SP
(a) (B)
6.4 Lenguaje de máquina 329
registra que la función se modificará. También contiene matrices locales y cualquier exceso de
variables locales. Si el destinatario de la llamada tiene más de cuatro argumentos, los
encuentra en el llamador ' s marco de pila. El acceso a argumentos de entrada adicionales es la
única excepción en la que una función puede acceder a datos de pila que no están en su propio
marco de pila.
Procesamiento de datos
Figura 6.16 Procesamiento de datos 31:28 27:26 25:20 19:16 15:12 11: 0
Inmediato
11: 8 7: 0
putrefacción imm8
Procesamiento de datos
Yo = 1
31:28 27:26 25 24:21 20 19:16 15:12 11: 0 11: 7 6: 5 4 3: 0
funct Yo = 0
11: 8 7 6: 5 4 3: 0
Rs 0 sh 1 Rm
Registro cambiado
Registrarse
Figura 6.17 Formato de instrucción de procesamiento de datos que muestra funct campo y Src2 variaciones
6.4 Lenguaje de máquina 331
Cuadro 6.7 Rotaciones inmediatas y constante de 32 bits resultante para imm8 = 0xFF
Si un inmediato tiene múltiples
putrefacción Constante de 32 bits codificaciones posibles, el
representación con el
0000 0000 0000 0000 0000 0000 0000 1111 1111 valor de rotación más pequeño putrefacción
se utiliza. Por ejemplo, el # 12 se
0001 1100 0000 0000 0000 0000 0000 0011 1111
representaría como ( pudrición, imm8) =
0010 1111 0000 0000 0000 0000 0000 0000 1111 (0000, 00001100), no
(0001, 00110000).
... ...
31:28 27:26 25 24:21 20 19:16 15:12 11: 7 6: 5 4 3: 0 31:28 27:26 25 24:21 20 19:16 15:12 11: 7 6: 5 4 3: 0
AÑADIR R5, R6, R7
1110 2 00 2 0 0100 2 0 6 5 0 0 0 7 1110 00 0 0100 0 0110 00 0 0101 00000 00 0 0111
(0xE0865007)
SUB R8, R9, R10 1110 2 00 2 0 0010 2 0 9 8 0 0 0 10 1110 0010 0 1001 1000 00000 00 0 1010
(0xE049800A)
cond op yo cmd S Rn Rd shamt5 sh Rm cond op yo cmd S Rn Rd shamt5 sh Rm
31:28 27:26 25 24:21 20 19:16 15:12 11: 8 7: 0 31:28 27:26 25 24:21 20 19:16 15:12 11: 8 7: 0
AÑADIR R0, R1, # 42
(0xE281002A) 1110 2 00 2 1 0100 2 0 1 0 0 42 1110 00 1 0100 0 0001 0000 0000 00101010
SUB R2, R3, # 0xFF0 1110 2 00 2 1 0010 2 0 3 2 14 255 1110 00 1 0010 0 0011 op I 0010 1110 11111111
(0xE2432EFF)
cond op yo cmd S Rn Rd putrefacción imm8 cond cmd S Rn Rd putrefacción imm8
Figura 6.19 Instrucciones de procesamiento de datos con operandos inmediatos y de dos registros
332 CAPITULO SEIS Arquitectura
Instrucción sh Operación
31:28 27:26 25 24:21 20 19:16 15:12 11: 8 7 6: 5 4 3: 0 31:28 27:26 25 24:21 20 19:16 15:12 11: 8 7 6: 5 4 3: 0
LSR R4, R8, R6
1110 2 00 2 0 1101 2 0 0 4 6 0 01 2 1 8 1110 00 0 1101 0 0000 0100 0110 0 01 1 1000
(0xE1A04638)
ASR R5, R1, R12 1110 2 00 2 0 1101 2 0 0 5 12 0 10 2 1 1 1110 00 0 1101 0 0000 0101 1100 0 10 1 0001
(0xE1A05C51)
cond op yo cmd S Rn Rd Rs sh Rm cond op yo cmd S Rn Rd Rs sh Rm
Sentido
Poco I U
antes de, cmd es 13 (1101 2), sh codifica el tipo de turno, Rm contiene el valor que se
Cuadro 6.10 Bits de control de modo de
va a cambiar, y el resultado cambiado se coloca en Rd. Esta instrucción usa índice para instrucciones de memoria
la registro de registro desplazado modo de direccionamiento, donde un registro ( Rm) se
desplaza por la cantidad mantenida en un segundo registro ( Rs). Debido a que los 8 bits PAG Modo de índice W
menos significativos de Rs son usados, Rm se puede cambiar hasta 255 posiciones. Por 0 0 Post-índice
ejemplo, si Rs tiene el valor 0xF001001C, la cantidad de cambio es 0x1C (28). Un
desplazamiento lógico de más de 31 bits empuja todos los bits al final y produce todos 0 1 No soportado
los ceros. La rotación es cíclica, por lo que una rotación de 50 bits equivale a una rotación
1 0 Compensar
de 18 bits.
1 1 Pre-índice
6. 4. 2 Instrucciones de memoria
un funct codificación de campo, tienen dos variaciones de Src2, y usa un de memoria para instrucciones de memoria
funct shamt5 sh 1 Rm
Yo = 1
Registrarse
Figura 6.22 Formato de instrucción de memoria para LDR, STR, LDRB, y STRB
334 CAPITULO SEIS Arquitectura
Solución: STR es una instrucción de memoria, por lo que tiene una op de 01 2. De acuerdo a
Note lo contrario a la intuición
Cuadro 6.11 , L = 0 y B = 0 para STR. La instrucción utiliza post-indexación,
codificación de post-indexación
modo.
así que de acuerdo con Cuadro 6.10 , P = 0 y W = 0. El desplazamiento inmediato se resta de la
base, por lo que Yo = 0 y U = 0. Figura 6.23 muestra cada campo y el código de la máquina. Por
lo tanto, la instrucción en lenguaje de máquina es 0xE405B01A.
31:28 27:26 25:20 19:16 15:12 11: 0 31:28 27:26 25:20 19:16 15:12 11: 0
STR R11, [R5], n.º -26 1110 2 01 2 0000000 2 5 11 26 1110 01 000000 0101 1011 0000 0001 1010
cond op IPUBWL Rn Rd imm12 mi 4 0 5 B 0 1 A
Figura 6.23 Código de máquina para la instrucción de memoria del ejemplo 6.3
6. 4. 3 Instrucciones de sucursal
Las instrucciones de bifurcación utilizan un solo operando inmediato firmado de 24 bits, imm24,
como se muestra en Figura 6.24 . Al igual que con las instrucciones de procesamiento de datos y
memoria, las instrucciones de bifurcación comienzan con un campo de condición de 4 bits y un
op, que es 10 2. El funct El campo tiene solo 2 bits. La parte superior de funct es
siempre 1 para las sucursales. La parte inferior L, indica el tipo de rama
operación: 1 para licenciado en Derecho y 0 para B. Los dos restantes de 24 bits ' s complemento
imm24 El campo se utiliza para especificar una dirección de instrucción relativa a PC + 8.
El ejemplo de código 6.28 muestra el uso de la rama si es menor que ( BLT)
instrucción y Figura 6.25 muestra el código de máquina para esa instrucción. El dirección
de destino de sucursal (BTA) es la dirección de la siguiente instrucción a ejecutar si
se toma la rama. El BLT instrucción en Figura 6.25 tiene un BTA de 0x80B4, la
dirección de instrucción del ALLÍ etiqueta.
El campo inmediato de 24 bits proporciona el número de instrucciones
entre el BTA y el PC + 8 (dos instrucciones más allá de la rama). En este caso, el
valor del campo inmediato ( imm24) de BLT es 3 porque el BTA (0x80B4) es tres
instrucciones más allá de PC + 8 (0x80A8).
Rama
31:28 27:26 25:24 23: 0
op
cond 10 1 litro imm24
funct
BLT ALLÍ 1011 2 10 2 10 2 3 1011 10 10 0000 0000 0000 0000 0000 0011
(0xBA000003)
cond opfunct imm24 cond op funct imm24
Solución: Figura 6.26 muestra el código de máquina para la rama y la instrucción de enlace ( LICENCIADO
EN DERECHO). Su dirección de destino de rama (0x8040) está seis instrucciones detrás de PC + 8 (0x8058),
Montaje
Valores de campo Codigo de maquina
Código
31:28 27:26 25:24 23: 0 31:28 27:26 25:24 23: 0
PRUEBA BL 1110
2 10 2 11 2 -6 1110 10 11 1111 1111 1111 1111 1111 1010
(0xEBFFFFFA)
cond op funct imm24 cond op funct imm24
6. 4. 4 Modos de direccionamiento
ARM es inusual entre las arquitecturas
RISC porque permite que el segundo Esta sección resume los modos usados para direccionar operandos de instrucción. ARM utiliza
operando de origen se cambie en los
cuatro modos principales: registro, inmediato, base y direccionamiento relativo a PC. La
modos de direccionamiento de registro y
mayoría de las otras arquitecturas proporcionan modos de direccionamiento similares, por lo
base. Esta
que comprender estos modos le ayuda a aprender fácilmente otros lenguajes ensambladores.
requiere una palanca de cambios en
El registro y el direccionamiento base tienen varios submodos que se describen a continuación.
serie con la ALU en la implementación
Los primeros tres modos (registro, inmediato y direccionamiento base) definen modos de
del hardware, pero
reduce significativamente el código
lectura y escritura de operandos. El último modo (direccionamiento relativo a PC) define el
longitud en programas comunes, modo de escribir el contador de programa (PC). Cuadro 6.12
especialmente accesos a matrices. Por resume y da ejemplos de cada modo de direccionamiento.
ejemplo, en una matriz de elementos de Las instrucciones de procesamiento de datos usan registro o direccionamiento
datos de 32 bits, el índice de la matriz debe inmediato, en el que el primer operando fuente es un registro y el segundo es un
desplazarse a la izquierda en 2 para registro o inmediato, respectivamente. ARM permite que el segundo registro sea
calcular el desplazamiento de bytes en la
opcionalmente desplazado por una cantidad especificada en un registro inmediato o en
matriz. Se permite cualquier tipo de turno,
un tercer registro. Las instrucciones de memoria usan direccionamiento base, en el que
pero los turnos a la izquierda para la
la dirección base proviene de un registro y el desplazamiento proviene de un inmediato,
multiplicación son más
un registro o un registro desplazado por un inmediato. Las sucursales utilizan
común.
direccionamiento relativo a PC en el que la dirección de destino de la sucursal se calcula
agregando un desplazamiento a PC + 8.
Para interpretar el lenguaje de máquina, es necesario descifrar los campos de cada palabra de
instrucción de 32 bits. Diferentes instrucciones usan diferentes formatos, pero todos
Registrarse
Registro de cambio inmediato SUB R4, R5, R9, LSR # 2 ORR R0, R4 ← R5 - ( R9 >> 2) R0 ← R10 |
Registro de cambio de registro R10, R2, ROR R7 SUB R3, R2, # (R2 ROR R7) R3 ← R2 - 25
Inmediato 25
Base
Compensación inmediata STR R6, [R11, # 77] LDR mem [R11 + 77] ← R6
Desplazamiento de registro R12, [R1, - R5] R12 ← mem [R1 - R5] R8 ← mem
Desplazamiento de registro con desplazamiento inmediato LDR R8, [R9, R2, LSL # 2] B [R9 + (R2 << 2)]
Los formatos comienzan con un campo de condición de 4 bits y un campo de condición de 2 bits. op. El mejor lugar para
0xE0475001
0xE5949010
Solución: Primero, representamos cada instrucción en binario y miramos los bits 27:26 para
encuentra el op para cada instrucción, como se muestra en Figura 6.27 . El op los campos son 00 2
y 01 2, indicando un procesamiento de datos y una instrucción de memoria, respectivamente. A
continuación, miramos el funct campo de cada instrucción.
El funct el campo para la instrucción de memoria es 011001 2. B = 0 y L = 1, entonces este es un LDR instrucción.
P = 1 y W = 0, que indica direccionamiento offset. Yo = 0, entonces el
La compensación es inmediata. U = 1, por lo que se agrega el desplazamiento. Por lo tanto, es una instrucción
de registro de carga con un desplazamiento inmediato que se agrega al registro base. Rd es 9,
Rn es 4, y imm12 es 16. Figura 6.27 muestra el código ensamblador equivalente a las dos
instrucciones de la máquina.
1110 00 0 0010 0 0111 0101 00000 00 0 0001 1110 2 00 20 2 0 7 5 0 0 0 1 SUB R5, R7, R1
1110 01 011001 0100 1001 0000 0001 0000 1110 2 01 2 25 4 9 dieciséis LDR R9, [R4, # 16]
Un programa escrito en lenguaje de máquina es una serie de números de 32 bits que representan las
instrucciones. Como otros números binarios, estas instrucciones se pueden almacenar en la memoria. Esto se
llama programa almacenado concepto, y es una razón clave por la que las computadoras son tan poderosas.
Ejecutando una diferente
338 CAPITULO SEIS Arquitectura
Programa almacenado
Dirección Instrucciones
Memoria principal
el programa no requiere una gran cantidad de tiempo y esfuerzo para reconfigurar o volver a
cablear el hardware; solo requiere escribir el nuevo programa en la memoria. A diferencia del
hardware dedicado, el programa almacenado ofrece computación de propósito general. De
esta manera, una computadora puede ejecutar aplicaciones que van desde una calculadora
hasta un procesador de texto y un reproductor de video simplemente cambiando el programa
almacenado.
Se recuperan las instrucciones de un programa almacenado, o traído, de la memoria y
ejecutado por el procesador. Incluso los programas grandes y complejos son simplemente una
serie de lecturas de memoria y ejecuciones de instrucciones.
Figura 6.28 muestra cómo se almacenan las instrucciones de la máquina en la memoria. En los
programas ARM, las instrucciones normalmente se almacenan comenzando en direcciones bajas, en este caso
0x00008000. Recuerde que la memoria ARM se puede eliminar mediante bytes, por lo que las direcciones de
instrucción de 32 bits (4 bytes) avanzan 4 bytes, no 1.
Para ejecutar o ejecutar el programa almacenado, el procesador obtiene las
instrucciones de la memoria de forma secuencial. Las instrucciones obtenidas son
luego decodificadas y ejecutadas por el hardware digital. La dirección de la
instrucción actual se mantiene en un registro de 32 bits llamado contador de
programa (PC), que es el registro R15. Por razones históricas, una lectura en la PC
devuelve la dirección de la instrucción actual más 8.
Para ejecutar el código en Figura 6.28 , la PC se inicializa para direccionar
0x00008000. El procesador obtiene la instrucción en esa dirección de memoria
y ejecuta la instrucción, 0xE3A01064 ( MOV R1, # 100). Luego, el procesador
incrementa la PC en 4 a 0x00008004, busca y ejecuta esa instrucción y la repite.
Ada Lovelace, 1815 - 1852.
Un matemático británico que
El estado arquitectónico de un microprocesador mantiene el estado de un
escribió el primer programa de
programa. Para ARM, el estado de la arquitectura incluye el archivo de registro y los
computadora. Calculó los números
registros de estado. Si el sistema operativo (SO) guarda el estado de la arquitectura en
de Bernoulli usando
algún punto del programa, puede interrumpir el programa, hacer otra cosa y luego
Charles Babbage ' s analítico
Motor. Ella era la hija del poeta restaurar el estado de manera que el programa continúe correctamente, sin saber que
Lord Byron. alguna vez fue interrumpido. El estado arquitectónico también es de gran importancia
cuando construimos un microprocesador en el Capítulo 7.
6.5 Luces, cámara, acción: compilación, montaje y carga 339
Y CARGANDO *
Compilador
Hasta ahora, hemos mostrado cómo traducir fragmentos de código cortos de alto
nivel en código ensamblador y de máquina. Esta sección describe cómo compilar y
Código de ensamblaje
ensamblar un programa completo de alto nivel y cómo cargar el programa en la
memoria para su ejecución. Comenzamos presentando un ARM de ejemplo
Ensamblador
mapa de memoria, que define dónde se encuentran el código, los datos y la memoria de pila.
Figura 6.29 muestra los pasos necesarios para traducir un programa de un lenguaje Archivos de objeto
Archivo de objeto
de alto nivel al lenguaje de máquina y comenzar a ejecutar ese programa. Primero un compilador Archivos de biblioteca
Dirección Segmento
0xFFFFFFFC
Operando
Sistema y E / S
0xC0000000
0xBEFFFAE8 Apilar SP
Datos dinámicos
Montón
Datos globales
SB
Texto
0x00008000
Excepción
manipuladores
0x00000000 ordenador personal
mientras trabajaba para Remington La parte más baja del mapa de memoria ARM está reservada para la tabla de vectores de
Rand Corporation y fue fundamental excepción y los controladores de excepciones, comenzando en la dirección 0x0 (consulte Sección
en el desarrollo del lenguaje de 6.6.3 ). La parte más alta del mapa de memoria está reservada para el sistema operativo y
programación COBOL.
las E / S con mapa de memoria (consulte la Sección 9.2).
Como oficial naval, recibió muchos
premios, incluida la Medalla de la Victoria
6. 5. 2 Compilacion
de la Segunda Guerra Mundial y la Medalla
del Servicio de Defensa Nacional. Un compilador traduce código de alto nivel a lenguaje ensamblador. Los ejemplos
de esta sección se basan en GCC, un compilador gratuito popular y ampliamente
utilizado, que se ejecuta en la computadora de placa única Raspberry Pi
6.5 Luces, cámara, acción: compilación, montaje y carga 341
La salida, prog.s, es bastante detallado, pero las partes interesantes se muestran en el ejemplo
de código 6.29. Tenga en cuenta que GC requiere que las etiquetas vayan seguidas de dos
puntos. La salida de GCC está en minúsculas y tiene otras directivas de ensamblador que no se
describen aquí. Observa eso suma regresa usando el BX instrucción en lugar de
MOV PC, LR. Además, observe que GCC eligió guardar y restaurar R3 a pesar de que no es
uno de los registros conservados. Las direcciones de las variables globales se
almacenarán en una tabla que comienza en la etiqueta .L3.
342 CAPITULO SEIS Arquitectura
6. 5. 3 Montaje
objdump - S prog.o
00000000 <suma>:
int suma (int a, int b) {
retorno (a + b);
}
0: e0800001 agregar r0, r0, r1
4: e12fff1e bx lr
00000008 <principal>:
int b);
28: e59f300c ldr r3, [pc, # 12]; 3c <principal + 0x34> r0, [r3]
2c: e5830000 str
return y;
}
30: e8bd8008 pop {r3, pc}
...
También podemos ver la tabla de símbolos desde el archivo de objeto usando objdump con el - t bandera.
Las partes interesantes se muestran a continuación. Observe que el suma
La función comienza en la dirección 0 y tiene un tamaño de 8 bytes. principal comienza en la
dirección 8 y tiene un tamaño de 0x38. Los símbolos de variable global f, g, y h se enumeran y
tienen 4 bytes cada uno, pero aún no se les han asignado direcciones.
objdump - t prog.o
TABLA DE SÍMBOLOS:
00000000 ld .text 00000000 .texto
00000000 ld .data 00000000 .data
00000000 g F .text 00000008 suma
00000008 g F .text 00000038 principal
00000004 O * COM * 00000004 f
00000004 O * COM * 00000004 g
00000004 O * COM * 00000004 años
6. 5. 4 Enlace
objdump - S -t prog
El código de inicio es demasiado extenso para mostrarlo, pero nuestro programa comienza en la
dirección 0x8390 en el segmento de texto y las variables globales tienen direcciones asignadas.
344 CAPITULO SEIS Arquitectura
00008390 <suma>:
TABLA DE SÍMBOLOS:
000082e4 ld .text 00010564 00000000 . texto
ld .data 00008390 g 00000000 . datos
F .texto 00000008 suma
00008398 g F .texto 00000038 principal
00010570 g O .bss 00000004 F
00010574 g O .bss 00000004 gramo
00010578 g O .bss 00000004 y
6. 5. 5 Cargando
Dirección Memoria
SO y E / S
0xBEFFFAE8 Apilar SP
y
gramo
0x00010570 F
0x00010578
0x00010574 Figura 6.31 Ejecutable cargado en
0x00010570 memoria
0xE8BD8008
0xE5830000
0xE59F300C
0xEBFFFFF5
0xE5831000
0xE59F3014
0xE3A01003
0xE5830000
0xE59F301C
0xE3A00002
0xE92D4008 PC = 0x00008398
0xE12FFF1E
0x00008390 0xE0800001
Excepción
Manipuladores
6.6 RETAZOS*
Esta sección cubre algunos temas opcionales que no encajan naturalmente en ninguna otra
parte del capítulo. Estos temas incluyen la carga de literales de 32 bits, NOP y excepciones.
6. 6. 1 Cargando literales
Muchos programas necesitan cargar literales de 32 bits, como constantes o direcciones. MOV
solo acepta una fuente de 12 bits, por lo que LDR La instrucción se utiliza para cargar
estos números desde un grupo literal en el segmento de texto. Los ensambladores ARM
aceptan cargas de la forma
El primero carga una constante de 32 bits especificada por literal, y el segundo carga
la dirección de una variable o puntero en el programa especificado por etiqueta.
En ambos casos, el valor a cargar se mantiene en un piscina literal, que es una parte del
segmento de texto que contiene literales. El grupo literal debe tener menos de 4096
bytes del LDR instrucción para que la carga se pueda realizar como LDR Rd, [PC,
# offset_to_literal]. El programa debe tener cuidado de ramificarse alrededor del
grupo literal porque la ejecución de literales sería absurda o peor.
int a = 0x2B9056F; ; R1 = a
LDR R1, = 0x2B9056F
...
Literal
0000815C 0x02B9056F Piscina
...
Figura 6.32 Grupo literal de ejemplo
00008114 ...
Memoria principal
instrucciones, pero son una forma abreviada de NOP es un mnemónico para " No operacion " y se pronuncia " no op. " Es un pseudoinstrucció
instrucciones o secuencias de instrucciones que eso no hace nada. El ensamblador lo traduce a
suelen utilizar los programadores y compiladores. El MOV R0, R0 ( 0xE1A00000). Los NOP son útiles para, entre otras cosas, lograr
ensamblador traduce las pseudoinstrucciones en
cierto retraso o alinear instrucciones.
6. 6. 3 Excepciones
Un excepción es como una llamada de función no programada que se bifurca a una nueva
dirección. Las excepciones pueden deberse al hardware o al software. Por ejemplo, el
procesador puede recibir una notificación de que el usuario presionó una tecla en un teclado. El
procesador puede detener lo que está haciendo, determinar qué tecla se presionó, guardarla
para referencia futura y luego reanudar el programa que se estaba ejecutando. Una excepción
de hardware de este tipo desencadenada por un dispositivo de entrada / salida (E / S), como un
teclado, a menudo se denomina interrumpir. Alternativamente, el programa puede encontrar
una condición de error como una instrucción indefinida. A continuación, el programa se
ramifica al código en el sistema operativo (SO), que puede optar por emular la instrucción no
implementada o terminar el programa infractor. Las excepciones de software a veces se
denominan trampas. Una forma particularmente importante de trampa es llamada al sistema, por
lo que el programa invoca una función en el sistema operativo que se ejecuta en un nivel de
privilegio superior. Otras causas de excepciones incluyen el restablecimiento y los intentos de
leer la memoria inexistente.
Como cualquier otra llamada de función, una excepción debe guardar la dirección de
retorno, saltar a alguna dirección, hacer su trabajo, limpiar después de sí misma y regresar al
programa donde lo dejó. Las excepciones usan un vector tabla para determinar
donde saltar a la manejador de excepciones y use registros bancarios para mantener
Conserve copias adicionales de los registros de claves para que no corrompan los registros del
programa activo. Las excepciones también cambian nivel de privilegio del programa, lo que
permite que el controlador de excepciones acceda a partes protegidas de la memoria.
Modo CPSR 4: 0
Usuario 10000
Supervisor 10011
Abortar 10111
Indefinido 11011
Por lo tanto, un banco de registros de estado del programa guardados ( SPSR) se utiliza para guardar
una copia del CPSR durante las excepciones.
Si se produce una excepción mientras un programa está manipulando su marco de
pila, el marco puede estar en un estado inestable (por ejemplo, los datos se han escrito
en la pila pero el puntero de la pila aún no apunta a la parte superior de la pila). Por lo
tanto, cada modo de ejecución también usa su propia pila y copia almacenada de SP
apuntando a la parte superior de su pila. La memoria debe reservarse para la pila de
cada modo de ejecución y las versiones almacenadas de los punteros de la pila deben
inicializarse al inicio.
Lo primero que debe hacer un manejador de excepciones es empujar todos los
registros que podría cambiar a la pila. Esto lleva algo de tiempo. ARM tiene un interrupción
rápida modo de ejecución FIQ en el que R8 - Los R12 también se almacenan. Por lo tanto,
el manejador de excepciones puede comenzar inmediatamente sin guardar estos
registros.
Manejo de excepciones
Ahora que hemos definido los modos de ejecución, los vectores de excepción y los
registros bancarios, podemos definir qué ocurre durante una excepción. Al detectar
una excepción, el procesador:
Los programas operan con un nivel de privilegio bajo, mientras que el sistema
operativo tiene un nivel de privilegio más alto. Para hacer la transición entre niveles
de forma controlada, el programa coloca argumentos en registros y emite un llamada
de supervisor SVC) instrucción, que genera una excepción y plantea la
350 CAPITULO SEIS Arquitectura
Puesta en marcha
El procesador ARM1 fue desarrollado por primera vez por Acorn Computer en
Gran Bretaña para las computadoras BBC Micro en 1985 como una
actualización del microprocesador 6502 utilizado en muchas computadoras
personales de la época. A lo largo del año le siguió el ARM2, que entró en
producción en la computadora Acorn Archimedes. ARM era un acrónimo de
Máquina RISC Bellota. El producto implementó la versión 2 del conjunto de
instrucciones ARM (ARMv2). El bus de direcciones tenía solo 26 bits y los 6 bits
superiores de la PC de 32 bits se utilizaron para contener los bits de estado. La
arquitectura incluía casi todas las instrucciones descritas en este capítulo, incluido
el procesamiento de datos, la mayoría de cargas y almacenes, sucursales y
multiplicaciones.
ARM pronto extendió el bus de direcciones a 32 bits completos, moviendo los
A partir de ARMv7, el CPSR se
bits de estado a un Registro de estado del programa actual (CPSR) dedicado.
denomina Aplicación ARMv4, introducido en 1993, agregó cargas de media palabra y almacena y
Registro de estado del programa proporcionó cargas de media palabra y byte firmadas y sin firmar. Este es el núcleo
(APSR). del conjunto de instrucciones ARM moderno y es lo que hemos cubierto en este
capítulo.
El conjunto de instrucciones ARM ha experimentado muchas mejoras descritas
en las secciones siguientes. El exitoso procesador ARM7TDMI en 1995 introdujo el
conjunto de instrucciones Thumb de 16 bits en ARMv4T para mejorar la densidad
del código. ARMv5TE agregó procesamiento de señal digital (DSP) e instrucciones
de punto flotante opcionales. ARMv6 agregó instrucciones multimedia y mejoró el
conjunto de instrucciones Thumb. ARMv7 mejoró las instrucciones multimedia y de
coma flotante, renombrándolas como Advanced SIMD. ARMv8 introdujo una
arquitectura de 64 bits completamente nueva. Se han introducido varias otras
instrucciones de programación del sistema a medida que ha evolucionado la
arquitectura.
6.7 Evolución de la arquitectura ARM 351
Las instrucciones de pulgar tienen 16 bits de longitud para lograr una mayor densidad de
código; Son idénticas a las instrucciones ARM normales, pero generalmente tienen
limitaciones, entre ellas:
Casi todas las instrucciones ARM tienen equivalentes en Thumb. Debido a que las instrucciones
son menos poderosas, se requieren más para escribir un programa equivalente. Sin embargo,
las instrucciones son la mitad de largas, lo que da un tamaño general del código Thumb de
aproximadamente el 65% del equivalente de ARM. El conjunto de instrucciones Thumb es
valioso no solo para reducir el tamaño y el costo de la memoria de almacenamiento de código,
sino también para permitir un bus económico de 16 bits a la memoria de instrucciones y para
reducir la energía consumida al obtener instrucciones de la memoria.
comunes y permitir que cualquier programa se escriba en modo Thumb. decodificación de instrucciones.
352 CAPITULO SEIS Arquitectura
15 0
010000 funct Rm Rdn <función> S Rdn, Rdn, Rm (procesamiento de datos)
Las instrucciones Thumb-2 se identifican porque sus 5 bits más significativos son
11101, 11110 o 11111. A continuación, el procesador obtiene una segunda media
palabra que contiene el resto de la instrucción. La serie de procesadores Cortex-M
opera exclusivamente en el estado Thumb.
6. 7. 2 Instrucciones DSP
La Transformada Rápida de
Los procesadores de señales digitales (DSP) están diseñados para manejar de manera eficiente
Fourier (FFT), el algoritmo DSP
algoritmos de procesamiento de señales como la Transformada Rápida de Fourier (FFT) y los
más común, es complicado y
crítico para el rendimiento. El filtros de Respuesta de Impulso Finito / Infinito (FIR / IIR). Las aplicaciones comunes incluyen
Las instrucciones DSP en arquitecturas de codificación y decodificación de audio y video, control de motores y reconocimiento de voz.
computadora están destinadas a realizar ARM proporciona una serie de instrucciones DSP para estos fines. Las instrucciones DSP
FFT eficientes, especialmente en datos incluyen multiplicar, sumar y multiplicar-acumular (MAC) - multiplica y suma el resultado a una
fraccionarios de 16 bits. suma corriente: suma = suma + src1 × src2. MAC es una característica que distingue los
conjuntos de instrucciones DSP de los conjuntos de instrucciones regulares. Se utiliza con
mucha frecuencia en los algoritmos DSP y duplica el rendimiento en relación con las
Las instrucciones básicas de
instrucciones de multiplicación y adición separadas. Sin embargo, MAC requiere especificar un
multiplicación, enumeradas en el Apéndice
registro adicional para contener la suma acumulada.
B, son parte de ARMv4. ARMv5TE agregó
las instrucciones matemáticas saturadas y Las instrucciones DSP a menudo operan con datos cortos (16 bits) que representan
multiplicaciones empaquetadas y muestras leídas desde un sensor por un convertidor de analógico a digital. Sin embargo, los
fraccionarias para admitir algoritmos DSP. resultados intermedios se mantienen con mayor precisión (por ejemplo, 32 o 64 bits)
6.7 Evolución de la arquitectura ARM 353
corto 1 15 0
largo 1 31 0
largo largo 1 63 0
Q15 1 0 15
Q31 1 0 31
que no debe confundirse con números de coma flotante de precisión simple y suficiente para manejar la mayoría de las
doble. Para mayor eficiencia, dos números de precisión media se empaquetan en entradas, pero los casos patológicos
pueden sobrepasar el rango de precisión
una sola palabra de 32 bits.
simple. Un desbordamiento provoca un
El entero los tipos vienen en sabores firmados y sin firmar con el bit de
cambio abrupto de signo en una respuesta
signo en el msb. Fraccionario los tipos (Q15 y Q31) representan un número
radicalmente incorrecta, que puede
fraccionario con signo; por ejemplo, Q31 abarca el rango [ - 1, 1 - 2 - 31] con un
parecerle al usuario un clic en una
paso de 2 - 31 entre números consecutivos. Estos tipos no están definidos en el secuencia de audio o un píxel de color
estándar C, pero son compatibles con algunas bibliotecas. Q31 se puede extraño en una secuencia de video.
convertir a Q15 mediante truncamiento o redondeo. En truncamiento, el Pasando a la aritmética de doble precisión
resultado Q15 es solo la mitad superior. Al redondear, se agrega 0x00008000
al valor Q31 y luego se trunca el resultado. Cuando un cálculo implica muchos previene el desbordamiento pero
pasos, el redondeo es útil porque evita acumular varios errores pequeños de degrada el rendimiento y
A × B = a × B × 2 - 30 = 2 × a × B × 2 - 31
Esto significa que para multiplicar dos números Q15 y obtener un resultado Q31, haga
una multiplicación ordinaria con signo y luego duplique el producto. Luego, el producto
se puede truncar o redondear para volver a colocarlo en el formato Q15 si es necesario.
La multiplicación ordinaria de 32 bits funciona tanto con signo como sin signo.
MUL 32 = 32 × 32 Multiplicar
MLA 32 = 32 + 32 × 32 32 = Multiplicar-acumular
MLS 32 - 32 × 32 Multiplicar-restar
SMULW {B / T} 32 = (32 × 16) >> 16 Multiplicar palabra-media palabra firmada {bottom / top}
corto corto corto SMULBB R2, R0, R1 LDR R3, SMLABB R2, R0, R1 LDR R3, =
= 0x0000FFFF 0x0000FFFF
Y R2, R3, R2 Y R2, R3, R2
corto corto largo SMULBB R2, R0, R1 SMLABB R2, R0, R1, R2
corto corto largo largo MOV R2, # 0 SMLALBB R2, R3, R0, R1
MOV R3, # 0
SMLALBB R2, R3, R0, R1
largo corto largo SMULWB R2, R0, R1 MUL SMLAWB R2, R0, R1, R2 MLA
largo largo largo R2, R0, R1 R2, R0, R1, R2 SMLAL R2, R3,
corto sin firmar corto sin firmar corto sin firmar MUL R2, R0, R1 MLA R2, R0, R1, R2 LDR R3, =
LDR R3, = 0x0000FFFF 0x0000FFFF
Y R2, R3, R2 Y R2, R3, R2
corto sin firmar corto sin firmar largo sin firmar MUL R2, R0, R1 MLA R2, R0, R1, R2 MLA
largo sin firmar corto sin firmar largo sin firmar MUL R2, R0, R1 R2, R0, R1, R2 MLA R2,
largo sin firmar largo sin firmar largo sin firmar MUL R2, R0, R1 R0, R1, R2
Q15 Q15 Q15 SMULBB R2, R0, R1 SMLABB R2, R0, R1, R2 SSAT
QADD R2, R2, R2 R2, 16, R2
LSR R2, R2, n. ° 16
El punto flotante es más flexible que los números de punto fijo preferidos en DSP y
facilita la programación. El punto flotante se usa ampliamente en gráficos,
aplicaciones científicas y algoritmos de control. La aritmética de punto flotante se
puede realizar con una serie de instrucciones ordinarias de procesamiento de
datos, pero es más rápida y consume menos energía si se utilizan instrucciones y
hardware dedicados de punto flotante.
El conjunto de instrucciones ARMv5 incluye instrucciones opcionales de punto
flotante. Estas instrucciones acceden al menos a 16 registros de doble precisión de
64 bits separados de los registros ordinarios. Estos registros también pueden
tratarse como pares de registros de precisión simple de 32 bits. Los registros se
denominan D0 - D15 como doble precisión o S0 - S31 como precisión simple. Para
ejemplo, VADD.F32 S2, S0, S1 y VADD.F64 D2, D0, D1 realizar solo
y adiciones de punto flotante de doble precisión, respectivamente. Instrucciones de
coma flotante, enumeradas en Cuadro 6.18 , tienen el sufijo. F32 o . F64 para indicar
coma flotante de precisión simple o doble.
Instrucción Función
Rd, Rn, Rm
El MRC y MCR Las instrucciones se utilizan para transferir datos entre los
registros ordinarios y los registros del coprocesador de coma flotante.
ARM define el Registro de control y estado de coma flotante (FPSCR). Como el
registro de estado ordinario, contiene N, Z, C, y V banderas para operaciones de
punto flotante. También especifica modos de redondeo, excepciones y condiciones
especiales como desbordamiento, subdesbordamiento y división por cero. El VMRS y
VMSR Las instrucciones transfieren información entre un registro regular y el FPSCR.
Los dispositivos que funcionan con baterías ahorran energía al pasar la mayor parte del
tiempo en modo de suspensión. ARMv6K introdujo instrucciones para respaldar dichos
ahorros de energía. La espera de la interrupción ( WFI) La instrucción permite que el
procesador entre en un estado de bajo consumo hasta que se produzca una interrupción.
El sistema puede generar interrupciones basadas en eventos del usuario (como tocar una
pantalla) o en un temporizador periódico. La espera del evento WFE) La instrucción es
similar pero es útil en sistemas multiprocesador (vea la Sección 7.7.8) para que un
procesador pueda entrar en suspensión hasta que otro procesador lo notifique. Se
despierta durante una interrupción o cuando otro procesador envía un evento usando el SEV
instrucción.
ARMv7 mejora el manejo de excepciones para admitir la virtualización y la seguridad. En virtualización,
múltiples sistemas operativos pueden ejecutarse simultáneamente en el mismo procesador, sin darse
cuenta de los demás ' s existencia. Un hipervisor cambia entre los sistemas operativos. El hipervisor
opera en el nivel de privilegio PL2. Se invoca con una excepción de captura de hipervisor. Con
seguridad extensiones, el procesador define un estado seguro con medios de entrada limitados
y acceso restringido a partes seguras de la memoria. Incluso si un atacante compromete el
sistema operativo, el kernel seguro puede resistir la manipulación. Por ejemplo, el kernel
seguro puede usarse para desactivar un teléfono robado o para hacer cumplir la administración
de derechos digitales de manera que un usuario pueda ' t contenido duplicado protegido por
derechos de autor.
6. 7. 5 Instrucciones SIMD
El término SIMD (pronunciado " sim-dee ") representa instrucción única datos
múltiples, en el que una sola instrucción actúa sobre varios datos en paralelo. Una
aplicación común de SIMD es realizar muchas operaciones aritméticas breves a la
vez, especialmente para el procesamiento de gráficos. Esto también se llama lleno aritmética.
▶ Comparaciones
▶ de MAC y multiplicar
63 56 55 48 47 40 39 32 31 24 23 15
dieciséis 87 0 Posición de bit
a7 a6 a5 a4 a3 a2 a1 a0 D0
Figura 6.34 Aritmética empaquetada:
+ B7 B6 B5 B4 B3 B2 B1 B0 D1
ocho adiciones simultáneas de 8 bits
ARMv6 también definió un conjunto más limitado de instrucciones SIMD que operan
en los registros regulares de 32 bits. Estos incluyen sumas y restas de 8 y 16 bits, e
instrucciones para empaquetar y descomprimir de manera eficiente bytes y medias
palabras en una palabra. Estas instrucciones son útiles para manipular datos de 16 bits
en código DSP.
6. 7. 6 Arquitectura de 64 bits
Las arquitecturas de 32 bits permiten que un programa acceda directamente como máximo a 2 32 bytes
= 4 GB de memoria. Los grandes servidores informáticos lideraron la transición a
arquitecturas de 64 bits que pueden acceder a grandes cantidades de memoria.
Siguieron las computadoras personales y luego los dispositivos móviles. Las
arquitecturas de 64 bits a veces también pueden ser más rápidas porque mueven más
información con una sola instrucción.
Muchas arquitecturas simplemente extienden sus registros de propósito general de
32 a 64 bits, pero ARMv8 también introdujo un nuevo conjunto de instrucciones para
optimizar las idiosincrasias. El conjunto de instrucciones clásico carece de suficientes
registros de propósito general para programas complejos, lo que obliga al costoso
movimiento de datos entre los registros y la memoria. Mantener la PC en R15 y SP en R13
también complica la implementación del procesador, y los programas a menudo
necesitan un registro que contenga el valor 0.
Las instrucciones de ARMv8 todavía tienen una longitud de 32 bits y el
conjunto de instrucciones se parece mucho a ARMv7, pero con algunos problemas
resueltos. En ARMv8, el archivo de registro se expande a 31 registros de 64 bits
(llamados X0 - X30) y el PC y el SP ya no forman parte de los registros de propósito
general. X30 sirve como registro de enlace. Tenga en cuenta que no hay registro
X31; en cambio, se llama registro cero (ZR) y está cableado a 0. Las instrucciones de
procesamiento de datos pueden operar en valores de 32 o 64 bits, mientras que las
cargas y los almacenes siempre usan direcciones de 64 bits. Para dejar espacio para
los bits adicionales para especificar los registros de origen y destino, el campo de
condición se elimina de la mayoría de las instrucciones. Sin embargo, las ramas aún
pueden ser condicionales. ARMv8 también agiliza el manejo de excepciones, duplica
el número de registros SIMD avanzados y agrega instrucciones para la criptografía
AES y SHA. Las codificaciones de instrucciones son bastante complejas y no se
clasifican en un puñado de categorías.
Al reiniciar, los procesadores ARMv8 se inician en modo de 64 bits. El procesador
puede pasar al modo de 32 bits estableciendo un bit en un registro del sistema e
invocando una excepción. Vuelve al modo de 64 bits cuando vuelve la excepción.
banderas de condición sí sí
6. 8. 1 Registros x86
Byte 3
Byte 2
Byte 1
Byte 0
El microprocesador 8086 proporcionó ocho registros de 16 bits. Podría acceder por
EAX
separado a los ocho bits superior e inferior de algunos de estos registros. Cuando
0 HACHA
6. 8. 2 Operandos x86
EBP
5 BP Las instrucciones ARM siempre actúan sobre registros o inmediatos. Se necesitan
instrucciones explícitas de carga y almacenamiento para mover datos entre la memoria y
ESI los registros. Por el contrario, las instrucciones x86 pueden operar en registros,
6 SI inmediatos o memoria. Esto compensa parcialmente el pequeño conjunto de registros.
EDI Las instrucciones ARM generalmente especifican tres operandos: dos fuentes y
7 DI un destino. Las instrucciones x86 especifican solo dos operandos. El primero es una
fuente. El segundo es tanto un origen como un destino. Por lo tanto, las
Figura 6.35 registros x86 instrucciones x86 siempre sobrescriben una de sus fuentes con el resultado.
Cuadro 6.20 enumera las combinaciones de ubicaciones de operandos en x86. Todas las
combinaciones son posibles excepto de memoria a memoria.
Registrarse memoria agregar EAX, [20] EAX < - EAX + Mem [20] Mem [20] < -
memoria Registrarse añadir [20], EAX Mem [20] + EAX Mem [20] < - Mem
agregar EAX, [ESP] EAX < - EAX + Mem [ESP] EAX < - EAX + Mem direccionamiento base
agregar EAX, [EDX + 40] [EDX + 40] EAX < - EAX + Mem [60 + EDI * 4] base + desplazamiento
agregar EAX, [60 + EDI * 4] EAX < - EAX + Mem [EDX + 80 + EDI * 2] desplazamiento + índice escalado
Al igual que ARM, x86 tiene un espacio de memoria de 32 bits direccionable por bytes. Sin
embargo, x86 admite una variedad más amplia de modos de indexación de memoria. Las
ubicaciones de memoria se especifican con cualquier combinación de un registro base,
desplazamiento, y un registro de índice escalado. Cuadro 6.21 ilustra estos
combinaciones. El desplazamiento puede ser un valor de 8, 16 o 32 bits. La escala
que multiplica el registro de índice puede ser 1, 2, 4 u 8. El modo base +
desplazamiento es equivalente al modo de direccionamiento base ARM para cargas
y tiendas. Al igual que ARM, x86 también proporciona un índice escalado. En x86, el
índice escalado proporciona una manera fácil de acceder a matrices o estructuras
de elementos de 2, 4 u 8 bytes sin tener que emitir una secuencia de instrucciones
para generar la dirección.
Si bien ARM siempre actúa en palabras de 32 bits, las instrucciones x86 pueden
operar en datos de 8, 16 o 32 bits. Cuadro 6.22 ilustra estas variaciones.
6. 8. 3 Indicadores de estado
x86, como muchas arquitecturas CISC, usa indicadores de condición (también llamados banderas
BRAZO ' El uso de indicadores de
de estado) para tomar decisiones sobre las ramas y realizar un seguimiento de los
condición lo distingue de otras
acarreos y el desbordamiento aritmético. x86 usa un registro de 32 bits, llamado EFLAGS,
arquitecturas RISC.
que almacena los indicadores de estado. Algunos de los bits del registro EFLAGS se dan
en Cuadro 6.23 . El sistema operativo utiliza otros bits.
364 CAPITULO SEIS Arquitectura
Nombre Sentido
6. 8. 4 Instrucciones x86
x86 tiene un conjunto de instrucciones más grande que ARM. Cuadro 6.24 describe
algunas de las instrucciones de propósito general. x86 también tiene instrucciones para
aritmética de punto flotante y para aritmética en varios elementos de datos cortos
empaquetados en una palabra más larga. D indica el destino (un registro o ubicación de
memoria), y S indica la fuente (un registro, ubicación de memoria o inmediata).
Tenga en cuenta que algunas instrucciones siempre actúan sobre registros
específicos. Por ejemplo, 32 × La multiplicación de 32 bits siempre toma una de las
fuentes de EAX y siempre coloca el resultado de 64 bits en EDX y EAX. CÍRCULO
siempre almacena el contador de bucle en ECX. PUSH, POP, CALL, y RETIRADO use el
puntero de pila, ESP.
Los saltos condicionales comprueban las banderas y se bifurcan si se cumple la
condición apropiada. Vienen en muchos sabores. Por ejemplo, JZ salta si la bandera cero ( ZF)
es 1. JNZ salta si el indicador de cero es 0. Al igual que ARM, los saltos suelen seguir una
instrucción, como la instrucción de comparación ( CMP), que pone las banderas. Cuadro
6.25 enumera algunos de los saltos condicionales y cómo dependen de las banderas
establecidas por una operación de comparación previa.
1 Es posible construir instrucciones de 17 bytes si se utilizan todos los campos opcionales. Sin embargo, x86
NO NO lógico D=D
RCR / RCL girar a la derecha / izquierda con la prueba Girar CF y D por S CF = D [S] ( la S th
JZ / JE saltar si ZF = 1 saltar si D = S
JC / JB saltar si CF = 1
JNC saltar si CF = 0
JO saltar si OF = 1
JNO saltar si OF = 0
JS saltar si SF = 1
JNS saltar si SF = 0
Reg /
Modificación
Código de operación
R/M Escala Índice Base
2 bits 3 bits 3 bits 2 bits 3 bits 3 bits
x86 contiene instrucciones de cadena que actúan sobre cadenas completas de bytes
Intel y Hewlett-Packard
o palabras. Las operaciones incluyen mover, comparar o escanear en busca de un valor
Desarrolló conjuntamente una
nueva arquitectura de 64 bits específico. En los procesadores modernos, estas instrucciones suelen ser más lentas que
llamada IA-64 a mediados de 1990 ' s. realizar la operación equivalente con una serie de instrucciones más simples, por lo que
Fue diseñado desde cero, sin pasar es mejor evitarlas.
por el enrevesado Como se mencionó anteriormente, el 0x66 prefijo se utiliza para elegir entre
historia de x86, tomando tamaños de operandos de 16 y 32 bits. Otros prefijos incluyen los que se usan para
ventaja de 20 años de nuevas bloquear el bus (para controlar el acceso a variables compartidas en un sistema
investigaciones en informática
multiprocesador), para predecir si se tomará una rama o no y para repetir la instrucción
arquitectura y proporcionar un espacio
durante un movimiento de cadena.
de direcciones de 64 bits.
La pesadilla de cualquier arquitectura es quedarse sin capacidad de memoria. Con
Sin embargo, IA-64 aún no se ha convertido
direcciones de 32 bits, x86 puede acceder a 4 GB de memoria. Esto era mucho más de lo que
en un éxito de mercado. La mayoría de las
tenían las computadoras más grandes en 1985, pero a principios de la década de 2000 se había
computadoras que necesitan un gran
Para aquellos que tengan curiosidad por conocer más detalles de la arquitectura x86, el
desarrollador de software de arquitectura Intel x86 ' s Manual está disponible gratuitamente en
Intel ' s sitio web.
6. 8. 7 El panorama
Esta sección ha dado una idea de algunas de las diferencias entre la arquitectura
ARM logra un equilibrio entre
instrucciones simples
ARM RISC y la arquitectura x86 CISC. x86 tiende a tener programas más cortos,
y código denso al incluir características porque una instrucción compleja es equivalente a una serie de instrucciones ARM
tales como indicadores de condición y simples y porque las instrucciones están codificadas para minimizar el uso de
operandos de registro desplazados. memoria. Sin embargo, la arquitectura x86 es una mezcolanza de características
Características de Thease acumuladas a lo largo de los años, algunas de las cuales ya no son útiles pero
hacen que el código ARM sea deben conservarse para que sean compatibles con programas antiguos. Tiene muy
más compacto que otras pocos registros y las instrucciones son difíciles de decodificar. Simplemente explicar
arquitecturas RISC.
el conjunto de instrucciones es difícil. A pesar de todas estas fallas, x86 está
firmemente arraigado como la arquitectura de computadora dominante para PC,
porque el valor de la compatibilidad del software es muy grande y porque el
enorme mercado justifica el esfuerzo requerido para construir microprocesadores
x86 rápidos.
6,9 RESUMEN
Para controlar una computadora, debes hablar su idioma. Una arquitectura de computadora define
cómo controlar un procesador. Muchas arquitecturas informáticas diferentes tienen un uso comercial
generalizado en la actualidad, pero una vez
6.9 Resumen 369
entiendes uno, aprender otros es mucho más fácil. Las preguntas clave que debe
hacerse al abordar una nueva arquitectura son:
Ejercicios
Ejercicio 6.1 Dé tres ejemplos de la arquitectura ARM de cada uno de los principios de
diseño de la arquitectura: (1) la regularidad apoya la simplicidad; (2) acelerar el caso
común; (3) más pequeño es más rápido; y (4) un buen diseño exige buenos
compromisos. Explique cómo cada uno de sus ejemplos exhibe el principio de diseño.
Ejercicio 6.2 La arquitectura ARM tiene un conjunto de registros que consta de 16 registros de
32 bits. ¿Es posible diseñar una arquitectura de computadora sin un conjunto de registros? Si es
así, describa brevemente la arquitectura, incluido el conjunto de instrucciones. ¿Cuáles son las
ventajas y desventajas de esta arquitectura sobre la arquitectura ARM?
Ejercicio 6.3 Considere el almacenamiento en memoria de una palabra de 32 bits almacenada en la palabra 42 de
(b) ¿Cuáles son las direcciones de bytes que abarca la palabra de memoria 42?
Ejercicio 6.4 Repetir Ejercicio 6.3 para el almacenamiento en memoria de una palabra de 32 bits almacenada en la
Ejercicio 6.5 Explique cómo se puede usar el siguiente programa ARM para determinar si
una computadora es big-endian o little-endian:
Ejercicio 6.6 Escriba las siguientes cadenas con codificación ASCII. Escribe tus respuestas
finales en hexadecimal.
(a) SOS
(b) Genial
(c) ¡abucheo!
Ejercicio 6.7 Repetir Ejercicio 6.6 para las siguientes cadenas. (a)
hola
(b) leones
Ejercicio 6.8 Muestre cómo las cuerdas en Ejercicio 6.6 se almacenan en una memoria direccionable
por bytes en una máquina little-endian comenzando en la dirección de memoria 0x00001050C. Indique
claramente la dirección de memoria de cada byte.
Ejercicio 6.9 Repetir Ejercicio 6.8 para las cuerdas en Ejercicio 6.7 .
Ejercicio 6.10 Convierta el siguiente código ensamblador ARM en lenguaje de máquina. Escribe
las instrucciones en hexadecimal.
Ejercicio 6.11 Repetir Ejercicio 6.10 para el siguiente código de ensamblaje ARM:
Ejercicio 6.13 Repetir Ejercicio 6.12 para las instrucciones en Ejercicio 6.11 .
0x00008008 0xE3A02000
0x0000800C 0xE1A03001
0x00008010 0xE1510000
0x00008014 0x8A000002
0x00008018 0xE2822001
0x0000801C 0xE0811003
0x00008020 0xEAFFFFFA
0x00008024 0xE1A00002
372 CAPITULO SEIS Arquitectura
Ejercicio 6.15 Repetir Ejercicio 6.14 para el siguiente código de máquina. R0 y R1 son las
entradas. R0 contiene un número de 32 bits y R1 es la dirección de una matriz de caracteres de
32 elementos (char).
0x00008104 0xE3A0201F
0x00008108 0xE1A03230
0x0000810C 0xE2033001
0x00008110 0xE4C13001
0x00008114 0xE2522001
0x00008118 0x5AFFFFFA
0x0000811C 0xE1A0F00E
Ejercicio 6.16 El NI La instrucción no es parte del conjunto de instrucciones ARM, porque la misma
funcionalidad se puede implementar usando instrucciones existentes. Escriba un fragmento de código
de ensamblaje corto que tenga la siguiente funcionalidad: R0 = R1 NOR R2. Utilice la menor cantidad de
instrucciones posible.
Ejercicio 6.17 El NAND La instrucción no es parte del conjunto de instrucciones ARM, porque la misma
funcionalidad se puede implementar usando instrucciones existentes. Escriba un breve fragmento de
código de ensamblaje que tenga la siguiente funcionalidad: R0 = R1 NAND R2. Utilice la menor cantidad
de instrucciones posible.
Ejercicio 6.18 Considere los siguientes fragmentos de código de alto nivel. Suponga las
variables enteras (con signo) gramo y h están en los registros R0 y R1, respectivamente.
(I) si (g> = h)
g = g + h;
demás
g = g - h;
(ii) si (g <h)
h = h + 1;
demás
h = h * 2;
(a) Escriba los fragmentos de código en lenguaje ensamblador ARM asumiendo que la ejecución condicional
está disponible solo para instrucciones de bifurcación. Utilice la menor cantidad de instrucciones posible
(dentro de estos parámetros).
(B) Escriba los fragmentos de código en lenguaje ensamblador ARM con ejecución condicional
disponible para todas las instrucciones. Utilice la menor cantidad de instrucciones posible.
(C) Compare la diferencia en la densidad del código (es decir, el número de instrucciones)
entre (a) y (b) para cada fragmento de código y analice las ventajas o desventajas.
Ejercicios 373
Ejercicio 6.19 Repetir Ejercicio 6.18 para los siguientes fragmentos de código.
(I) si (g> h)
g = g + 1;
demás
h = h - 1;
(ii) si (g <= h)
g = 0;
demás
h = 0;
Ejercicio 6.20 Considere el siguiente fragmento de código de alto nivel. Suponga que las
direcciones base de array1 y array2 se llevan a cabo en R1 y R2 y que array2 se inicializa
antes de su uso.
int i;
int array1 [100];
int array2 [100];
...
para (i = 0; i <100; i = i + 1)
matriz1 [i] = matriz2 [i];
(a) Escriba el fragmento de código en el ensamblaje ARM sin usar la indexación previa o posterior
o un registro escalado. Utilice la menor cantidad de instrucciones posible (dadas las limitaciones).
(b) Escriba el fragmento de código en el ensamblaje ARM con indexación previa o posterior y una
registro escalado disponible. Utilice la menor cantidad de instrucciones posible.
(c) Compare la diferencia en la densidad del código (es decir, el número de instrucciones) entre
(a y B). Analice las ventajas o desventajas.
Ejercicio 6.21 Repetir Ejercicio 6.20 para el siguiente fragmento de código de alto nivel. Asumir que temperatura
se inicializa antes de que se utilice y que R3 contiene la dirección base de temperatura
int i;
int temp [100];
...
para (i = 0; i <100; i = i + 1)
temp [i] = temp [i] * 128;
Ejercicio 6.22 Considere los siguientes dos fragmentos de código. Suponga que R1 se mantiene I
y que R0 tiene la dirección base del vals formación.
(I) int i;
int vals [200];
para (i = 0; i <200; i = i + 1)
vals [i] = i;
374 CAPITULO SEIS Arquitectura
(ii) int i;
int vals [200];
(b) Escriba cada fragmento de código utilizando lenguaje ensamblador ARM. Utilice tan pocas instrucciones
ciones como sea posible.
Ejercicio 6.23 Repetir Ejercicio 6.22 para los siguientes fragmentos de código de alto nivel. Suponga
que R1 se mantiene I, R0 contiene la dirección base del nums matriz, y que la matriz se inicializa antes
de su uso.
(I) int i;
int nums [10];
...
para (i = 0; i <10; i = i + 1)
nums [i] = nums [i] / 2;
(ii) int i;
int nums [10];
...
para (i = 9; i> = 0; i = i-1)
nums [i] = nums [i] / 2;
Esta simple copia de cadena Ejercicio 6.24 Escribir una función en un lenguaje de alto nivel para int find42 (int matriz [],
La función tiene un defecto grave: int tamaño). Talla especifica el número de elementos en formación, y formación
no tiene forma de saber que especifica la dirección base del formación. La función debe devolver el número de índice de la
dst tiene suficiente espacio para recibir primera entrada de la matriz que contiene el valor 42. Si ninguna entrada de la matriz es 42,
src. Si un programador debe devolver el valor - 1.
malintencionado pudiera ejecutar strcpy
con una cuerda larga src, el
Ejercicio 6.25 La función de alto nivel strcpy copia la cadena de caracteres src a la cadena
programador podría escribir bytes en
de caracteres dst.
toda la memoria, posiblemente incluso
modificando el código almacenado en // código C
ubicaciones de memoria posteriores. void strcpy (char dst [], char src []) {
int i = 0;
Con algo de inteligencia, el código hacer {
Ejercicio 6.27 Considere el código de ensamblaje ARM a continuación. func1, func2, y func3
son funciones que no son hojas. func4 es una función de hoja. El código no se muestra para
cada función, pero los comentarios indican qué registros se utilizan dentro de cada función.
(b) Dibuje la pila después func4 se llama. Indique claramente qué registros son
almacenado en la pila y marque cada uno de los marcos de la pila. Da valores siempre
que sea posible.
Ejercicio 6.28 Cada número de la serie de Fibonacci es la suma de los dos números
anteriores. Cuadro 6.26 enumera los primeros números de la serie, fib (n).
(b) Escriba una función llamada mentira en un lenguaje de alto nivel que devuelve el Fibonacci
número para cualquier valor no negativo de norte. Sugerencia: probablemente desee utilizar un
bucle. Comente claramente su código.
(C) Convierta la función de alto nivel de la parte (b) en código ensamblador ARM. Agregue
comentarios después de cada línea de código que expliquen claramente lo que hace. Utilice el
simulador Keil MDK-ARM para probar su código en mentira( 9). (Consulte el Prefacio para saber
cómo instalar el simulador Keil MDK-ARM).
norte 1 2 3 4 5 6 7 8 9 10 11 ...
Ejercicio 6.29 Considere el ejemplo de código 6.27. Para este ejercicio, asuma factorial (n)
se llama con argumento de entrada n = 5.
(b) Suponga que reemplaza las instrucciones en las direcciones 0x8500 y 0x8520 por
PRESIONE {R0, R1} y POP {R1, R2}, respectivamente. ¿El programa:
(1) entrar en un bucle infinito pero no chocar;
(2) bloqueo (hace que la pila crezca o se reduzca más allá del segmento de datos
dinámicos o que la PC salte a una ubicación fuera del programa);
(3) producir un valor incorrecto en R0 cuando el programa vuelve al bucle (si es
así, ¿qué valor?); o
(4) ejecutar correctamente a pesar de las líneas eliminadas?
Ejercicio 6.30 Ben Bitdiddle está intentando calcular la función f (a, b) = 2 a + 3 B para no
negativo B. Se excede en el uso de llamadas a funciones y recursividad y produce el
siguiente código de alto nivel para funciones F y gramo.
j = a;
devuelve j + a + g (b);
}
int g (int x) {
int k;
k = 3;
si (x == 0) devuelve 0; si no,
devuelve k + g (x - l);
}
Ben luego traduce las dos funciones al lenguaje ensamblador de la siguiente manera. También
escribe una función, prueba, que llama a la función f (5, 3).
Ejercicios 377
EsFigura
Probablemente encontrarás en útil hacer dibujos
6.14 para de la
ayudarlo pila similares
a responder las al de
siguientes preguntas.
(a) Si el código se ejecuta a partir de prueba, ¿Qué valor hay en R0 cuando el programa obtiene
para ¿círculo? ¿Su programa calcula correctamente 2 a + 3 ¿B?
(B) Suponga que Ben cambia las instrucciones en las direcciones 0x00008010 y
0x00008030 a PRESIONE {R1, R0, R4} y POP {R4}, respectivamente. ¿El programa
(c) Repita la parte (b) cuando se cambien las siguientes instrucciones. Tenga en cuenta que las etiquetas
aren ' t cambiado, solo instrucciones.
(I) instrucciones en 0x00008010 y 0x00008024 cambian a PRESIONE {R1, LR, R4} y POP
{R1}, respectivamente.
(ii) instrucciones en 0x00008010 y 0x00008024 cambian a PRESIONE {R0, LR, R4} y POP
{R0}, respectivamente.
(iii) las instrucciones en 0x00008010 y 0x00008030 cambian a PRESIONE {R1, R0,
LR} y POP {LR}, respectivamente.
(iv) se eliminan las instrucciones en 0x00008010, 0x00008024 y 0x00008030.
(v) las instrucciones en 0x00008038 y 0x0000805C cambian a PRESIONE {R4} y
POP {R4}, respectivamente.
(vi) las instrucciones en 0x00008038 y 0x0000805C cambian a PRESIONE {LR} y
POP {LR}, respectivamente.
(vii) se eliminan las instrucciones en 0x00008038 y 0x0000805C.
Ejercicio 6.31 Convierta las siguientes instrucciones de bifurcación en código de máquina. Las
direcciones de instrucción se dan a la izquierda de cada instrucción.
// código C
void setArray (int num) {
int i;
int matriz [10];
para (i = 0; i <10; i = i + 1)
matriz [i] = comparar (num, i);
}
int compare (int a, int b) {
si (sub (a, b)> = 0)
return 1;
demás
return 0;
}
int sub (int a, int b) {
devolver un - B;
}
(B) Asumir setArray es la primera función llamada. Dibuja el estado de la pila antes de llamar setArray
y durante cada llamada de función. Indique los nombres de los registros y variables
almacenados en la pila, marque la ubicación de SP y marque claramente cada marco de
pila.
// código C
int f (int n, int k) {
int b;
b = k + 2;
si (n == 0) b = 10;
si no b = b + (n * n) + f (n - 1, k + 1); return b * k;
}
380 CAPITULO SEIS Arquitectura
(a) Traducir la función de alto nivel F en lenguaje ensamblador ARM. Preste especial atención
a guardar y restaurar correctamente los registros a través de llamadas a funciones y usar
las convenciones de registros preservados de ARM. Comente claramente su código.
Puedes usar el ARM MUL instrucción. La función comienza en la dirección de instrucción
0x00008100. Mantener la variable local B en R4.
(B) Repase manualmente su función del inciso a) para el caso de f (2, 4).
Haz un dibujo de la pila similar a la de Figura 6.14 , y suponga que SP es igual a
0xBFF00100 cuando F se llama. Escriba el nombre del registro y el valor de los datos
almacenados en cada ubicación de la pila y realice un seguimiento del valor del
puntero de la pila (SP). Marque claramente cada marco de pila. También puede
resultarle útil realizar un seguimiento de los valores en R0, R1 y R4 durante la
ejecución. Asume que cuando F se llama, R4 = 0xABCD y LR = 0x00008010. ¿Cuál es el
valor final de R0?
Ejercicio 6.35 Dé un ejemplo del peor de los casos para una bifurcación directa (es decir, una
bifurcación a una dirección de instrucción superior). El peor de los casos es cuando la rama no puede
ramificarse lejos. Muestre instrucciones y direcciones de instrucciones.
(a) En el peor de los casos, ¿hasta dónde puede B bifurcar hacia adelante (es decir, a direcciones superiores)?
(El peor de los casos es cuando la instrucción de bifurcación no puede bifurcarse lejos). Explique
utilizando palabras y ejemplos, según sea necesario.
(B) En el mejor de los casos, ¿qué tan lejos B bifurcación hacia adelante? (El mejor caso es cuando la
instrucción de bifurcación puede bifurcar más lejos). Explique.
(C) En el peor de los casos, ¿qué tan lejos B bifurcar hacia atrás (a direcciones más bajas)? Explicar. En
(D) el mejor de los casos, ¿qué tan lejos B rama hacia atrás? Explicar.
Ejercicio 6.37 Explique por qué es ventajoso tener un campo inmediato grande,
imm24, en el formato de máquina para las instrucciones de bifurcación, B y LICENCIADO EN DERECHO.
Ejercicio 6.39 Escriba una función en código de alto nivel que tome una matriz de 10 entradas
de enteros de 32 bits almacenados en formato little-endian y la convierta a formato big-endian.
Después de escribir el código de alto nivel, conviértalo en código ensamblador ARM. Comente
todo su código y use un número mínimo de instrucciones.
Ejercicios 381
(a) Escriba código de alto nivel para una función llamada concat que concatena (une
juntos) las dos cadenas: void concat (char string1 [], char string2 [], char stringconcat []). La
función no devuelve ningún valor. Se concate-
nates cadena1 y cadena2 y coloca la cadena resultante en stringconcat.
Puede suponer que la matriz de caracteres stringconcat es lo suficientemente grande para
acomodar la cadena concatenada.
Ejercicio 6.41 Escriba un programa de ensamblaje ARM que agregue dos números de punto flotante de
precisión simple positivos contenidos en R0 y R1. No utilice ninguna de las instrucciones de ARM de
punto flotante. No necesita preocuparse por ninguna de las codificaciones que están reservadas para
propósitos especiales (por ejemplo, 0, NAN, etc.) o números que se desbordan o se desbordan. Utilice
el simulador Keil MDK-ARM para probar su código. (Consulte el Prefacio para saber cómo instalar el
simulador Keil MDK-ARM.) Deberá configurar manualmente los valores de R0 y R1 para probar su
código. Demuestre que su código funciona de manera confiable.
Ejercicio 6.42 Considere el siguiente programa ARM. Suponga que las instrucciones se colocan
comenzando en la dirección de memoria 0x8400 y que L1 está en la dirección de memoria 0x10024.
(a) Primero muestre la dirección de instrucción junto a cada instrucción de ensamblaje. (b)
Describa la tabla de símbolos: es decir, enumere la dirección de cada una de las etiquetas. (c)
(d) ¿Qué tamaño (cuántos bytes) son los segmentos de datos y texto?
(e) Dibuje un mapa de memoria que muestre dónde se almacenan los datos y las instrucciones,
Similar a Figura 6.31 .
382 CAPITULO SEIS Arquitectura
Ejercicio 6.43 Repetir Ejercicio 6.42 para el siguiente código ARM. Suponga que las instrucciones se
colocan comenzando en la dirección de memoria 0x8534 y que L2 está en la dirección de memoria
0x1305C.
Ejercicio 6.44 Nombre dos instrucciones ARM que pueden aumentar la densidad del código (es
decir, disminuir el número de instrucciones en un programa). Dé ejemplos de cada uno,
mostrando el código ensamblador ARM equivalente con y sin usar las instrucciones.
Preguntas de entrevista
Los siguientes ejercicios presentan preguntas que se han hecho en entrevistas para trabajos de
diseño digital (pero generalmente están abiertas a cualquier lenguaje ensamblador).
Pregunta 6.1 Escriba el código ensamblador ARM para intercambiar el contenido de dos
registros, R0 y R1. No puede utilizar ningún otro registro.
Pregunta 6.2 Suponga que se le da una matriz de números enteros positivos y negativos. Escriba el
código de ensamblaje ARM que encuentre el subconjunto de la matriz con la suma más grande.
Suponga que la matriz ' La dirección base y el número de elementos de la matriz están en R0 y R1,
respectivamente. Su código debe colocar el subconjunto resultante de la matriz comenzando en la
dirección base en R2. Escribe código que se ejecute lo más rápido posible.
Pregunta 6.3 Se le da una matriz que contiene una cadena C. La cadena forma una
oración. Diseñe un algoritmo para invertir las palabras en la oración y almacenar la
nueva oración nuevamente en la matriz. Implemente su algoritmo usando código
ensamblador ARM.
Pregunta 6.5 Escriba el código ensamblador ARM para invertir los bits en un registro. Utilice la menor cantidad
de instrucciones posible. Suponga que el registro de interés es R3.
Pregunta 6.6 Escriba el código de ensamblaje de ARM para probar si se produce un desbordamiento
cuando se agregan R2 y R3. Utilice un número mínimo de instrucciones.
Pregunta 6.7 Diseñe un algoritmo para probar si una determinada cadena es un palíndromo.
(Recuerde que un palíndromo es una palabra que es lo mismo hacia adelante y hacia atrás. Por
ejemplo, las palabras " Guau " y " coche de carreras " son palíndromos.) Implemente su algoritmo
usando el código ensamblador ARM