Pavel Perevezencev
Pavel Perevezencev

Reputation: 2978

Calling NASM float in x86 assembly from C

I have this example. For my task I need for using float instead of int:

#include <stdio.h>

extern float my_pow(float base, float exp);

int main(int argc, char const *argv[]) {
  float base = 2.0, exp = 8.0;
  printf("Result: %f\n", my_pow(base, exp));
  return 0;
}

Build with nasm and gcc:

nasm -f macho64 calc.asm
gcc -m64 -o main main.c calc.o

As a output I get this:

Result: 2.000000

When my result should be 256.0. What I am doing wrong?

UPDATE: my asm code did not change

global _my_pow
section .text

_my_pow:
    push    rbp             ; create stack frame
    mov     rbp, rsp

    cmp     edi, 0          ; Check if base is negative
    mov     eax, 0          ; and return 0 if so
    jl      end

    mov     eax, edi        ; grab the "base" argument
    mov     edx, esi        ; grab the "exponent" argument

multiply:
    imul    eax, edi        ; eax * base
    sub     esi, 1          ; exponent - 1

    cmp     esi, 1          ; Loop if exponent > 1
    jg      multiply

end:
    pop     rbp             ; restore the base pointer
    ret                     ; return from procedure

Upvotes: 0

Views: 702

Answers (1)

JMcCoy
JMcCoy

Reputation: 316

I'd like to expand on Peter's answer because what he's saying might not be clear to beginners.

In the first part of your code under _my_pow you get the first two arguments from edi and esi, which would be correct for most x86_64 functions. However it's different with floating point numbers. When dealing with floating point numbers the first argument is in register xmm0, and the second argument is in xmm1. A float return value is returned in xmm0.

So what Peter was saying is that the C complier will put the arguments you provide in those registers (because that's what the prototype you used + the calling convention require it to do). Since you don't operate on them in your code you just end up with your base argument in xmm0 at the end. And that means your base argument 2.0 gets returned to C as the return value.

Look up instructions like "movss" and "mulss" online, those are the kind of instructions you need to know to do this right. (Links to docs in the x86 tag wiki)

Upvotes: 2

Related Questions