Passing+floating-point+values+to+DLLs

//by Richard Russell, July 2009//

The majority of functions that you are likely to want to call using [|SYS] (typically Windows API functions, or functions in third-party DLLs) will require only **integer** or **string** parameters; you can specify those straightforwardly in the **SYS** statement.

Occasionally, however, you may need to pass a floating-point parameter to such a function. Unfortunately you cannot usually do that simply by including the appropriate variable or constant in the **SYS** statement; this article describes what you need to do instead.

There are two main ways of passing floating-point parameters: **by reference** or **by value**. In the first case you pass a **pointer** to the floating-point value (the memory address at which it is stored), and in the second case you pass the **value** itself. There are also (principally) two different types of floating-point value that the function may require: a 32-bit **float** value or a 64-bit **double** value.

You must first ascertain from the documentation of the function whether the parameter is to be passed **by reference** or **by value**, and whether the parameter needs to be a **float** or a **double**; this is vitally important!

Below are listed the methods you must adopt in each of the four possible circumstances:

64-bit doubles passed by reference
This is the simplest case to deal with in BB4W. Suppose you want to pass the variable **fpv**:

code format="bb4w" fpv# = fpv * 1.0# SYS "FunctionName", ^fpv# code Here the value is converted (if necessary) to a 64-bit float (by multiplying it by **1.0#**) and then stored in the 64-bit variable **fpv#**. The parameter supplied in the SYS call is the address of the variable (**^fpv#**).

Rather than a scalar value, you may need to pass an **array** of values (arrays are invariably passed by reference). You can do that in a similar way, for example to pass the array **fpv#**:

code format="bb4w" fpv# *= 1.0# SYS "FunctionName", ^fpv#(0) code Here it is assumed that the array was originally declared with 64-bit elements (i.e. either in ***FLOAT 64** mode or by explicitly specifying a **#** suffix).

If your array uses suffices starting at **1** rather than **0** then you will need to pass the address of the base element:

code format="bb4w" fpv# *= 1.0# SYS "FunctionName", ^fpv#(1) code

64-bit doubles passed by value
Since each parameter of the **SYS** statement is passed as a 32-bit value, a 64-bit floating-point value must be passed as **two** parameters. Suppose you want to pass the variable **fpv**:

code format="bb4w" SYS "FunctionName", FN_dl(fpv), FN_dh(fpv) code The 'helper functions' **FN_dl** and **FN_dh** are listed below. If you prefer to use 'inline' code rather than call the helper functions you can do that as follows:

code format="bb4w" fpv# = fpv * 1.0# SYS "FunctionName", !^fpv#, !(^fpv#+4) code

32-bit floats passed by reference
BBC BASIC for Windows doesn't have a native 32-bit float type, so a conversion is required. Suppose you want to pass the variable **fpv** by reference:

code format="bb4w" temp% = FN_f4(fpv) SYS "FunctionName", ^temp% code Here the temporary variable **temp%** is loaded with the 32-bit float, and then its address is passed as the parameter.

32-bit floats passed by value
This is similar to the previous case, except that the temporary variable is not required. So to pass the variable **fpv** by value:

code format="bb4w" SYS "FunctionName", FN_f4(fpv) code The 'helper function' **FN_f4** is listed below.

Helper functions
Here are the functions used in the above examples:

code format="bb4w" REM Convert to 64-bit double (low 4 bytes) DEF FN_dl(A#) A# *= 1.0# = !^A#

REM Convert to 64-bit double (high 4 bytes) DEF FN_dh(A#) A# *= 1.0# = !(^A#+4)

REM Convert to 32-bit float DEF FN_f4(A#) LOCAL A%,P% PRIVATE F%     IF F%=0 THEN DIM P%10 [OPT 2 .F%       mov esi,[ebp+2]:mov edi,[ebp+7] fld qword [esi]:fstp dword [edi] ret ]     ENDIF A# *= 1.0# CALL F%,A#,A% =A% code