home *** CD-ROM | disk | FTP | other *** search
- TPFort v 1.2 - A link from Turbo Pascal to MS Fortran.
-
- Copyright (c) 1989 D.J. Murdoch. All rights reserved.
-
- PURPOSE
-
- TPFort is a collection of several procedures that allow Microsoft Fortran
- routines to be called from Turbo Pascal. I wrote it so that I could use the
- binary-only NAG Fortran library for numerical routines in Turbo Pascal
- programs, but it ended up being a general purpose linker.
-
- PRICE
-
- TPFort is absolutely free to use and incorporate into your own programs for
- any purpose. Distribute it to anyone you like, but please don't remove my
- copyright notice or modify it in any other way. Source code is available
- (see below), but is not necessary to be able to use TPFort with Turbo Pascal
- version 5.5.
-
- METHOD
-
- The Fortran routines are compiled into their own loader file which is loaded at
- run time by a Turbo Pascal program, making most of the Fortran subroutines and
- functions available to the Pascal program. The molasses-slow Fortran compiler
- and linker need only be run once to create the loader; changes to the Pascal
- part of the program don't force recompiling or re-linking of the Fortran part.
-
- INSTRUCTIONS
-
- There are several steps involved in preparing a Fortran routines to be called
- from Turbo Pascal.
-
- 1. Preparing the Fortran Program
-
- Write a Fortran program which includes the following declarations and a call
- to CALLTP, in the following format:
-
- EXTERNAL routine1, routine2, ..., routineX
-
- CALL CALLTP(routine1, routine2, ..., routineX, X)
-
- where routine1 through to routineX are the names of the Fortran routines you
- wish to make available to your Turbo Pascal program, and X is an integer value
- giving the number of routines being passed. The external declaration is
- extremely important; if not given, Fortran will assume the routine names are
- local integer or real variables, and things will get really messed up.
-
- This loader may do anything else, such as reading data from files, allocating
- space, etc. It's not all that important where the call to CALLTP takes place,
- but more efficient use will be made of the program stack if the call comes
- somewhere in the main program, rather than in a function or subroutine.
-
- After this call and any other initialization necessary, the program should
- exit. As this will close any open files, and I/O done while TP is active
- is probably unreliable, it should complete any I/O operations before quitting,
- and the routines being passed should avoid doing I/O.
-
- Compile this routine and link it to the object file CALLTP.OBJ. Be sure to
- specify to the linker that a larger than normal stack will be needed - I'd
- suggest a minimum of 16K. The Turbo Pascal program will be using this stack
- instead of its own much of the time, and TP makes much heavier use of the
- stack than does Fortran.
-
- Warning: Don't try running the loader program on its own, unless you avoid
- executing the call to CALLTP. If TP isn't there to catch that call, you're
- very likely to crash. It might be a good idea to rename the .EXE with a non-
- executable extension such as .LDR just to be sure.
-
- 2. Preparing the TP dummy procedures
-
- You need to create dummy versions of all the Fortran routines that you want to
- call. They _must_ be declared as "far" routines, either through the use of
- the $F+ compiler directive, or by putting them in the interface section of a
- unit. I'd suggest isolating all of them into their own unit and interfacing
- them.
-
- Each of the dummy routines takes an argument list that corresponds exactly to
- the argument list of the Fortran routine. By default, all Fortran arguments
- are passed by reference, so these should be too, by declaring them as "var"
- parameters. The following list gives corresponding types between the two
- languages:
-
- Fortran TP
-
- INTEGER*2 integer
- INTEGER*4 longint
- INTEGER longint
- REAL single
- REAL*4 single
- REAL*8 double
- DOUBLE PRECISION double
- CHARACTER*n array[1..n] of char
- COMPLEX fort_complex8
- COMPLEX*8 fort_complex8 These types will be declared in
- COMPLEX*16 fort_complex16 the FortLink unit someday
- LOGICAL fort_logical
- EXTERNAL (special - see note below)
-
- Note also that Fortran and TP use different conventions for the order of
- indices in multi-dimensional arrays. For example, the Fortran array
-
- REAL X(10,20,30)
-
- would be declared as
-
- x : array[1..30,1..20,1..10] of single;
-
- in TP. Note also that TP (up to version 5.5, at least) has no facility for
- variable dimensions on arrays: to handle an array which is declared as X(N,M)
- you have to declare X as a one-dimensional array and handle the indexing
- yourself.
-
- Thus a call to the NAG matrix inversion routine F01AAF with Fortran
- declaration
-
- SUBROUTINE F01AAF(A, IA, N, UNIT, IUNIT, WKSPCE, IFAIL)
- INTEGER IA, N, IUNIT, IFAIL
- REAL*8 A(IA,N), UNIT(IUNIT,N), WKSPCE(N)
-
- would be simulated with dummy declarations something like
-
- procedure f01aaf(var a:realarray; { realarray is declared in the
- FortLink unit }
- var ia, n:longint;
- var unit:realarray;
- var iunit:longint;
- var wkspce:realarray;
- var ifail:longint);
-
- and element A(I,J) would be addressed at a[i+(j-1)*ia].
-
- The content of the dummy pascal routine is very simple, and should not be
- varied. If the Fortran routine is a SUBROUTINE, use a definition like
-
- const
- f01aaf_num = 1; { this is the position of F01AAF in the call to CALLTP }
-
- procedure f01aaf;
- begin
- callfort(f01aaf_num);
- end;
-
- If desired, additional instructions can be put before the call to callfort;
- however, no local variables may be declared and no instructions may follow the
- call.
-
- If the Fortran routine is a FUNCTION, what to do depends on the function's
- type. Fortran and TP agree on the convention for returning values up to 4
- bytes (except singles/REAL*4), so callfort can be used for these functions.
- The most common would be a Fortran INTEGER function being declared as a TP
- longint function and using callfort.
-
- However, Fortran and TP use different conventions for other return types, and
- you need to use special calls to do the conversion. If the Fortran routine is
- a REAL*8-valued FUNCTION, the "fdouble" procedure replaces callfort. Use
- "fsingle" for REAL*4 values. For example, for the Gaussian random number
- generator G05DDF, the Fortran declaration is
-
- REAL*8 FUNCTION G05DDF(A, B)
- REAL*8 A, B
-
- and the Pascal declarations are
-
- function g05ddf(var a,b:double):double;
-
- with implementation
-
- const g05ddf_num = 2;
- function g05ddf;
- begin
- fdouble(g05ddf_num); { Note that this is a procedure! }
- end;
-
- Other structured types can also be returned with some care. You have to
- declare the dummy function to be a pointer to the appropriate type, and use
- the "fpointer(procnum)" call to the Fortran routine. TPFORT only reserves
- 8 bytes of space for return values, but larger values can be returned with
- some trickery as described in FORTLINK.DOC in the header for fpointer.
-
- 3. Preparing the TP main program
-
- Once you have your dummy procedure unit set up, you have to make some
- modifications to the main program to link in the Fortran at run-time.
- This is all done in a single call to
-
- function loadfort(prog:string;TPentry:pointer):boolean;
-
- The prog argument should contain a string giving the fully qualified name of
- the Fortran program to be loaded; TPentry should give the address of a TP
- routine taking no arguments, which is going to do all the calculations with
- the Fortran routines. It's necessary to do things this way because the call
- to loadfort switches over to the Fortran stack; TPentry^ and any routine it
- calls must be able to execute there. If LoadFort is successful, it won't exit
- until TPentry^ returns, and it'll give a True return value. If it fails
- for some reason, it'll print a message and return a False value. In this
- case TPEntry^ wasn't executed at all. It's possible to call loadfort several
- times if you want to switch in and out of "Fortran mode", though I don't know
- any reason to do so. Only the first time will load anything, but they'll all
- attempt to switch to Fortran mode. Be sure never to call a Fortran
- routine when you're not in Fortran mode, or you're likely to crash (or at
- least get garbage out). To help determine the current status, the FortLink
- unit interfaces two variables:
-
- fortloaded : boolean; { True indicates Fortran routines are in memory }
- fortsafe : boolean; { True indicates you're in Fortran mode }
-
- If you like, you can put tests of fortsafe into your dummy routines before
- the callfort or fdouble calls, to abort if there's a problem.
-
- PASSING FUNCTION REFERENCES
-
- It is possible to pass function or procedure references to a Fortran routine,
- but it's a little tricky.
-
- The Fortran setup is just the same as for any other kind of routine. Just
- pass the procedure name in CALLTP.
-
- The dummy definition is much the same. Declare the parameter which is the
- routine being passed as type "extval", passed by value.
-
- The main routine then calculates the reference extval using a call to
- "fort_external(procnum)", where procnum is the number of a Fortran procedure
- being passed, or "pas_external(@proc)", where proc is the Pascal procedure
- being passed, and saves the answer in a temporary variable. It passes this
- variable to the dummy routine.
-
- The main bug in this procedure is that fort_external and pas_external
- allocate space for a pointer on the stack, and leave it there. Thus you can't
- execute them in the middle of an expression, or it will get messed up.
- You should call the routine Clean_External as soon as possible after using
- the temporary variable, to restore the stack to normal. Call it once for each
- call you made to fort_external or pas_external. In a loop it's probably safe
- to calculate the temporary once at the beginning and only clean it up once at
- the end. You MUST reassign the temporary variable every time you enter the
- routine that uses it, because its value becomes worthless as soon as you call
- Clean_external or exit.
-
- There's another bug in pas_external - the Pascal routine will be executed
- fully in the Fortran context. In particular, this means all global references
- will reference the wrong data segment, and TP is likely to overwrite registers
- that Fortran expects to have preserved. To fix this up, at the very beginning
- you must call Enter_Pascal, and you must call Leave_Pascal just before
- exiting. This temporarily restores the Pascal context, and saves some
- registers. Note that stack checking has to be disabled in a routine being
- passed this way, since the stack checker makes a reference to the global
- System.Stacklimit, and gets executed before Enter_Pascal.
-
- Calls back to Fortran routines are allowed. Note that only dummy procedures
- and functions defined with callfort may be recursive; functions using fsingle,
- fdouble or fpointer can not be. For example, if the Fortran REAL*8 FUNCTION
- Minimizer gets passed the TP Myfunction to minimize, then Myfunction can't
- call Minimizer, but it can call some other Fortran routine. This isn't such a
- large restriction though, because most Fortran routines don't allow recursive
- calls anyways. (Actually there's a way around this: pass the Fortran
- function through CALLTP several times. If the Fortran routine could handle
- recursive calls normally, then the separate dummy functions will be able to
- call each other.)
-
- This method of passing routines works for TP functions only if they use the
- same function-value passing convention as Fortran. Effectively this means
- only char, integer and longint valued functions may be passed. There's no way
- to call most other TP functions, but it's possible to construct TP functions
- which simulate any Fortran function. Fortran expects the caller to allocate
- temporary space for larger return values and expects the function to put the
- value there. So, to write a TP routine that looks to Fortran as though it has
- the declaration
-
- REAL*8 FUNCTION SUMSQ(N,X)
- INTEGER N
- REAL*8 X(N)
-
- write the header as follows:
-
- function sumsq(var n:longint; var x:realarray; { Mimic the Fortran parameters
- first }
- value_ofs:word):double_ptr; { Always add another word for the
- return address, and return a
- pointer. "double_ptr" is a
- pointer to a double declared in
- FortLink }
-
- See the sample program for the rest of the details.
-
- EXAMPLE
-
- A sample program is contained in the following files:
-
- PSAMPLE.PAS The TP source for the main program
- FSAMPLE.FOR The MS Fortran source for the loader & routines
- FSAMPLE.PAS The dummy definitions for FSAMPLE
-
- Also included is a Borland style MAKEFILE that compiles both parts.
-
- Warning: There's a bug in MS Fortran 5.0 which means the sample program won't
- run on some XT machines. If you crash when you try to run it, read about the
- problem and a patch to fix it in FORTRAN.BUG.
-
- LIMITATIONS
-
- I have real doubts that Fortran I/O will work properly when called from TP,
- but haven't tested it enough to know for sure.
-
- Because Fortran keeps so much data in the stack segment, you might not be able
- to increase the stack size large enough.
-
- FILES
-
- The following files should be included here:
-
- MAKEFILE A Borland style make file for the demo. Just run MAKE.
- TPFORT DOC This file
- FSAMPLE FOR The demo Fortran source code
- CALLTP OBJ The object code to be linked to the Fortran routine
- FSAMPLE PAS The dummy definitions to access the Fortran code
- PSAMPLE PAS The demo Pascal source code
- FORTLINK TPU The unit that handles the linking, compiled under TP 5.5
- FORTLINK DOC The interface section from FortLink.tpu
- FORTRAN BUG Description of a bug and a patch for MS FORTRAN 5.0
-
- COMMENTS AND QUESTIONS
-
- Send any comments and/or bug reports to me at one of the following addresses:
-
- Duncan Murdoch
- 79 John St W
- Waterloo, Ontario, Canada
- N2L 1B7
-
- Internet: dmurdoch@watstat.waterloo.edu
- Fidonet: dj murdoch at 1:221/180.4
- Compuserve: 71631,122
-
- SOURCE CODE
-
- TPFORT makes some use of the Turbo Professional library from TurboPower. It's
- a mixture of Turbo Pascal 5.5 and A86 assembler, and requires the Turbo
- Professional library to recompile. The source code can be obtained from me
- for $125. If you need the source for educational or other non-profit purposes
- and this price is too high, write to me and we may be able to arrange a
- discount.
-
- WARRANTY
-
- There is no warranty of any kind with this program. Use it for free; I hope
- you get some value out of it.
-
- RELEASE HISTORY
-
- 1.2 - added support for floating point emulation.
- 1.1 - added support for limited recursion, and many function return types.
- 1.0 - first release.
-