2

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.

Dale K
  • 25,246
  • 15
  • 42
  • 71
Matt Jones
  • 61
  • 1
  • 10
  • I do not see any output function. Your output indicates that you get `SDWORD`s as output, but you should get `DOUBLE`s. Are you sure that your output function displays `DOUBLE`s? – zx485 Aug 09 '19 at 20:59
  • 2
    You don't show any code actually using these functions, so we can only assume what the types of the parameters are. The parameter to the `fahrentocel` function is a `double`, but the parameter to `celtofahren` seems to be an integer, because it is loaded with a `fild`. Is this intentional? – Daniel Kamil Kozar Aug 09 '19 at 21:00
  • 1
    Please post all the rest of the code needed to produce a complete, compilable, runnable program, i.e an [mcve]. – Nate Eldredge Aug 09 '19 at 21:02
  • I've added the C code, I apologize for that. Thank you fore notifying me! – Matt Jones Aug 09 '19 at 21:10
  • You're printing the double as an integer. – Daniel Kamil Kozar Aug 09 '19 at 21:11
  • @DanielKamilKozar It's fixed now. Thank you for your help I appreciate it! – Matt Jones Aug 09 '19 at 21:18
  • `%lf` is also wrong, just use `%f`. At least some systems will expect an long double rather than a double. – Ross Ridge Aug 09 '19 at 22:00
  • @RossRidge `%lf` is a double, it's `%Lf` that is for long doubles. – fuz Aug 10 '19 at 00:38
  • 1
    @fuz `%f` is double, `%Lf` is long double, and `%lf` is whatever the compiler vendor wants to make it. – Ross Ridge Aug 10 '19 at 00:44
  • @RossRidge No, `%lf` is also `double`. Unless you have a compiler that does not conform to the standard. – fuz Aug 10 '19 at 09:23
  • The [`printf(3)`](https://linux.die.net/man/3/fprintf) format is totally ignored by the compiler. It's not related. So you're both wrong. That being said, the `l` should be ignored and the `L` is for `long double` according to the Linux documentation. Other OSes certainly have different ideas about those two flags. – Alexis Wilke Aug 11 '19 at 02:51

1 Answers1

5

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);
Daniel Kamil Kozar
  • 18,476
  • 5
  • 50
  • 64
  • Hey Daniel, i've managed to fix it by printing it as an integer, I was only passing in whole numbers so loading it as an int didn't seem to be an issue, but I imagine if I passed temperatures with decimal values that would be an issue, thank you for pointing it out. I'll remember your answer in the future to avoid issues. Thank you so much for the help! Apologies if this seems like a simple blunder, my C is very rusty. – Matt Jones Aug 09 '19 at 21:20