Decisiones y bucles

Decisiones y bucles

Por defecto, la CPU ejecuta tus instrucciones en el orden en que aparecen, de arriba a abajo. Por sí solo, ese orden nunca puede tomar una decisión ni repetir trabajo. Una bifurcación es la forma en que un programa abandona ese camino recto: evalúa una condición y, cuando la condición se cumple, dirige a la CPU a continuar en un lugar diferente en vez de en la línea siguiente. Toda decisión y todo bucle provienen de este único mecanismo.

No existe if ni for. Los construyes a partir de etiquetas (posiciones con nombre) y bifurcaciones (salto a una posición cuando se cumple una condición). Una etiqueta es un nombre seguido de :.

Para entender cómo se ejecuta una bifurcación, necesitas conocer el Contador de Programa (PC). El PC es un registro especial, oculto a tu código, que contiene la dirección de memoria de la instrucción que se está ejecutando. Una instrucción de bifurcación escribe un nuevo valor en el PC, de modo que la CPU obtiene su siguiente instrucción desde tu etiqueta destino en lugar de desde la línea de código siguiente.

La familia de ramas

Cada bifurcación compara dos registros y salta a la etiqueta solo cuando la prueba se cumple. Cuando la prueba falla, la ejecución continúa en la línea siguiente. Seis pruebas forman el núcleo:

Las seis ramas reales
beq a, b, label # salta si a == b
bne a, b, label # salta si a != b
blt a, b, label # salta si a < b (con signo)
bge a, b, label # salta si a >= b (con signo)
bltu a, b, label # salta si a < b (sin signo)
bgeu a, b, label # salta si a >= b (sin signo)

y comprueban la igualdad e ignoran el signo. y forman el par de ordenación, menor que y mayor o igual que, y comparan los registros como números con signo. y realizan las mismas comparaciones pero tratan los valores como sin signo, lo cual conviene a direcciones o recuentos que nunca son negativos.

¿Qué significan con signo y sin signo? Un registro contiene 32 bits, y los mismos bits pueden leerse de dos maneras. Leídos como sin signo, forman un número entero simple desde 0 hasta unos 4 mil millones. Leídos como con signo, el bit más alto cuenta como signo menos, por lo que la mitad de los patrones de bits representan números negativos. Los bits en sí no cambian. Solo cambia cómo los lees. Por eso hay dos conjuntos de comparaciones: usa las de signo (, ) para números ordinarios que pueden ser negativos, y las sin signo (, ) para valores que nunca son negativos, como las direcciones de memoria.

No existe una "bifurcación si es mayor" ni "bifurcación si es menor o igual" directa. Las obtienes intercambiando los dos registros, y el ensamblador proporciona y como pseudobifurcaciones que realizan el intercambio por ti, de modo que se convierte en . Dos atajos más: y comparan un registro con cero, y salta sin ninguna condición.

Suma 1..5 con un bucle
li t0, 0 # suma = 0
li t1, 1 # i = 1
li t2, 6 # límite = 6 (bucle mientras i < 6)
loop:
bge t1, t2, done # sale del bucle si i >= 6
add t0, t0, t1 # suma += i
addi t1, t1, 1 # i++
j loop # vuelve a comprobar
done:
mv a0, t0 # imprime la suma (debería ser 15)
li a7, 1
ecall
li a7, 10
ecall

Rastréalo. t0 contiene la suma acumulada (empieza en 0), t1 contiene el contador i (empieza en 1) y t2 contiene el límite 6. En cada pasada por loop: abandona el bucle una vez que i llega a 6. En caso contrario, suma i a la suma, incrementa i en uno, y salta de vuelta para probar de nuevo. El bucle suma 1, 2, 3, 4 y 5, y done imprime el total, 15.

Bucle con una rama
loop:
bge t1, t2, done
add t0, t0, t1
addi t1, t1, 1
j loop
i (t1)1
yo (t0)0
sínot1 < 6?sum += t1t1 += 1hecho

bge compara t1 con t2 para decidir si el bucle continúa.

La figura siguiente marca la instrucción que actualiza el contador y la que actualiza la suma acumulada, distinguiéndolas del resto del bucle. avanza el contador, y el que le sigue añade el nuevo valor del contador a la suma. El último fotograma muestra la prueba que termina el bucle. Cuando t1 llega a 6, transfiere el control a done en lugar de dejar que el cuerpo se ejecute de nuevo. Esa prueba es lo que separa una pasada que se repite de la pasada que se detiene.
💡
Avanza por el programa con Step en lugar de Run. A medida que avanzas, t1 aumenta y el contador de programa (pc) salta de vuelta a loop. Cada vez que pc vuelve a loop, la condición de la bifurcación era falsa, así que el bucle se ejecuta de nuevo.
⚠
Si la condición de salida es incorrecta, el bucle nunca termina. El simulador detiene un bucle descontrolado, pero la lógica de bifurcación es tuya para hacerla bien.