External Call (XCALL) Technical Specifications
The XCALL directive enables Thoroughbred Basic programs to directly interact with system and user-defined libraries through the Dynamic Link Library (DLL) interface. After the interface is set up and initialized Thoroughbred Basic programs can call library functions, pass data to these functions, and receive data from these functions.
To use XCALL, your operating system must support DLL calls:
- Under UNIX systems this feature is usually implemented by the DLOPEN, DLCLOSE, and DLSYM library routines, which are available in ATT/UNIVEL SYSTEM V Release 4, and OSF systems.
- Under Microsoft Windows operating systems this feature is standard.
- Under OPEN VMS operating systems this feature is available through the LIB$FIND_IMAGE_SYMBOL library interface.
Most of the information in this section is specific to the UNIX operating system.
How to set up and initialize your system
Section titled “How to set up and initialize your system”You will need at least one DLL and you will need to define references to the DLLs you plan to make available to the XCALL directive. The following sections describe these requirements.
XCALL can call any defined function in a DLL, provided that the function uses the C language calling interface. If your DLLs were created by a C compiler, the functions will be available to XCALL.
PASCAL libraries, Fortran libraries, and libraries created by other compilers will be available only if the functions in these libraries conform to the C function calling conventions specific to your operating system.
DLLs can be provided by the operating system or by a programmer:
- XCALL can call functions in DLLs that are provided as part of an operating system.
- XCALL can call functions in user-defined DLLs. The DLLs must operate under the constraints of the operating system.
For more information on system-provided DLLs and on how to create DLLs, please refer to the documentation for your operating system. If you plan to create DLLs you may need to pay special attention to the descriptions of the cc, ld, and loader programs.
Variable definitions
Section titled “Variable definitions”Thoroughbred Basic needs to know which DLLs you plan to use. You must create references to libraries you plan to call before you execute Thoroughbred Basic. To create the references, you must define the TBRED_EXTERNAL environment variable in one of the following ways:
-
Under UNIX, TBRED_EXTERNAL is a shell environment variable that contains the names of DLLs that contain functions you plan to call. Separate the names of DLLs with a : (colon).
Some UNIX systems will require you to define the LD_LIBRARY_PATH shell variable in addition to the TBRED_EXTERNAL variable. The UNIX DLOPEN routine uses the information in this variable to find the directories that contain DLLs.
For more information on shell environment variables please refer to the documentation for your operating system.
-
Under Microsoft Windows, TBRED_EXTERNAL is an Environment Variable that contains the names of DLLs that contain functions you plan to call. Separate the DLL names with a ; (semicolon). Full path names may be used. If a path name contains a space, tab or semicolon the entire name must be enclosed in double quotes.
Refer to Microsoft documentation of the LoadLibrary function for details on how DLLs are located by the operating system. Refer to the operating system Help for information on setting Environment Variables.
-
Under OPEN VMS, TBRED_EXTERNAL is a logical definition. For more information on how to create a logical definition please refer to the OPEN VMS documentation.
When Thoroughbred Basic executes the first instance of XCALL, it loads all the DLLs defined by TBRED_EXTERNAL into shared memory. If a DLL cannot be located and loaded, all attempts to call a function in that DLL will generate an error.
XCALL Directive
Section titled “XCALL Directive”The following description of XCALL syntax expands on the description contained in the Thoroughbred Basic Language Reference:
XCALL function-name [,ERR=line-ref|,ERC=numeric-value] [,format-string, arg1[, arg2] . . .]
- function-name
- is the name of the DLL function you plan to call. Valid values are the ASCII names of functions. You cannot specify the ordinal numbers associated with DLL functions.
No case translation is performed, so the function-name must exactly match the name of the DLL function. If the function is not contained in any of the DLLs specified in TBRED_EXTERAL, Thoroughbred Basic will generate an ERR=12.
- line-ref
- is the program line number or label to branch to if this directive produces an error. If you plan to include an error processing routine the ERR= clause must follow function-name.
- format-string
- specifies how Thoroughbred Basic will pass each of the following arguments to the called library function. The format string must be enclosed by quotation marks. You must specify one format definition for each argument. Each format definition is a field separated from the next field by a , (comma). The first format definition specifies how the first argument (arg1) will be passed; the second format definition specifies how the second argument (arg2) will be passed, and so on.
The format definition field is specified in the following way:
method[:type]
- method
- specifies how the argument will be passed. Specify one of the following valid values:
D specifies that the argument will be passed by an OPEN VMS descriptor.
R specifies that the argument will be passed by reference.
V specifies that the argument will be passed by value.
[:type] is provided for OPEN VMS compatibility. It is not required for most uses of the XCALL directive. Valid values are:
| Z | unspecified data type |
| BU | single byte unsigned integer |
| WU | word (two bytes) unsigned integer |
| LU | longword (four bytes) unsigned integer |
| QU | quadword (eight bytes) unsigned integer |
| OU | octaword (sixteen bytes) unsigned integer |
| B | byte, signed integer |
| W | word (two bytes) signed integer |
| L | longword (four bytes) signed integer |
| Q | quadword (eight bytes) signed integer |
| O | octaword (sixteen bytes) signed integer |
| F | F_floating (four bytes) (OPEN VMS FPU-specific) |
| D | D_floating (eight bytes) (OPEN VMS FPU-specific) |
| G | G_floating (eight bytes) (OPEN VMS FPU-specific) |
| H | H_floating (sixteen bytes) (OPEN VMS FPU-specific) |
| T | fixed string size |
If there are more format definitions than arguments, Thoroughbred Basic will ignore the extra format definitions. If there are fewer format definitions than arguments, Thoroughbred Basic will generate an ERR=17.
arg1, arg2, . . . are the Thoroughbred Basic variables that will be passed to the called library function.
Make sure that the format of each argument is specified in the preceding format-string.
For now, XCALL only passes strings and signed integers. Strings can be any length, but signed integers can only occupy up to 32 bits. If the passed number is too large Thoroughbred Basic will generate an error. To pass numbers that occupy more than 32 bits, or numbers that contain a decimal point, convert the number to a string. Make sure that the function called by XCALL expects such a number to be received as a string.
Under UNIX you can pass up to 16 arguments to the function called by XCALL, and you can pass up 12,288 bytes in one call. Under Microsoft Windows you can pass up to 20 arguments and up 12,288 bytes in one call. Under OPEN VMS you can pass up to 256 arguments, and there is no upper limit to the number of bytes you can pass.
The XCALL directive manages string arguments and numeric arguments in the following ways:
String Arguments
Thoroughbred Basic knows the lengths of its string variables at all times. The C language is more flexible, but many implementations expect their string functions to manage null-terminated strings. In general, the XCALL directive and the function it calls must agree that a passed string is composed of a given number of characters.
The function called by XCALL cannot change the length of the string, but the function can change the characters or values in the string:
-
Arguments passed by value will contain the same value after control returns to the Thoroughbred Basic program. The called function can change the value of the string argument, but the change will not be reflected in the Thoroughbred Basic program.
If a string argument is passed by value, it will occupy 32 bits. Only strings that are four bytes long or smaller can be passed by value. The function called by XCALL must be able to decipher the 32-bit format.
-
Arguments passed by reference do not have to contain the same value after control returns to the Thoroughbred Basic program. The called function can change the value of the string argument and the change will be reflected in the Thoroughbred Basic program.
A string argument passed by reference can be any length. If you want a change in string value to be reflected in the calling program you must pass the string in a string variable.
If you use the D (pass by descriptor) option then the [:type] of the field defaults to T. If you plan to use this option in a UNIX environment you must be familiar with how OPEN VMS passes arguments by descriptor.
Numeric Arguments
Thoroughbred Basic provides several forms of numeric representation:
- OPDPI
- represents a 32-bit signed integer
- OPFIX
- represents a 32-bit signed integer with an assumed decimal place between the 100's and 10's places.
- OPFLT
- represents an eight-byte value. The first byte contains the operand type, the second byte contains a sign bit and a 7-bit excess 128 exponent value, and the remaining six bytes contain a 48-bit mantissa.
For now, XCALL can only pass signed integers that can occupy up to 32 bits. To pass numbers that occupy more than 32 bits, or numbers that contain a decimal point, convert the number to a string. Make sure that the function called by XCALL expects such a number to be received as a string.
Numeric arguments can be passed by value or passed by reference:
-
Arguments passed by value will contain the same value after control returns to the Thoroughbred Basic program. The called function can change the value of the numeric argument, but the change will not be reflected in the Thoroughbred Basic program.
If a numeric argument is passed by value, it will occupy 32 bits. The function called by XCALL must be able to decipher the 32-bit format. OPDPI and OPFIX values can be passed by value but OPFLT values cannot.
-
Arguments passed by reference do not have to contain the same value after control returns to the Thoroughbred Basic program. The called function can change the value of the numeric argument and the change will be reflected in the Thoroughbred Basic program.
If a numeric argument is passed by reference, it will occupy 32 bits. If you want a change in numeric value to be reflected in the calling program you must pass the value in a numeric variable. The function called by XCALL must be able to decipher the 32-bit format. OPDPI and OPFIX values can be passed by reference but OPFLT values cannot.
If you plan to use the D (pass by descriptor) option in a UNIX environment you must be familiar with how OPEN VMS passes arguments by descriptor.
How to manage return values
Section titled “How to manage return values”By convention, C functions use the return statement to return a value to a calling program. Returned values are 32 bits long. DFLOAT values will be truncated. Thoroughbred Basic stores the return value. You can use option 3 of the Thoroughbred Basic TCB function to retrieve the return value from the function called by the XCALL directive.
Examples
Section titled “Examples”The following sets of examples will show you how to create DLLs, how to and specify shell environment variables so that Thoroughbred Basic can recognize DLLs, and how to use the XCALL directive to call DLL functions. The examples in this section are specific to UNIX operating systems.
How to create DLLs
Section titled “How to create DLLs”- You can create the following DLL under the UNIVEL V Release 4 operating system:
edatest1_call(value) int value;{ value = value + 1; return(value);}.w test1.cqcc -K -PIC -G -o test1.so test1.cIn this example, the ed editor is used to create a simple C function, which is written to the test1.c file. The test1_call function receives a value, adds 1 to the value, and uses the return command to return the value. The cc program and, implicitly, the ld program are used to compile the test1_call function and create the test1.so DLL.
- You can create the following DLL on the OSF/1 operating system on a DEC Alpha machine:
edatest2_call(s,i,p,j) int i, j; /* Variables i and j are integers */ char *s, *p; /* Variables s and p are pointers */{ /* that are passed by reference */ int max, k; /* Max # of characters to process */ max = (i < j) ? i:j; /* Get lower value of i or j */ for (k=max; k; k-- ) { /* Do until max = 0 */ *p = *s; /* Get char pointed to by s, and */ /* store it in the location */ /* pointed to by p */ if ( isupper(*p)) /* Determine if the stored */ /* character is uppercase */ *p = tolower(*s); /* If it is, make it lowercase */ p++; /* Add 1 to p pointer address */ s++; /* Add 1 to s pointer address */ } return(max); /* Return # of characters processed*/}.w test2.cqcc -c -O test2.cld -all -shared -o test2.so test2.oThis example contains the test2_call function, which converts uppercase characters into their corresponding lowercase characters. The s and p arguments are pointers to the values passed by reference. The i and j variables are integers that receive values passed by value.
The DLL is created in two steps. First, the cc compiler creates the C object code. Next, the ld program creates the DLL.
The ld program may encounter undefined references, which are routines that the ld program cannot find. In most cases, you can ignore them. During the initial XCALL loading process undefined references may be located because they may be part of some other DLL that XCALL loads. For more information on how to manage DLLs and undefined references please refer to system documentation on the cc, ld, and loader programs.
- You can create a multi-function DLL under the UNIVEL V Release 4 operating system:
edatest3_add(value1, value2) int value1; int value2;{ value1 = value1 + value2; return( value1 );}test3_sub(value1, value2) int value1; int value2;{ value1 = value1 - value2; return( value1 );}test3_mult(value1, value2) int value1; int value2;{ value1 = value1 * value2; return( value1 );}test3_div(value1, value2) int value1; int value2;{ if ( value2 == 0 ) return(0); /* Attempt to divide by zero */ value1 = value1 / value2; return( value1 );}.w test3.cqcc -K -PIC -G -o test3.so test3.cThis example contains four functions that perform addition, subtraction, multiplication, or division using two numeric values. The functions are compiled as a single entity and made into a single DLL. XCALL can call any one of these functions.
- The following files can be used to create a DLL using Microsoft’s Visual C++ Version 6 to be used on Microsoft Windows operating systems. The resulting xcallsample.dll contains all the functions described in the preceding test1, test2, and test3 examples. When you have created the DLL, set up a TBRED_EXTERNAL Environment Variable, as described earlier in this section, and test using the Thoroughbred Basic program from Example 4 under How to use the XCALL directive, later in this manual.
File xcallsample.c/*** sample functions to test xcall*/#include <ctype.h>
test1_call(value) int value;{ value = value + 1; return(value);}
test2_call(s,i,p,j) int i, j; /* Variables i and j are integers */ char *s, *p; /* Variables s and p are pointers */{ /* that are passed by reference */ int max, k; /* Max # of characters to process */ max = (i < j) ? i:j; /* Get lower value of i or j */ for (k=max; k; k-- ) { /* Do max characters */ *p = *s; /* Get char pointed to by s, and */ /* store it in the location */ /* pointed to by p */ if ( isupper(*p)) /* Determine if the stored */ /* character is uppercase */ *p = tolower(*s); /* If it is, make it lowercase */ p++; /* Add 1 to p pointer address */ s++; /* Add 1 to s pointer address */ } return(max); /* Return # of characters processed*/}
test3_add(value1, value2) int value1; int value2;{ value1 = value1 + value2; return( value1 );}
test3_sub(value1, value2) int value1; int value2;{ value1 = value1 - value2; return( value1 );}
test3_mult(value1, value2) int value1; int value2;{ value1 = value1 * value2; return( value1 );}
test3_div(value1, value2) int value1; int value2;{ if ( value2 == 0 ) return(0); /* Attempt to divide by zero */ value1 = value1 / value2; return( value1 );}File xcallsample.defLIBRARY XCALLSAMPLEDESCRIPTION 'Sample XCALL DLL for Windows'HEAPSIZE 1024EXPORTStest1_calltest2_calltest3_addtest3_subtest3_multtest3_divFile xcallsample.rc//Microsoft Developer Studio generated resource script.//
#define APSTUDIO_READONLY_SYMBOLS///////////////////////////////////////////////////////////////////////////////// Generated from the TEXTINCLUDE 2 resource.//#define APSTUDIO_HIDDEN_SYMBOLS#include "windows.h"#undef APSTUDIO_HIDDEN_SYMBOLS
/////////////////////////////////////////////////////////////////////////////#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)#ifdef _WIN32LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US#pragma code_page(1252)#endif //_WIN32
#ifndef _MAC///////////////////////////////////////////////////////////////////////////////// Version//
VS_VERSION_INFO VERSIONINFO FILEVERSION 1,0,0,0 PRODUCTVERSION 1,0,0,0 FILEFLAGSMASK 0x3fL#ifdef _DEBUG FILEFLAGS 0x1L#else FILEFLAGS 0x0L#endifFILEOS 0x40004L FILETYPE 0x1L FILESUBTYPE 0x0LBEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "FileDescription", "Thoroughbred XCALL Sample DLL\0" VALUE "FileVersion", "01/13/2004\0" VALUE "OriginalFilename", "XCALLSAMPLE.DLL\0" VALUE "CompanyName", "Thoroughbred Software International, Inc.\0" VALUE "ProductName", "Thoroughbred Environment\0" VALUE "ProductVersion", "8.5.1+\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 ENDEND#endif // !_MAC#endif // English (U.S.) resources/////////////////////////////////////////////////////////////////////////////File xcallsample.dsp# Microsoft Developer Studio Project File - Name="xcallsample" - Package Owner=<4># Microsoft Developer Studio Generated Build File, Format Version 6.00# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
CFG=xcallsample - Win32 Release!MESSAGE This is not a valid makefile. To build this project using NMAKE,!MESSAGE use the Export Makefile command and run!MESSAGE!MESSAGE NMAKE /f "xcallsample.mak".!MESSAGE!MESSAGE You can specify a configuration when running NMAKE!MESSAGE by defining the macro CFG on the command line. For example:!MESSAGE!MESSAGE NMAKE /f "xcallsample.mak" CFG="xcallsample - Win32 Release"!MESSAGE!MESSAGE Possible choices for configuration are:!MESSAGE!MESSAGE "xcallsample - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")!MESSAGE "xcallsample - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")!MESSAGE
# Begin Project# PROP AllowPerConfigDependencies 0# PROP Scc_ProjName ""# PROP Scc_LocalPath ""CPP=cl.exeMTL=midl.exeRSC=rc.exe
!IF "$(CFG)" == "xcallsample - Win32 Release"
# PROP BASE Use_MFC 0# PROP BASE Use_Debug_Libraries 0# PROP BASE Output_Dir ".\WinRel"# PROP BASE Intermediate_Dir ".\WinRel"# PROP Use_MFC 0# PROP Use_Debug_Libraries 0# PROP Output_Dir "Release"# PROP Intermediate_Dir "Release"# PROP Ignore_Export_Lib 0# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FR /YX /c# ADD CPP /nologo /Zp1 /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c# SUBTRACT CPP /Fr# ADD BASE MTL /nologo /D "NDEBUG" /win32# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32# ADD BASE RSC /l 0x409 /d "NDEBUG"# ADD RSC /l 0x409 /d "WIN32" /d "NDEBUG"BSC32=bscmake.exe# ADD BASE BSC32 /nologo# ADD BSC32 /nologoLINK32=link.exe# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.libshell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:windows /dll /machine:I386# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.libole32.lib oleaut32.lib uuid.lib /nologo /version:4.0 /subsystem:windows /dll /pdb:none /map/machine:I386File xcallsample.dsp!ELSEIF "$(CFG)" == "xcallsample - Win32 Debug"
# PROP BASE Use_MFC 0# PROP BASE Use_Debug_Libraries 1# PROP BASE Output_Dir ".\WinDebug"# PROP BASE Intermediate_Dir ".\WinDebug"# PROP Use_MFC 0# PROP Use_Debug_Libraries 1# PROP Output_Dir "Debug"# PROP Intermediate_Dir "Debug"# PROP Ignore_Export_Lib 0# ADD BASE CPP /nologo /MT /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /YX /c# ADD CPP /nologo /Zp1 /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c# SUBTRACT CPP /Fr# ADD BASE MTL /nologo /D "_DEBUG" /win32# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32# ADD BASE RSC /l 0x409 /d "_DEBUG"# ADD RSC /l 0x409 /d "WIN32" /d "_DEBUG"BSC32=bscmake.exe# ADD BASE BSC32 /nologo# ADD BSC32 /nologoLINK32=link.exe# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.libshell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:windows /dll /debug /machine:I386# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.libole32.lib oleaut32.lib uuid.lib /nologo /version:4.0 /subsystem:windows /dll /debug /machine:I386# SUBTRACT LINK32 /incremental:no!ENDIF
# Begin Target
# Name "xcallsample - Win32 Release"# Name "xcallsample - Win32 Debug"# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"# Begin Source File
SOURCE=.\xcallsample.c# End Source File# Begin Source File
SOURCE=.\xcallsample.def# End Source File# Begin Source File
SOURCE=.\xcallsample.rc# End Source File# End Group# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"# End Group# Begin Group "Resource Files"
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"# End Group# End Target# End ProjectHow to specify environment variables
Section titled “How to specify environment variables”You can set up UNIX shell environment variables so that Thoroughbred Basic can recognize and load the DLLs that contain functions you plan to call:
TBRED_EXTERNAL=test.so:libc.so:libnsl.soexport TBRED_EXTERNALLD_LIBRARY_PATH=.;/usr/libexport LD_LIBRARY_PATHIn this example, the TBRED_EXTERNAL and LD_LIBRARY_PATH shell environment variables are created:
- The TBRED_EXTERNAL shell variable specifies three DLLs that will be loaded into shared memory when the first instance of the XCALL directive executes. The test.so DLL is a user-defined DLL. The libc.so and libnsl.so are C runtime packages usually found in the /usr/lib directory.
- The export command tells the shell that TBRED_EXTERNAL will be passed to all child processes created by this shell.
- The LD_LIBRARY_PATH shell variable specifies a list of directories that contain the DLLs specified in the TBRED_EXTERNAL variable. For more information on this shell variable please refer to your operating system documentation. In some cases, this documentation will be included in the description of the loader utility.
- The export command tells the shell that LD_LIBRARY_PATH will be passed to all child processes created by this shell.
The shell will permanently keep these variables. They will not be changed until the owner decides to modify or delete them. To review the names of the functions and print other information about the DLLs you can use the nm program.
After these commands are executed under UNIX you can start Thoroughbred Basic. The Thoroughbred Basic XCALL directive can call any of the functions in the three DLLs.
You can set up Microsoft Windows environment variables in the System Properties dialog. To locate System Properties right-click on My Computer and select Properties or go to the Control Panel and select System. Next select the Advanced tab and then press the Environment Variables button. In the Environment Variables display you can add or edit variables for both yourself and the entire system. Your choice depends on who will be using XCALL. The Variable Name is TBRED_EXTERNAL. The Variable Value is the name of your DLL including the path if necessary. For example: C:\XCALL\Debug\xcallsample.dll. Note that changes do not affect applications already running, including Visual C++ and Thoroughbred Basic.
How to use the XCALL directive
Section titled “How to use the XCALL directive”- You can use the Thoroughbred Basic XCALL directive to call the function defined in the first example earlier in this section:
TBRED_EXTERNAL=test1.soexport TBRED_EXTERNALLD_LIBRARY_PATH=.export LD_LIBRARY_PATH
basic IPLINPUT
0010 FOR I=1 TO 50 STEP 2; XCALL "test1_call",ERR=999,"V",I0020 IF TCB(3) <> I+1 THEN M$="The XCALLed function test1_call0020: did not return the correct results"; EXITTO 80000030 NEXT I0040 PRINT "Finished testing XCALL"; GOTO 90000999 M$="XCALL failed to complete its task"; EXITTO 80008000 PRINT M$9000 ENDThis example contains three steps:
-
The shell environment variables are defined. The TBRED_EXTERNAL variable contains the name of the test1.so DLL, which is described in the first example earlier in this section. The LD_LIBRARY_PATH variable contains the name of the current directory. The export commands make both variables available to all the programs run under the shell.
-
Thoroughbred Basic is started. This command specifies the default IPL file.
-
A Thoroughbred Basic program is written and executed: - Line 10 uses the XCALL directive to call the test1_call function. The numeric variable is passed by value to the function.
If the DLL cannot be found, if the function cannot be found within the DLL, or if the argument is invalid, Thoroughbred Basic will use the ERR= option to process errors. You cannot use the ERR= option to find or process errors generated by the external function.
If the DLL function is found the value contained in the variable will be contained in the value variable of the test1_call function.
- If the XCALL directive succeeded the test1\_call function can process the value contained in its value variable.- When the test1\_call function has finished, it will use the return(value) command to return the value of the value variable to Thoroughbred Basic. Line 20 uses option 3 of the TCB function to retrieve that number.2. You can use the Thoroughbred Basic XCALL directive to call the function defined in the second example earlier in this section:
TBRED_EXTERNAL=test2.soexport TBRED_EXTERNALLD_LIBRARY_PATH=.export LD_LIBRARY_PATH
basic IPLINPUT
0005 DIM X$(3*1024,$00$)0010 OPEN(1,OPT="TEXT") "Ascii_File"0012 TEXT "Lower_case",0,0 ; OPEN(2,OPT="TEXT") "Lower_case"0020 READ (1,END=999) A$0030 XCALL "test2_call",ERR=998,"R,V,R,V",A$,STL(A$),X$,STL(X$)0040 IF( TCB(3) > 0 ) THEN WRITE(2) X$(1,TCB(3))0050 GOTO 200998 PRINT "XCALL got an error ", ERR,",",ERM(ERR)0999 CLOSE(1); CLOSE(2)9000 ENDThis example contains three steps:
- The shell environment variables are defined. The TBRED_EXTERNAL variable contains the name of the test2.so DLL, which is described in the second example earlier in this section. The LD_LIBRARY_PATH variable contains the name of the current directory. The export commands make both variables available to all of the programs run under the shell.
- Thoroughbred Basic is started. This command specifies the default IPL file.
- A Thoroughbred Basic program is written and executed:
-
Line 5 uses the DIM directive to create and initialize a fixed storage variable. The X$ variable is 3*1024 characters long.
-
Line 10 opens an external TEXT file on channel 1. This file contains characters.
-
Line 12 creates and opens an external TEXT file on channel 2. This file will receive output.
-
Line 20 uses the READ directive to read one line from the file open on channel 1 and store the line in the A$ string variable. In this case, a line is a string of characters terminated by a line-feed character.
If there are no more characters left to read, the Thoroughbred Basic program will branch to line 999.
-
Line 30 uses the XCALL directive to call the test2_call function. Arguments are passed in the following order:
- The A$ variable is passed by reference.
- The length of the A$ variable, specified as STL(A$), is passed by value.
- The X$ variable is passed by reference.
- The length of the X$ variable, specified as STL(X$), is passed by value.
If the DLL cannot be found, if the function cannot be found within the DLL, or if the argument is invalid, Thoroughbred Basic will use the ERR= option to process errors. You cannot use the ERR= option to trap or process errors generated by the external function.
If the DLL function is found the values contained in the four variables will be contained in the corresponding variables in the test2_call function.
- If the XCALL directive succeeded the test2_call function will convert any uppercase characters in the A$ variable into their lowercase equivalents.
- When the test2_call function has finished, it will use the return command to return the number of characters it processed. This number is the number of characters stored in the X$ variable.
- Line 40 uses option 3 of the TCB function to retrieve the number of characters that test2_call processed. If characters were processed, they will be written to the output TEXT file open on channel 2.
Lines 20 through 50 will be repeated until all the characters in the TEXT file open on channel 1 have been read. After the characters have been processed and put in the TEXT file open on channel 2, Thoroughbred Basic will close the TEXT files and end the program.
- You can use the Thoroughbred Basic XCALL directive to call the functions defined in the third example earlier in this section:
TBRED_EXTERNAL=test3.soexport TBRED_EXTERNALLD_LIBRARY_PATH=.export LD_LIBRARY_PATH
basic IPLINPUT
0010 XCALL "test3_add","V,V",1,5;0010 : IF TCB(3) <> 6 THEN GOTO GOT_ERR0020 XCALL "test3_sub","V,V",5,1;0020 : IF TCB(3) <> 4 THEN GOTO GOT_ERR0030 XCALL "test3_mult","V,V",32,5;0030 : IF TCB(3) <> 32*5 THEN GOTO GOT_ERR0040 XCALL "test3_div","V,V",9,2;0040 : IF TCB(3) <> INT(9/2) THEN GOTO GOT_ERR0100 PRINT "Done Testing"0110 GOTO 90008000 GOT_ERR: REM8010 PRINT "Function failed to return proper value"9000 ENDThis example contains three steps:
- The shell environment variables are defined. The TBRED_EXTERNAL variable contains the name of the test3.so DLL, which is described in the third example earlier in this section. The LD_LIBRARY_PATH variable contains the name of the current directory. The export commands make both variables available to all the programs run under the shell.
- Thoroughbred Basic is started. This command specifies the default IPL file.
- A Thoroughbred Basic program is written and executed: - Lines 10 through 40 use the XCALL directive to call the test3_add, test3_sub, test3_mult, and test3_div functions in the test3.so DLL. Each function accepts two arguments. The arguments in this example are numeric constants passed by value. - If the XCALL directives and the called functions execute successfully the value generated by the function will be returned to Thoroughbred Basic by the function’s return command. The TCB(3) statement will retrieve the returned values.
- You can write one program that enables you to use the Thoroughbred Basic XCALL directive to call all the functions defined in all of the examples earlier in this section:
TBRED_EXTERNAL=test3.so:test2.so:test1.soexport TBRED_EXTERNALLD_LIBRARY_PATH=.export LD_LIBRARY_PATH
basic IPLINPUT
0010 XCALL "test3_add","V,V",1,5;0010 : IF TCB(3) <> 6 THEN GOTO GOT_ERR0020 XCALL "test3_sub","V,V",5,1;0020 : IF TCB(3) <> 4 THEN GOTO GOT_ERR0030 XCALL "test3_mult","V,V",32,5;0030 : IF TCB(3) <> 32*5 THEN GOTO GOT_ERR0040 XCALL "test3_div","V,V",9,2;0040 : IF TCB(3) <> INT(9/2) THEN GOTO GOT_ERR0050 XCALL "test1_call","V",7;0050 : IF TCB(3) <> 8 THEN GOTO GOT_ERR0060 A$="This Is All Lower Case"; Z$=A$;0060: XCALL "test2_call","R,V,R,V",A$,STL(A$),Z$,STL(Z$);0060: IF Z$ <> "this is all lower case" THEN GOTO GOT_ERR0100 PRINT "Done Testing"0110 GOTO 90008000 GOT_ERR: REM8010 PRINT "Function failed to return proper value"9000 ENDThis example contains three steps:
- The shell environment variables are defined. The TBRED_EXTERNAL variable contains the name of the test3.so, test2.so, and test1.so DLLs, which are described in the examples earlier in this section. The LD_LIBRARY_PATH variable contains the name of the current directory. The export commands make both variables available to all the programs run under the shell.
- Thoroughbred Basic is started. This command specifies the default IPL file.
- A Thoroughbred Basic program is written and executed:
- Line 10 contains the first instance of the XCALL directive. If this directive is valid Thoroughbred Basic will load all the DLLs specified in the TBRED_EXTERNAL variable into shared memory. All the functions contained in the DLLs will be available to Thoroughbred Basic.
- Lines 10 through 40 use the XCALL directive to call the test3_add, test3_sub, test3_mult, and test3_div functions in the test3.so DLL. Each function accepts two arguments. All the arguments in this example are numeric constants passed by value.
- Line 50 uses the XCALL directive to call the test1_call function in the test1.so DLL. The argument is a numeric constant passed by value.
- Line 60 uses the XCALL directive to call the test2_call function in the test2.so DLL: This function enables you to convert uppercase characters to their lowercase equivalents in the following way:
- The A$ variable is created and initialized with the “This Is All Lower Case” string.
- The Z$ variable receives the value contained in the A$ variable.
- The XCALL directive calls the test2_call function. Arguments are passed in the following order:
- The A$ variable is passed by reference.
- The length of the A$ variable, specified as STL(A$), is passed by value.
- The Z$ variable is passed by reference.
- The length of the X$ variable, specified as STL(Z$), is passed by value.
- If the XCALL directive succeeded the test2_call function will convert any uppercase characters in the A$ variable into their lowercase equivalents. The converted string will be placed in the test2_call function variable that corresponds to the Z$ variable.
- When the test2_call function has finished, Thoroughbred Basic will use an IF directive to make sure that the conversion was successful.
From the Thoroughbred Basic Technical Appendices, version 8.9.1. Open this page in the PDF