Decisões e repetições
Por padrão, a CPU executa suas instruções na ordem em que aparecem, de cima para baixo. Por si só, essa ordem nunca pode fazer uma escolha ou repetir trabalho. Um branch é como um programa sai desse caminho reto: ele testa uma condição e, quando a condição é verdadeira, direciona a CPU para continuar em um lugar diferente em vez da próxima linha. Toda decisão e todo loop vêm desse único mecanismo.
Não existe if nem for. Você os constrói a partir de labels (posições nomeadas) e branches (salto para uma posição quando uma condição é verdadeira). Um label é um nome seguido de :.
Para entender como um branch é executado, você precisa conhecer o Program Counter (PC). O PC é um registrador especial, oculto do seu código, que contém o endereço de memória da instrução sendo executada. Uma instrução de branch escreve um novo valor no PC, então a CPU busca sua próxima instrução no label de destino em vez da linha de código seguinte.
A família de desvios
Todo branch compara dois registradores e salta para o label somente quando o teste é verdadeiro. Quando o teste falha, a execução continua na próxima linha. Seis testes formam o núcleo:
beq a, b, label # desvia se a == bbne a, b, label # desvia se a != bblt a, b, label # desvia se a < b (com sinal)bge a, b, label # desvia se a >= b (com sinal)bltu a, b, label # desvia se a < b (sem sinal)bgeu a, b, label # desvia se a >= b (sem sinal)
e testam igualdade e ignoram o sinal. e formam o par de ordenação, menor que e maior ou igual, e comparam os registradores como números com sinal. e fazem as mesmas comparações, mas tratam os valores como sem sinal, o que serve para endereços ou contagens que nunca são negativos.
O que significam com sinal e sem sinal? Um registrador contém 32 bits, e os mesmos bits podem ser lidos de duas maneiras. Lidos como sem sinal, eles formam um número inteiro simples de 0 até cerca de 4 bilhões. Lidos como com sinal, o bit mais alto conta como um sinal de menos, então metade dos padrões de bits representa números negativos. Os bits em si não mudam. Só muda a forma como você os lê. É por isso que existem dois conjuntos de comparações: use as com sinal (, ) para números comuns que podem ser negativos, e as sem sinal (, ) para valores que nunca são negativos, como endereços de memória.
Não existe um "branch se maior" nem "branch se menor ou igual" diretos. Você os obtém trocando os dois registradores, e o assembler fornece e como pseudo-branches que fazem a troca para você, então se torna . Mais dois atalhos: e testam um registrador contra zero, e salta sem nenhuma condição.
li t0, 0 # soma = 0li t1, 1 # i = 1li t2, 6 # limite = 6 (repete enquanto i < 6)loop:bge t1, t2, done # sai do laço se i >= 6add t0, t0, t1 # soma += iaddi t1, t1, 1 # i++j loop # volta para verificar de novodone:mv a0, t0 # imprime a soma (deve ser 15)li a7, 1ecallli a7, 10ecall
Trace isso. t0 contém a soma corrente (começa em 0), t1 contém o contador i (começa em 1), e t2 contém o limite 6. A cada passagem por loop: sai do loop quando i chega a 6. Caso contrário, adiciona i à soma, aumenta i em um, e salta de volta para testar novamente. O loop soma 1, 2, 3, 4 e 5, e done imprime o total, 15.
bge compara t1 com t2 para decidir se o loop continua.
t1 se torna 6, transfere o controle para done em vez de deixar o corpo rodar novamente. Esse teste é o que separa uma passagem que se repete da passagem que para.t1 aumenta e o program counter (pc) salta de volta para loop. Toda vez que pc retorna a loop, a condição do branch era falsa, então o loop roda novamente.