In the first issue of this magazine we have presented the PANORAMIC BASIC software. Here we describe how to call functions from dynamically loaded libraries (DLL).
The DLL is loaded in memory by the instruction dll_on "xxx.dll", where xxx.dll is the name of the library.
When the DLL is no longer necessary, the memory can be released by the instruction dll_off
The general syntax for calling a function from a DLL is:
result% = dll_calln("DllFunctionName", par1%, ..., parn%)
where n is an integer from 0 to 7 which represents the number of parameters passed to the function. There are indeed 8 different routines:
dll_call0("DllFunctionName") dll_call1("DllFunctionName", par1%) dll_call2("DllFunctionName", par1%, par%2) ..........................................
As you can see, the first parameter must be a string with the name of the function to be called, followed by the n parameters of the function.
Also note that :
Panoramic can only call functions in a DLL, not subroutines.
All function parameters must be passed as integer values.
All function called by the call_dll routines return an integer value.
By default, all function parameters are passed by value. If the DLL function requires a parameter passed by reference, the Panoramic adr() function must be used. This function may be used with simple variables of integer, string or float type.
At the present development stage of PANORAMIC, no array can be passed, neither by value nor by reference via adr().
Passing strings to a DLL requires some caution. Strings are stored zero byte terminated, and adr(s$) passes a pointer to the string descriptor. You can write back into the string, but you must NEVER overwrite or write past the zero byte.
The called DLL function must use the stdcall convention.
The DLL function names are case sensitive and must therefore be specified with the exact case of their name.
The goal of this example is to implement in PANORAMIC the Mersenne Twister random number generator, which is one of the most reliable.
Since this generator is the default one in FreeBASIC, we will write the DLL in this language. Of course, other languages like Pascal or C could be used.
Here is the FreeBASIC source of our DLL (file ranmt.bas):
' ******************************************************************* ' Mersenne Twister random number generator ' ******************************************************************* extern "Windows-MS" function InitMT(Seed as integer) as integer export ' Initialize the generator randomize Seed InitMT = 0 end function function RanMT(byref X as double) as integer export ' Return a random real in [0, 1) X = rnd RanMT = 0 end function function IRanMT as integer export ' Return a random integer in [-2^31, 2^31 - 1] IRanMT = int((rnd - 0.5) * 4294967296.0) end function function IRanMT1(N as integer) as integer export ' Return a random integer in [0, N] IRanMT1 = int(rnd * (N + 1)) end function function IRanMT2(A as integer, B as integer) as integer export ' Return a random integer in [A, B] IRanMT2 = A + int(rnd * (B - A + 1)) end function end extern
We can notice that:
The whole code is embedded in a block delimited by:
This is necessary to ensure that the names of the functions are not
modified when they are passed to the calling program.
Each function returns an integer result. When the result is not obvious, we assign the value 0 to the function, as in InitMT and RanMT.
The function RanMT should return a real number, but this is not accepted by PANORAMIC in its present stage. So, we affect the result to a real parameter X which is passed by reference.
This function should be called by PANORAMIC as follows:
dim i%, x dll_on "ranmt.dll" i% = dll_call1("RanMT", adr(x)) print x dll_off
We did not add the stdcall keyword, since it is the default calling convention in FreeBASIC.
The DLL is compiled with:
fbc ranmt.bas -dll
We obtain two files: ranmt.dll and libranmt.dll.a. Only the first one is necessary for our application.
The following program uses the DLL to simulate a dice game:
' --------------------------------------------------------- ' Simulation of dice game with khi2 test ' ' The DLL RANMT provides the "Mersenne Twister" random ' number generator ' --------------------------------------------------------- dim f%, nt%, i%, d%, n%(6), khi2, diff f% = 100 : ' Expected frequency for each number nt% = 6 * f% : ' Total number of runs dll_on "ranmt.dll" ' Initialize random number generator ' (try different values) i% = dll_call1("InitMT", 123456789) ' Do nt% runs ' Each number should appear about f% times for i% = 1 to nt% d% = dll_call2("IRanMT2", 1, 6) n%(d%) = n%(d%) + 1 next i% dll_off ' Compute khi2 khi2 = 0 for i% = 1 to 6 diff = n%(i%) - f% khi2 = khi2 + diff * diff / f% next i% ' Display results print "Simulation of dice game" print "-----------------------" print for i% = 1 to 6 print "Frequency of "; i%; " = "; n%(i%) next i% print print "khi2 = "; khi2 end
The comparison between the observed frequency for each number and the expected frequency is performed by a χ² (khi2) test:
χ² = Σi=1..p (Oi - Ci)² / Ci
where p denotes the number of classes, Oi and Ci the observed and calculated (expected) frequencies, respectively. In our case, p = 6, Oi = n%(i%) and Ci = f%.
This sum has p - 1 = 5 degrees of freedom. In these conditions, the critical value of χ² having a 95% probability of being exceeded is 11.07
Running the previous program in the PANORAMIC editor gives the following results:
Simulation of dice game ----------------------- Frequency of 1 = 98 Frequency of 2 = 122 Frequency of 3 = 97 Frequency of 4 = 96 Frequency of 5 = 81 Frequency of 6 = 106 khi2 = 9.1
The observed frequencies differ from the theoretical one, but the low χ² value shows that these differences are not statistically significant.
It is recommended to run the program with different initializations and increasing values of f%.
In spite of some limitations, which are likely to be overcome in the near future, the use of DLLs is an easy way to enhance the possibilities of the PANORAMIC language.
Once again, I would like to express my gratitude to the members of the french PANORAMIC forum especially:
Klaus, for writing the first version of the PANORAMIC calling rules. Klaus also provides his own DLLs on his web site.
D. J. Peters, for helpful advice on how to write the FreeBASIC code.