Operações matemáticas

Operações matemáticas

Todos os cálculos acontecem entre registradores. Uma instrução aritmética nomeia o destino primeiro, seguido pelas duas fontes que ela lê: significa dest = src1 + src2.

Operação aritmética
t2, t0, t1←add
t0 = 7t1 = 5+t2 = 12

add coloca t0 + t1 em t2.

As duas linhas que entram pela esquerda são t0 e t1, sempre nessa ordem. Elas entram na mesma forma independentemente de qual operação está em execução: adição, subtração, multiplicação, divisão ou resto. Dois valores entram, uma operação acontece, e um resultado sai em t2 à direita. nomeia t2 primeiro porque é onde o resultado chega, o que corresponde à única regra de ordenação que toda instrução aritmética nesta lição segue.

Soma e subtração

pega os números contidos em dois registradores e armazena sua soma em um terceiro. faz o mesmo para subtração: calcula t0 - t1. Ambas as instruções leem apenas de registradores.

Registrar aritmética
add t2, t0, t1 # t2 = t0 + t1
sub t2, t0, t1 # t2 = t0 - t1

Uma coisa a saber: um registrador contém apenas 32 bits, então há um teto. Se um resultado for grande demais para caber, nenhum erro é gerado. O valor silenciosamente dá a volta para números muito baixos e a execução continua. Valores do dia a dia não atingirão esse limite, mas vale a pena conhecer o teto.

Para usar um número fixo no lugar de um segundo registrador, adicione a letra i, que significa imediato: adiciona a constante 1. Não existe subi, então para subtrair uma constante você adiciona uma negativa: .

💡
adiciona uma constante. adiciona dois registradores. Confundir os dois é o erro de iniciante mais comum.

Multiplicação e divisão

multiplica dois registradores. os divide como números inteiros e descarta a fração, então 7 / 2 resulta em 3. dá o resto, então 7 % 2 resulta em 1. A divisão nunca causa um travamento. Dividir por zero retorna -1.

Multiplicar, dividir, resto
mul t2, t0, t1 # t2 = t0 * t1
div t2, t0, t1 # t2 = t0 / t1 (inteiro)
rem t2, t0, t1 # t2 = t0 % t1 (resto)

Montando tudo

Some dois números e exiba o resultado
li t0, 7
li t1, 5
add t1, t0, t1 # t1 = 7 + 5 = 12
mv a0, t1 # o serviço de impressão lê a0
li a7, 1
ecall
li a7, 10
ecall

carrega 7 em t0. carrega 5 em t1. os soma e escreve 12 em t1. copia o resultado para a0, porque o serviço de impressão sempre lê seu valor de a0. Depois seleciona o serviço de impressão de inteiro e o executa. Por fim, e encerram o programa.

Bônus: multiplicação com deslocamentos

Quando o fator é uma potência de dois, existe um método mais rápido que . (deslocamento lógico à esquerda) move os bits de um registrador para a esquerda, e cada posição deslocada dobra o valor: desloque 1 para multiplicar por 2, 2 para 4, 3 para 8. Deslocar custa menos que multiplicar, então os compiladores o usam com frequência.

Multiplique por 8 usando deslocamentos
li t0, 5 # começa com 5
# desloca 3 bits à esquerda (5 * 2^3 = 5 * 8)
sll a0, t0, 3 # a0 = 40
li a7, 1 # imprimir inteiro
ecall
li a7, 10
ecall

coloca 5 em t0. desloca seus bits três posições para a esquerda. Cada posição dobra o valor, então 5 dobra três vezes (5, 10, 20, 40), resultando em 40 em a0. O resto segue o padrão de impressão anterior. Tente mudar o 3 para 1 ou 2 e preveja o resultado antes de executar.

Multiplicar por deslocamento
5sll a0, t0, 0
×2 →
10sll a0, t0, 1
×2 →
20sll a0, t0, 2
×2 →
40sll a0, t0, 3
0
0
0
0
0
1
0
1

5 deslocado 0 bits para a esquerda permanece 5.

As oito caixas são a forma binária do número acima delas, um bit cada, lidas da esquerda para a direita como de costume. Deslocar a linha inteira uma posição para a esquerda é a mesma operação que multiplicar por 2, porque mover um dígito uma posição para a esquerda em qualquer base multiplica seu valor por essa base. O binário tem apenas dois dígitos, então o multiplicador é 2. Deslocar três vezes acumula isso em uma multiplicação por 8, o que corresponde ao que faz com o número 5.