Greetings Everybody!
I am working on a code that receives a double and an int from a c program. It then is supposed to multiply the two and return a 64 bit floating point result. I am successfully to receive high part of double to EAX and low part to EBX. I put the int into ECX. Now, I am stuck as to how to proceed :'( Any help would be greatly appreciated. Below is my code so far:
section .text
global _multiply
_multiply:
push ebp ; save program status
mov ebp,esp ;
push ebx ;
push esi ;
push edi ;
mov ebx,[ebp+8] ; low part of double
mov eax,[ebp+12] ; high part of double
mov ecx,[ebp+16] ; integer
;mov edi,eax ; get sign of double
;test ecx,ecx ;
;jns intpositive ; integer is positive
;xor edi,ecx ; multiply signs
movzx eax,(word or byte)
will zero-extend the word or byte into the eax register (for unsigned values)
movsx eax,(word or byte)
will sign-extend the word or byte into the eax register (for signed values)
the high dword of the result after mul or imul winds up in edx, the low dword winds up in eax
i like to use xchg, as it is a one byte opcode when used with eax
xchg eax,ebx
xchg eax,edx
thing is - what was in ebx is now in edx
if that's a problem, use this
mov ebx,eax
mov eax,edx
hi,
fild DWORD ptr [ebp+16] ; load integer
fmul REAL8 ptr [ebp+8] ;mul by double
fstp REAL8 ptr [r32] ;pointer to dest.
; or in registers:
;fstp REAL8 ptr [esp-8]
;mov eax,DWORD ptr [esp-8] ;low-dword
;mov edx,DWORD ptr [esp-4] ;high-dword
regards, qWord
ah yes - i missed the word "float" - lol
Thank you dedndave and qWord both for your input. You guys are lightning quick :U I ended up using qWord's advise. Now the program is working correctly. Since I compiled it under NASM, I had to change two details from qWord's advise. Here is the code in case anyone needs it in the future:
section .text
global _multiply
_multiply:
push ebp ; save program status
mov ebp,esp ;
push ebx ;
push esi ;
push edi ;
mov ebx,[ebp+8] ; low part of double
mov eax,[ebp+12] ; high part of double
mov ecx,[ebp+16] ; integer
fldz ; initialize
fild DWORD [ebp+16] ; load integer
fmul qword [ebp+8] ;mul by double
fstp qword [ebp+20] ;pointer to dest
fld qword[ebp+20] ; store in fp register
add esp,8 ; free place on stack
pop esi ; restore program status
pop ebx ;
mov esp,ebp ;
pop ebp ;
ret ;
;============================================================
;============================================================
; and here is the corresponding c source
#include <stdio.h>
double multiply ( double a , int b ) ;
int main ( int argc , char * argv [] )
{
double a ;
int b ;
printf ( "Enter a number (double): " ) ;
scanf ( "%lf" , &a ) ;
printf ( "Enter a number (int): " ) ;
scanf ( "%d" , &b ) ;
printf ( "%lf * %d = %lf\n" , a , b , multiply ( a , b ) ) ;
scanf( "%d", &b); // suprefluous read to make cmd window stay open
return 0 ;
}
A few comments.
i) The "fldz" instruction is totally useless in this context.
ii) Storing the result of the multiplication on the stack and reloading it in the same FPU register is also totally useless in this context.
iii) You preserve ebx, esi and edi (even though you are not even using esi nor edi within that procedure). However, you only restore esi and ebx before exiting the procedure. That would cause a stack imbalance and the proper return address from the procedure would not be loaded in the EIP register most probably causing the program to crash.
iv) Storing some value from the FPU onto the stack does NOT modify the ESP register. I'm surprised your program didn't crash on exit from the procedure by modifying the ESP register like you did (i.e. add esp,8) which would overcompensate for the 4 bytes you forgot to pop off.
Those are some of the details of assembly! :dazzled: :eek
As Raymond points out you do some strange things...
Setting up a stack frame is not necessary, given you push and pop nothing, don't use locals, or make any function calls.
You push ebx, esi, and edi, but pop them back out to different registers (new esi = old edi, new ebx = old esi, old ebx left on the stack).
You store things to registers, which you never use.
fldz does nothing for you here.
You store your result to some memory, then re-load it.
You add 8 to esp, then overwrite esp with ebp - hence the add does nothing.
You should be wary that the function declaration is using the C calling convention, if it was not then you would need to use "ret 12" rather than plain ret. I would comment it as such.
Your code should work using the following:
_multiply:
fild DWORD [esp + 16] ; Load the integer "b"
fmul REAL8 [esp + 8] ; Load the double "a"
ret ; Use ret 12 if using stdcall
Thanks,
Mirno