I have been trying to print out a float value in FASM for a pretty long time until I finally found a solution, it worked but, why is it like that? On a C program I made the assembly code generated by x64dbg was something like this:
mov dword ptr ss:[esp 1C],eax
fld st(0), dword ptr ss:[esp 1C] ;<--- focusing on these 2
fstp qword ptr ss:[esp 4],st(0) ;<---
mov dword ptr ss:[esp],c.404000
call <JMP.&printf>
I thought the fact fld
loaded the float value into st(0) and fstp
loaded the st(0) value on an address, basically, quite obvious so I used to try things like:
format PE
entry start
include '%include%\win32a.inc'
section '.text' code readable executable
start:
fld dword ptr dVal
fstp qword ptr dVal2
push msg
call [printf]
call [getchar]
call [exit]
section '.idata' import data readable
library msvcrt, 'msvcrt.dll'
import msvcrt, printf, 'printf'\
exit, 'exit', getchar, 'getchar'
section '.bss' data readable writeable
dVal2 dq ?
section '.data' data readable
msg db '%f',10,13,0
dVal dq 3.14
Which after many tries, rendered me crashes or results like 0 or random numbers that have nothing to do with what I wanted. However, after so much research I finally found out that I could simply do something like
...
section '.text' code readable executable
start:
fld dword ptr dVal
fstp qword ptr dVal2
push dword ptr dVal2 4
push dword ptr dVal2
push msg
call [printf]
call [getchar]
call [exit]
...
or just
...
section '.text' code readable executable
start:
push dword ptr dVal 4
push dword ptr dVal
push msg
call [printf]
call [getchar]
call [exit]
...
Why do I need to push the same value 4 before printing a float value? And what essentially is the REAL4 value from MASM?
CodePudding user response:
The printf
function can only print 64 bit floats (double). Such a double comprises 8 bytes. Either push this value onto the stack by storing to the stack from the FPU:
sub esp, 8 ; make space for the double
fstp qword ptr [esp] ; store double into that space
push OFFSET msg
call [printf]
or push to the stack in two steps of 4 bytes each:
fstp [dVal2] ; store double to global/static variable
push dword ptr [dVal2 4] ; push high half of double
push dword ptr [dVal2] ; push low half of double
push OFFSET msg
call [printf]
Note that as the stack grows down, we have to push the high 4 bytes before the low 4 bytes. Two separate pushes are required as you can only push 4 bytes at a time, but a double is 8 bytes.
Assuming you declared dVal2
as a qword like in the question, that will imply a size for fstp [dVal2]
. fstp
can also convert to dword (float) or store as the raw 10-byte internal format used in x87 registers. That's why we needed fstp qword ptr [esp]
since [esp]
doesn't imply an operand size.
As always, to access storage near a data label with a size different from the first item after that label, MASM needs you to override the size or it will complain about a mismatch, thus push dword ptr [dVal2 4]
, because push qword ptr
is only available in 64-bit mode. (Where it would be passed in XMM1 as the 2nd arg to a function.)