Olá pessoal! Hoje vou mostrar para vocês como codificar, em Assembly MIPS, o comando de controle FOR. Primeiro mostro um código em C, o qual será usado como base de raciocínio lógico. No MARS, usaremos essa lógica para a codificação. Neste exemplo vamos colocar em prática praticamente tudo o que já aprendemos durante esses três anos de caminhada! Vou fazer passo a passo para o entendimento ficar mais fácil, comentando blocos de código. Se você tiver dúvidas a respeito das instruções, sugiro consultar os artigos anteriores, para verificar se consegue por eles é possível resolver. Caso não consiga mesmo entender, deixe sua dúvida nos comentários tá bom. Prontos para mais este desafio?
Código em C
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
int Vetor[10];
int indice, soma = 0, med =0;
int main() {
soma(Vetor);
media();
return 0;
}
void soma(int Vetor[]){
for(indice=0; indice<10; indice++) {
soma = Vetor[indice] + soma;
}
printf("\nSoma: %d", soma);
}
void media(){
med = soma / 10;
printf("\nMedia: %d", med);
}
Codificando em Assembly MIPS
O primeiro passo é definirmos os dados que vamos usar no programa Assembly:
.data $LS: .asciiz " a soma é: " $LM: .asciiz " a media é: " vetor: .word 0, 1, 2, 3, 4
No espaço de dados defino dois Labels, um LS para o texto que será impresso no console para a soma, e LM para o texto da média. Em seguida defino um vetor de 5 elementos com os valores de 0 a 4. Escolhi assim para ficar mais fácil acompanhar o cálculo na execução do MARS e corrigir possíveis erros. Depois começamos o espaço do código, definindo o programa principal:
.text .globl main
Feito isto, temos de fazer todas aquelas configurações da pilha do procedimento principal. Então escrevi o label main para identificar o inicio e as quatro linhas seguintes são referentes a pilha.
main: # configurações do programa principal subu $sp, $sp, 32 # cria um frame de pilha com 32 bytes sw $ra, 20($sp) # salva o registrador $ra sw $fp, 16($sp) # salva o registrador $fp addiu $sp, $sp, 28 # alinhamento de memória
As próximas linhas são referentes às chamadas dos procedimentos.
la $a0, vetor # carrega o vetor jal soma # chama o procedimento soma # Move o conteúdo do registrador de retorno ($v0) para o registrador # ($a1), liberando-o para ser usado novamente move $a1, $v0 # chama o procedimento media jal media move $a1, $v0 # libera $v0
Carregamos o vetor para o registrador $a0, que é o primeiro parâmetro do procedimento, usando a instrução la. A instrução jal será executada depois que o procedimento soma terminar. Liberando $v0, conseguimos então chamar na sequencia o procedimento media. Novamente precisamos liberar $v0 copiando seu conteúdo para $a1. As próximas linhas são referentes à impressão no console das strings e dos valores inteiros retornados pelos procedimentos:
.text # imprimindo a string li $v0, 4 la $a0, $LS syscall # imprimindo o inteiro li $v0, 1 move $a0, $s1 syscall # imprimindo a string li $v0, 4 la $a0, $LM syscall # imprimindo o inteiro li $v0, 1 move $a0, $s2 syscall
Note que para os dois procedimentos foi necessário replicar código. Para a soma é necessário passar como parâmetro o label $LS e para a média o $LM. Além disso, o resultado da soma será armazenado no registrador $s1, e o da média em $s2, por isso eles são usados na chamada de sistema para impressão de números inteiros. Tome muito cuidado com esses detalhes, é muito fácil confundir labels, registradores, etc. Preste bastante atenção quando estiver fazendo seus próprios códigos. As próximas linhas são referentes à configuração do programa principal.
# configurações do programa principal lw $ra, 20($sp) # restaura valor de $ra lw $fp, 16($sp) # restaura valor de $fp addiu $sp, $sp, 32 # remove o frame de pilha j fim # encerra o programa
Agora vamos ver o procedimento da soma, que tem um for para fazer a soma de todos os elementos do vetor. Primeiro defini um label para o procedimento, chamado soma, e em seguida fiz todas as configurações da pilha de procedimento. Nunca se esqueça deles!!! Esse trecho de código você sempre pode utiliza-lo como padrão quando usar procedimentos.
.textsoma: # configurações do procedimento subu $sp, $sp, 32 # reserva o espaço do frame sw $ra, 20($sp) # salva o endereço de retorno sw $fp, 16($sp) # salva o frame pointer addiu $fp, $sp, 28 # prepara o frame pointer sw $a0, 0($fp) # salva o argumento
Próximo passo é carregar o vetor para ser usado no procedimento, assim como definir um registrador para guardar o número total de elementos ($s3) no vetor e outro que guardará a soma ($s1). Esses registradores serão usados no FOR.
la $s4, vetor # indice do vetor li $s3, 5 # número total de elementos no vetor li $s1, 0 # soma
Chegamos na parte que é novidade, como codificar o FOR no MIPS. Antes de iniciar a parte do código referente ao LOOP, precisamos definir i = 0. O registrador que usarei para isto será o $s0. Assim, movi o valor zero, que está no registrador $zero, para $s0. Pronto, a variável i foi inicializada com zero, lembrando que o nosso i é o índice do vetor.
#FOR move $s0, $zero # i = 0 ($s0 é i)
Feito isto, comecemos o LOOP. O FOR é uma estrutura de controle como o WHILE e o DO-WHILE. Para escolher para onde ir, vamos precisar usar a instrução SLT juntamente com a BEQ, como já estudamos antes. Essas duas linhas também podem ser usadas como um padrãozinho toda vez que você for codificar um LOOP FOR. A instrução SLT verifica se i >= n, ou $s0>= $s3, que é a condição de parada aqui. Se for verdade, armazena zero em $t0, caso contrário, armazena 1. Bom, mas a instrução SLT, apenas armazena um valor 0 ou 1, ela não toma nenhuma decisão, então, quem é que faz isso? A instrução BEQ é quem faz esse papel, ela decide continuar, ou não, executando o código dentro do LOOP. Se já alcançamos o final do vetor, então, a execução deve parar (EXIT), mas enquanto não atingirmos o fim do vetor, o bloco deve continuar executando. Assim, se i>=n, sai do LOOP, caso contrário, continua executando.
LOOP: # configurações do FOR slt $t0, $s0, $s3 # t0 = 0 se $s0 >= $s3 ( i >= n), t0 = 1 caso contrário beq $t0, $zero, EXIT # se $s0 >= $s3 ( i >= n) vá para EXIT
Depois de codificar corretamente a instrução FOR, pelo menos a primeira e a segunda parte dele, devemos agora tratar do ARRAY. Jamais se esqueçam do alinhamento de memória usado no MIPS. Temos de fazer aquele cálculo, padrãozinho também, toda vez que usarmos ARRAYs. Assim, usei a instrução SLL para tornar isso mais fácil, ela já faz o cálculo de 4*i juntamente com o registrador que vamos usar, que neste caso, é o $s0 (i). Em seguida, usei o registrador $s2 para armazenar o resultado final do endereço, que é a soma do endereço do vetor com o (4*i). Depois de tudo isso, podemos fazer o carregamento do elemento que está na posição que queremos, e então soma-lo com o valor que está em $s1 (que será a nossa soma final).
# configuração do ARRAY sll $t1, $s0, 2 # $t1 = 4 * i, ou 4 * $s0 add $t2, $s4, $t1 # t2 = ( vetor + ( 4 * i) ) lw $t3, 0($t2) # $t3 = vetor[i], carregando o elemento do índice i add $s1, $s1, $t3 # somando os elementos (soma = soma + vetor[i]
Elemento carregado, somado e resultado guardado. Agora é hora de ir para a próxima posição do vetor. Como fazer isso? No FOR usamos um contador, i +=1, mas e no Assembly? Não é tão difícil ou complicado, só é preciso cuidar com os registradores que devem ser utilizados. Usei a instrução ADDI para somar o valor que está no índice i ($s0), com o imediato 1, assim incrementamos o índice e conseguimos passar para a próxima posição. A instrução J volta para o inicio do LOOP e o label EXIT sai desse bloco de código ao término da execução do LOOP.
# configurações do FOR addi $s0, $s0, 1 # $s0 = $s0 + 1 (ou i = i + 1) é o contador j LOOP # volta para o LOOP EXIT:
Finalizando o procedimento soma, temos de terminar as configurações referentes aos registradores. Passei $s1 na instrução ADD pois é este registrador que contem o resultado da soma, e que deve retornar ao programa principal. A instrução JR encerra o procedimento.
# configurações do procedimento add $v0, $s1, $zero # retorna para quem chamou. jr $ra
O procedimento media é exatamente a mesma coisa, só que mais fácil ainda, pois não há um FOR dentro dele a ser codificado. Dessa forma, as configurações do procedimento devem ser codificadas corretamente. Aqui eu usei a instrução DIV para calcular a média, portanto, $s3 é o numero total de elementos no vetor, e $s1 é a soma calculada no procedimento soma. O resultado é armazenado em $s2, o qual é passado na instrução ADD que retorna ao programa principal.
media: # configurações do procedimento subu $sp, $sp, 32 # reserva o espaço do frame sw $ra, 20($sp) # salva o endereço de retorno sw $fp, 16($sp) # salva o frame pointer addiu $fp, $sp, 28 # prepara o frame pointer sw $a0, 0($fp) # salva o argumento li $s3, 5 # número total de elementos no vetor li $s2, 0 # media div $s2, $s1, $s3 # calcula a média # configurações do procedimento add $v0, $s2, $zero # retorna para quem chamou. jr $ra
Encerrando nosso “programinha” em Assembly MIPS, precisamo encerrá-lo corretamente:
fim: li $v0, 10 syscall
A seguir você confere o código completo, para facilitar a visualização e também para que você teste em sua máquina.
.data
$LS: .asciiz " a soma é: "
$LM: .asciiz " a media é: "
vetor: .word 0, 1, 2, 3, 4
.text
.globl mainmain:
# configurações do programa principal
subu $sp, $sp, 32
sw $ra, 20($sp)
sw $fp, 16($sp)
addiu $sp, $sp, 28
la $a0, vetor # chama o procedimento
jal soma
# Move o conteúdo do registrador de retorno ($v0) para outro
# registrador ($a1), para que seja liberado e usado novamente.
move $a1, $v0
jal media
move $a1, $v0.text
# imprimindo a string
li $v0, 4
la $a0, $LS
syscall
# imprimindo o inteiro
li $v0, 1
move $a0, $s1
syscall
# imprimindo a string
li $v0, 4
la $a0, $LM
syscall
# imprimindo o inteiro
li $v0, 1
move $a0, $s2
syscall
# configurações do programa principal
lw $ra, 20($sp)
lw $fp, 16($sp)
addiu $sp, $sp, 32
j fim # encerra o programa
.text
soma:
# configurações do procedimento
subu $sp, $sp, 32 # reserva o espaço do frame
sw $ra, 20($sp) # salva o endereço de retorno
sw $fp, 16($sp) # salva o frame pointer
addiu $fp, $sp, 28 # prepara o frame pointer
sw $a0, 0($fp) # salva o argumento
la $s4, vetor # indice do vetor
li $s3, 5 # número total de elementos no vetor
li $s1, 0 # soma
#FOR
move $s0, $zero # i = 0 ($s0 é i)
LOOP:
# configurações do FOR
slt $t0, $s0, $s3 # t0 = 0 se $s0 >= $s3 ( i >= n), t0 = 1 caso contrário
beq $t0, $zero, EXIT # se $s0 >= $s3 ( i >= n) vá para EXIT
# configuração do ARRAY
sll $t1, $s0, 2 # $t1 = 4 * i (4 * $s0)
add $t2, $s4, $t1 # t2 = ( vetor + ( 4 * i) )
lw $t3, 0($t2) # $t3 = vetor[i]
add $s1, $s1, $t3 # somando os elementos (soma = soma + vetor[i]
# configurações do FOR
addi $s0, $s0, 1 # $s0 = $s0 + 1 (ou i = i + 1) é o contador
j LOOP # volta para o LOOP
EXIT:
# configurações do procedimento
add $v0, $s1, $zero # retorna para quem chamou.
jr $ra
media:
# configurações do procedimento
subu $sp, $sp, 32 # reserva o espaço do frame
sw $ra, 20($sp) # salva o endereço de retorno
sw $fp, 16($sp) # salva o frame pointer
addiu $fp, $sp, 28 # prepara o frame pointer
sw $a0, 0($fp) # salva o argumento
li $s3, 5 # número total de elementos no vetor
li $s2, 0 # media
div $s2, $s1, $s3 # calcula a média
# configurações do procedimento
add $v0, $s2, $zero # retorna para quem chamou.
jr $ra
fim:
li $v0, 10
syscall
Conclusão
E então pessoal? Acharam muito difícil? Se tiverem dúvidas, deixem aqui nos comentários, que responderei o mais breve possível. Para exercitar um pouco, sugiro vocês criarem mais procedimentos usando vetores. É isso galera, até o próximo artigo.










Por enquanto, Elaine, quero apenas te parabenisar por esse teu trabalho! o descobri recentemente tanto como venho descobrindo aos poucos esse novo mundo da computação. Já li, reli várias vezes algumas dessas postagens suas aqui, até entender, e com um prazer enorme te falo que tem ajudado bastante, sobretudo nesse período de pandemia! Que esse teu trabalho seja coroado de bençãos e que muitos bons e benéficos frutos (incluindo um dindim que nunca é demais!) possam surgir dai. Sou iniciante na computação e teu valioso material já é uma referencia pra mim. Grande obrigado!
Oi João. Em primeiro lugar, desculpe pela demora em responder. Em segundo lugar, muito obrigada pelo carinho. Fico muito feliz em saber da sua trajetória e desejo todo sucesso do mundo =) Muito Obrigada <3