Technical Correspondence
APL-Fortran calls using quadNA
Vector 12:2, page 105
Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 105
APL2/OS2 ← ⎕NA → FORTRAN
Many APLs in recent years have provided facilities to connect across to compiled software in some other language. I have looked at the process in APL2 on PCs, in particular under the latest version, APL2/OS2.
Mainframe APL provides ⎕NA, or ‘name association’, along with a file that must describe the elements in the calling sequence. Naturally there are variations to fit the ultimate language that will be ‘associated’. The first version of APL2 for PCs was constructed for DOS-based hardware, and while it had a connection across to compiled routines and followed many of the constructs of the mainframe version any resemblance was superficial. An attached support processor was used (with the usual calling connections). The Fortran source (as a subroutine) was compiled and linked, with one or more entry points. Some provided APL functions then took the appropriate listings and generated source for an assembler that was then used by a supplied PFORTPAR.EXE file to complete the connections. While it sounds complicated, if the instructions were followed closely the result usually worked. The assembler source contained data about each variable in the calling sequence, type, size, results returned, as might be expected. Usage now required connecting to the attached support processor, sharing the needed two variables and then sending data over and bringing back results.
The arrival of APL2/2 opened up this connection, and materially simplified its usage. ⎕NA was now the interface, a file contained all the data about variables in the calling sequence, and the actual compiled Fortran code was stored as a ***.DLL file. The conventions for the identifying file become very similar to that of the mainframe, i.e. type, dimensions of each variable in the calling sequence, with a flag for ‘returning results’.
It is, however, reasonably clear that the principal use of this connection, within the development group, was to connect to C or C++ programs, and Fortran had not been used (this is not meant as any condemnation, just a statement of apparent fact). Fortran has some problems which can be easily overcome, once you have the ‘scheme of things’. Here they are.
-
APL2/2 runs under OS2, version 2 or later. This is a 32-bit addressing system. As of late 1994 there was only one Fortran that was built around 32-bit addressing (as far as I or the APL development group could discover). IBM’s earlier Fortran (ex Microsoft) would compile and link under OS2, but all Real*8 operations appear to give 0 as an answer. There are several Fortrans that have extenders to access higher memory, but these also probably have trouble in the OS2, v.2 environment (I have not actually tested them, no promises). The Fortran that was used came from Watcom, Fortran77(32) (for the rest of this article I shall just refer to it as Fortran). This Fortran has the ability to compile and link into an ***.DLL (Dynamic Link Library) subject to a few caveats.
-
Any floating point number in APL2 is based on a high precision representation, whether as REAL or COMPLEX. The
⎕NAassociation will provide for matching in these conversions. As a result most Fortran source that is intended for sharing will have been built around declarations of REAL*8 and COMPLEX*16. All Fortrans provide for ‘generic’ functions for many functions; Watcom 77 is no exception. There are two exceptions, which are documented if the user thinks to look for them (I did not at first). Here they are:If X is COMPLEX*16 and you want to get the real part you must use DREAL(X). The choice of REAL(X) will give only single precision. (Surprise, IMAG works as you might expect).
If X and Y are REAL*8, the double precision complex requires DCMPLX(X,Y). If you use CMPLX(X,Y) you will get a single precision complex.
-
In order to use
⎕NAthe calling sequences must follow OS2 conventions exactly. Hence there can be no communications that use registers. When staying entirely in a Fortran environment, Watcom allows for register communicating; APL2 cannot. Thus you must force the compiler to avoid registers in communications, and thus the compiler must be given two options, /BD which says a DLL is being built, and /SC which effectively says not to use registers in communicating. -
There are two files that are needed for
⎕NA, one of which describes the contents of the calling sequence, the other is the ***.DLL file just created. These must be put in the proper libraries. While many APLers may know where these are, or can interpret what ‘default’ really means, the documenting is ‘a wee tad short’ in this respect. Add to that that the OS2 ‘search and find’ utility first located a back-up directory when I went searching, and it is not surprising that nothing really worked. The calling sequence file, which might typically be inside an ***.NAM file, should be stored inside the BIN sub-directory of the directory APL2OS2. There is a sample .NAM file provided, but it is suggested that the user not add to that file, it could be changed in a later service release. The translated Fortran should be stored as an ***.DLL file inside the DLL sub-directory of the directory APL2OS2.
Example¶
Suppose we have a Fortran routine ROOTS(N,X,Y), which takes the complex coefficients X of a polynomial of degree N, and returns the solutions in Y. We assume the source has been sufficiently well checked that errors are remote. Here are the steps.
-
Compile the source:
/bd forces a .DLL design, /sc controls registers in the calling sequences. There may be several ‘entry points’, but there is only one in this example (more entry points won’t affect this command).
-
Link. While this can be done with a ‘linker directive’ file, I prefer (upper case is from system, except for control z).
C:\ wlink WLINK system os2v2 dll for OS2, as a DLL file WLINK file roots assumes there is a file ROOTS.OBJ WLINK export roots entry point is roots, as many entries as desired, each entry should be 'export'ed WLINK CTL Z ends linker entriesYour directory will now contain ROOTS.OBJ from the compilation (no need to keep), and a file ROOTS.DLL which needs to be moved to the sub-directory DLL of the directory APL2OS2.
-
Create the communications data lists. This is well described in available documention. For this example X is complex, of size determined on use, as is Y. Since Y is used to return results it must be created properly in the APL using routine (see later). The file, called TEST.NAM, is stored in the BIN sub-directory of the directory APL2OS2, and is
The usage of > in the last line is to indicate that this argument is used for returning results. There is some case sensitivity, so create the Fortran routine with upper case for the routine name, and keep all names upper case in TEST.NAM. The APL program that calls this routine is now (shortened to show only the critical parts)
Y ←ROOTS X;N;ROOTS
N ←'<TEST.NAM>' 11 ⎕NA'ROOTS'
⍝Comment:TEST.NAM is name of characteristic file
Y ←(N←(-1)+↑⍴X)⍴ 0J1
⍝Comment: create Y of correct length and type
ROOTS N W 'Y'
Comment: The argument for ROOTS must be a nested list, so no “,”.
Since results are in Y, it is passed as a name. N is generated as a scalar with ↑ since it is defined as a scalar in TEST.NAM. The results will be in the variable Y. I note that since the DLL file has the entry name ROOTS, it is important to make ROOTS local to the APL function. If enough different names are used this localizing can be avoided.
The primary use of ⎕NA that I have made is this one application, but I believe it should cover nearly all uses of Fortran. I have not attempted using a Fortran function routine (i.e. one that transmits a result back in-line) with ⎕NA.
I also offer many thanks to the APL2 development group, who offered major support in tracking all this down.
Ralph Selfridge
300 CSE
University of Florida
Gainesville, FL 32611, USA
Email: selfrid@nervm.nerdc.ufl.edu