Table of Contents
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:
fpv# = fpv * 1.0# SYS "FunctionName", ^fpv#
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#():
fpv#() *= 1.0# SYS "FunctionName", ^fpv#(0)
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:
fpv#() *= 1.0# SYS "FunctionName", ^fpv#(1)
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:
SYS "FunctionName", FN_dl(fpv), FN_dh(fpv)
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:
fpv# = fpv * 1.0# SYS "FunctionName", !^fpv#, !(^fpv#+4)
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:
temp% = FN_f4(fpv) SYS "FunctionName", ^temp%
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:
SYS "FunctionName", FN_f4(fpv)
The 'helper function' FN_f4 is listed below.
Helper functions
Here are the functions used in the above examples:
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#)LOCALP%:P%=^a#:IFABSa#<1E-38 THEN=FALSE =P%!4 AND NOT&7FFFFFFFOR(P%!4-&38000000<<3 OR!P%>>29 AND7)+(!P%>>28 AND1)