News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Using Raymond Filiatreault’s FpuLib.lib In C

Started by Fred Harris, January 29, 2016, 11:32:48 AM

Previous topic - Next topic

Fred Harris

I'm stuck getting one of Raymond Filiatreault's FpuLib.lib functions to work in my C program.  I need to use FpuFLtoA() to convert a floating point number to an Asci Null terminated Z String.  I can get it to work using masm32...


; C:\Code\MASM\Projects\FPU>ml /c /coff /Cp Fpu2.asm
; C:\Code\MASM\Projects\FPU>link /SUBSYSTEM:CONSOLE Fpu2.obj
include \masm32\include\masm32rt.inc
include \masm32\include\Fpu.inc
includelib \masm32\lib\Fpu.lib

.data
dblNumber  dt 123.456      ; this is the number we want to convert to a char string
dwSrc2     dd 0            ; this memory variable will store in al the decimal places; ah padding to right
pszDbl     db 32  dup(0)   ; buffer for storing converted number to asci string
CrLf       db 13, 10, 0    ; carriage return / line feed bytes

.code
start:
  mov al, 2                     ; two decimal places to right of decimal point is all we want
  mov ah, 4                     ; four spaces padding to left of decimal we want
  mov dwSrc2, eax               ; mov to dwSrc2 our desired padding and decimal places in ax
  invoke FpuFLtoA,              ; call FpuFLtoA (Floating Point To Asci)
  ADDR dblNumber,               ; 1st parameter is address of floating point number
  ADDR dwSrc2,                  ; 2nd parameter is where left padding and number of decimal places is stored in dwSrc2
  ADDR pszDbl,                  ; 3rd parameter is address of buffer to output Z String
  SRC1_REAL or SRC2_DMEM        ; 4th parameter are equate flags to inform Fpu lib of nature of parameters 1, 2, and 3
  printf("eax    = %u\n",eax);
  printf("pszDbl = %s\n",ADDR pszDbl);
  invoke crt_getchar
  exit
end start

eax    = 1
pszDbl =  123.46


I have the output right after the code above.  It works perfectly.  My understanding of the parameters from Ray's documentation is as follows.  First, there are four parameters.  The first can be the address of an 80 bit real number.  I have a data segment directive above using dt (ten byte) to allocate 10 bytes in the data segment and store 123.456 there under the name dblNumber.  I used the ADDR entity to pass that variables address in that first parameter. 

The second parameter Ray has typed as a DWORD (he has all four typed as DWORDs) but he only uses the low word.  In the low byte goes the number of desired decimal places, and in the high byte goes the padding to the left of the decimal point.  I specified 2 decimal places and 4 bytes padding.  Add those up and add one byte for the decimal point and I'm expecting 7 bytes to be returned to me in string form in the third parameter, which is the output parameter where FpuLib writes the converted string.  I have that named pszDbl in my data segment, and allocated 32 bytes there with db 32 dup(0).

The fourth parameter is a tricky bunch of bit flags from Ray's documentation and are found in fpu.inc.  I have it specified as SRC1_REAL which means the 1st parameter is a pointer to an 80 bit real number, and SRC2_DMEM which means the 2nd parameter is a pointer to a 32 bit integer.

Those are my understandings of things and the program above works perfectly.  Now things turn sour.  I'm trying to use that function in C++.  I desperately need it because I'm trying to link without the C Standard Library Runtime, and without that I have no way of outputing to console, window, or file a floating point number.  I would think assembler folks could empathize with me over that, as you all are in about the same boat without a hefty library of routines to pull in. 

Anyway, here is my attempt at using that function in a C++ program using MS Visual Studio 2008 and VC9.  But first let me state there are some tricky spots.  The first one is right at the start with Ray's 1st parameter...

lpSrc1    // pointer to an 80-bit REAL number

Actually, here is Ray's function declaration...


FpuFLtoA
(
  lpSrc1    // pointer to an 80-bit REAL number
  lpSrc2    // pointer to, or value of, 2nd parameter
  lpszDest  // pointer to destination of result
  uID       // ID flags for sources and format
)


Well, that doesn't map very well to any of C or C++'s variable types.  I learned both of those languages a long time ago, but unless they've added something I don't know about, which is possible, those languages don't have any 10 byte 80 bit data types!  A double is 64 bits or 8 bytes.  A float is 32 bits.  I'm thinking no good can come of passing the address of a C double in the 1st parameter as a 10 byte read operation will read 8 bytes of the double and 2 bytes of whatever is stored after it!

So what I did was declare a char* (character pointer) variable (pMem) and use malloc to allocate 10 bytes for my floating point number 123.456.  Then I allocated a double pointer (pDbl) and using a cast got my 10 byte memory block address set into it.  Then using indirection through the pointer stored 123.456 in my 10 byte memory block.  Of course I zeroed it out.  Here is the program where you can see all that... 


// cl Test1.cpp fpu.lib /MT /O1 /Os
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef unsigned int UINT;

#define  SRC1_REAL       2         // SRC1_REAL    Src1 is a pointer to an 80-bit REAL number
#define  SRC2_DMEM    1024         // SRC2_DMEM    Src2 is a pointer to a 32-bit signed integer

extern "C" int __stdcall FpuFLtoA  // FpuFLtoA PROTO lpSrc1:DWORD, lpSrc2:DWORD, lpszDest:DWORD, uID:DWORD
(
  double*       lpFltSrc1,         // This points to an 80-bit REAL number.
  unsigned int* lpSrc2,            // pointer to 2nd parameter
  char*         lpszDest,          // pointer to destination of result
  int           uID                // ID flags for sources and format
);

int main()
{
unsigned short LoByteDecPlaces = 0;    // how many decimal places in result?
unsigned short HiBytePadding   = 0;    // padding spaces to left of decimal point
unsigned int   iFormat         = 0;    // 32 bit entity; AL=Number Decimal Places; AH=Padding Left of Decimal
double*        pDbl            = NULL; // Pointer To C double
char*          pMem            = NULL; // Pointer To 10 Byte (80 bits) Memory Block To Be Allocated
int            iReturn         = 0;    // Return Value From FpuFLtoA() Function Call
char           szBuffer[64];           // Buffer For Returned Asci Z String.  Make It Plenty Big!

pMem=(char*)malloc(10);                // Allocate 10 Bytes Cuz FpuFLtoA() Docs Says It Reads 10 Bytes - Not Eight!
if(pMem)
{
    printf("Malloc() Succeeded!\n");
    printf("pMem     = %p\n",pMem);
    memset(pMem,0,10);                     // Zero Out Memory Block
    pDbl=(double*)pMem;                    // Set A Pointer To A C double Equal To pMem (Our 10 Byte Block)
    printf("pDbl     = %p\n",pDbl);
    memset(szBuffer,0,64);                 // Zero Out Output Buffer
    *pDbl=123.456;                         // Store 123.456 In Our Allocated 10 Byte Memory Block
    printf("*pDbl    = %7.2f\n",*pDbl);    // The high byte of the low word of the integer indicates the number of
    HiBytePadding   = 4;                   // characters before the decimal point, spaces being used for padding.
    LoByteDecPlaces = 2;                   // The low byte of the low word of the integer indicates the number of
    HiBytePadding=HiBytePadding<<8;        // decimal digits to be returned in the result.  I use the C << shft left
    iFormat=HiBytePadding|LoByteDecPlaces; // operator here to shift the '4' left 8 bits.  The '2' for dec places I Or
    iReturn=FpuFLtoA(pDbl, &iFormat, szBuffer, SRC1_REAL | SRC2_DMEM);
    printf("iReturn  = %d\n",iReturn);
    printf("Len()    = %d\n",strlen(szBuffer));
    printf("szBuffer = %s\n",szBuffer);
    free(pMem);
}
getchar();

return 0;
}


In terms of the 2nd parameter where we need to store the decimal places in the low byte and the padding to left of decimal in the high byte, I used C's shift left operator to move the 4 over 8 bits, and I or'ed the 2 in place...


HiBytePadding   = 4;                   // characters before the decimal point, spaces being used for padding.
LoByteDecPlaces = 2;                   // The low byte of the low word of the integer indicates the number of
HiBytePadding=HiBytePadding<<8;        // decimal digits to be returned in the result.  I use the C << shft left
iFormat=HiBytePadding|LoByteDecPlaces; // operator here to shift the '4' left 8 bits.  The '2' for dec places I Or


The actual number should come out to 1026, and it does.  The variable I used is iFormat, and you can see I'm using C's address of operator in the function call to pass the address to Ray's function. 

The 3rd parameter is the address of the output buffer where Ray's code should store the converted double.  I have that variable – szBuffer[64] allocated locally on the stack of main(), and that variable without the brackets resolves in C to a pointer to the memory block, so you can see I have just szBuffer in the function call. 

The last parameter are the flags telling the function what's in the first three parameters. SRC1_REAL or SRC2_DMEM worked in the masm version so it should work in the C version.  Should it not?

And I typed the declare like so ...


extern "C" int __stdcall FpuFLtoA  // FpuFLtoA PROTO lpSrc1:DWORD, lpSrc2:DWORD, lpszDest:DWORD, uID:DWORD
(
  double*       lpFltSrc1,         // This points to an 80-bit REAL number.
  unsigned int* lpSrc2,            // pointer to 2nd parameter
  char*         lpszDest,          // pointer to destination of result
  int           uID                // ID flags for sources and format
);


Note I fooled around with that some but to no avail.  At first I typed the 1st parameter as a void*.  Later I used double pointer.  It doesn't make any difference except some casting needs to take place.  The same numbers are going into Ray's function either way.  Ditto for the 2nd parameter.

Anyway, its almost working.  First good thing is it compiles and links clean.  That's positive.  Second piece of good news is it runs without crashing!  That's always edifying!  Third piece of extremely good news is that Ray specifies a return value of non-zero or one as indicating function call success, and zero as indicating failure.  I'm getting a 1 every time!  He further specifies that on failure the string 'ERROR' will be written to the output buffer.  I'm not getting that ever.  Fourth piece of good news is that Ray's function is successfully writing to my output buffer.  The fifth piece of really, really good news is that his function is recognizing my specification of two digits of output to the right of the decimal point and four digits of padding to the left, which when one adds to that the decimal point one comes up with a seven byte string.  I'm using strlen() to see what the length is of what's getting written to my buffer, and as you can see in the output below, its coming up with seven bytes!

However, in spite of all this good news there is one fatal piece of bad news.  All that's ending up in my output buffer is this perfectly formatted seven byte string...

   0.00

Here is the full console output from a compile/run 


C:\Code\VStudio\VC++9\AsmImport>cl Test1.cpp fpu.lib /MT /O1 /Os
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

Test1.cpp
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Test1.exe
Test1.obj
fpu.lib

C:\Code\VStudio\VC++9\AsmImport>Test1.exe
Malloc() Succeeded!
pMem     = 00334FD0
pDbl     = 00334FD0
*pDbl    =  123.46
iReturn  = 1
Len()    = 7
szBuffer =    0.00


Been working on this all day and into the night.  Can't get it.  I suppose it's a lot to ask to post failing C code in a masm forum, but there you have it when a person hits the end of the line with nowhere to go.  Is there something stupid staring me in the face I can't see?  I mean, how can this be?  Its compiling/linking/running without errors or crashes.  Its clearly recognizing all my input and output parameters.  If it weren't reading my 2nd parameter it wouldn't know I'm asking for a seven byte string to be returned.  I've played with those numbers and it'll return whatever lengths I specify as being needed for the hi and low bytes.  It can find my 3rd or output buffer parameter because its writing to there.  Its even giving me a success return code!

I keep coming back to that problematic 1st parameter which can't map to any possible C data type.  How else could that be done?  I tried passing in the address of a local variable typed as a double and it indeed did fail and write ERROR to my buffer.  So I'm kind of lost.

The only ideas I have are as follows...

In the asm program I'm passing in addresses of variables allocated in the data segment.  In the C program I'm using stack locals.  Could that affect it?

I have ml from Visual Studio 2008 (same as compiler).  Should I try recompiling Ray's source to an obj file with that and attempt to pull that in?

Any other ideas?


hutch--

Fred,

What about something crude, allocate a string that has a long enough byte count, make a pointer to it and perform any type casting necessary ?

qWord

With SRC1_REAL the function expects a REAL10 value, however you are only filling in a double (REAL8). Actual you pass in a denormal number. Change SRC1_REAL to SRC1_REAL8.
Another problem is that the precision setting of the FPU is 53 Bit (double) in Windows (due to the CRT) , thus the function FLtoA only work with that precision. You must change the precision on program entry (e.g. using finit).
MREAL macros - when you need floating point arithmetic while assembling!

Fred Harris

I got it guys!!!!  Can't tell you when I worked and stressed out on an issue as with this one.  What I decided to try was to do it as Ray did it in his one example, that is, put that floating point right in the fpu.  There is an equate for that.  C/C++ has the __asm keyword for inline assembler.  All that needs done is to tell the function that the number to be converted is in the fpu.  That is done with the SRC1_FPU bit flag that goes in the 3rd param.  When I did that it worked right off.  Code is much simpler.  Here it is...


// cl Test3.cpp fpu.lib /MT /O1 /Os
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef unsigned int UINT;

#define  SRC1_FPU        1         // SRC1_FPU     Src1 is already on the FPU
#define  SRC1_REAL       2         // SRC1_REAL    Src1 is a pointer to an 80-bit REAL number
#define  SRC2_DMEM    1024         // SRC2_DMEM    Src2 is a pointer to a 32-bit signed integer

extern "C" int __stdcall FpuFLtoA  // FpuFLtoA PROTO lpSrc1:DWORD, lpSrc2:DWORD, lpszDest:DWORD, uID:DWORD
(
  double*       lpFltSrc1,         // This points to an 80-bit REAL number.
  unsigned int* lpSrc2,            // pointer to 2nd parameter
  char*         lpszDest,          // pointer to destination of result
  int           uID                // ID flags for sources and format
);

int main()
{
unsigned short LoByteDecPlaces = 0;       // how many decimal places in result?
unsigned short HiBytePadding   = 0;       // padding spaces to left of decimal point
unsigned int   iFormat         = 0;       // 32 bit entity; AL=Number Decimal Places; AH=Padding Left of Decimal
int            iReturn         = 0;       // Return Value From FpuFLtoA() Function Call
char           szBuffer[64];              // Buffer For Returned Asci Z String.  Make It Plenty Big!
double         dblNumber       = 123.456;

memset(szBuffer,0,64);                    // Zero Out Output Buffer
HiBytePadding   = 4;                      // characters before the decimal point, spaces being used for padding.
LoByteDecPlaces = 2;                   
HiBytePadding=HiBytePadding<<8;       
iFormat=HiBytePadding|LoByteDecPlaces;
__asm fld dblNumber
iReturn=FpuFLtoA(0,&iFormat,szBuffer,SRC1_FPU | SRC2_DMEM);
printf("iReturn  = %d\n",iReturn);
printf("Len()    = %d\n",strlen(szBuffer));
printf("szBuffer = %s\n",szBuffer);
getchar();

return 0;
}

/*

C:\Code\VStudio\VC++9\AsmImport>Test3
iReturn  = 1
Len()    = 7
szBuffer =  123.46

*/


Fred Harris

I'd still like to figure out why my original version isn't working, and qword's ideas sound worth investigating.  But Tomorrow!  I'm really new to this stuff.  Never looked at the fpu before.  I'm out of my 'comfort zone', but that's good!

If it weren't for the amazing documentation you all put together (Thanks again Hutch) I'd have never got this far!

raymond

Even if your C program cannot use 10-bytes floats, you can get around that easily and may be a better option for you.

1. Determine the float type used (float or double).
2. Get the address where it is stored.
3. Load it on the FPU with assembly code (see http://www.ray.masmcode.com/tutorial/fpuchap4.htm#fld).
4. Use the FpuFLtoA instruction with the parameters indicating that the floating point number is on the FPU.
5. Just as important, remember to remove that value from the FPU before attempting other such conversions.

While at it, you may want to have a cursory look at the remainder of the tutorial referred above. If interested, you can also download it for offline use.
Whenever you assume something, you risk being wrong half the time.
https://masm32.com/masmcode/rayfil/index.html

Fred Harris

Thank you very much for the reply Raymond.  I've been studying your material and code extensively.  As you can tell no doubt, I'm a beginner at this.  Actually, if I progress no further on the fpu unit front, I will have achieved my goal as I described two posts back.  Using fld to put the number on the fpu for retrieval there worked.

I seem to have won that battle on the fpu front but am in danger or losing the war in eliminating the C Runtime if things don't look up.  I'm in deep trouble!  My whole intent in coming here and learning to use your fpulib code was because I found out that the %f format specifier wasn't included in any of the Windows specific functions - as opposed to the C Runtime ones, and I had no way of converting the binary info in floating point values to strings.  Your fpulib function solved that problem for me.  However, I didn't recognize it at the time, but that little problem was just the tip of the iceberg! 

In in the middle of trying to solve it now.  But here's what happened.  When I attempted to use my inline assembler and link to your fpuFLtoA() function in the environment where I was using LibCTiny.lib and the /NODEFAULTLIB C linker switch, I found out I couldn't even initialize a C double without getting a linker error.  I found I could do this...

double dblNumber;

but not this...

double dblNumber = 3.14159;

or this...

double dblNumber;
dblNumber=3.14159;

In other words, I could declare a double uninitialized, but not assign a number to it!!!!!

My conclusion was that all floating point math is in the C runtime - not part of the bare bones language itself.  This shocked the daylights out of me.  I had always accepted the concept that in C/C++ all i/o, i.e., console/port/window/whatever, was in standard libraries, but the bare language contained the logic for the data type primitives and stuff like that.  However, here I couldn't even initialize a floating point number without the C Runtime!!!

Upon that horrible realization I was so stunned I about dropped this whole endeavor.  But then the thought occurred to me of how successful I've been with this so far.  I mean, I have been able to use the entirety of my string class, which is a substantial piece of code, eccept for that small part where I'm able to initial a string with a floating point value.  So what I have accomplished is significant even without floating point capability.

The next movement in the evolution of my thought was that perhaps I could do all floating point operations in asm code linked into my C++ code through *.obj files.  To that end I'm very grateful for the code you and qWord have made available.  I spent several hours today studying qWord's SimplMath code.

Then just this evening in doing an internet search I found out that my battle with C++ over floats might not be over afterall.  There is an unresolved external error I was getting involving a symbol...

_fltused

...and it turns out a lot of folks have had that problem and there are solutions for it.  So that's what I'm working on now.  Haven't got it working yet but all is not lost.  So I may have jumped to conclusions about the float support being in the standard library instead of within the compiler.  So this is just an update to let folks know I'm still sweating blood over this but haven't given up yet! :biggrin:

raymond

 :t  :eusa_clap:

Don't give up. There is a light at the end of that tunnel.
Whenever you assume something, you risk being wrong half the time.
https://masm32.com/masmcode/rayfil/index.html

Fred Harris

Hello Raymond!  Big breakthrough!  Just got the C++ program to finally link.  Been fighting linker and compiler switches for two days!  The breakthrough came when I searched for ...

_fltused

and came upon this...

https://hero.handmadedev.org/forum/code-discussion/79-guide-how-to-avoid-c-c-runtime-on-windows

Its point #3 about halfway down.  Now I can finally try calling your fpuFLtoA !!!  Imagine my misery.  I couldn't even assign a floating point number to a double in C without an unresolved external linker error!

Fred


Fred Harris

#9
Just wanted to let everyone know I'm still working at this hard and making progress.  To review the big picture, I'm trying to create a build environment for my C++ work where I won't have the massive executables the majority of C++ coders live with.  I've found I can eliminate the C Standard Library and all its associated runtime code from my executables (I never under any circumstances use anything whatsoever from the C++ Standard Library - as far as I'm concerned its total bloatware crap), and still have a very, very usable development environment.  The only significant problem I've run into is this business with floating point numbers.  While the Windows base libraries, i.e., kernel32.lib and user32.lib have functionality that duplicates C Standard Library low level i/o for strings and integral data types, there is nothing there to deal with floating point numbers.  Raymond's fpulib has come to the rescue big time for me there, and I much appreciate that he has made it available.

However, I can't use that for 64 bit work.  I do like to be able to create both 32 bit and 64 bit binaries.  I can't link 32 bit binaries to 64 bit binaries.

But I have ideas about it and I have come up with one possible solution.  And it works.  Tried it yesterday.  But their are some 'philosophical' issues with it.  Here goes!  :biggrin:

If one is attempting to build stand alone executables that don't require runtime binaries to be installed along side of them as with .NET, old unmanaged Visual Basic, MFC Class Framework type stuff, etc., just what must one limit oneself to in terms of library code linked within the executable?  For example, for Windows, one wouldn't have a working installation without kernel32 and user32.  I guess one would call these system libraries.  If I create a Windows executable and I want to give it to somebody, I don't need to give them a copy of kernel32.dll with it!

But what about msvcrt.lib and msvcrt.dll?  Ahaaaa!  Now we're getting into some treacherous territory!  On *nix systems the C Runtime is considered more or less part of the operating system itself.  The situation with Windows seems to be a bit trickier.  In VC6 times, circa 1998 - 2000 times or so, msvcrt.lib and msvcrt.dll were considered part of the operating system libraries and also the VC6 Runtime.  Both Microsoft and developers linked against that library and one would always be able to assume it would be there.  But then versioning problems developed, apparently, as new additions or changes were made to the library, and sometimes installers would overcopy newer versions with older versions, and misery would ensue.  So Microsoft came up with the plan of making a file named msvcrt.dll a system dll under system protection, so that errant installers couldn't overwrite it.  And their plan was to only allow themselves to statically link against it.  For everyone else they provided numbered C Runtime versions such as...

msvcrt70.dll
msvcrt71.dll
msvcrt80.dll
msvcrt90.dll
msvcrt100.dll
msvcrt110.dll

etc.  And the default setup in Microsoft's Visual Studio bloatware IDE is to dynamically link against the dll.  The resulting executable is quite small, but to use the application on non-development computers it is expected that an installer will be used and Microsoft's 'Redistributable Package' will be installed alongside it which involves many megabytes of binary dependencies, i.e., some of the files mentioned above.  Of course, one can change the default linkage and statically link to obtain a stand alone executable, but it will be quite large.  In many cases we're talking 50 - 100 k for a Hello, World! console program.

But the alternate GCC MinGW suite of Windows compilers does things differently.  They rely upon not the numbered msvcrt.dll versions but the original msvcrt.dll that is 'Microsoft's private' version.  That works.  One can be certain it will be there.  Its under system protection, I believe.  By doing that executables created by the MinGW compiler suite were much, much, smaller than the ones produced by Microsoft's compilers.  We're talking like 5-6k for a Hello, World program. 

But not anymore.  That was true for the older versions of MinGW before the w64 branch and the incorporation of the newer C++ Standards that have the entire C++ world in a state of ecstacy and happiness over all the bizarre new features and opportunities to create new and improved bloatware on a scale like they've never been able to do before, and in the fullness of time might even rival .NET.  So the executables produced by the newer MinGW compilers are even larger than the ones produced by Microsoft's compilers.  What specifically did it was the incorporation of the pThreads and winpthreads libraries into the executables.  As far as I know there are no compiler options to not add that code to the executables.

The reason I'm telling you all this is because the only way I have at this point of solving my floating point number issue with 64 bit builds is by doing a LoadLibrary()/GetProcAddress() on msvcrt.dll and using the C Runtime sprintf() family of functions to output floats into strings.  It works and I feel reasonably good about it.  msvcrt.dll will always be there, as will be kernel32.dll and user32.dll.  And MinGW/CigWin does it. 

     But I heard Bob Zale of PowerBASIC say one time that symetry is a powerful concept.  It would be nice to have the same technique in use for both my 32 bit work and my 64 bit work.  As things stand now I'm using Raymond's fpulib for my 32 bit work and msvcrt.dll loaded dynamically for my 64 bit work.  It would kind of be more intellectually satisfying to use the same technique for both.  For example, in my String Class, if I keep this dual solution, I'm going to need conditional compilation directives to keep both sets of code apart. 

     A possible solution would be to obtain a 64 bit assembler - maybe ml64, and compile Raymond's fpuFLtoA to a 64 bit obj file, that is, if Raymond would give me permission to do that.  But there are some issues with that.  First, it might not compile as is, and as of yet I wouldn't have the fpu knowledge to fix it.  I could study up on it though.  The other issue is more severe and immediate.  In my research I've found out the nasty little secret that 64 bit Microsoft C/C++ doesn't allow inline assembler.  If you recall, I had to do that to get Raymond's fpuFLtoA working, because I had to use fld to move my C double onto the FPU.  I wasn't able to get the function call working without doing that because of the mis-match of variable types between C and the FPU.  qWord said this...

Quote
With SRC1_REAL the function expects a REAL10 value, however you are only filling in a double (REAL8). Actual you pass in a denormal number. Change SRC1_REAL to SRC1_REAL8.   

I don't believe SRC1_REAL8, as qWord suggested, is one of your bit flags for your fpulib, is it Raymond?  At least I didn't see it in your documentation.  I would be interested in persuing this further and trying to get your function to work without having to use inline assembler within C++ to move the floating point number onto the fpu, as, like I said, there are issues with this in 64 bit C/C++.  It worked fine in 32 bit for which I'm exceedingly grateful, but I'm afraid its a no-go in 64 bit. 

A question, assumming Raymond would allow me to compile his fpuFLtoA proc with a 64 bit assembler and I could get it to assemble and work, would it be feasible to wrap the function fpuFLtoA call within another assembler proc where I could convert a double passed in from C/C++ to a number which Raymond's fpuFLtoA could digest? 

Sorry this is so long.  A lot of it is opinion I know which belongs in the Colloseum or elsewhere! :biggrin: