Loops
Ahora que hemos cubierto las instrucciones básicas, podemos comenzar a aprender Program Control Instructions
. Como ya sabemos, el código Assembly está basado en líneas, por lo que siempre buscará la siguiente línea para procesar instrucciones. Sin embargo, como podemos esperar, la mayoría de los programas no siguen un conjunto simple de pasos secuenciales, sino que generalmente tienen una estructura más compleja.
Aquí es donde entran en juego las instrucciones de Control
. Estas instrucciones nos permiten cambiar el flujo del programa y dirigirlo a otra línea. Hay muchos ejemplos de cómo se puede hacer esto. Ya hemos discutido las Directives
que le dicen al programa que dirija la ejecución a una etiqueta específica.
Otros tipos de Control Instructions
incluyen:
Loops |
Branching |
Function Calls |
---|---|---|
Loop Structure
Comencemos discutiendo Loops
. Un loop en assembly es un conjunto de instrucciones que se repiten por un número de veces definido en rcx
. Veamos el siguiente ejemplo:
exampleLoop:
instruction 1
instruction 2
instruction 3
instruction 4
instruction 5
loop exampleLoop
Una vez que el código assembly alcanza exampleLoop
, comenzará a ejecutar las instrucciones debajo de él. Debemos establecer el número de iteraciones que queremos que el loop ejecute en el registro rcx
. Cada vez que el loop alcanza la instrucción loop
, disminuirá rcx
en 1
(es decir, dec rcx
) y saltará de regreso a la etiqueta especificada, en este caso exampleLoop
. Por lo tanto, antes de entrar en cualquier loop, debemos usar mov
para asignar el número de iteraciones al registro rcx
.
Instruction | Description | Example |
---|---|---|
mov rcx, x |
Establece el contador del loop (rcx ) en x |
mov rcx, 3 |
loop |
Salta al inicio del loop hasta que el contador llegue a 0 |
loop exampleLoop |
loopFib
Para demostrar esto, volvamos a nuestro código fib.s
:
global _start
section .text
_start:
xor rax, rax
xor rbx, rbx
inc rbx
add rax, rbx
Dado que cualquier número de Fibonacci actual es la suma de los dos números anteriores, podemos automatizar esto con un loop. Supongamos que el número actual se almacena en rax
, por lo que es Fn
, y el siguiente número se almacena en rbx
, por lo que es Fn+1
.
Comenzando con el último número como 0
y el número actual como 1
, podemos definir nuestro loop de la siguiente manera:
- Obtener el siguiente número con
0 + 1 = 1
- Mover el número actual al último número (
1 en lugar de 0
) - Mover el siguiente número al número actual (
1 en lugar de 1
) - Loop
Si hacemos esto, terminaremos con 1
como el último número y 1
como el número actual. Si hacemos otro loop, obtendremos 1
como el último número y 2
como el número actual. Así que implementemos esto como instrucciones en assembly. Dado que podemos descartar el último número 0
después de usarlo en la suma, almacenemos el resultado en su lugar:
add rax, rbx
Necesitamos mover el número actual al lugar del último número y mover el siguiente número al número actual. Sin embargo, tenemos el siguiente número en rax
y el ahora número anterior en rbx
, por lo que están intercambiados. ¿Puedes pensar en alguna instrucción para intercambiarlos?
Usemos la instrucción xchg
para intercambiar ambos números:
xchg rax, rbx
Ahora simplemente podemos hacer un loop
. Sin embargo, antes de entrar en un loop, debemos establecer rcx
al número de iteraciones deseadas. Comencemos con 10
iteraciones y añadámoslo después de inicializar los valores de rax
y rbx
en 0
y 1
:
_start:
xor rax, rax ; initialize rax to 0
xor rbx, rbx ; initialize rbx to 0
inc rbx ; increment rbx to 1
mov rcx, 10
Ahora podemos definir nuestro loop como se discutió anteriormente:
loopFib:
add rax, rbx ; get the next number
xchg rax, rbx ; swap values
loop loopFib
Entonces, nuestro código final es:
global _start
section .text
_start:
xor rax, rax ; initialize rax to 0
xor rbx, rbx ; initialize rbx to 0
inc rbx ; increment rbx to 1
mov rcx, 10
loopFib:
add rax, rbx ; get the next number
xchg rax, rbx ; swap values
loop loopFib
Loop loopFib
Vamos a ensamblar nuestro código y ejecutarlo con gdb
. Estableceremos un punto de interrupción en b loopFib
, para poder examinar el código en cada iteración del bucle. Vemos los siguientes valores de los registros antes de la primera iteración:
$ ./assembler.sh fib.s -g
gef➤ b loopFib
Breakpoint 1 at 0x40100e
gef➤ r
───────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x0
$rbx : 0x1
$rcx : 0xa
Vemos que comenzamos con rax = 0
y rbx = 1
. Presionemos c
para continuar a la siguiente iteración:
───────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x1
$rbx : 0x1
$rcx : 0x9
Ahora tenemos 1
y 1
, como se esperaba, con 9
iteraciones restantes. Continuemos (c
) de nuevo:
`───────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x1
$rbx : 0x2
$rcx : 0x8`
Ahora estamos en 1
y 2
. Revisemos las siguientes tres iteraciones:
───────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x2
$rbx : 0x3
$rcx : 0x7
───────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x3
$rbx : 0x5
$rcx : 0x6
───────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x5
$rbx : 0x8
$rcx : 0x5
Como podemos ver, el script está calculando exitosamente la secuencia de Fibonacci como 0, 1, 1, 2, 3, 5, 8
. Continuemos hasta la última iteración, donde rbx
debería ser 55
:
───────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x22
$rbx : 0x37
$rcx : 0x1
Vemos que rbx
es 0x37
, equivalente a 55
en decimal. Podemos confirmarlo con el comando p/d $rbx
:
gef➤ p/d $rbx
$3 = 55
Como podemos observar, hemos usado con éxito bucles para automatizar el cálculo de la secuencia de Fibonacci. Intenta incrementar rcx
para ver cuáles son los siguientes números en la secuencia de Fibonacci.