Oi pessoal! No último artigo da série MIPS eu mostrei como compilar o comando FOR e, portanto, dessa forma, todos os comandos de controle foram tratados nesta série. Em outros artigos, também expliquei as instruções aritméticas, lógicas e relacionais, assim como também os procedimentos. De maneira geral, vimos praticamente tudo a respeito de MIPS. Como existem muitas instruções, eu tratei das mais importantes aqui nesta série, mas se você consultar a documentação oficial do MIPS, você encontrará muitas outras, que normalmente são variações das principais. Falta falar sobre Ponto Flutuante, que será o tema deste artigo. Vamos nessa então?
Ponto Flutuante
O coprocessador 1 do MIPS é usado para Ponto Flutuante (P.F.). Ele lida com precisão simples, o que é equivalente a 32 bits, e precisão dupla, 64 bits. Se observarmos no MARS, veremos uma parte dedicada somente a este coprocessador, conforme mostra a Figura abaixo.
Os registradores são numerados de $f0 até $f31. Note também que somente os registradores pares possuem suporte à precisão dupla. Além disto, existem oito flags de código de condição (cc) que são testados, ou alterados, por instruções de movimentação condicionais, desvio e comparação. As instruções lwc1, swc1, mtc1 e mtc1 fazem a movimentação dos valores de precisão simples nesses registradores, enquanto as instruções l.s, l.d, s.s e s.d, fazem a movimentação de precisão dupla. A tabela a seguir resume as instruções de movimentação rapidamente:
|
Instrução |
opcode |
rs |
rt |
rd |
shamt |
funct | |
|
|
6 bits |
5 bits |
5 bits |
5 bits |
5 bits |
6 bits | |
|
mfhi rd |
0 |
0 |
rd |
0 |
0x10 | ||
|
mflo rd |
0 |
0 |
rd |
0 |
0x12 | ||
|
mthi rs |
0 |
rs |
0 |
0x11 | |||
|
mtlo rs |
0 |
rs |
0 |
0x13 | |||
|
mfc0 rt, rd |
0x10 |
0 |
rt |
rd |
0 | ||
|
mfcl rt, fs |
0x11 |
0 |
rt |
fs |
0 | ||
|
mtc0 rd, rt |
0x10 |
4 |
rt |
rd |
0 | ||
|
mtc1, rd, fs |
0x11 |
4 |
rt |
fs |
0 | ||
|
movn rd, rs, rt |
0 |
rs |
rt |
rd |
0xb | ||
|
movz rd, rs, rt |
0 |
rs |
rt |
rd |
0xa | ||
|
movf rd, rs, cc |
0 |
rs |
cc |
0 |
rd |
0 |
1 |
|
movt rd, rs, cc |
0 |
rs |
cc |
1 |
rd |
0 |
1 |
|
|
6 bits |
5 bits |
3 bits |
2 bits |
5 bits |
5 bits |
6 bits |
Os bits de 21 a 26 das instruções de ponto flutuante usam o código 0 quando for precisão simples, e 1 para precisão dupla. Vamos agora ver vários exemplos de instruções de ponto flutuante.
Instruções de P.F.
Vamos conhecer a instrução absoluto. Veja o código Assembly MIPS abaixo:
.data numero1: .float -10.5 .text lwc1 $f0, numero1 abs.d $f2, $f0
Diferente do que vínhamos fazendo, que era usar a instrução li para carregar números imediatos, ou la para endereços de memória, aqui devemos usar lwc1 e lwc2. Além disso, como é um número real, de precisão simples ou dupla, devemos definir o tipo do dado numérico, da mesma forma que faríamos em linguagens de programação de médio e alto nível. Não é tão difícil assim não é mesmo? Basta seguir o mesmo raciocínio lógico na programação que você conhece.
Então, primeiro, declaramos o tipo de número, usando um label. Neste exemplo, numero1 é o label que leva ao valor, do tipo float, -10.5. Somente depois disso, conseguir carregar o número para um registrador. No exemplo que estamos trabalhando, eu carreguei o numero1 para o registrador $f0, que é um registrador que lida com precisão dupla. Feito isto, aí sim podemos usar o valor em alguma instrução. Ao executar esse código Assembly MIPS no MARS você verá que ele funcionará corretamente.
A instrução abs.d encontra o valor absoluto de ponto flutuante double, enquanto a instrução abs.s, encontra o valor absoluto de ponto flutuante simples. Basicamente toda as instruções de ponto flutuante tem uma versão idêntica para precisão simples, denotada por s, e precisão dupla, denotada por d. O exemplo a seguir mostra a soma entre dois valores de ponto flutuante, usando tanto precisão simples, quanto dupla.
.data numero1: .float 10.5 numero2: .float 2.5 numero3: .double 5.3 numero4: .double 15.9 .text lwc1 $f1, numero1 lwc1 $f3, numero2 lwc1 $f0, numero3 lwc1 $f2, numero4 add.d $f4, $f2, $f0 add.s $f5, $f1, $f3
Note que para carregar e calcular os números em precisão dupla, eu usei os registradores pares, e para a precisão simples, usei os registradores ímpares. Também usei a palavra chave double para indicar números de precisão dupla. O exemplo a seguir mostra como usar a instrução de comparação igual.
.data numero1: .float 10.5 numero2: .float 2.5 numero3: .double 5.3 numero4: .double 15.9 .text # números float lwc1 $f1, numero1 lwc1 $f3, numero2 # números double lwc1 $f0, numero3 lwc1 $f2, numero4 # verifica se $f0 é igual a $f2 # a resposta fica no cc: 1 se igual, 0 se diferente c.eq.d $f0, $f2 # verifica se $f1 é igual a $f3 # a resposta fica no cc: 1 se igual, 0 se diferente c.eq.s $f1, $f3
A seguir, apresento vários com todas as instruções de ponto flutuante, tanto para precisão simples, quanto para precisão dupla. Se você quiser ver o resultado no console, você deve usar as chamadas do sistema para isto, conforme mostrei no artigo sobre o FOR. Isto pode dar um pouco mais de trabalho, mas é interessante fazer para treinar seu conhecimento.
.data
numero1: .float 10.5
numero2: .float 2.5
numero3: .double 5.3
numero4: .double 15.9
numero5: .float 3
.text
lwc1 $f1, numero1
lwc1 $f3, numero2
lwc1 $f0, numero3
lwc1 $f2, numero4
lwc1 $f5, numero5
# compara se $f0 é menor que $f2
# a resposta fica no cc: 1 se menor igual, 0 se diferente
c.le.d $f0, $f2
c.le.s $f1, $f3
# compara se $f1 é menor que $f3
# a resposta fica no cc: 1 se menor igual, 0 se diferente
c.lt.d $f0, $f2
c.lt.s $f1, $f3
Código de conversão entre precisão simples e dupla:
.data numero1: .float 10.5 numero2: .float 2.5 numero3: .double 5.3 numero4: .double 15.9 numero5: .float 3 .text lwc1 $f1, numero1 # $f1 número de precisão simples lwc1 $f3, numero2 # $f3 número de precisão simples lwc1 $f0, numero3 # $f0 número de precisão dupla lwc1 $f2, numero4 # $f4 número de precisão dupla lwc1 $f5, numero5 # $f5 número inteiro de precisão simples # converte simples para dupla # $f1 é o número de precisão simples # $f4 é o registrador que vai receber o número convertido cvt.d.s $f4, $f1 # converte dupla para simples # $f0 é o número de precisão dupla # $f7 é o registrador que vai receber o número convertido cvt.s.d $f7, $f0
Código de conversão de números inteiros para precisão simples e dupla:
.data numero1: .float 10.5 numero2: .float 2.5 numero3: .double 5.3 numero4: .double 15.9 numero5: .float 3 .text lwc1 $f1, numero1 # $f1 número de precisão simples lwc1 $f3, numero2 # $f3 número de precisão simples lwc1 $f0, numero3 # $f0 número de precisão dupla lwc1 $f2, numero4 # $f4 número de precisão dupla lwc1 $f5, numero5 # $f5 número inteiro de precisão simples # converte inteiro para dupla # $f5 é o número inteiro de precisão simples # $f4 é o registrador que recebe o número convertido cvt.d.w $f4, $f5 # converte inteiro para simples # $f5 é o número inteiro de precisão simples # $f4 é o registrador que recebe o número convertido cvt.s.w $f7, $f5
Convertendo para número inteiro
.data numero1: .float 10.5 numero2: .float 2.5 numero3: .double 5.3 numero4: .double 15.9 numero5: .float 3 .text lwc1 $f1, numero1 # $f1 número de precisão simples lwc1 $f3, numero2 # $f3 número de precisão simples lwc1 $f0, numero3 # $f0 número de precisão dupla lwc1 $f2, numero4 # $f4 número de precisão dupla lwc1 $f5, numero5 # $f5 número inteiro de precisão simples # converte dupla para inteiro # $f0 é o número de precisão dupla # $f4 é o registrador que recebe o número convertido cvt.w.d $f4, $f0 # converte simples para inteiro # $f1 é o número de precisão dupla # $f7 é o registrador que recebe o número convertido cvt.w.d $f7, $f1
Divisão:
.data numero1: .float 10.5 numero2: .float 2.5 numero3: .double 5.3 numero4: .double 15.9 .text # números de precisão simples lwc1 $f1, numero1 lwc1 $f3, numero2 # números de precisão dupla lwc1 $f0, numero3 lwc1 $f2, numero4 # divisão dupla div.d $f4, $f0, $f2 # divisão simples div.s $f5, $f1, $f3
Multiplicação:
.data numero1: .float 10.5 numero2: .float 2.5 numero3: .double 5.3 numero4: .double 15.9 .text # números de precisão simples lwc1 $f1, numero1 lwc1 $f3, numero2 # números de precisão dupla lwc1 $f0, numero3 lwc1 $f2, numero4 # multiplicação dupla mul.d $f4, $f0, $f2 # multiplicação simples mul.s $f5, $f1, $f3
Subtração:
.data numero1: .float 10.5 numero2: .float 2.5 numero3: .double 5.3 numero4: .double 15.9 .text # números de precisão simples lwc1 $f1, numero1 lwc1 $f3, numero2 # números de precisão dupla lwc1 $f0, numero3 lwc1 $f2, numero4 # subtração dupla sub.d $f4, $f0, $f2 # subtração simples sub.s $f5, $f1, $f3
Raíz Quadrada:
.data numero1: .float 10.5 numero2: .float 2.5 numero3: .double 5.3 numero4: .double 15.9 numero5: .float 3 .text # números de precisão simples lwc1 $f1, numero1 lwc1 $f3, numero2 # números de precisão dupla lwc1 $f0, numero3 lwc1 $f2, numero4 # raíz quadrada dupla sqrt.d $f4, $f0 sqrt.d $f6, $f2 # raiz quadrada simples sqrt.s $f5, $f1 sqrt.s $f7, $f3
Truncamento:
.data numero1: .float 10.5 numero2: .float 2.5 numero3: .double 5.3 numero4: .double 15.9 numero5: .float 3 .text # números de precisão simples lwc1 $f1, numero1 lwc1 $f3, numero2 # números de precisão dupla lwc1 $f0, numero3 lwc1 $f2, numero4 # truncamento dupla trunc.w.d $f4, $f0 trunc.w.d $f6, $f2 # truncamento simples trunc.w.s $f5, $f1 trunc.w.s $f7, $f3
Conclusão
Para o artigo não ficar muito longo e cansativo, termino o assunto aqui. No próximo artigo veremos como trabalhar com as instruções de load/store e também arrays.











Sdds o próximo artigo, kkkkkkkkkk. Muito bacana esse artigo!
Obrigada e desculpe a demora!!!!
Oi Bruno, obrigada pelo seu comentário. Eu vou conferir. Tentarei responder e esclarecer todas as dúvidas nos artigos até domingo que vem (17/01/2021), assim como postar as respostas dos exercícios. Mais uma vez obrigada e solicito que aguarde ok. [ ]s