- Arquitetura de Conjunto de Instruções MIPS
- Primeira Instrução MIPS
- Compilação de Expressões no MIPS
- Convertendo uma instrução com Array no MIPS
- Armazenando um valor em Array no MIPS
- Instruções LW e SW com Array no MIPS
- Instrução IF Simples no MIPS
- Instrução IF Composto no MIPS
- Instrução SLT no MIPS
- Operações Lógicas no MIPS
- Operação Lógica AND no MIPS
- Operação Lógica OR no MIPS
- Operação Lógica NOT no MIPS
- Endereços de Memória no MIPS
- Operandos Imediatos e Constantes no MIPS
- Compilando Arrays com índice variável no MIPS
- Testando as instruções MIPS no MARS
- Executando um Array no MARS para MIPS
- Sinal e Overflow no MIPS
- Compilando instruções com ou sem Sinal, Overflow e Imediato no MIPS
- Compilando While no MIPS
- Compilando Switch/Case no MIPS
- Compilando Funções e Procedimentos no MIPS
- Compilando Procedimentos Recursivos e Aninhados no MIPS
- Detalhamento da Compilação de Procedimentos no MIPS
- MIPS: Instruções de Multiplicação
- MIPS: Instruções de Divisão
- MIPS: Subtração e outras instruções
- Compilando o comando FOR no MIPS
- Ponto Flutuante no MIPS
- Convertendo Código de Máquina em Assembly MIPS – Parte 1
- MIPS: Resolução dos exercícios – Parte 1
- MIPS: Resolução de Exercícios Parte 2
Oi pessoal! Nos artigos anteriores eu mostrei a vocês como compilar operações lógicas, números com e sem sinal, como testar algumas instruções no MARS e, também, alguns comandos de controle, como o IF. Bem, hoje vou mostrar a vocês como compilar o comando de controle WHILE para Assembly MIPS. Vamos começar!
O comando while
A palavra while significa “enquanto” e, em programação significa algo como “enquanto não alcançar o critério de parada, continue executando”. Isso caracteriza o que chamamos de LOOP pois, aquele bloco de código dentro dele continuará executando repetidamente até que algo o faça parar. Entramos em LOOP infinito quando o bloco de código nunca para de executar, sendo isto causado, muitas vezes, por um erro na codificação ou lógica da resolução do problema. Bom, para mais informações sobre os comandos de controle, sugiro ler o meu artigo sobre eles clicando aqui!
Exemplo de compilação
Vou mostrar agora como é feita a compilação de um comando while, na linguagem C, para o MIPS. Considere o seguinte exemplo:
1 2 |
while(save[i] == k) i += 1; |
São apenas duas linhas de instruções em linguagem C, parece simples, e de fato é, porém, a compilação é trabalhosa! Analisando a instrução verificamos que existe um array chamado save o qual é usado como condição de parada do while, “enquanto save[i] == k faça”, isto é, continue executando, significando que o bloco de código só parara quando save[i] for diferente de k. Esse array save deve ser carregado antes para que possa ser utilizado no while, somente depois compilaremos a comparação “==” e o restante do código.
Suponha então que i seja o registrador $s3, k o registrador $s5 e save o registrador $s6, vamos compilar o código Assembly MIPS correspondente a esse segmento em C. Como já aprendemos em artigos anteriores, quando temos um índice variável, precisamos calcular o seu endereço multiplicando i por 4, pela questão do endereçamento em bytes. Mostrei como fazer isso de forma um pouco bruta, somando várias vezes, e depois usando a instrução de deslocamento lógico à esquerda, que vou usar aqui:
LOOP: sll $t1, $s3, 2 # $t1 = 4 * i
Fácil não?! O rótulo (label) LOOP precisa ser inserido no início desta instrução para identificar que ali começa um LOOP. Feito o cálculo do endereço do índice variável, agora precisamos do endereço de save[i], conforme segue:
add $t1, $t1, $s6
Bem, agora que já temos o endereço completo de save[i] podemos fazer a leitura do array, conforme já visto em outros artigos.
lw $t0, 0 ($t1) # $t0 = save[i]
Tranquilo né?! Bem, já calculamos o endereço de i, o endereço de save[i], carregamos o array, agora temos de fazer a comparação, o teste de igualdade, o qual também já aprendemos em um artigo anterior:
bne $t0, $s5, EXIT # vá para EXIT se save[i] diferente de k
EXIT é um label que indica o final desse bloco de código e o mesmo também já foi apresentado em um artigo anterior. Vejam só que legal, estamos usando praticamente tudo o que já aprendemos até agora para compilar o while, assim, essa linha diz que se o array save na posição i for diferente de k, então o compilador deve “pular” para o label EXIT que é o fim deste bloco de código, caso contrário, entra no bloco de código e executa as linhas que estão ali dentro. A instrução em C seguinte é a soma de i com 1, ou seja, um contador, que fica da seguinte forma:
add $s3, $s3, 1 # $s3 = $s3 + 1 (ou i = i + 1)
Bem, assim terminamos a compilação das instruções, mas ainda precisamos fazer uma coisa. While é um LOOP, correto, e lembram-se que na primeira linha criamos um label chamado LOOP?! Então, esse LOOP precisa ser chamado de volta ao final do bloco de código pois, enquanto save[i] for igual a k ele deve repetir o bloco. Em C o início e o fim dos blocos são representados por chaves { }, mas em Assembly MIPS não existe essa representação, ainda assim precisamos dizer quando e onde o bloco de repetição começa e termina. Dessa forma utilizaremos uma instrução de salto para fazer esse papel junto com o label EXIT ao final:
j LOOP # volta para o LOOP
EXIT:
Essas duas últimas linhas então finalizam a compilação, onde j Loop volta para o label Loop e EXIT sai da execução da repetição. Abaixo código completo:
1 2 3 4 5 6 7 |
LOOP: sll $t1, $s3, 2 # $t1 = 4 * i add $t1, $t1, $s6 # $t1 = (4i + $s6) lw $t0, 0 ($t1) # $t0 = save[i] bne $t0, $s5, EXIT # vá para EXIT se save[i] diferente de k addi $s3, $s3, 1 # $s3 = $s3 + 1 (ou i = i + 1) j LOOP # volta para o LOOP EXIT: |
A compilação está pronta, agora vamos encontrar o restante!
a) Linguagem de Máquina
1 2 3 4 5 6 7 |
LOOP: sll $9, $19, 2 add $9, $9, $22 lw $8, 0 ($9) bne $8, $21, EXIT addi $19, $19, 1 j LOOP EXIT: |
b) Representação da Linguagem de Máquina
Endereço de Memória |
OPCODE |
RS |
RT |
RD |
SHAMT |
FUNCT |
0x 0040 0010 |
0 |
0 |
19 |
9 |
4 |
0 |
0x 0040 0014 |
0 |
9 |
22 |
9 |
0 |
32 |
0x 0040 0018 |
35 |
8 |
9 |
0 | ||
0x 0040 001c |
4 |
8 |
21 |
0x 0040 0028 | ||
0x 0040 0020 |
8 |
19 |
19 |
1 | ||
0x 0040 0024 |
2 |
0x 0040 0010 | ||||
0x 0040 0028 |
EXIT |
|
c) Código de Máquina
Endereço de Memória |
Instrução |
0x 0040 0010 |
000000 00000 10011 01001 00100 000000 |
0x 0040 0014 |
000000 01001 10110 01001 00000 100000 |
0x 0040 0018 |
100011 01000 01001 00000 00000 000000 |
0x 0040 001c |
000100 01000 10101 01000 00000 101000 |
0x 0040 0020 |
001000 10011 10011 00000 00000 000001 |
0x 0040 0024 |
000010 00000 00000 01000 00000 010000 |
0x 0040 0028 |
|
Onde:
- 0040 = 0100 0000
- 0010 = 0001 0000
- 0028 = 0010 1000
d) Código no MARS
Para testar o código passo a passo e verificar o comportamento, mude os valores de $s5 e de save. Neste exemplo eu coloquei os valores das cinco primeiras posições de save como zero e k também como zero, assim, enquanto 0 = 0, ele continuará, até chegar em 1, quando finalmente para.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# criando o array save .data save: .word 0, 0, 0, 0, 0, 1 # atribuído valores e endereços para i, k e save .text li $s5, 0 # k = $s5 = 0 li $s3, 0 # i = $s3 = 0 la $s6, save # carregando o endereço de save no registrador $s6 # criando o LOOP LOOP: sll $t1, $s3, 2 # $t1 = 4 * i (4 * $s3) add $t1, $t1, $s6 # save[i] = (4*i + $s6) lw $t0, 0($t1) # $t0 = save[i] bne $t0, $s5, EXIT # vá para EXIT se save[i] diferente de k addi $s3, $s3, 1 # $s3 = $s3 + 1 (ou i = i + 1) j LOOP # volta para o LOOP EXIT: |
Conclusão
Bom pessoal, e assim terminamos mais um artigo da série MIPS. Apenas a título de curiosidade, tentem mudar de BNE para BEQ e vejam o que acontece no MARS. Notem que o conhecimento é cumulativo, isto é, tudo o que você aprende artigo a artigo é necessário para desenvolver uma solução MIPS completa. Espero que estejam gostando e, como sempre, se houver dúvidas, deixem aqui nos comentários que eu responderei assim que for notificada, ok! Muito Obrigada pessoal e até o próximo artigo.