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? (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.
p
no final da instrução indica que, além de sua função principal, ela
faz um 'pop' na pilha de registradores.
?
representa um indicador do tamanho do operando:
l
para double
s
para float
As instruções FILD? e FISTP? 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!!!!
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.
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
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