Unidade de Ponto Flutuante

A UPF tem seu próprio conjunto de registradores. Eles são organizados como uma pilha (de 8 registradores):

              --------------
              |             |
              --------------
         /|\  |             | <- st(0)  ---- top
          |   --------------
pilha     |   |             |
cresce    |   --------------
              |             |
              --------------
              |             | 
              --------------
              |             |
              --------------
              |             |
              --------------
              |             |
              --------------

Cada registrador da pilha de ponto flutuante tem 80 bits, para maior precisão nas operações.

Há várias instruções para manipulação desses registradores, mas vamos sempre usar a notação de pilha, ou seja, se quisermos fazer alguma operação, empilhamos os dois operandos e em seguida realizamos a operação:

        fldl  8(%ebp)           /* coloca primeiro argumento na pilha UFP */
        fldl  16(%ebp)          /* idem segundo argumento */
        faddp                   /* soma os dois */

As instruções de transferência de dados da UFP, FLD? e FSTP?P (Load e Store) movimentam dados entre memória principal e pilha de registradores. Não é possível movimentar dados diretamente entre os registradores da CPU e os da UPF.

As instruções FILD? e FIST?P realizam conversões entre a representação em ponto flutuante e a representação de inteiro em complemento a 2.

atenção!! Essa é uma pilha de registradores, dentro da CPU, e não tem nada a ver com a pilha de registros de execução, cujo topo é apontado por ESP!!!!

Passagem e retorno de valores

A passagem de valores float ou double para uma função deve ser feita, como sempre, através da pilha. A convenção little-endian do pentium é estendida para o caso do double, ou seja, a palavra menos significativa deve aparecer em endereços de memória menores do que a mais significativa.

Quando uma função C retorna um valor float ou double, esse valor deve ser retornado no topo da pilha de registradores da UPF.

Exemplo de Uso da UFP

  1. A função C abaixo:
    double soma (double a, double b) {
      return a+b;
    }
    
    poderia ser escrita em assembly como:
    soma:   push  %ebp
            mov   %esp, %ebp
    
            fldl  8(%ebp)		/* carrega para a pilha da UPF o argumento a */
            fldl  16(%ebp)          /* idem b */
            faddp 			/* soma os dois */
    
            mov %ebp, %esp
            pop %ebp
            ret
    
  2. A função C abaixo:

    double dot (double a[], double b[], int tam) {
      int i;
      double res = 0.0;
      for (i=tam;i>0;i--)
        res += a[i]*b[i];
      return res;
    }
    
    poderia ser escrita em assembly como:
    dot:    push %ebp
            mov %esp, %ebp
    
            mov 8(%ebp), %eax   /* endereco de a[0] */
            mov 12(%ebp), %edx  /* endereco de b[0] */
            fldz                /* res = 0.0 */
            mov 16(%ebp), %ecx  /* i = tam */
    
    fori:   cmp $0, %ecx
            jle fim
    
            fldl (%eax)     /* a[i] */
            fldl (%edx)     /* b[i] */
            fmulp           /* a[i]*b[i] */
            faddp           /* res += a[i]*b[i] */
    
            add $8, %eax		/* atualiza endereco de a[i] */
            add $8, %edx 		/* atualiza endereco de b[i] */
            dec %ecx    		/* atualiza i */
            jmp fori
    
    fim:    mov %ebp, %esp 		/* resultado ja' esta' na pilha! */
            pop %ebp
            ret
    
Referência