This Section discusses writing portable FORTRAN code for VICAR applications and describes how to deal with machine dependencies in VICAR code. It does not explain the rules for writing portable FORTRAN; see a reference book on FORTRAN. There are a few allowed VMS extensions to FORTRAN, which are described below.
Portable FORTRAN applications use the rules below when calling the RTL:
The use of EQUIVALENCE to convert among datatypes, especially byte to integer, is a common trick. This practice is not portable. EQUIVALENCE must not be used to convert any data types. EQUIVALENCE is used only to conserve memory. Access the data using the same data type you stored it with.
We recommend the x/zvtrans family of routines for data conversion. See 1.6 Converting Data Types & Hosts. An alternative efficient method for converting between byte and integer uses the functions INT2BYTE and BYTE2INT. Include the file “fortport” in your subroutine. “fortport” is a SUBLIB include; add it to FTNINC_LIST in the imakefile.
INT2BYTE and BYTE2INT assume that the data is in the 0 to 255 range. No bounds checking is performed. The functions are implemented either as array lookups (rather than functions), or as statement functions, so the include file must be present to get the appropriate definition. Example:
SUBROUTINE do_something(b, i) BYTE b INTEGER i INCLUDE 'fortport' b = INT2BYTE(i) i = BYTE2INT(b) RETURN
The FORTRAN standard does not support the use of BYTE arrays as character strings. They must be declared as CHARACTER*n variables. Moreover, descriptors are not used on most machines, so there is no way to tell the difference between a BYTE and a CHARACTER*n variable. Since CHARACTER*n variables have a declared length associated with them, and BYTE arrays do not, any routine expecting a length will get garbage if a BYTE variable is passed in.
The RTL expects that all strings will be CHARACTER*n variables. This applies to all RTL routines, but is especially important for the output routines qprint and x/zvmessage ( x/zvmessage is preferred over qprint for new code). All strings destined for output must be CHARACTER*n variables. READ & WRITE to Strings (page 38) discusses how to do output conversion to create the strings.
While changing output strings to CHARACTER*n is possible, there are many areas in the code, in files and data structures, where strings are stored in BYTE arrays, and it is not practical to change. For that reason, character strings in BYTE arrays may be used if it is embedded too deeply to change. The SUBLIB routines mvcl and mvlc have been provided to convert between BYTE arrays and CHARACTER*n variables. Do not copy data directly to a CHARACTER*n variable from a BYTE array, or vice-versa, since that will fail if there are descriptors.
Subroutines should use CHARACTER*n string arguments. In cases where it is impractical to change, BYTE arrays may be passed. Passing a CHARACTER*n argument where a BYTE array is expected is just as bad as the opposite. If possible, include separate entry points to the routine that accept both types. For example, the routine vic1lab returns data in a character string, while vic1labx returns data in a BYTE array.
To perform input and output conversion, use the FORTRAN I/O package on an “internal file”. An internal file to FORTRAN is simply a CHARACTER*n variable. Give the name of the variable (it may be a substring) instead of the unit number in a FORTRAN READ or WRITE statement. Write to a string and send the string to qprint or x/zvmessage. Do not attempt to write directly to the terminal. Any messages written directly to the terminal will not appear in the session log and maybe printed in unexpected ways due to interaction with VICAR I/O or SAGE.
Here is one way to code the above example (the leading blank is not required in xvmessage):
REAL*8 VRES, VRAD, VTAN CHARACTER*80 MS1 WRITE (MS1,1001) VRES, VRAD, VTAN 1001 FORMAT ('VEL=', F9.2, ' (VRAD,VTAN)=(', F7.2, ',', F9.2, ') M/SEC') CALL XVMESSAGE(MS1, ')
There are three things to note about this example:
The xvmessage routine always prints the first character, with no carriage control. So, all the character position numbers are one less than in the outcon example.
Conversion of data from string to numeric form using READ is similar. Use standard FORTRAN I/O using the CHARACTER*n variable as the internal file. Substrings are much more useful on READ to read only the portion you are interested in. For example, the following call form is typical:
CALL INCON(NPAR, %REF(ALABEL(OFF+I+5:)),PAR,20) BUF(2) = PAR(1) !Frame number
where ALABEL is the input string (already a CHARACTER*n variable), BUF(2) is an integer receiving the value, and NPAR is ignored. The value is an integer in the range 0 to 99 (obtained from the documentation), so this call could be converted to:
READ (ALABEL(OFF+I+5:),'BN,I2') BUF(2)
You may want to make use of the ERR=n clause on reads in order to trap errors such as a decimal point being present in an integer.
VMS has many non-standard extensions to its FORTRAN compiler, which are used frequently in VICAR code. Some of these extensions are widely available in other vendors' FORTRAN compilers, while others are not. Only standard FORTRAN77 code should be used in a portable program.
To find out what is standard and what isn't, refer to the ANSI FORTRAN standard, or the VAX FORTRAN Language Reference Manual . Anything printed in blue in that manual is non-standard FORTRAN and should not be used, except as noted below.
Some of the FORTRAN extensions are so useful that it would be impractical to write VICAR code without them. Fortunately, these extensions are common industry-wide and are available in the FORTRAN compilers for every machine MIPL is interested in. Therefore some extensions to standard FORTRAN77 are allowed. These extensions are listed below.
You should not use any non-standard statements or features that are not mentioned below. If you absolutely have to, then make sure it is isolated in a machine-specific section of code, and provide a means for performing the same function on machines that don't have that extension.
Following are the only permit VMS FORTRAN extensions:
Many current VICAR applications and subroutines have VMS-specific code embedded in them. Some of it is obvious, like a system service call. Some is insidiously difficult to find, like using a double precision floating point value as a single. This works on VMS because the first half of a double value looks like a float. This is not the case on any other machine.
All VMS-specific code must be eliminated or isolated. If the same thing can be done in a portable way, do it that way. If not, then isolate the VMS-specific code and write UNIX code to perform the same function. If the function is useful as a general-purpose subroutine, then put it in SUBLIB. If not, include it with your code. See 18.104.22.168 Machine Dependencies for methods of dealing with machine-dependent code.
An attempt is made below to list the types of VMS-specific code you will run into. This is not and can not be an exhaustive list, as there are far too many potential problem areas that will only be uncovered with more experience. Use this list as examples of what to look out for. You should be familiar with writing portable FORTRAN code; if not then see a standard FORTRAN manual (the black type in the VAX FORTRAN Language Reference Manual is a good source).
This practice is most common (and hardest to find) in subroutine calls, where a program is sloppy about passing data of the correct type. If the subroutine expects a REAL, the value passed in better be a REAL and not a DOUBLE PRECISION. The same is true for INTEGER, INTEGER*2, and BYTE. Be very careful to watch out for this, as it is difficult to find.
There are two choices for dealing with these. The first is to dispense with system-specific calls and do things in a standard way, when possible. This could be achieved by using standard UNIX-style calls that do the same thing (many of which are implemented under VMS), which may imply calling C code that calls the UNIX routines. The second is to isolate the VMS-specific code, and write corresponding code that works on a UNIX system. Both versions could either be included as separate modules in the program itself, or they could be put in a SUBLIB subroutine if it's a capability that could be generally useful. See 22.214.171.124 Machine Dependencies for ways of handling machine-dependent code. In general, system routines should not be called from FORTRAN, as there is little standardization.
The ultimate solution to this problem is to have a consistent naming scheme for RTL internal names that won't conflict with applications. Until this is implemented, however, watch out for conflicts. Even this fix won't help other subroutine packages that MIPL does not have direct control over, such as TAE, SPICE, and X-windows.
Filenames and path names that are embedded in the program should be removed and made available as arguments, both to handle architecture differences and differences in directory structure on other machines. Any code that parses filenames from user input must be aware of the differences and have code to handle each system. Such code should be rare as the RTL does most of the filename parsing; however, it does exist.