3

I need to send two real numbers from C (using extern 'C' double (double num1,double num2)) to a assembly procedure that returns the addition of these numbers. I don't know how to do this yet.

I tried this but it didn't work.

.data

 res dq 0

.code

        procSuma proc num1,num2

           fld num1   ; load num1 and push it onto the fpu stack
           fld num2   ; load num2 and push it onto the fpu stack
           faddp      ; pop two numbers, add them, push sum on the stack
           fstp res   ; pop sum from the stack and store it in res

        procSuma endp
 end
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • 2
    "didn't work" means what? Didn't compile, didn't produce the right result, something else? – Mats Petersson Nov 15 '15 at 03:24
  • it didn't produce the right result. – Christian Salazar Nov 15 '15 at 03:28
  • Please clearly state the expected output and the output you actually get in your question – LBes Nov 15 '15 at 03:57
  • 1
    The calling convention on Windows says that st(0) should contain the return value for a double. So you should be able to eliminate the `res` variable altogether, get rid of of `fstp res`. `faddp` should have added the two value and put the result in `st(0)`, so you should only have to do `ret` right after. Since you are passing double floats into _procSuma_ you should change `procSuma proc num1,num2` to `procSuma proc num1:REAL8,num2:REAL8` so MASM knows your two parameters are REAL8(8 bytes) – Michael Petch Nov 15 '15 at 04:03
  • 1
    One other issue could be that you should specify `PROC C` instead of `PROC` so that MASM sets the procedure up using C calling convention. So `procSuma proc num1:REAL8,num2:REAL8` should probably be `procSuma proc C num1:REAL8,num2:REAL8` – Michael Petch Nov 15 '15 at 04:27
  • 3
    FYI, learning x87 FPU is not tremendously useful in 2015. If you really want to, there's a good tutorial linked from [the x86 tag wiki](http://stackoverflow.com/tags/x86/info). For new code, use SSE/SSE2, unless you need backwards compat with ancient 32bit-only CPUs (e.g. Athlon XP was the last CPU to *not* support SSE2). The 32bit ABI returns floating point results in `st(0)`, so you can't avoid it completely in 32bit code, which is another reason 32bit sucks and should be considered obsolete. – Peter Cordes Nov 15 '15 at 04:34
  • 2
    @PeterCordes The x87 instruction set still provides extended-double format and operations with 64 bits of precision, and is very useful in 2015 for this reason. There is little reason to use it from assembly, though. – Pascal Cuoq Nov 15 '15 at 15:56

2 Answers2

2

Where you went wrong is you copy pasted code that was originally not contained in a function into a function, which I believe you got from here. Adding floating point/double numbers in assembly

Since the assembly code is now contained within a proc, a variable is no longer needed to store the result because the result is stored on the stack at the address of st(0), which would be your return value. When fstp is called, it pops the stack (Hint: the p at the end) so your return value stored at st(0) is no longer valid. Removing the fstp instuction will give you the intended result.

Step by step this is what the code is doing.

procSuma proc num1,num2   

       fld num1   ; This pushes num1 onto the stack at the address of st(0)
       fld num2   ; This pushes num2 onto the stack at the address of st(1)
       faddp      ; This multiplies st(1) and st(0), stores the result at st(0), and then pops the variable stored at st(1) off the stack   
       fstp res   ; !REMOVE THIS. This is why you aren't getting the expected result.It pops the variable in st(0) off the stack, which is your return value.

procSuma endp
Community
  • 1
  • 1
  • 1
    Welcome to SO. This would be a better answer if you explained *why* the result needs to be in `st(0)`, instead of some randomly-named global he made up. (i.e. because of the Windows x86 32bit ABI). Also, suggesting re-loading with `fld` after storing is just confusing. – Peter Cordes Nov 15 '15 at 04:40
  • Thank you, @PeterCordes . It is a "just get it working answer" The reloading of res would be redundant and cargo cultish. – Daren Coffey Nov 15 '15 at 05:03
  • Michael Petch's answer-in-comments is much better than this answer, because it explains they reason. This answer doesn't do much to help a confused newbie understand what's going on, and so isn't very long-term useful to anyone else whose google search turns up this Q&A. – Peter Cordes Nov 15 '15 at 05:10
2

I'm assuming this is a homework assignment that requires you to use x87 FPU instructions. Alternatively you could be using SSE/SSE2 SIMD instructions to perform the addition.

C calling (cdecl) convention on Win32 requires single and double precision floats to be returned in ST(0) (register on top of the FPU stack). Since faddp adds two numbers and places the result in ST(0) means that we don't need any temporary variables to store the result. These lines can then be removed:

.data
res dq 0

and

fstp res   ; pop sum from the stack and store it in res

When creating a procedure you should specify the size of the arguments being passed so that MASM knows exactly what size the operands are for operations. To ensure you are using double precision values mark the double arguments as REAL8 (8 byte REALS). You should also mark the PROC as using C calling convention with PROC C so the stack and the arguments are properly set up and addressed. The procedure declaration should look like:

procSuma proc C num1:REAL8,num2:REAL8 

The resulting code could look like:

.model flat, c
.code

        procSuma proc C num1:REAL8,num2:REAL8

           fld num1   ; load num1 and push it onto the fpu stack
           fld num2   ; load num2 and push it onto the fpu stack
           faddp      ; pop two numbers, add them, push sum on the stack.
                      ; result in st(0) (top of FPU stack)
                      ; We return double values in st(0) so nothing to do
                      ; since faddp put our result in st(0)
           ret

        procSuma endp
 end

You could reduce the addition to a couple instructions by using FADD instead of FADDP:

       fld num1   ; load num1 and push it onto the fpu stack
       fadd num2  ; Add ST(0)(num1) to num2 and store result in ST(0).
                  ; We return double values in st(0) so nothing to do
                  ; since fadd put our result in st(0)
Michael Petch
  • 46,082
  • 8
  • 107
  • 198