mingw_stdio example

General FreeBASIC programming questions.
srvaldez
Posts: 2019
Joined: Sep 25, 2005 21:54

mingw_stdio example

Postby srvaldez » Jun 25, 2019 13:22

Windows CRT sscanf and sprintf don't support 80-bit reals but mingw does, here's an example

Code: Select all

#ifdef __FB_WIN32__
   #ifdef __FB_64BIT__
      '64-bit
      Type ext_real
         As Ulongint hi, lo
      end type

      Sub sprint_ Naked Cdecl(Byval buf As Zstring Ptr, Byval frmt As Zstring Ptr, Byref x As ext_real)
         Asm
           Sub      rsp, 56
           fld      tbyte Ptr [r8]
           fstp      tbyte Ptr [rsp+32]
           lea      r8,[rsp+32]
           Call      __mingw_sprintf
           nop
           Add   rsp, 56
           ret
         End Asm
      End Sub

      Sub sscan_ Naked Cdecl(Byref x As ext_real, Byval buf As Zstring Ptr, Byval frmt As Zstring Ptr)
         Asm
           mov      r9, rcx
           Xor      eax, eax
           mov      rcx, rdx
           mov      rdx, r8
           mov      r8, r9
           jmp      __mingw_sscanf
         End Asm
      End Sub
   #else
      '32-bit
      Type ext_real
         As Ulong ldfp(0 To 2)
      end type
      
      Sub sprint_ Naked Cdecl(Byval buf As Zstring Ptr, Byval frmt As Zstring Ptr, Byref x As ext_real)
         Asm
           Sub     esp, 24
           mov     eax, dword Ptr [esp+36]
           Push    dword Ptr [eax+8]
           Push    dword Ptr [eax+4]
           Push    dword Ptr [eax]
           Push    dword Ptr [esp+44]
           Push    dword Ptr [esp+44]
           Call    ___mingw_sprintf
           Add     esp, 44
           ret
         End Asm
      End Sub

      Sub sscan_ Naked Cdecl(Byref x As ext_real, Byval buf As Zstring Ptr, Byval frmt As Zstring Ptr)
         Asm
           mov     eax, dword Ptr [esp+8]
           mov     edx, dword Ptr [esp+12]
           mov     ecx, dword Ptr [esp+4]
           mov     dword Ptr [esp+8], edx
           mov     dword Ptr [esp+12], ecx
           mov     dword Ptr [esp+4], eax
           'jmp     __isoc99_sscanf
           jmp     ___mingw_sscanf
         End Asm
      End Sub   
   #endif
#endif


Function ldbl_val(Byref s As String) As ext_real
   Dim As ext_real ret
   Dim As String frmt = "%Lf"
   sscan_(ret, s, frmt)
   Function = ret
End Function

Function ldbl_str(Byref y As ext_real, Byval prec As Long = 19, Byref f As String="g") As String
   Dim As Zstring Ptr buf=Callocate(256)
   Dim As Zstring Ptr frmt=Callocate(16)
   *frmt= "%."+Trim(Str(prec))+"L"
   If Lcase(f)="e" Or Lcase(f)="f" Or Lcase(f)="a" Then
      *frmt=*frmt+f
   Else
      *frmt=*frmt+"g"
   End If
   Dim As String ret
   sprint_ (buf, frmt, y)
   ret=*buf
   If Left(ret,1)<>"-" Then
      ret=" "+ret
   End If
   Deallocate(buf)
   Deallocate(frmt)
   Function = ret
End Function

dim as ext_real x
dim as string e, s="2.718281828459045235360287"

x=ldbl_val(s)
print "extended float to string using defaults          ";ldbl_str(x)
print "extended float to string, 10 digits              ";ldbl_str(x, 10)
print "extended float to string, 12 decimals f notation ";ldbl_str(x, 12, "f")
print "extended float to string, 14 decimals e notation ";ldbl_str(x, 14, "e")
print "extended float to string, 16 digits   g notation ";ldbl_str(x, 16, "g")
print "extended float to string, 15 heximals a notation ";ldbl_str(x, 15, "A")
print "e=""0XA.DF85458A2BB4A9BP-2""" : e="0XA.DF85458A2BB4A9BP-2"
print "x=ldbl_val(e)" : x=ldbl_val(e)
print "print ldbl_str(x) -->";ldbl_str(x)

Code: Select all

extended float to string using defaults           2.718281828459045235
extended float to string, 10 digits               2.718281828
extended float to string, 12 decimals f notation  2.718281828459
extended float to string, 14 decimals e notation  2.71828182845905e+00
extended float to string, 16 digits   g notation  2.718281828459045
extended float to string, 15 heximals a notation  0XA.DF85458A2BB4A9BP-2
e="0XA.DF85458A2BB4A9BP-2"
x=ldbl_val(e)
print ldbl_str(x) --> 2.718281828459045235
caseih
Posts: 1360
Joined: Feb 26, 2007 5:32

Re: mingw_stdio example

Postby caseih » Jun 25, 2019 14:13

Why do you need assembly wrappers? Can't you just call the mingw functions directly?
srvaldez
Posts: 2019
Joined: Sep 25, 2005 21:54

Re: mingw_stdio example

Postby srvaldez » Jun 25, 2019 14:19

hello caseih
I don't see how you could call these functions from FB other than asm or a C wrapper library, do you have any ideas?
caseih
Posts: 1360
Joined: Feb 26, 2007 5:32

Re: mingw_stdio example

Postby caseih » Jun 25, 2019 14:34

Why not? They are just C functions after all. FB can call them directly, just like any other function in the C runtime.

Code: Select all

declare function mingw_sprintf CDECL alias "__mingw_sprintf" (byval as zstring ptr, byval as zstring ptr, ...) as long

dim a as zstring*256

mingw_sprintf (a, "%d", 5)
print a
I borrowed the declaration from the stdio.bi file, and modified it to refer to the non-standard __mingw_sprintf function. You can do something similar for all the mingw functions you want to use that aren't part of the standard C runtime bi files. Look in stdio.bi and see how the normal C runtime functions are declared, and then adapt them to the hidden functions you want to access. Hope that makes sense. Or am I completely misunderstanding what you are trying to do?
srvaldez
Posts: 2019
Joined: Sep 25, 2005 21:54

Re: mingw_stdio example

Postby srvaldez » Jun 25, 2019 14:47

yes, you are right, here's the version without the asm
note that in the function sscanf x must (obviously) be passed byref whereas in sprintf it must be passed byval.

Code: Select all

#include "crt/longdouble.bi"
extern "c"
   declare function __mingw_sscanf(byval as zstring ptr, byval as zstring ptr, Byref x As clongdouble) as long
   declare function __mingw_sprintf (byval as zstring ptr, byval as zstring ptr, Byval x As clongdouble) as long
end extern

Function ldbl_val(Byref s As String) As clongdouble
   Dim As clongdouble ret
   Dim As String frmt = "%Lf"
   __mingw_sscanf(s, frmt, ret)
   Function = ret
End Function

Function ldbl_str(Byref y As clongdouble, Byval prec As Long = 19, Byref f As String="g") As String
   Dim As Zstring Ptr buf=Callocate(256)
   Dim As Zstring Ptr frmt=Callocate(16)
   *frmt= "%."+Trim(Str(prec))+"L"
   If Lcase(f)="e" Or Lcase(f)="f" Or Lcase(f)="a" Then
      *frmt=*frmt+f
   Else
      *frmt=*frmt+"g"
   End If
   Dim As String ret
   __mingw_sprintf(buf, frmt, y)
   ret=*buf
   If Left(ret,1)<>"-" Then
      ret=" "+ret
   End If
   Deallocate(buf)
   Deallocate(frmt)
   Function = ret
End Function

dim as clongdouble x
dim as string e, s="2.718281828459045235360287"

x=ldbl_val(s)
print "extended float to string using defaults          ";ldbl_str(x)
print "extended float to string, 10 digits              ";ldbl_str(x, 10)
print "extended float to string, 12 decimals f notation ";ldbl_str(x, 12, "f")
print "extended float to string, 14 decimals e notation ";ldbl_str(x, 14, "e")
print "extended float to string, 16 digits   g notation ";ldbl_str(x, 16, "g")
print "extended float to string, 15 heximals a notation ";ldbl_str(x, 15, "A")
print "e=""0XA.DF85458A2BB4A9BP-2""" : e="0XA.DF85458A2BB4A9BP-2"
print "x=ldbl_val(e)" : x=ldbl_val(e)
print "print ldbl_str(x) -->";ldbl_str(x)
Last edited by srvaldez on Jun 25, 2019 16:51, edited 1 time in total.
srvaldez
Posts: 2019
Joined: Sep 25, 2005 21:54

Re: mingw_stdio example

Postby srvaldez » Jun 25, 2019 16:46

having discovered how the clongdouble variable is to be passed to sscanf and sprintf, there's much simplification, a naked sub with a simple jump, I like the simplicity of it.

Code: Select all

#include "crt/longdouble.bi"

Sub sprint_ Naked Cdecl(Byval buf As Zstring Ptr, Byval frmt As Zstring Ptr, Byval x As clongdouble)
   Asm
   #ifdef __FB_64BIT__
      jmp   __mingw_sprintf
   #else
      jmp   ___mingw_sprintf
   #endif
   End Asm
End Sub

Sub sscan_ Naked Cdecl( Byval buf As Zstring Ptr, Byval frmt As Zstring Ptr, Byref x As clongdouble)
   Asm
   #ifdef __FB_64BIT__
      jmp   __mingw_sscanf
   #else
      jmp   ___mingw_sscanf
   #endif
   End Asm
End Sub

Function ldbl_val(Byref s As String) As clongdouble
   Dim As clongdouble ret
   Dim As String frmt = "%Lf"
   sscan_(s, frmt, ret)
   Function = ret
End Function

Function ldbl_str(Byref y As clongdouble, Byval prec As Long = 19, Byref f As String="g") As String
   Dim As Zstring Ptr buf=Callocate(256)
   Dim As Zstring Ptr frmt=Callocate(16)
   *frmt= "%."+Trim(Str(prec))+"L"
   If Lcase(f)="e" Or Lcase(f)="f" Or Lcase(f)="a" Then
      *frmt=*frmt+f
   Else
      *frmt=*frmt+"g"
   End If
   Dim As String ret
   sprint_ (buf, frmt, y)
   ret=*buf
   If Left(ret,1)<>"-" Then
      ret=" "+ret
   End If
   Deallocate(buf)
   Deallocate(frmt)
   Function = ret
End Function

dim as clongdouble x
dim as string e, s="2.718281828459045235360287"

x=ldbl_val(s)
print "extended float to string using defaults          ";ldbl_str(x)
print "extended float to string, 10 digits              ";ldbl_str(x, 10)
print "extended float to string, 12 decimals f notation ";ldbl_str(x, 12, "f")
print "extended float to string, 14 decimals e notation ";ldbl_str(x, 14, "e")
print "extended float to string, 16 digits   g notation ";ldbl_str(x, 16, "g")
print "extended float to string, 15 heximals a notation ";ldbl_str(x, 15, "A")
print "e=""0XA.DF85458A2BB4A9BP-2""" : e="0XA.DF85458A2BB4A9BP-2"
print "x=ldbl_val(e)" : x=ldbl_val(e)
print "print ldbl_str(x) -->";ldbl_str(x)
SARG
Posts: 903
Joined: May 27, 2005 7:15
Location: FRANCE

Re: mingw_stdio example

Postby SARG » Jun 25, 2019 21:03

Hi srvaldez,

No need naked subs : just use declare with alias, simplier....
32bit tested but maybe declares should be added for 64 bit.

Code: Select all

#include "crt/longdouble.bi"

declare sub sprint_ cdecl Alias "__mingw_sprintf" (byval s As zstring ptr,byval frmt as zstring ptr,byval c as clongdouble)
declare sub sscan_ cdecl Alias  "__mingw_sscanf"  (byval s As zstring ptr,byval frmt as zstring ptr,byref c as clongdouble)

Function ldbl_val(Byref s As String) As clongdouble
   Dim As clongdouble ret
   Dim As String frmt = "%Lf"
   sscan_(s, frmt, ret)
   Function = ret
End Function

Function ldbl_str(Byref y As clongdouble, Byval prec As Long = 19, Byref f As String="g") As String
   Dim As Zstring Ptr buf=Callocate(256)
   Dim As Zstring Ptr frmt=Callocate(16)
   *frmt= "%."+Trim(Str(prec))+"L"
   If Lcase(f)="e" Or Lcase(f)="f" Or Lcase(f)="a" Then
      *frmt=*frmt+f
   Else
      *frmt=*frmt+"g"
   End If
   Dim As String ret
   sprint_ (buf, frmt, y)
   ret=*buf
   If Left(ret,1)<>"-" Then
      ret=" "+ret
   End If
   Deallocate(buf)
   Deallocate(frmt)
   Function = ret
End Function

dim as clongdouble x
dim as string e, s="2.718281828459045235360287"

x=ldbl_val(s)
print "extended float to string using defaults          ";ldbl_str(x)
print "extended float to string, 10 digits              ";ldbl_str(x, 10)
print "extended float to string, 12 decimals f notation ";ldbl_str(x, 12, "f")
print "extended float to string, 14 decimals e notation ";ldbl_str(x, 14, "e")
print "extended float to string, 16 digits   g notation ";ldbl_str(x, 16, "g")
print "extended float to string, 15 heximals a notation ";ldbl_str(x, 15, "A")
print "e=""0XA.DF85458A2BB4A9BP-2""" : e="0XA.DF85458A2BB4A9BP-2"
print "x=ldbl_val(e)" : x=ldbl_val(e)
print "print ldbl_str(x) -->";ldbl_str(x)
sleep
srvaldez
Posts: 2019
Joined: Sep 25, 2005 21:54

Re: mingw_stdio example

Postby srvaldez » Jun 25, 2019 21:52

hello SARG
your declarations are very similar to my next to last post, tested ok both 32 and 64 bit
sadly, the same approach won't work for functions like sinl, it works ok in 64-bit but not in 32-bit, apparently, there are some differences between linux cdecl and windows cdecl, or perhaps FB is generating different code than C, not sure.
caseih
Posts: 1360
Joined: Feb 26, 2007 5:32

Re: mingw_stdio example

Postby caseih » Jun 26, 2019 3:58

When you say it's not working, what are you seeing?

Feel free to check the intermediate output (C or assembler) to see what the compiler is doing. All CDECL means is that the parameters are passed left to right on the stack, instead of right to left, which is the default for FB and the Windows API (stdcall). So there's no difference between how FB handles CDECL and how C does. The only problems could be how the parameter is passed, or whether FB is coercing it without you knowing.
dodicat
Posts: 5892
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: mingw_stdio example

Postby dodicat » Jun 26, 2019 9:49

True caseih, but I can also use cdecl to substitute extern "c", which I don't see mentioned in the help file.
If I call a function from a c dll (not necessarily msvcrt.dll) I find it a bit hit and miss.
I start with
#inclib "something"
I try extern "c", and cdecl.
or
skip #inclib "something"
Then I try lib "SOMETHING" or lib"something" alias "SOMEFUNCTION" or alias "somefunction"
Or if it fails I try dylibload (as a last resort)

Code: Select all


'extern "c"
declare function sin_ cdecl alias "sin" (byval as double) as double
'end extern
print sin_(1)

'remove cdecl, try, then use extern "c" and try.
sleep

 
srvaldez
Posts: 2019
Joined: Sep 25, 2005 21:54

Re: mingw_stdio example

Postby srvaldez » Jun 26, 2019 9:58

my question is, why does the following example work in 64-bit but not in 32-bit?

Code: Select all

#Include "crt/longdouble.bi"

Extern "c"
   Declare Sub __mingw_sprintf (Byval s As Zstring Ptr,Byval frmt As Zstring Ptr,Byval c As clongdouble)
   Declare Sub __mingw_sscanf  (Byval s As Zstring Ptr,Byval frmt As Zstring Ptr,Byref c As clongdouble)
   Declare Function sinl (Byval x As clongdouble) As clongdouble
End Extern

Dim As clongdouble x, y
Dim As String f="%Lf", s="1"
Dim As Zstring Ptr buf=Callocate(256), frmt=Callocate(16)
*frmt="%.19Lg"
__mingw_sscanf(s, f, x)
y=sinl(x)
__mingw_sprintf(buf, frmt, y)
Print "sinl(1) = ";*buf
Deallocate(buf)
Deallocate(frmt)
Sleep

whereas the following will work with FB-32

Code: Select all

#Include "crt/longdouble.bi"

Extern "c"
   Declare Sub __mingw_sprintf (Byval s As Zstring Ptr,Byval frmt As Zstring Ptr,Byval c As clongdouble)
   Declare Sub __mingw_sscanf  (Byval s As Zstring Ptr,Byval frmt As Zstring Ptr,Byref c As clongdouble)
End Extern

Function sinl_ Naked Cdecl(Byval x As clongdouble) As clongdouble
   Asm
      lea     eax, [esp+8] 'address of x data, esp+4 = address of return value
      Push    DWORD Ptr [eax+8]
      Push    DWORD Ptr [eax+4]
      Push    DWORD Ptr [eax]
      Call    _sinl
      mov     eax, DWORD Ptr [esp+16] 'esp+4 + 12 = address of ret val
      fstp    TBYTE Ptr [eax]
      Add     esp, 12
      ret
   End Asm
End Function

Dim As clongdouble x, y
Dim As String f="%Lf", s="1"
Dim As Zstring Ptr buf=Callocate(256), frmt=Callocate(16)
*frmt="%.19Lg"
__mingw_sscanf(s, f, x)
y=sinl_(x)
__mingw_sprintf(buf, frmt, y)
Print "sinl(1) = ";*buf
Deallocate(buf)
Deallocate(frmt)
Sleep
SARG
Posts: 903
Joined: May 27, 2005 7:15
Location: FRANCE

Re: mingw_stdio example

Postby SARG » Jun 26, 2019 20:29

srvaldez wrote:your declarations are very similar to my next to last post, tested ok both 32 and 64 bit
Sorry I missed your post and caseih's one.

srvaldez wrote:sadly, the same approach won't work for functions like sinl, it works ok in 64-bit but not in 32-bit, apparently, there are some differences between linux cdecl and windows cdecl, or perhaps FB is generating different code than C, not sure.

To be forgiven I have searched with an asm debugger why it's not working on 32bit.

Code: Select all

##xdouble=sinl(xdouble)
   push dword ptr [ebp-8]    <------ pushing the long double argument
   push dword ptr [ebp-12]
   push dword ptr [ebp-16]
   mov dword ptr [ebp-108], 0  <----- resetting the space for the returned value
   mov dword ptr [ebp-104], 0
   mov dword ptr [ebp-100], 0
   lea eax, [ebp-108]
   push eax     <------ extra hidden parameter, address for the returned long double (udt)
   call _sinl

The long double value is put on the stack but also an hidden parameter (the famous for UDT).
So when sinl function is executed it takes the hidden parameter and only a part of the long double giving a wrong result.
However worst point, nothing is done to store the result , it stays in ST(0)..... letting the returned value empty
But in your naked function you do the job.

Defining sinl as a sub allows to take entirely the right value however impossible to get the result, unless using extra asm code.

Hope it's clear.
srvaldez
Posts: 2019
Joined: Sep 25, 2005 21:54

Re: mingw_stdio example

Postby srvaldez » Jun 26, 2019 20:46

@SARG
thanks a million for your fine detective work :-)
a take it that if the return type were one of FB's built-in types it would work.
SARG
Posts: 903
Joined: May 27, 2005 7:15
Location: FRANCE

Re: mingw_stdio example

Postby SARG » Jun 27, 2019 8:03

srvaldez wrote:@SARG
thanks a million for your fine detective work :-)
One thanks was enough :-)


srvaldez wrote:a take it that if the return type were one of FB's built-in types it would work.

Sure I tested this and it works fine.

Code: Select all

    declare function sin2 cdecl alias "sin" (byval c as double) as double
    dim as double sdouble=3.14/2
    sdouble=sin2(sdouble)
    print sdouble

Code: Select all

##sdouble=sin2(sdouble)
   push dword ptr [ebp-20]
   push dword ptr [ebp-24]
   call _sin
   add esp, 8
   fstp qword ptr [ebp-24]


Edit :
a funny way

Code: Select all

declare sub sinl cdecl alias "sinl" (byval c as clongdouble)
...

then after the call retrieving the value with just one asm line. It works also fine.

Code: Select all

sinl(xdouble)
asm
    fstp    TBYTE Ptr [xdouble]
end asm

Return to “General”

Who is online

Users browsing this forum: No registered users and 3 guests