Search code examples
cassemblygnu-assemblerx87

Access floating-point return of assembly function


our course exercise asks us to create a delta = b2 - 4ac function in GNU assembly, and access it from C. Since this is a course about compilers, and not about assembly, the professor chose to only demonstrate integer capabilities, and expects an integer function.

However I would like for personal learning to go a little beyond the call of duty, and create a usable function returning floating points rather than integers.

I came up with this C (dont't mind the globals, the purpose of the next exercise is to have a proper function with parameters):

# include <stdio.h>

extern float delta();
float a, b, c;

int main() {
    a = 3;
    b = 5;
    c = 4;
    printf("δ = %f\n", delta());
    return 0;
}

and this GNU GAS:

.globl a
.globl b
.globl c
.globl delta
.f4: .float 4.0     # constante 4

.text
delta:
    fld b           # b sur la pile
    fmul b          # b2 sur la pile
    fld .f4         # 4 sur la pile 
    fmul a          # 4a sur la pile
    fmul c          # 4ac sur la pile
    fsubp           # b2 - 4ac sur la pile
ret

From Google I was led to believe that I should leave a floating point result at the top of the floating point stack, however this doesn't work, and the result printed in the C caller is invariably 0.0000000.

I must miss a very small something, but no amount of googling brings it up, can anyone point me in the right direction? Thanks for your attention.


Solution

  • It does work for me. Make sure you are not accidentally using 64 bit mode, because calling convention is different there. That is, use gcc -g -m32 foo.c bar.s to compile.

    That said, I also see some potential problems that should be fixed.

    • Since your globals are defined in the C code, you should not use .globl in the assembly. If anything, you should use .extern, but GAS does not require that.
    • You should not rely on default operand size. You should explicitly use s suffix for floats and l suffix for doubles when you have memory operands. For example, flds b to make sure it's loaded as a float.
    • You should use fsubrp because the top of stack is your 4ac, so what you calculate using fsubp is 4ac-b^2.