Saltar a contenido

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:

  1. Obtener el siguiente número con 0 + 1 = 1
  2. Mover el número actual al último número (1 en lugar de 0)
  3. Mover el siguiente número al número actual (1 en lugar de 1)
  4. 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.