Escolar Documentos
Profissional Documentos
Cultura Documentos
Tema 4:
Fundamentos de Lenguaje Máquina
Profesor responsable:
Santiago Romaní (santiago.romani@urv.cat)
Departamento de Ingeniería Informática y Matemáticas
Escuela Técnica Superior de Ingeniería
Universitat Rovira i Virgili
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
Índice de contenido
Sección 1 :Introducción al lenguaje máquina ARM................................................4
1.1 El sistema programable................................................................................................... 4
1.2 El procesador................................................................................................................... 5
1.3 La memoria......................................................................................................................6
1.4 Codificación de datos e instrucciones............................................................................. 7
1.5 La secuencia de ejecución............................................................................................... 8
1.6 Cálculo del tiempo de ejecución de un programa........................................................... 9
1.7 Los registros.................................................................................................................. 10
1.8 El operando inmediato...................................................................................................11
1.9 El operando registro desplazado....................................................................................12
1.10 Instrucciones aritméticas............................................................................................. 13
1.11 Instrucciones lógicas................................................................................................... 14
1.12 Instrucciones de salto.................................................................................................. 15
1.13 Instrucciones de acceso a memoria............................................................................. 16
1.14 Instrucciones de acceso a la pila..................................................................................17
2
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
3
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
Sistema programable
Entrada/ exterior
Procesador Memoria
Salida
Bus
direcciones
datos
control
4
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
1.2 El procesador
El procesador es la unidad electrónica (habitualmente contenida en un chip) que se encarga de leer y
procesar las instrucciones y los datos de un programa, que están almacenadas en la memoria del
computador.
El siguiente esquema muestra un modelo muy simplificado de los elementos que componen un
procesador y su relación con los buses del sistema, ignorando el bloque de entradas y salidas:
Procesador Memoria
direcciones dirección contenido
Cálculo de
000...000000 00000000
direcciones 000...000001 00111000
Registros 000...000010 00001010
000...000011 00000111
temporales 000...000100 11110100
000...000101 10010101
000...000110 00000011
000...000111 00000000
Registros datos
Unidad 000...001000 11111111
Aritmético de L.M. 000...001001 11110000
000...001010 11110000
Lógica 000...001011 00010111
000...001100 00011100
000...001101 01101001
… …
… …
control … …
Unidad de Control … …
111...111111 00000000
5
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
1.3 La memoria
En la memoria están almacenadas todas las instrucciones y los datos de los programas a ejecutar,
organizadas como un vector de 2n posiciones de m bits cada una, donde n indica el número de bits
de la dirección.
Habitualmente, m=8, o sea que la unidad mínima de información que se puede leer o escribir en la
memoria es un byte, es decir, un paquete de 8 bits.
En los procesadores ARM, n=32, o sea que se pueden generar más de 4 mil millones de direcciones
diferentes, es decir, 4 Gigabytes.
Además, los procesadores ARM pueden procesar datos de 8, 16 o 32 bits. Esto significa que es
posible acceder a 1, 2 o 4 posiciones consecutivas de memoria con una sola transferencia. Cuando
se accede a 2 o 4 posiciones a la vez, es preciso conocer el orden de los bytes. Existen dos
posibilidades:
• Little Endian: los bytes están ordenados de menor a mayor peso,
• Big Endian: los bytes están ordenados de mayor a menor peso.
Los siguientes esquemas muestran ejemplos de ordenación de varias combinaciones de bytes; dado
que la lectura de secuencias largas de bits es muy confusa para las personas, habitualmente se
utiliza la notación hexadecimal, ya que cada dígito representa una agrupación de 4 bits. El prefijo
'0x' significa que el número que viene a continuación está expresado en base 16. Además, si se trata
de paquetes de 32 bits, los 8 dígitos hexadecimales se separan en dos grupos de 4 con un espacio en
blanco, también para facilitar su lectura:
dirección bytes
: : : Ordenación Little Endian
Byte = 0xC3
0x0000 001F 0x00 Dirección = 0x0000 00A0 Half Word = 0x81C3
0x0000 00A0 0xC3 Word = 0xF410 81C3
0x0000 00A1 0x81
0x0000 00A2 0x10
0x0000 00A3 0xF4
0x0000 00A4 0x92 Ordenación Big Endian
0x0000 00A5 0x5B Byte = 0xC3
0x0000 00A6 0xEA Dirección = 0x0000 00A0 Half Word = 0xC381
: : : Word = 0xC381 10F4
: : :
En el ejemplo anterior se introduce la nomenclatura que utiliza ARM para denominar los paquetes
de 8 bits (byte), 16 bits (half word) y 32 bits (word). Hay que observar que el acceso a la misma
dirección de memoria (0x0000 00A0) implica acceder a 1, 2 o 4 posiciones consecutivas, según el
tamaño de los paquetes. Además, el valor del paquete depende del tipo de ordenación, excepto en el
caso del byte.
Los procesadores ARM se pueden configurar para trabajar con los dos tipos de ordenación, pero el
tipo que se escoja no se puede cambiar mientras se está ejecutando el programa. Como convenio, en
este manual vamos a utilizar siempre la ordenación Little Endian.
6
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
Las instrucciones de los programas también se codifican en binario, siguiendo un patrón establecido
por los diseñadores del procesador. En el caso del ARM, todas las instrucciones son de 32 bits. El
siguiente ejemplo muestra los códigos binarios de la instrucción “ add r0, r1, r2”, cuya acción es
sumar los registros de L.M. r1 y r2, y almacenar el resultado en el registro r0:
cnd TA IF TB S Rn Rd Rs ST SF Rm
1110 00 0 0100 0 0001 0000 0000 0 00 0 0010 = 0xE081 0002
De los diversos campos que componen la instrucción, vamos a fijarnos en los más significativos
para generar una instrucción del tipo “ add Rd, Rn, Rm”:
• TA: código de operación A → “00” para instrucciones aritméticas o lógicas,
• TB: código de operación B → “0100” para la suma,
• Rn: primer registro fuente → “0001” para r1,
• Rd: registro destino → “0000” para r0,
• Rm: segundo registro fuente → “0010” para r2.
Sólo con estos campos se pueden definir hasta 16 instrucciones aritméticas o lógicas, y podemos
especificar hasta 16 registros distintos como operandos, puesto que disponemos de 4 bits para cada
uno de los campos correspondientes.
Como resultado de la codificación, 0xE081 0002 es el Código Máquina de “add r0, r1, r2”, que
es la representación simbólica de la instrucción. Esta representación simbólica se llama Lenguaje
Ensamblador, y nos permite expresar el lenguaje máquina de una forma más comprensible que el
código máquina puro. Además, existen programas que traducen automáticamente el lenguaje
ensamblador a código máquina, que se denominan Ensambladores.
Hay que señalar que la mayoría de procesadores ARM también son capaces de interpretar un
“dialecto” de lenguaje máquina que se denomina THUMB, y que utiliza sólo 16 bits por
instrucción, lo que permite ahorrar espacio de memoria para codificar el programa, a costa de
limitar algunas de las opciones que permite la codificación ARM. Por ejemplo, sólo se pueden
utilizar 8 de los 16 registros del procesador, porque para cada operando de tipo registro sólo se
reservan 3 bits para su codificación. En este manual utilizaremos siempre la codificación ARM,
aunque no estudiaremos todas las instrucciones y operandos posibles, ya que no se pretende dar un
curso exhaustivo sino una visión introductoria y ágil de la programación en ARM.
7
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
dirección instrucciones
0x0000 mov r0, #0
0x0004 mov r1, #2
0x0008 add r0, r1
0x000C cmp r0, #6
0x0010 bne 0x0008
0x0014 b 0x0014
0x0018 :
: :
Se trata de un par de inicializaciones (mov) de los registros r0 y r1, una suma (add) de r1 sobre r0,
una comparación (cmp) de r0 con el valor 6, un salto condicional (bne) si r0 diferente de 6, y un
salto incondicional (b) hacia 0x0014, lo que supone un bucle infinito. La secuencia de ejecución de
este programa será la siguiente:
etc.
8
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
9
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
32 bits
r0: dato
r1: dato
r2: dato
r3: dato
r4: dato
r5: dato
r6: dato
r7: dato
r8: dato
r9: dato
r10: dato
r11: dato
r12: dato
r13: sp
r14: lr
r15: pc
10
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
IR IN valor
0000 00000010 = 0x0000 0002
0100 00111011 = 0x3B00 0000
1001 00111011 = 0x000E C000
Existe otro método para especificar un número inmediato cualquiera de 32 bits, que se explicará en
el apartado 2.9.
11
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
12
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
13
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
La instrucción de test de bits iguales (tst) permite comprobar si existen al menos una coincidencia
de dos bits a 1 en la misma posición de los operandos fuente. Se realiza con una y lógica, pero no se
guarda el resultado en ningún registro destino, de forma análoga a la instrucción de comparación
aritmética (cmp). El flag Z se actualizará según el resultado obtenido, de forma que tomará el valor
1 si no existe ninguna coincidencia de bits a 1 (resultado de 32 bits igual a cero), y tomará el valor 0
si existe al menos una coincidencia de bits a 1 (resultado de 32 bits diferente de cero). En el ejemplo
anterior de la y bit a bit, el resultado de 32 bits es diferente de cero, porque hay 4 coincidencias de
pares de bits a 1, por lo tanto el flag Z tomaría el valor 0. Este tipo de comprobaciones sirven para
verificar si determinados bits de un operando fuente están a 1, utilizando el otro operando fuente
como máscara, es decir, como patrón de los bits que queremos verificar.
El resto de instrucciones lógicas también son capaces de modificar el flag de Z de acuerdo con el
resultado, pero para simplificar no vamos a utilizar esta posibilidad en este manual.
Otras operaciones lógicas de interés son los desplazamientos lógicos (y aritméticos) de los bits, pero
en ARM no existen instrucciones específicas para estas operaciones puesto que se pueden conseguir
fácilmente con la instrucción de mover y el modo de registro desplazado como segundo operando
fuente. Por consiguiente, se pueden realizar los 4 desplazamientos básicos lsl, lsr, asr y ror que
se han introducido en el apartado 1.9.
14
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
15
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
16
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
Dirección Contenido
r0: 0x00000002 : : r0: 0x00000002
r1: 0xFFCE0467 Zona r1: 0xFFCE0467
0x000007D0 0x00000000
r2: 0x0345FBA2 libre r2: 0x0345FBA2
0x000007D4 0x00012340
r3: 0x12A3338F r3: 0x12A3338F
0x000007D8 0x001A5D00
r4: 0x00000000 r4: 0x00000002
0x000007DC 0x00000002
r5: 0x00000001 r5: 0xFFCE0467
push 0x000007E0 0xFFCE0467
r6: 0xFFFFFFFE r6: 0x0345FBA2
0x000007E4 0x0345FBA2 Último
r7: 0xC300012D r7: 0x12A3338F
0x000007E8 0x12A3338F push
r8: 0x2850AA1F r8: 0xFFFFFFFE
0x000007EC 0xFFFFFFFE pop
r9: 0x00000000 r9: 0x00000000
0x000007F0 0x000084AC
r10: 0x00000000 r10: 0x00000000
0x000007F4 0x00000000 Contenido
r11: 0x00000000 r11: 0x00000000
0x000007F8 0x00112FFC
r12: 0xE40013F2 Anterior r12: 0xE40013F2
r13: 0x000007F4 0x000007FC 0xB3000000 r13: 0x000007F4
r14: 0x000084AC 0x00000800 0x0A1039FC r14: 0x000084AC
r15: 0x000086D0 : : r15: 0x000084AC
(r13: 0x000007DC)
17
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
El texto anterior contiene nemotecnias de las instrucciones y registros de lenguaje máquina (en
azul), directivas (en naranja), símbolos (en negro) y valores (en rosa) que se traducen
sistemáticamente por el Programa Ensamblador en un fichero binario con los códigos máquina
correspondientes:
Direcciones Símbolos Código Máquina Lenguaje Máquina
0x8000 _start: 0xE3A0 0000 mov r0, #0
0x8004 0xE3A0 1002 mov r1, #2
0x8008 .Lbucle: 0xE080 0001 add r0, r0, r1
0x800C 0xE350 000A cmp r0, #10
0x8010 0x1AFF FFFC bne 0x8008 @;bne .Lbucle
0x8014 0xE59F 4010 ldr r4, [pc, #16] @;ldr r4, =var1
0x8018 0xE594 0000 ldr r0, [r4]
0x801C 0xE3A0 203B mov r2, #59
0x8020 0xE59F 3008 ldr r3, [pc, #8] @;ldr r3, =var2
0x8024 0xE7C3 2181 strb r2, [r3, r1, lsl #3]
0x8028 .Lstop: 0xEAFF FFFE b 0x8028 @;b .Lstop
0x802C 0x0001 0034 (dirección var1)
0x8030 0x0001 0038 (dirección var2)
18
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
2.2 Comentarios
Los comentarios son texto que se añade al código fuente para ayudar a entender el funcionamiento
del programa. Este texto es ignorado por el programa ensamblador.
Para el ensamblador GAS (Gnu ASsembler), un comentario se prefija con el carácter '@'. El
comentario es todo el texto siguiente, hasta el final de la linea. Por ejemplo:
@;=== programa de ejemplo en lenguaje ensamblador para ARM ===
es un comentario que ocupa toda la linea, mientras que en este otro ejemplo:
ldr r0, [r4] @;acceso a las variables
el comentario empieza detrás de una instrucción de lenguaje máquina.
19
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
Otra sección importante es '.bss', que es una sección de datos que sirve para definir variables de
programa sin inicializar, al contrario de la sección '.data' dónde las variables ya están inicializadas.
Por ejemplo:
.bss @;sección de variables no inicializadas
suma: .space 4
incluye la declaración de una variable de nombre 'suma' y reserva 4 bytes de espacio para almacenar
el contenido de la variable con la directiva '.space n'.
Respecto al ejemplo del apartado 2.1, hay que observar que el programa ensamblador ha ubicado la
sección '.data' en la dirección de memoria 0x10034 y la sección '.text' en la dirección de memoria
0x8000.
El manejo automático de las direcciones físicas de memoria constituye en herramienta muy útil para
la programación en lenguaje máquina, puesto que libera al programador de la tarea de especificar
constantemente dichas posiciones de memoria.
20
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
21
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
En este ejemplo se observa el uso de etiquetas para identificar el nombre de las variables, como 'x',
'n_array' y 'short1'. Estas etiquetas, que tienen que ir seguidas del símbolo ':', se corresponden con
las direcciones de memoria que utiliza el ensamblador para ubicar los valores de las variables. De
este modo, 'x' → 0x10034, 'n_array' → 0x10038 y 'short1' → 0x1004E.
Después de una etiqueta hay que especificar una directiva de tamaño ('.word', '.hword' o '.byte') y a
continuación uno o varios valores separados por comas. Si hay más de un valor se entiende que la
variable es un vector, puesto que apunta al principio de una lista de valores, como es el caso de
'n_array'.
Además, se pueden especificar valores sin necesidad de utilizar etiqueta, como es el caso de la
última definición de 4 bytes.
22
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
La primera directiva '.align 1' ha introducido un desplazamiento de un byte para asegurar que la
dirección de la variable 'y' sea múltiple de 2, mientras que la directiva '.align 2' ha introducido un
desplazamiento de tres bytes para asegurar que la dirección de la variable 'z' sea múltiple de 4.
Hay que observar que el número de bytes de desplazamiento que añade una directiva '.align n'
depende de la dirección actual del proceso de ensamblado. Por ejemplo, una directiva '.align 2'
puede añadir 1, 2, 3 bytes o ningún byte, si la dirección siguiente ya está alineada correctamente.
En cualquier caso, siempre hay que utilizar directivas '.align 1' delante de '.hword' o '.align 2'
delante de '.word' o de instrucciones de lenguaje máquina, cuando pueda pasar que la dirección de
esas variables o instrucciones no sea múltiple del correspondiente número de bytes, lo cual es
posible si antes se ha ensamblado algún valor con una multiplicidad inferior.
23
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
Después de indicar la sección de instrucciones con la directiva '.text', se introduce una directiva
'.align 2' para asegurar que el ensamblado empieza en una posición múltiple de 4, más otra directiva
'.arm' para indicar el tipo de codificación que se requiere (instrucciones de 32 bits).
A continuación se especifica una directiva '.global' seguida del símbolo '_start' para indicar que este
símbolo se puede referenciar desde un módulo externo al fichero que lo contiene. En el ejemplo se
trata de la Rutina de Inicio de cualquier programa ARM, que por defecto tiene que empezar por la
etiqueta de salto '_start'.
La siguiente etiqueta de salto '.Lbucle', sin embargo, es local al fichero que contiene el texto del
programa ensamblador. Por convención, se aconseja anteceder las etiquetas locales con el prefijo
'.L', aunque no es estrictamente necesario. En cualquier caso, todas las etiquetas de salto tienen que
terminar con el carácter ':', de igual modo que se utilizaba para definir variables.
El ensamblado del extracto anterior produce el siguiente contenido de memoria:
Direcciones Símbolos Código Máquina Lenguaje Máquina
0x8000 _start: 0xE3A0 0000 mov r0, #0
0x8004 0xE3A0 1002 mov r1, #2
0x8008 .Lbucle: 0xE080 0001 add r0, r0, r1
0x800C 0xE350 000A cmp r0, #10
0x8010 0x1AFF FFFC bne 0x8008 @;bne .Lbucle
Del listado anterior se puede deducir que el ensamblador ha asignado la dirección 0x8000 a la
etiqueta '_start' y la dirección 0x8008 a la etiqueta '.Lbucle'. Todavía más importante es observar
que el ensamblador ha sustituido el símbolo '.Lbucle' de la última instrucción 'bne .Lbucle' por su
correspondiente dirección de salto 'bne 0x8008'.
De este modo, el programador de lenguaje ensamblador puede utilizar etiquetas simbólicas en
instrucciones de salto o en instrucciones de acceso a variables, es decir, sin necesidad de conocer
exactamente la posición de memoria que se asigna a cada etiqueta.
Nota: en esta sección se visualizan las direcciones y contenidos de memoria como resultado del
proceso de ensamblado por razones didácticas; en el ciclo normal de desarrollo de programas en
lenguaje ensamblador no es necesario conocer dicha información.
24
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
En la primera instrucción 'ldr r4, =var1' la dirección de memoria de la variable 'var1' se carga sobre
el registro r4. Nótese que una dirección de memoria en ARM es de 32 bits, por lo tanto, no se puede
utilizar una instrucción como 'mov r4, dir.', dónde la dirección sea un valor inmediato.
Por esta razón se utiliza la instrucción 'ldr r4, =var1', dónde '=var1' indica al ensamblador que
utilice el modo de direccionamiento Relativo a PC, que consiste en guardar la dirección de 'var1' en
un lugar próximo a la instrucción 'ldr', para poder especificar la posición dónde se guarda la
dirección como el valor actual del PC (puntero de programa) más un desplazamiento. Esto se puede
observar en el contenido de la memoria:
Direcciones Símbolos Código Máquina Lenguaje Máquina
0x8014 0xE59F 4010 ldr r4, [pc, #16] @;ldr r4, =var1
0x8018 0xE594 0000 ldr r0, [r4]
0x801C 0xE3A0 203B mov r2, #59
0x8020 0xE59F 3008 ldr r3, [pc, #8] @;ldr r3, =var2
0x8024 0xE7C3 2181 strb r2, [r3, r1, lsl #3]
0x8028 .Lstop: 0xEAFF FFFE b 0x8028 @;b .Lstop
0x802C 0x0001 0034 (dirección var1)
0x8030 0x0001 0038 (dirección var2)
25
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
En este ejemplo, la primera instrucción salva el contenido de los registros r0, r1, r2 y lr (r14) en la
pila. Se supone que los registros r0-r2 son de trabajo para las instrucciones contenidas en la rutina,
por eso hay que salvar su valor antes de entrar en la rutina y hay que restaurar su valor de la pila
antes de retornar de la rutina. El contenido del registro lr, sin embargo, no se restaura sobre sí
mismo, sino sobre el registro pc (r15), lo cual provoca el retorno a la siguiente instrucción después
de 'bl func1'.
Las rutinas pueden declararse globales o no, con la directiva '.global etiqueta', si se necesita que la
rutina se accesible (o no) desde otros ficheros del programa.
En la sección 4 se abordará más en profundidad el tema de las rutinas.
26
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
Una posible traducción en lenguaje ensamblador GAS para ARMv4 seria la siguiente:
@;=== Sumatorio: traducción de un programa simple en C ===
.data
max: .word 200 @;variables global inicializada
.bss
s: .space 4 @;variables global no inicializada
.text
.align 2
.arm
.global _start
_start: @;programa principal
ldr r2, =max @;r2 apunta a variable 'max'
ldr r3, [r2] @;r3 captura el contenido de 'max'
mov r0, #0 @;r0 acumulara temporalmente el valor de 's'
mov r1, #1 @;r1 sera la variable local 'i'
.Lfor: @;etiqueta local de inicio de bucle
add r0, r1 @;s += i;
add r1, #1 @;incrementa indice 'i'
cmp r1, r3 @;compara indice 'i' con valor máximo 'max'
bne .Lfor @;salto al inicio del bucle
ldr r4, =s @;r4 apunta a variable 's'
str r0, [r4] @;almacena el sumatorio en memoria
.Lstop: b .Lstop @;bucle infinito
27
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
.data
a: .word 2 @;variables globales inicializada
b: .word 5
.bss
c: .space 4 @;variables global no inicializada
.text
.align 2
.arm
.global _start
_start: @;programa principal
ldr r3, =a @;r3 apunta a variable 'a'
ldr r0, [r3] @;r0 captura el contenido de 'a'
mul r2, r0, r0 @;r2 = a*a
ldr r3, =b @;r3 apunta a variable 'b'
ldr r1, [r3] @;r1 captura el contenido de 'b'
add r1, r1, lsl #1 @;r1 = 3*b (b + 2*b)
sub r2, r1 @;r2 = a*a - 3*b
add r2, #7 @;r2 = a*a – 3*b + 7
ldr r3, =c @;r3 apunta a variable 'c'
str r2, [r3] @;almacena el resultado en memoria
.Lstop: b .Lstop @;bucle infinito
Una instrucción secuencial en alto nivel puede generar varias instrucciones en lenguaje máquina,
debido principalmente a que las instrucciones en lenguaje máquina tienen sólo dos operandos
fuente, lo cual obliga a calcular resultados parciales. Además, hay que cargar el contenido de las
variables de entrada en registros y después guardar el resultado en memoria.
28
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
.text
:
:
ldr r3, =c @;r3 contiene la dirección de c
ldr r0, [r3] @;r0 = c
cmp r0, #0 @;comprobar condición
ble .Lelse @;saltar si r0 <= 0 (caso del else)
mov r1, r0, asr #1 @;r1 = c/2
add r0, #1 @;r0 = c+1
str r0, [r3] @;actualiza variable c
b .Lcontinue @;saltar al final de la estructura if-else
.Lelse:
mov r1, r0, asr #2 @;r1 = c/4
.Lcontinue:
ldr r2, =d @;r2 contiene la dirección de d
str r1, [r2] @;almacena c/2 o c/4 en d
:
:
Hay que observar que la condición que se evalúa es justamente la contraria de la que se indica en
alto nivel, porque es muy recomendable mantener la misma ordenación del cuerpo de instrucciones
del 'if' y el cuerpo de instrucciones del 'else', por motivos de claridad del código fuente.
Por lo tanto, dado que en lenguaje máquina sólo disponemos de saltos condicionales, hay que saltar
el cuerpo del 'if' cuando NO se cumple la condición, hacia el cuerpo del 'else' (o al final del 'if', si no
existe 'else'). Además, cuando acaba el cuerpo del 'if', hay que saltar de manera incondicional (con
instrucción 'brach') al final del 'if', para no ejecutar el cuerpo del 'else'.
Puesto que la instrucciones 'ldr r2, =d' y 'str r1, [r2]' se tienen que ejecutar en el cuerpo del 'if' y
también en el cuerpo del 'else', se ha decidido extraerlas de los dos cuerpos y colocarlas al final una
sola vez. Esto es una optimización del código máquina, que traducido sin optimizar repetiría las
instrucciones en los dos cuerpos.
29
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
.text
:
ldr r4, =a
ldr r0, [r4] @;r0 = a
ldr r4, =b
ldr r1, [r4] @;r1 = b
ldr r4, =c
ldr r2, [r4] @;r2 = c
ldr r4, =d
ldr r3, [r4] @;r3 = d
cmp r2, #0 @;comprobar primera condición
ble .Lelse @;salta si (c <= 0)
sub r5, r0, r1 @;r5 = a-b
cmp r3, r5 @;comprobar segunda condición
bne .Lelse @;salta si (d != a-b)
sub r3, #1 @;r3 = d-1
b .Lcontinue @;saltar al final de if-else if
.Lelse:
add r5, r0, r1 @;r5 = a+b
cmp r3, r5 @;comprobar condición segundo if
beq .Lcontinue @;salta si (d == a+b)
add r3, #1 @;r3 = d+1
.Lcontinue:
str r3, [r4] @;actualiza variable d
:
En el caso de dos condiciones conectadas con un operador lógico AND hay que saltar a
continuación del cuerpo del 'if' cuando alguna de las condiciones sea falsa. Si las condiciones están
conectadas con operadores OR hay que saltar dentro del cuerpo del 'if' cuando alguna de las
condiciones sea cierta.
En el caso de la estructura 'else if' se trata simplemente de colocar las instrucciones de
comprobación del segundo 'if' al principio del cuerpo del 'else'. Esta estructura se puede encadenar
repetidas veces, y permite implementar las estructuras de caso ('switch', en C).
30
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
.text
:
ldr r3, =max
ldr r1, [r3] @;r1 = max
mov r2, #1 @;r2 calcula m, se guardará al final
mov r0, #2 @;r0 es indice i, se inicializa a 2
cmp r0, r1 @;comprobar si hay que entrar en el bucle
bgt .Lfinfor @;salta el cuerpo del 'for' si i > max
.Lfor:
mul r2, r2, r0 @;cuerpo del 'for' (m *= i;)
add r0, #1 @;i++
cmp r0, r1 @;comprobar si hay que continuar el bucle
ble .Lfor @;repite el 'for' mientras i <= max
.Lfinfor:
ldr r3, =m
str r2, [r3] @;actualiza variable m
:
En lenguaje ensamblador hay que explicitar la instrucción de inicialización del índice (mov r0, #2),
el incremento del índice (add r0, #1) y la comprobación de la condición de repetición (cmp r0, r1;
ble .Lfor).
Además, si no se puede garantizar que el bucle tendrá al menos una iteración (una ejecución del
cuerpo del bucle), hay que comprobar al principio si se tiene que producir dicha iteración o por el
contrario hay que saltar todo el cuerpo del bucle. En este ejemplo dependería del valor contenido en
la variable max, ya que si éste es menor que 2 el cuerpo del bucle no se debe ejecutar nunca.
En bucles que se pueda asegurar que iteran al menos una vez no hace falta dicha verificación inicial,
por ejemplo, en un bucle como “for (i=0; i<10; i++)”.
31
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
En el principio de un bucle 'while' hay que verificar la condición de fin, si no ha llegado el fin
ejecutar el cuerpo del bucle y finalmente hay que saltar a inicio del bucle. La siguiente versión
utiliza una estructura 'do-while':
:
i = 0;
do
{
i++;
} while (frase[i] != 0);
:
La traducción a lenguaje ensamblador podría ser la siguiente:
@;=== Ejemplo de traducción de estructura repetitiva do-while ===
:
ldr r2, =frase @;r2 contiene la dirección base de frase
mov r0, #0 @;r0 es indice i, se inicializa a 0
.Ldowhile:
add r0, #1 @;cuerpo del bucle (i++;)
ldrb r1, [r2, r0] @;r1 captura el contenido de frase[i]
cmp r1, #0 @;comprobar si hay que continuar el bucle
bne .Ldowhile @;salta al principio si frase[i] != 0
:
Aquí sólo se realiza un salto condicional, si se cumple la condición de continuación. Aunque esta
versión es más sencilla que la anterior, nunca analiza el contenido de frase[0], lo cual podría ser
perjudicial si el contenido de la primera posición de la frase puede ser cero.
32
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
dónde la dirección base es la dirección del primer elemento del vector en memoria y el
desplazamiento consiste en multiplicar el índice por el número de bytes que ocupa cada elemento
del vector. En el ejemplo de la frase, 'i' se multiplica por 1, ya que cada posición del vector es un
byte. El siguiente ejemplo en C consiste en multiplicar todos los elementos de un vector de 10
words por un valor constante (k):
:
for (i = 0; i < 10; i++)
v[i] *= k;
:
La traducción a lenguaje ensamblador podría ser la siguiente:
@;=== Ejemplo de acceso a vector de words ===
:
ldr r4, =k
ldr r3, [r4] @;r3 = 'k'
ldr r0, =v @;r0 contiene la dirección base de 'v'
mov r2, #0 @;r2 es indice 'i', se inicializa a 0
.Lfor:
ldr r1, [r0, r2, lsl #2] @;r1 captura el contenido de 'v[i]'
mul r1, r1, r3 @;multiplica v[i]*k
str r1, [r0, r2, lsl #2] @;actualizar el contenido de 'v[i]'
add r2, #1 @;incremento de índice (i++;)
cmp r2, #10 @;comprobar si hay que continuar el bucle
bne .Lfor @;repite el 'for' desde el principio
:
El modo de direccionamiento '[r0, r2, lsl #2]' significa que r2 se multiplica por 4 y se suma a r0 que
contiene la dirección base del vector. La siguiente representación gráfica muestra un acceso al
vector de words con índice en r2:
0x00010040 0x001AFE40
@v[] 0x00010044 0x8F4209A0 :v[0]
0x00010048 0x048FFFE2 :v[1]
0x0001004C 0xFFCE0467 :v[2]
0x00010050 0x0345FBA2 :v[3]
r2*4 +
0x00010054 0x12A3338F :v[4]
0x00010058 0xFFFFFFFE :v[5]
0x0001005C 0x000084AC :v[6]
@v[r0] 0x00010060 0x002F4531 :v[7]
0x00010064 0x00000000 :v[8]
0x00010068 0x00112FFC :v[9]
0x0001006C 0xB3000000
0x00010070 0x2E089D12
33
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
En general, para calcular la posición de un elemento (i, j) de una matriz de MF filas por MC
columnas y con un tamaño T de cada elemento hay que utilizar la siguiente fórmula:
dir(i,j) = Base + (i*MC + j)*T
34
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
#define N 10
int matriz[N][N];
void main(void)
{
for (i = 0; i < N; i++)
for (j = 0; j < N; j++)
if (i == j) matriz[i][j] = 1;
else matriz[i][j] = 0;
}
.text
.align 2
.arm
.global _start
_start: @;programa principal
ldr r3, =matriz @;r3 contiene dirección base de matriz
mov r2, #N @;r2 = N
mov r6, #0 @;r6 = 0
mov r7, #1 @;r7 = 1
mov r0, r6 @;r0 es 'i'
.Lfori: @;primer bucle
mul r4, r0, r2 @;r4 = i*N
mov r1, r6 @;r1 es 'j'
.Lforj: @;segundo bucle
add r5, r4, r1 @;r5 = i*N + j
cmp r1, r0 @;verificar si estamos en diagonal
bne .Lelse @;en caso negativo saltar al else
str r7, [r3, r5, lsl #2] @;matriz[i][j] = 1;
b .Lfinif @;salta el cuerpo del else
.Lelse:
str r6, [r3, r5, lsl #2] @;matriz[i][j] = 0;
.Lfinif:
add r1, r7
cmp r1, r2 @;cierra bucle j
blo .Lforj
add r0, r7
cmp r0, r2 @;cierra bucle i
blo .Lfori
35
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
int matriz[N*N];
void main(void)
{
for (i = 0; i < N*N; i++)
matriz[i] = 0;
i = 0;
for (j = 0; j < N; j++)
{
matriz[i] = 1;
i += N+1;
}
}
La traducción a ensamblador (sin multiplicaciones dentro de los bucles) podría ser la siguiente:
@;=== Ejemplo de acceso a matriz de words, con un índice ===
N = 10 @;constante!
.bss
matriz: .space N*N*4 @;variables global no inicializada
.text
.align 2
.arm
.global _start
_start: @;programa principal
ldr r3, =matriz @;r3 contiene dirección base de matriz
mov r2, #N @;r2 = N
mul r5, r2, r2 @;r5 = N*N
mov r6, #0 @;r6 = 0
mov r7, #1 @;r7 = 1
mov r0, r6 @;r0 es 'i'
.Lfori: @;primer bucle
str r6, [r3, r0, lsl #2] @;matriz[i] = 0;
add r0, r7
cmp r0, r5 @;cierra bucle i
blo .Lfori
mov r0, r6 @;r0 = 0
mov r1, r6 @;r1 es 'j'
.Lforj: @;segundo bucle
str r7, [r3, r0, lsl #2] @;matriz[i] = 1;
add r0, r2 @;i += N
add r0, r7 @;i += 1
add r1, r7
cmp r1, r2 @;cierra bucle j
blo .Lforj
.Lstop: b .Lstop @;bucle infinito
36
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
#define N 10
int fact;
void main(void)
{
fact = factorial(N);
}
El código anterior sólo muestra la llamada a una función que recibe un número entero como único
parámetro y devuelve el factorial de ese número. Por convenio, se establece que la rutina que vamos
a implementar para realizar dicha función recibe el parámetro por el registro r0 y devuelve el
resultado por el registro r1. La traducción a ensamblador podría ser la siguiente:
@;=== Ejemplo de programa que llama a la función (rutina) factorial ===
N = 10 @;constante!
.bss
fact: .space 4 @;variable resultado
.text
.align 2
.arm
.global _start
_start: @;programa principal
mov r0, #N @;r0 = N
bl factorial @;llamada a la función
ldr r2, =fact
str r1, [r2] @;guardar el resultado en 'fact'
.Lstop: b .Lstop @;bucle infinito
37
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
int factorial(int n)
{
int i, f = 1;
return f;
}
.global factorial
factorial: @;etiqueta de inicio de rutina
push {r2, lr} @;salvar estado
cmp r0, #0 @;verificación rango de entrada
blt .Labortar @;si n < 0, saltar a sección de abortar
cmp r0, #12 @;verificación rango de entrada
bgt .Labortar @;si n > 12, saltar a sección de abortar
b .Lnormal @;salta la sección de cálculo normal
.Labortar:
mov r1, #0
sub r1, #1 @;resultado r1 = -1
b .Lfinfactorial @;salta al final de la rutina
.Lnormal:
mov r1, #1 @;r1 es variable local 'f'
mov r2, #2 @;r2 es variable local 'i'
cmp r2, r0 @;comprobar si hay que entrar en el bucle
bgt .Lfinfactorial @;salta el cuerpo del 'for' si i > n
.Lfor:
mul r1, r1, r2 @;cuerpo del 'for' (f *= i;)
add r2, #1 @;i++
cmp r2, r0 @;comprobar si hay que continuar el bucle
ble .Lfor @;repite el 'for' mientras i <= n
.Lfinfactorial:
pop {r2, pc} @;restaura estado y retorna al invocador
38
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
.data
a: .word 5 @;variables globales
b: .word 3
.text
.align 2
.arm
.global _start
_start: @;programa principal
ldr r0, =a @;r0 es dirección de 'a'
ldr r1, =b @;r1 es dirección de 'b'
bl swap @;llamada a la función
.Lstop: b .Lstop @;bucle infinito
39
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
.text
.align 2
.arm
.global _start
_start: @;programa principal
ldr r0, =v1 @;r0 es dirección de 'vector'
mov r1, #8 @;r1 es longitud del vector
bl reverse @;llamada a la función
.Lstop: b .Lstop @;bucle infinito
40
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
add r2, #1
cmp r2, r1
blo .Lfor_i @;repite tantas veces como filas haya
add r3, #1
cmp r3, #8
blo .Lfor_j @;repite para 8 columnas
41
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
add r7, #1
cmp r7, r1
blo .Lfor_i @;repite tantas veces como filas haya
add r3, #1
cmp r3, r2
blo .Lfor_j @;repite el número de columnas indicado
42
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
void main(void)
{
sumaFilas2((int *)mat4x6, 4, 6); /* hay que hacer un 'casting' */
sumaFilas2((int *)mat3x4, 3, 4); /* porque no coinciden los tipos */
}
.data
mat4x6: .word 50, -276, 325, 217, 201, 30 @;declaración de matrices
.word 102, -8, 50, -276, 325, -12
.word -27, 541, 102, -8, 69, 35
.word 23, 50, -276, 325, -276, 325
.word 0, 0, 0, 0, 0, 0 @;fila adicional para resultados
.text
.align 2
.arm
.global _start
_start:
ldr r0, =mat4x6 @;r0 apunta al inicio de mat4x6
mov r1, #4 @;no es necesario hacer ningún 'casting'
mov r2, #6
bl sumaFilas2 @;primera llamada a la función
43
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
sub r0, r2
bl factorial
mov r4, r1 @;r4 = (m-n)!
mov r0, r2
bl factorial
mul r2, r4, r1 @;r2 = (m-n)!·n!
mov r0, r3
mov r1, r2
bl dividir @;llamar la función definida en problema 4.1
mov r2, r0 @;r2 = m! / (m-n)!·n!
pop {r0, r1, r3, r4, pc} @;restaura estado y retorna al invocador
El siguiente esquema muestra la evolución de la pila cuando se llama a la rutina 'factorial' desde
dentro de la rutina 'combinatorio':
:
Zona libre
SP2 R2 Llamada a rutina
LR (@ret2) 'factorial'
SP1 R0
R1
R3 Llamada a rutina
R4 'combinacional'
LR (@ret1)
Zona ocupada
44
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
.Lrecursividad:
sub r0, #1 @;r0 = m-1; r1 = n;
bl combinatorioR
mov r3, r2 @;r3 = (m-1 sobre n)
.Lfincombi:
pop {r0, r1, r3, pc} @;restaura estado y retorna al invocador
45
Teoría de Fundamentos de Computadores Tema 4: Fundamentos de Lenguaje Máquina
Zona libre
SP8 R0 (1)
R1 (1) Llamada
R3 Recursiva 7
LR (@ret2)
SP7 R0 (2)
R1 (1) Llamada
R3 Recursiva 6
LR (@ret2)
SP6 R0 (3)
R1 (1) Llamada
R3 Recursiva 5
LR (@ret2)
SP5 R0 (4)
R1 (1) Llamada
R3 Recursiva 4
LR (@ret2)
SP4 R0 (5)
R1 (2) Llamada
0: 1: 2: 3: R3 Recursiva 3
LR (@ret2)
0: 1
SP3 R0 (6)
1: 1 1 R1 (3) Llamada
R3 Recursiva 2
2: 1 2 1 LR (@ret2)
SP2 R0 (7)
3: 1 3 3 1 R1 (3) Llamada
4: 1 4 6 4 1 R3 Recursiva 1
LR (@ret2)
5: 1 5 10 10 5 1 SP1 R0 (8)
R1 (3) Llamada
6: 1 6 15 20 15 6 1 R3 Externa
LR (@ret1)
7: 1 7 21 35 35 21 7 1
SP0 Zona ocupada
8: 1 8 29 56 70 56 29 8 1
:
Como se puede observar, cada llamada recursiva supone un incremento del nivel de profundidad de
acceso al grafo de números que aparece en la izquierda, dónde el índice de las columnas representa
la n y el índice de las filas representa la m. En la pila se representan las llamadas correspondientes
al “camino” de nodos del grafo marcado por la linea en rojo.
El recorrido en profundidad supone que la pila va creciendo por cada nivel de profundidad que se
“desciende”, y va disminuyendo cuando se retorna a niveles más cercanos a la llamada inicial
(externa). És importante tener esto en cuenta porque hay que calcular el tamaño necesario de la pila
en función de la ocupación de esta por cada nivel de profundidad que se pueda descender.
46