I've started learning assembly recently, and I'm now learning about the FPU x86 architecture and the FPU stack. I have two simple functions for converting between Celsius and Fahrenheit and vice versa.
I've looked into various different instructions, I've tried variants of the FPU instructions including ones that automatically perform POP
operations, and tried to go through the debugger to make sense of what I was seeing. So far to no avail.
.386
.model flat, c
.const
r8_ftoc real8 0.5555555556 ;5/9
r8_ctof real8 1.8 ;9/5
i4_32 dword 32
.code
fahrentocel PROC
push ebp
mov ebp, esp
fld[r8_ftoc]
fld real8 ptr [ebp+8] ; load f
fild[i4_32] ; load 32
fsubp
fmulp
pop ebp
ret
fahrentocel ENDP
celtofahren PROC
push ebp
mov ebp, esp
fild real8 ptr [ebp+8] ; load c
fmul[r8_ctof]
fiadd[i4_32]
pop ebp
ret
celtofahren endp
END
C code:
extern "C" double fahrentocel(double temp);
extern "C" double celtofahren(double temp);
int main()
{
double celsius = 30.0;
double fahrenheit = 212.0;
double output = fahrentocel(fahrenheit);
printf("%lf", output);
}
My input for fahrenheit to celsius is 212.0, so the celsius output is 100 and my conversion from celsius to fahrenheit is 30.0, so the result in fahrenheit should be 86.
But instead, I get 562950 and 858993459 respectively for each of the two functions. I don't receive any error codes, so the function seems to perform without exception, which tells me it's likely a logical error with the way I've written my code.
There's a couple of problems here. According to the functions' declarations in the C code that uses them, i.e. :
extern "C" double fahrentocel(double temp);
extern "C" double celtofahren(double temp);
Both these functions take a double
argument and return a double
result. This is fine when it comes to the interface, but your implementation of celtofahren
says otherwise :
fild real8 ptr [ebp+8] ; load c
Here, you load the function's argument onto the FPU stack as an integer, even though you have yourself instructed the C compiler that the function takes a double. The C compiler thus emitted code that pushes a double
onto the regular stack, but your assembly reads it as a regular integer due to the fild
instruction. Do note that fahrentocel
loads the argument correctly.
Secondly, you're passing a wrong format argument to printf
, which should be f
for double
values - d
is used for integers. Thus,
printf("%d", output);
should become
printf("%f", output);