Decisioni e cicli

Decisioni e cicli

Per impostazione predefinita la CPU esegue le tue istruzioni nell'ordine in cui appaiono, dall'alto verso il basso. Da solo, quest'ordine non può mai fare una scelta o ripetere un lavoro. Un branch è il modo in cui un programma abbandona quel percorso dritto: verifica una condizione e, quando la condizione è vera, indirizza la CPU a continuare in un punto diverso invece che alla riga successiva. Ogni decisione e ogni ciclo nascono da questo unico meccanismo.

Non esiste if né for. Li costruisci da label (posizioni con nome) e branch (salto a una posizione quando una condizione è vera). Una label è un nome seguito da :.

Per capire come viene eseguito un branch, devi conoscere il Program Counter (PC). Il PC è un registro speciale, nascosto al tuo codice, che contiene l'indirizzo di memoria dell'istruzione in esecuzione. Un'istruzione di branch scrive un nuovo valore nel PC, così la CPU preleva la sua istruzione successiva dalla label di destinazione invece che dalla riga di codice seguente.

La famiglia dei branch

Ogni branch confronta due registri e salta alla label solo quando il test è vero. Quando il test fallisce, l'esecuzione continua sulla riga successiva. Sei test formano il nucleo:

I sei rami reali
beq a, b, label # salta se a == b
bne a, b, label # salta se a != b
blt a, b, label # salta se a < b (con segno)
bge a, b, label # salta se a >= b (con segno)
bltu a, b, label # salta se a < b (senza segno)
bgeu a, b, label # salta se a >= b (senza segno)

e verificano l'uguaglianza e ignorano il segno. e formano la coppia di ordinamento, minore di e maggiore o uguale, e confrontano i registri come numeri con segno. e eseguono gli stessi confronti ma trattano i valori come senza segno, il che si adatta a indirizzi o conteggi che non sono mai negativi.

Cosa significano con segno e senza segno? Un registro contiene 32 bit, e gli stessi bit possono essere letti in due modi. Letti come senza segno, formano un numero intero semplice da 0 fino a circa 4 miliardi. Letti come con segno, il bit più alto conta come un segno meno, quindi metà dei pattern di bit rappresenta numeri negativi. I bit stessi non cambiano. Cambia solo il modo in cui li leggi. Ecco perché esistono due insiemi di confronti: usa quelli con segno (, ) per numeri ordinari che possono essere negativi, e quelli senza segno (, ) per valori che non sono mai negativi, come gli indirizzi di memoria.

Non esiste un "branch se maggiore" o "branch se minore o uguale" diretto. Li ottieni scambiando i due registri, e l'assembler fornisce e come pseudo-branch che eseguono lo scambio per te, così diventa . Altri due scorciatoie: e verificano un registro rispetto a zero, e salta senza alcuna condizione.

Somma 1..5 con un ciclo
li t0, 0 # somma = 0
li t1, 1 # i = 1
li t2, 6 # limite = 6 (ciclo finché i < 6)
loop:
bge t1, t2, done # esce dal ciclo se i >= 6
add t0, t0, t1 # somma += i
addi t1, t1, 1 # i++
j loop # torna a controllare
done:
mv a0, t0 # stampa la somma (dovrebbe essere 15)
li a7, 1
ecall
li a7, 10
ecall

Traccialo. t0 contiene la somma corrente (parte da 0), t1 contiene il contatore i (parte da 1), e t2 contiene il limite 6. A ogni passaggio attraverso loop: esce dal ciclo una volta che i raggiunge 6. Altrimenti aggiunge i alla somma, aumenta i di uno, e salta indietro per verificare di nuovo. Il ciclo somma 1, 2, 3, 4 e 5, e done stampa il totale, 15.

Ciclo con un ramo
loop:
bge t1, t2, done
add t0, t0, t1
addi t1, t1, 1
j loop
i (t1)1
sum (t0)0
sìnot1 < 6?sum += t1t1 += 1fatto

bge confronta t1 con t2 per decidere se il ciclo continua.

La figura seguente contrassegna l'istruzione che aggiorna il contatore e quella che aggiorna la somma corrente, distinguendole dal resto del ciclo. fa avanzare il contatore, e l' che segue aggiunge il nuovo valore del contatore alla somma. L'ultimo fotogramma mostra il test che termina il ciclo. Quando t1 diventa 6, trasferisce il controllo a done invece di lasciar eseguire di nuovo il corpo. Quel test è ciò che separa un passaggio che si ripete dal passaggio che si ferma.
💡
Avanza nel programma con Step invece di Run. Mentre procedi, t1 aumenta e il program counter (pc) salta indietro a loop. Ogni volta che pc torna a loop, la condizione del branch era falsa, quindi il ciclo viene eseguito di nuovo.
⚠
Se la condizione di uscita è sbagliata, il ciclo non termina mai. Il simulatore ferma un ciclo incontrollato, ma la logica del branch spetta a te farla correttamente.