12 Mar.87 HvdS R T F / 6 8 K ============= Real-Time Fortran-77 for 68K Processors ======================================= Manual of Compiler and Run-Time Library --------------------------------------- (Version 2.13) H.von der Schmitt Physikalisches Institut Universitaet Heidelberg March 12, 1986 ========================= DRAFT - not yet completed =========================
RECORD OF CHANGES Release Version Changes to former version --------- ------- ------------------------- 25 Oct 83 1.0 20 Dec 83 1.6 Last update of versions 1.x 30 Aug 84 2.0 Versions 2.x start with full F-77, NS16081 support, IEEE 32-bit FP numbers, position independent option, dynamic linkage capability. 10 Dec 84 2.1 In-line subroutines and functions added: _MOVs, _CMPs, OWNER. 01 Mar 85 2.2 Preparation for 68020 with 68881 FPCP. Optimizations in register save/restore; the syntax of the KEEP statement has changed accordingly. Pointer-based COMMON blocks added. Support for a FASTBUS coprocessor added. 25 Mar 85 2.3 Optimizations in array index and statement function evaluation. Corrections to CHARACTER handling. Dynamic storage allocation implemented. 16 Jul 85 2.4 Changed address alignment for 1-byte items. Options SAVE and INTSIZE added. Unified version of runtime library: unformatted, block-oriented I/O and REWIND, BACKSPACE, ENDFILE added. CACHECONTROL statement for 68020 added. RECORD declaration added. Names may contain $ now. In-line routines _SETs added. 27 Sep 85 2.5 Updates in documentation. Residual position-dependence in software floating point emulation of runtime library removed. Native (68000) implementation compatible with APPLE Macintosh. Initialization routine _SYI now returns via A0 instead of via stack. Changes to compiler and library towards zero-length character strings. Format descriptor Iw.n added. Absolute address of COMMON block can be PARAMETER now. NORD style octal constants no longer supported. 20 Oct 85 2.6 Generalizations in pointer-based COMMON blocks. 22 Dec 85 2.7 Run-time variable formats implemented. EQUIVALENCE with status register provided. Improvements in EQUIVALENCE and DATA . 19 Jun 86 2.8 Bugs removed in IAND etc. generic functions. Manual updated. New options CODE=AZTEC and NUMPAR=OFF added. Option CHECK=ON also checks stack limit now at routine entry. CPU=68020 generates position independent code also in the case SIZE=LONG. Bug removed in indexing variable-sized arrays. 11 Oct 86 2.9 Full compatibility with VERSADOS assembler. Improvements in memory indirect addressing for the 68020. Output of version number. Bug removed in Integer*2 Multiply/Divide and string concatenation on the 68020. EXTERNAL now also applicable for variables and Common blocks. Multi-level indirection for RECORDS and pointer-based COMMONs possible now. Integer expressions get definite type when used as actual arguments. 20 Nov 86 2.10 Code generation option for OS9 systems. Cross compiler available now on IBM VM/CMS. Some minor compiler bugs fixed. Static formats checking eliminated. 16 Jan 87 2.11 Changed calling sequence for CHARACTER funct- ions. For OS9, calling sequences compatible with C language. Bug fixed in KEEP statement. RETURN/STOP eliminated before END. EVENTFLAG directive added. Extra OS9 option to switch off C compatibility. ASSIGN statement also usable for dynamic equivalence. Bug fixed in using MOV_s with RECORD arrays. 11 Mar 87 2.12 Effect of EVENTFLAG changed for OS9. New Fastbus hardware subroutines implemented. 12 Mar 87 2.13 Looping mode primitives extended to 32-bit loop count. New option REGUSE introduced to use less address registers within routines.
ACKNOWLEDGEMENT --------------- RTF/68K emerged almost entirely from applications in various physics experiments. Most features (adaptation to run-time environment, code efficiency, portability of the compiler, extensions of the language) are due to the suggestions of various collaborators in these experiments, as well as to efforts of people working with 68K processors in other fields; namely V.Mertens, L.Nellen from TASSO at DESY/Bonn W.Jank, S.Cittolin, M.Demoulin, R.Wilcke, D.Samyn, P.Giacomelli, K.Bos, J.Dorenbosch, B. Haynes, J.-P.Porte from UA1 at CERN K.Einsweiler from UA2 at CERN M.LeVine, C.Watson from E802 at BNL E.Elsen, G.Eckerlin, P.v.Walter from JADE at DESY/Heidelberg A.Lacourt, S.Marchioro, W.v.Ruden from ALEPH at CERN Y.Bertsch from L3 at CERN/LAPP Annecy E.Lesquoy from OPAL at CERN J.Hooper from DELPHI at CERN/Copenhagen M.Freisberg from ELTEC Elektronik/Mainz J.Schanzenbach from Univ. Kaiserslautern. The most painful contributions of the people mentioned above and many others had to go into testing RTF/68K. The author is very grateful for all these contributions and, last but not least, for the possibility to start this project (which is rather unusual in the context of a physics institute). This is due to E.Hilger from the Physikalisches Institut Universitaet Bonn. CONTENTS: --------- 1. RTF/68K AT A GLANCE . . . . . . . . . ok 1. Implementation and availability . . . . . ok 2. Syntax . . . . . . . . . . . . . ok 3. Why Fortran with memory-mapped I/O . . . . ok 2. THE COMPILER . . . . . . . . . . . . 1. Source format . . . . . . . . . . . ok 2. Constants . . . . . . . . . . . . ok 3. Data types . . . . . . . . . . . . ok 4. Declarations . . . . . . . . . . . ok 1. Global and local declarations . . . . . ok 2. PARAMETER declarations . . . . . . . ok 3. Data type declarations . . . . . . . ok 4. DIMENSION declarations . . . . . . . ok 5. COMMON declarations . . . . . . . . ok 6. SAVE declarations . . . . . . . . . ok 7. EQUIVALENCE declarations . . . . . . ok 8. EXTERNAL declarations . . . . . . . ok 9. DATA statements . . . . . . . . . ok 10. INCLUDE directive . . . . . . . . . ok 11. Pointers . . . . . . . . . . . . ok 12. RECORD declarations . . . . . . . . ok 5. Subroutines and functions . . . . . . . ok 1. General overview . . . . . . . . . ok 2. Stack assignment during calls . . . . . ok 3. ENTRY specification . . . . . . . . ok 4. ASSEMBLER entry specification . . . . . ok 5. Statement functions . . . . . . . . ok 6. Intrinsic functions and subroutines . . . ok 7. Obtaining the actual number of arguments . ok 8. KEEP directive . . . . . . . . . . ok 9. INTERRUPT and EVENTFLAG directives . . . ok 10. CPU time required for subroutine calls . . ok 11. Type conversions for integer arguments . . ok 6. Other language elements . . . . . . . 1. Arithmetic, logical, and character string expressions . . . . . 2. Assignment statements . . . . . . . 3. Label and variable ASSIGN statements . . ok 4. DO loops . . . . . . . . . . . . ok 5. IF statements . . . . . . . . . . ok 6. GOTO statements . . . . . . . . . ok 7. Input/output statements . . . . . . . ok 8. PRIORITY statement . . . . . . . . ok 9. CACHECONTROL statement . . . . . . . ok 7. Use of pointers . . . . . . . . . . 1. Subroutine arguments as an example . . . 2. Obtaining and changing addresses . . . . 3. RECORDs and pointer-based COMMON blocks . 4. Dynamic storage allocation . . . . . . 5. Autoincrement/decrement addressing . . . 6. EQUIVALENCE with formal arguments . . . 7. Dynamic equivalence with ASSIGN . . . . 8. Registers and absolute addresses . . . . 1. Fields of application . . . . . . . 2. How to use registers . . . . . . . . 3. Saving/restoring registers . . . . . . 4. How to use absolute addresses . . . . . 9. Output from the compiler . . . . . . . ok 10. Compiler directives . . . . . . . . . ok 11. Compilation error messages and warnings . . ok 3. THE RUN-TIME LIBRARY . . . . . . . . . 1. Main features . . . . . . . . . . 2. Details of specific routines . . . . . 1. Floating point arithmetic . . . . . . 1. Software emulation . . . . . . . . ok 2. Code generated for the 68881 FPCP . . . 3. Code generated for NS16081 FPP . . . . 2. Input/output package . . . . . . . . 3. Dynamic interrupt handling . . . . . . 4. CAMAC support . . . . . . . . . . 5. FASTBUS coprocessor support . . . . . 6. Connection to operating system services . 4. IMPLEMENTATION DETAILS . . . . . . . . 1. The compiler entry points JFOR/CONFOR . . 2. Interactive compiling . . . . . . . 3. Representation of data in memory . . . . 4. Initialization at start of program . . . 5. Mechanism for dynamic linking . . . . . 5. USING RTF/68K ON SOME HOST AND NATIVE SYSTEMS . 1. Description of RTF/68K distribution media . 1. Cross-software package . . . . . . . 2. Native software package . . . . . . . 2. Installation and execution on VAX/VMS . . . 3. Installation and execution on CP/M-68K . . 4. Installation and execution on OS9-68K . . . 5. Flow of information . . . . . . . . . A. APPENDIX . . . . . . . . . . . . . 1. Syntax charts of RTF/68K . . . . . . . 2. Current developments . . . . . . . . 3. Associated documentation . . . . . . .
1. RTF/68K AT A GLANCE ====================== RTF/68K means 'Real-Time Fortran 77 for 68K Processors' and consists of a compiler and a run-time library. It provides full (almost full as of today) Fortran 77 for 68K series microprocessors while fulfilling some additional demands, namely: o compatibility with VAX-11 Fortran (which is a superset of the standard ANSI X3.9-1978), o very good overall run-time efficiency, o easy and efficient applicability to real-time and system implementation problems. The entire 68K processor family is supported: 68000/08/10/12 and 68020. While the latter is object-code compatible with the former group, the compiler optionally produces code which utilizes the additional features of the 68020. A variety of floating point options is covered: the MC68881 FP coprocessor, the often-used NS16081 FP peripheral processor, as well as FP software emulation - always obeying IEEE data format. 1.1 Implementation and availability ----------------------------------- RTF/68K is, for the purpose of easy adaptability, written in a comprehensive 'very-high-level' language (4000 lines of source code for the compiler). Nevertheless, the compiler is portable because a translator generates Fortran code out of this source. Thus, the compiler is also available in Fortran form (the same is true for the translator). RTF/68K runs on VAX and NORD as a cross-compiler and on 68K as a native compiler. RTF/68K produces an intermediate assembly code as output. This is then further processed by one of the available assemblers, e.g. AS68 of CP/M or M68MIL. The connection to linkers, e.g. CUFOM, is thus obtained automatically. Calling sequences and debug information generated by RTF/68K conform to the CERN standard for 68K processors. The RTF/68K run-time library is to 95% written in RTF/68K (2000 lines). The small rest, which provides the interfacing to the environment (hardware setup and operating system, if any) is assembler. Thus, RTF/68K is essentially self-contained and easily adapted to any software environment. The RTF/68K package is available free on distribution tape or Floppy disk - ready to be used with VAX computers running VMS and/or the ELTEC E-3 processor running CP/M-68K (see section 5.). Compiler and run-time system can be adapted to other 32-bit systems with little effort. Note that the CERN products M68MIL and CUFOM are not contained in the RTF/68K package. They can be obtained through CERN, Div. DD (free of charge for non-profit organizations). The following table shows the various 68k and other computer systems RTF runs on. 'Compile' means that the compiler itself runs on the system to generate code for either the same system and/or as a host for other systems. 'Exec' means that code generated by RTF can be executed on the system. - Maybe RTF has been transported to additional systems unknown to the author. Computer Operating system Compile Execute -------- ---------------- ------- ------- VAX VMS X NORD SINTRAN X IBM VM X PCS MUNIX X CSEE UNIX X X ELTEC CP/M X X " OS9 X X MicroSys CP/M X X " OS9 X X Macintosh MacSys X X " Aztec-C X X Motorola VersaDOS X X Aleph EVB OS9 X X Eltec E3 -none- X SAC -none- X MicroSys CPU06 -none- X CPU07 -none- X DataSud CPUA1 -none- X Motorola MVME130 -none- X XYCOM IPROTO -none- X Aleph EVB -none- X Amsterdam FAMP -none- X Robcon CPU020 -none- X 1.2 Syntax ---------- Since VAX computers are widely used in fields where 16/32-bit micros might also find their place, and since VAX-11 Fortran is friendly in that it digests virtually every Fortran, the RTF/68K syntax has been made as similar as possible to VAX-11 Fortran. RTF/68K is best described by stating the omissions and extensions as compared to VAX-11 Fortran. The formal syntax description is found in the appendix which is a copy of the syntax charts given in the ANSI X3.9-1978 standard. Omissions are indicated by blobs which close the 'railroads' for the non-implemented constructs. Extensions are extra rails drawn as fat lines. Omissions: - The input/output statements are not yet fully implemented; namely, direct-access read/write and INQUIRE are missing. - The EQUIVALENCE statement does not automatically increase storage allocation in case of size incompatibility. Moreover, only pairs of two variable names are accepted. - DATA statements may contain only one variable per constants list. - COMPLEX data are not supported, and REAL is restricted to single precision (32-bit IEEE format). - Arrays may have up to five indices only. Some important VAX-11 Fortran features are explicitly implemented, e.g. the syntax of hexadecimal and octal numbers; INCLUDE statements; short (one and two byte) data types; and end-of-line comments. Extensions: + Pointer-based variables are available. + Explicit access to the processor (and co-processor) registers is possible under Fortran variable names. + Explicit access to absolute addresses is possible under Fortran names, allowing easy and efficient memory-mapped I/O. + EQUIVALENCE is allowed with pointers and thus also with formal arguments. + Entire COMMON blocks may be based on pointers and can thus be assigned to memory dynamically. This offers almost everything that is available with 'structs' or 'records' in other languages. For convenience such COMMON blocks may be called RECORDs. + Entire COMMONs may also be placed at absolute addresses, e.g. for memory-mapped I/O to 'structured' sets of hardware registers. + Arguments may be passed by value as well as by reference. + Global variables are possible; e.g., all COMMON blocks could be stated once at the beginning of a file and are then valid for all routines that follow. + The data types INTEGER*1,*2 are supported. These are useful for accessing some hardware devices, and may as well serve for saving the famous factor of two or four, e.g. in big pattern recognition tasks. + 'Strong typing' may be forced by IMPLICIT NONE. This is highly recommended as a big step towards writing correct programs. In any case, warnings are given by the compiler if undeclared variables are definitely read-accessed. Misspelt names on the left hand side in assignment statements, however, require IMPLICIT NONE to be detected. + Besides DO index=.. ENDDO and DO WHILE, DO UNTIL and DO FOREVER are available. + The number of routine arguments may vary and can be obtained inside the routine by CALL NUMPAR(num). If this statement is omitted, however, and the actual number of arguments differs from the declared one, a run-time message is issued. + The generated code is position-independent. + All routines are re-entrant. Recursive calls are possible. Libraries are sharable. + Subroutines can serve as interrupt handlers without any software overhead between interrupt vector and routine entry. Also, they may be used as event flag handlers known under some operating systems. + Integer constants may be written in decimal, hexadecimal, octal, binary, and ASCII form. + Dynamic storage allocation is provided with the ALLOCATE ... ENDBLOCK construct. + All the ISA bit-handling functions are implemented with fast in-line code. + Special in-line subroutines and functions provide for efficient block-moves, bit handling, and semaphore control for system implementation. + Embedded assembly code is possible. + 'Hardware subroutines' (implemented as special coprocessors) are supported with generation of fast in-line code. Since some of these concepts are unusual in Fortran (but look a bit like Pascal or C), a few examples might illustrate their use. Remember that without the additional features used here you would have to write it in assembler or use external assembler routines (the calling of which costs extra time). Note that some of the examples below are intentionally quite tricky. equivalence (VIOR,'FFF980'X) ! this states the address of integer*2 VIOR ! .. the hardware register VIOR=0 ! issue command STATUS=VIOR ! .. to read hardware status integer function FAK(N) ! this is the most obsolete if(N.le.1) then ! .. example of recursion FAK=1 else FAK=N*FAK(N-1) endif end subroutine TERMIO ! this routine is activated implicit integer*1 (a-z) parameter (RxD_ready=3) equivalence (STAT,'FF2000'X), * (DATA,'FF2001'X) ! .. whenever interrupt with interrupt 122 ! .. vector #122 occurs if(btest(STAT,RxD_ready)) then I=I+1 BUF(I)=DATA ! .. and then reads some hardware ... logical function THERE(ADDR) ! tests if address ADDR is there integer*2 ADDR,JUNK,SSAVE,S ! (in supervisor mode) integer*4 IVEC(0:255),IVECSAV equivalence (IVEC,0),(S,SR) IVECSAV=IVEC(2) ! save old vector contents SSAVE=S ! .. and status register assign 1000 to IVEC(2) ! link bus errors to label JUNK=ADDR ! test if address responds THERE=.true. goto 1 1000 THERE=.false. ! go here if bus error 1 IVEC(2)=IVECSAV S=SSAVE ! the UNLK instruction will end ! .. clean up the stack program Dyn_Storage real @FIELD(0:3,N,N) ! dynamic storage allocation accept *,N ... allocate 4*4*N**2 for FIELD ! allocate proper number FIELD(0,2,3)=PHI ! of bytes for FIELD ... endblock allocate * for FIELD ! exactly the same as above FIELD(0,2,3)=PHI ! * allocates amount stated ... ! in declaration end block The next example shows how a pointer-based COMMON or RECORD may be used to describe data structures which don't have a fixed location in memory. In this example, the entire structure is even a formal subroutine argument. subroutine ANALYZE(EVENT,...) record /EVENT/ TOF(32),CALO(128),PAT,CNT(6) --or-- common /@EVENT/ TOF(32),CALO(128),PAT,CNT(6) integer TOF,CALO,PAT,CNT ... if(iand(PAT,'11'O) .ne. 0 .and. CALO(2).ge.MIN) ... This is an example where a COMMON block is bound to a fixed absolute address: common/'100000'X/ VMX_SPACE(0:'3FFFF'X),EXTRA_VMX('40000'X) RTF/68K has been used to implement a wide range of different applications, such as the FASTBUS routine package, Mini-GD3, CAMAC readout tasks, a complex VME multiprocessor system for driftchamber readout and on-line analysis, the RTF/68K runtime library, and last but not least the RTF/68K compiler itself systems. 1.3. Why Fortran with memory-mapped I/O --------------------------------------- Given that RTF/68K is made primarily for real-time applications of moderately to very complex microprocessor systems with close connections to hardware ('embedded systems'), it may be worthwile to summarize the arguments for specific extensions offered by RTF/68K. The most crucial and least usual extension is the provsion for memory mapped I/O, and the arguments for this are as follows. a) In the environment of physics experiments, the problem-oriented part of the application (e.g. analysis algorithms) usually will be, or is already, programmed in Fortran. b) One of the essentials of microprocessors with big address space is the simplicity and efficiency of memory-mapped I/O. c) The access to the hardware is only in simplistic cases a small subset of the whole application. Instead, a proven way to keep the hardware simple is by interaction with a programmable device like a microprocessor - hence the name 'embedded system'. The processors (and their programs) act as an interface with one leg deep inside the hardware. d) Complete hardware-independence of the application program is an illusion for this reason. There is no argument why only programs that are anyway unreadable (assembler) should be allowed to be hardware-dependent, unless one wants to maintain admired 'experts' and grateful 'users' that are supposed to understand nothing and hence are not allowed to really exploit the system. e) The access to static absolute addresses is very simple to understand for normal (not 'real-time') Fortran users because Fortran always had a 'hardware-dependent' component - the possible knowledge of how variables are arranged in memory. This access fits directly into the ordinary Fortran syntax. The extension to dynamic addressing by pointers is well known from other high-level languages. Both concepts are straightforward to implement into Fortran. f) A memory-mapped software interface to the hardware is by orders of magnitude faster than even the optimally coded usual subroutine/call interface - just because the processor hardware is able to do it directly in one instruction. g) In summary, Fortran with extensions like in RTF/68K is suited for problem and system programming. It allows to write the entire application in a single high-level language. Non-experts find an easy way to system programming. In fact, at least one very complex readout and analysis system with 34 tightly coupled (shared memory) VME processors has been written in RTF/68K. It was not necessary to use assembler statements. None of the participants started as an expert in this project, and yet is was finished in a very short time.
2. THE COMPILER --------------- This section gives a brief description of the RTF/68K syntax. Mainly the deviations (extensions and omissions) from standard Fortran-77 are reported here. As a reference we take the VAX-11 Fortran which is available on VAX systems and is an extension to standard Fortran-77 insofar as it supports DO-ENDDO, DO WHILE, IMPLICIT NONE etc. as well as compatibility with old Fortran (Hollerith, hexadecimal and octal constants). The reason is that VAX computers will very often act as hosts for, or will be used in close collaboration with, 68K microprocessor systems. The complete definition of the syntax of RTF/68K may be found in the appendix. The deviations from the ANSI standard X3.9-1978 (extensions and omissions) are obvious from the syntax charts reproduced there. 2.1. Source format ------------------ The RTF/68K source code is essentially free-format. Some formatting is required, however, in order to be compatible with the Fortran conventions. The following are extensions to the standard: a) Source lines may start in any column except column six (conflict with marker for continuation lines). They should also not start in column one (possible confusion with comment line marker). b) Continuation lines are marked in column six with a non-blank and non-zero character as usual. However, this mark may also be omitted (continuation is always obvious from context). c) Comment lines are marked by a "*" or "C" or "c" in column 1. "D" and "d" also denote comment lines unless the DEBUG=ON option is selected; then they are proper code lines. d) More than one statement may be given per source line. They must be separated by blank(s), or optionally by ";" for better readability. e) End-of-line comments may be given. They must be preceded by "!". f) Source lines may be up to 132 characters long. Thus, source line numbering in columns 73-80 is not available unless the compilation option LENGTH=72 is chosen. g) Lowercase letters are accepted and shifted to uppercase before interpretation unless they occur within strings. h) Variable names may contain the special characters underline "_" and '$'. Thus, e.g. Start_Time and SYS$SYSTEM are legal names. For determining the implicit data type, the first alphabetic character is taken. i) Names may have any length. All characters are relevant to the compiler. For names belonging to external objects (COMMON blocks, subroutines, functions), length restrictions might arise from the subsequent assembler and linker steps. j) Lines beginning with a "+" in column one are transferred to the output without changes (just stripping off the "+"), thus allowing for embedded assembly statements. The following remarks also refer to the source format, but deal with deviations from the standard, rather than extensions. a) Blanks are meaningful in RTF/68K. For example, DO100 will not be recognized correctly because a blank is required between keyword and label. GO TO and GOTO, END DO and ENDDO, etc. are however explicitly considered. b) The first TAB character met in a source line, if it is within the first eight columns, steps to column 10. Later TABs are converted to blank. The purpose of handling blanks in the described way is to eliminate some of the huge redundancy of the Fortran language; namely, the kind of redundancy which gives a very wrong semantic meaning to statements with small misprints which are yet formally correct. Maybe you know the example of the Mariner mission which failed due to the Fortran statement DO 10 I=1.3 .... where of course a loop was the programmer's intention. But "DO 10 I" is a valid variable name in the standard and was interpreted in that way (because of the "."). - It is invalid and would have been flagged as an error in RTF/68K. 2.2. Constants -------------- According to the data types discussed in the next chapter, there are four types of constants: Integer, Floating Point, Boolean, and Character String. Constants may be placed directly in the code, or they can be accessed by names assigned to them with PARAMETER statements (see 2.4.2.). Not much needs to be said about the latter three types of constants; examples are: Floating Point: 123.456 0.00000123456E8 123456e-3 Boolean: .TRUE. .true. .FALSE. .False. Character String: 'this is the same thing' 22Hthis is the same thing !..but not recommended 'THIS IS not THE SAME THING' Integer constants may be decimal, bit-pattern, or ASCII oriented: 1 decimal; value fits into one byte -128 256 -330 (two bytes) -32768 65535 2000000000 (four bytes) 'FFFF'X hexadecimal (two bytes) '1000'X 'ABCDEF'X (four bytes) '377777'O octal (four bytes) '111'O (two bytes) '101010'B binary (one byte) '1000110001'B (two bytes) 'ADAM'A right-justfied ASCII (four bytes) '0'A (one byte) Integer constants in a context where Floating Point is required are converted at compile-time. Use of integer constants requiring more bytes than are available in the context is flagged as an error; e.g. I1=1000 where I1 is an INTEGER*1 variable. Note that the types Arithmetic (i.e. Integer or Floating Point) Boolean Character string must not be intermixed, as opposed to VAX Fortran where Integer and Boolean may be mixed. RTF/68K would flag this as an error. Integer, floating point, and logical constants used as subroutine arguments are passed as 4-byte entities, unless word-size modifier intrinsic functions or the option INTSIZE=2 are used (see 2.5.6. and 2.10.). 2.3. Data Types --------------- The following data types are available in RTF/68K. a) Integer data types: INTEGER denotes 4-byte signed integer values by default or 2-byte signed integer values with option INTSIZE=2 INTEGER*1 denotes 1-byte signed integer values INTEGER*2 " 2-byte " " " INTEGER*4 " 4-byte " " " DOUBLE INTEGER " 4-byte " " " BYTE " 1-byte " " " NOTE that the integer data types denote always signed values. In operations with operands of different length (e.g. addition of a INTEGER*4 value and an INTEGER*2 value), the shorter lenghts are sign-extended to the longest length occuring. This may cause undesired effects in non-arithmetic operations sensitive to bit patterns (e.g. IOR, IAND etc.). b) Floating point data types: REAL denotes 4-byte IEEE floating point values REAL*4 " 4-byte " " " " ** REAL*8 " 8-byte " " " " ** DOUBLE PRECISION " 8-byte " " " " ** DOUBLE REAL " 8-byte " " " " NOTE that the 8-byte values are not yet included in RTF/68K. They are treated as 4-byte values and a warning is given. c) Boolean data types: LOGICAL denotes 4-byte boolean values by default or 2-byte boolean values with option INTSIZE=2 LOGICAL*1 denotes 1-byte boolean values LOGICAL*2 " 2-byte " " LOGICAL*4 " 4-byte " " Boolean data are determined in any case by one byte only; the most significant byte in memory or the least significant byte in registers, resp. An all-zero byte means .FALSE., any other value, preferrably all ones, means .TRUE. This is in accordance with the "set according to condition" instructions of the 68K family. d) Complex data types: ** COMPLEX denotes a pair of 4-byte IEEE FP values ** COMPLEX*8 " " " 4-byte " " " NOTE that complex data are not yet included in RTF/68K. e) Character string data types: CHARACTER denotes 1-byte ASCII character values CHARACTER*n " n-byte " " " CHARACTER*(*) " m-byte " " " where m is determined by the calling routine for formal arguments; else m=256 by default. Character variables are represented in memory just as the ASCII characters, without any additional information. Thus EQUIVALENCE of character and other variables is possible. Whenever a character variable is accessed, however, a descriptor containing the necessary extra information is formed. Note that character constants, other than variables, are represented as null-terminated strings. Details on the memory representation of the various data types may be found in chapter 4.4. 2.4. Declarations ----------------- Declarations are the field where most of the deviations - extensions and omissions - between RTF/68K and the standard show up. The reason is that on one hand, the aim was to introduce the extensions as far as possible by means of declarations, rather than by modifications in the executable statements. This is a rather obvious method and offers a way to enhance existing programs mainly by adding declarations. On the other hand, the most irregular structures of Fortran are found among the declarations. Some of these structures, e.g. recursion of EQUIVALENCE statements, have been omitted because of their irregularity. Other omissions (lack of some data types) are intended to keep the entire RTF/68K system small, with the on-line applications in mind. 2.4.1. Global and local declarations - - - - - - - - - - - - - - - - - - - A nice way to repeat a common set of declarations (e.g. COMMONs) in many subroutines is with INCLUDE files. Apart from the fact that this is not standard Fortran, the widely used implementations support it. RTF/68K also does. The other way to introduce globally valid declarations is normally offered by block-structured languages like Pascal etc. Fortran knows one level of declarations: the subroutine level. Subroutines have no block level above them (apart from the semantic binding of COMMONs which is recognized only during linking time) nor below them (apart from statement functions). The advantage is that subroutines can be compiled separately. Disadvantages are that conflicting declarations cannot be recognized by the compiler, and that identical declarations have to be repeated in different subroutines as mentioned above. Block structured languages allow for several, nested block levels. The scope of validity of a declaration is the block where the declaration is made and all lower-level blocks contained in it. If a variable already declared is re-declared on a lower block level, the latter declaration is valid within the lower block. RTF/68K ranges between Fortran and block-structured languages: 1) It accepts usual Fortran. The declarations made within subroutines are called 'local declarations' in what follows. 2) It allows for one level above the subroutine level: the 'global declarations'. These are usual Fortran declaration statements which are written once in front of a set of subroutines in a file. The global declarations are valid within all of these subroutines. 3) In order to exclude an obvious source of errors, it is not allowed to re-declare global variables locally. 4) In order that the compiler can recognize global declarations and distinguish them from local declarations within a main program, the PROGRAM statement is mandatory. 5) There is also one extra level of declarations below the subroutine level which allows for dynamic storage allocation (see 2.7.4. for the ALLOCATE-ENDBLOCK construct). Some examples might illustrate the use of global declarations. (a) 'Standard' example. Without use of global declarations, a set of routines would look like: PROGRAM P \ INCLUDE 'I.FOR' | INTEGER II,JJ | Main program REAL ... | ... | END / SUBROUTINE S \ INCLUDE 'I.FOR' | DATA A /1.0/ | Routine 1 ... | END / FUNCTION F(X) \ INCLUDE 'I.FOR' | Routine 2 ... | END / where the INCLUDE file contains e.g. COMMON /A/ A,B,C COMMON /S/ I,J,K PARAMETER (PI=3.14) Using global declarations, one can write it as follows: INCLUDE 'I.FOR' Global declarations PROGRAM P \ INTEGER II,JJ | REAL ... | Main program ... | END / SUBROUTINE S \ DATA A /1.0/ | Routine 1 ... | END / FUNCTION F(X) \ ... | Routine 2 END / Notes: 1) The PROGRAM statement is required in RTF68K. Without it, the local declarations of P would be taken as global, and the first executable statement of P would be flagged as an error. 2) Local declarations stay within each routine. 3) Global declarations of course need not be contained in INCLUDE files. The contents of I.FOR could be given in place of the INCLUDE statements in the example above. 4) The global variable A has been preset with DATA locally. This is possible but bad style. The preferred method would be to give the DATA statement globally. (b) In the above example, A is a global variable. Redeclaration of A like (referring to the example above) FUNCTION F(X) DIMENSION A(100) ... END is illegal and flagged as an error. (c) Global variables should be normally in COMMON blocks, but this is not necessary in principle. Consider, again referring to the example (a), the following contents of I.FOR: REAL A,B,C INTEGER I,J,K PARAMETER (PI=3.14) Still, the variables A,B,C,I,J,K and the constant named PI are globally defined. However, the variables reside now not in the memory for COMMON blocks, but in the space drawn from the stack when the program is initialized. Thus, the connection to separately compiled files which are linked with the example file is no longer as obvious as would be the case for COMMON blocks. In fact, non-COMMON global variables from separate files will normally overlap in memory, unless special care is taken (see chapter 4.4.). The use of such global variables should thus be restricted to system libraries. (d) Also other kinds of declarations may be written in the global declarations part, e.g. the special ones EQUIVALENCE (ILOOP,D7) KEEP D7 The effect is that ILOOP is now a globally declared variable which resides in register D7 for speed, and is saved/restored on each routine's entry/return (see chapter 2.5.8.). Apart from the scope of a declaration, the scope of validity of the value carried by a declared variable has to be considered. The former has to do with the static nesting of blocks as written down in the program source, the latter deals with the dynamic nesting of blocks during execution. Fortran usually disallows recursivity, i.e. a subroutine cannot call itself (neither directly or indirectly through another routine). At least, results of such calls are inpredictable. On the other hand, the standard says that the values of local variables of a subroutine are lost when you leave the routine and enter it again, unless the variables are in COMMON or SAVE. Nevertheless, many Fortran implementations leave those values unchanged. RTF/68K offers an option (SAVE=ALL) to guarantee this, too. Now note the following: recursivity has the consequence that the values of local variables must be lost when leaving the routine. Re-entrancy, which is a requirement in interrupt-driven real-time environments, is a more general case of recursivity. Thus, if you want to use RTF/68K in the field it was made for, the values of local variables are for sure lost when leaving a routine (COMMON or SAVE variables must be used with great care in cases where re-entrancy is required). The reason is of course that space for local variables has to be allocated on the stack, and this space is re-used for other variables after leaving the routine. One further consequence is on DATA statements: variables which are preset with DATA cannot be on the stack. They must be SAVEd or in COMMON. Routines which alter the values of DATA variables are not reentrant. If the values are not altered, the variables are constants and should better be declared with PARAMETER. 2.4.2. PARAMETER declarations - - - - - - - - - - - - - - - PARAMETER declarations assign names to arithmetic, logical, or character string constants. These names can be used in place of the constant, e.g. also as array bounds in DIMENSION declarations. They offer the possibility to define a constant, which is used in many places within a program, in one central place only. Whenever a value is really constant during program execution, PARAMETER is very much preferred over DATA statements. PARAMETER statements may be given globally or locally. Like on the VAX, PARAMETER statements may be written with (standard) or without parentheses, e.g.: PARAMETER (pi=3.14159,max=500,assert='YES') or PARAMETER pi=3.14159,max=500,assert='YES' The data type associated with the name is always the data type of the constant, e.g. REAL for pi, CHARACTER*3 for assert in the example above. Please refer to 2.2. for the various forms of integer constants. One may also state the data type in addition with a type declaration, but only before the PARAMETER statement. The type declaration, if given, must agree completely with the constant used in the PARAMETER statement. E.g., REAL pi; INTEGER max; CHARACTER*3 assert PARAMETER (pi=3.14159,max=500,assert='YES') is legal, while REAL pi,amax; CHARACTER*5 assert PARAMETER (pi=3.14159,amax=500,assert='YES') contains two errors. 2.4.3. Data type declarations - - - - - - - - - - - - - - - The data types available have already been listed in 2.3. above. Consider <type> to be one of INTEGER, INTEGER*1, INTEGER*2, INTEGER*4, DOUBLE INTEGER, BYTE REAL, REAL*4, REAL*8, DOUBLE PRECISION, DOUBLE REAL LOGICAL, LOGICAL*1, LOGICAL*2, LOGICAL*4 COMPLEX, COMPLEX*8 CHARACTER, CHARACTER*n, CHARACTER*(*). Then a data type declaration has the form ([..] denotes optional syntax elements): <type> <dcl>[,<dcl>...] where <dcl> is one of: <name> [<arraybounds>] or @<name> [<arraybounds>] . The former is the standard Fortran-77 variable or array declaration, while the latter declares a pointer-based variable or array (@ is the symbol for 'pointer to' or 'address of'). <name> is a usual Fortran name of any length and may, apart from uppercase letters and digits, also contain lowercase letters, underline, and dollar characters. Pointer-based variables have their address assigned or changed during execution of the program; please refer to 2.4.11. and 2.7. for further explanation. Note here that in case of pointer variables or arrays of any data type, four bytes are always reserved for the address of the variable. No space is allocated for the values. The data type, and accordingly the word size, is an attribute of the value of a variable and has nothing to do with its address. 2.4.4. DIMENSION declarations - - - - - - - - - - - - - - - This declaration is in principle redundant in Fortran because array bounds may also be given together with other declarations, e.g. of data type. The syntax is: DIMENSION <vdcl>[,<vdcl>...] where <vdcl> is one of <name> <arraybounds> or @<name> <arraybounds> . The former is the standard Fortran-77 array declaration, while the latter declares a pointer-based array. The array bounds have the usual form: (<bounds>[,<bounds>...]) with the following choices for <bounds>: <integer constant> or <integer constant:integer constant> or <integer variable> . In RTF/68K, up to five <bounds> are permitted; i.e. tensors of rank five can be handled. The third of the above forms of <bounds> provides variable array bounds. While normally restricted to formal arguments of routines, RTF/68K allows variable bounds also for pointer-based arrays; see the example of chapter 1.1. above. The mechanism is clear: bounds may be variable because in both cases memory is not allocated during compilation, but only during program execution. 2.4.5. COMMON declarations - - - - - - - - - - - - - - As in the standard, COMMON declarations serve are used to make storage blocks accessible globally for several routines. Storage allocation is usually performed by the linker. In addition, RTF allows to allocate storage under the programmer's control: either to a fixed address known when writing the program or to a variable address known only during execution. For the latter, see also the RECORD declaration below. The syntax of COMMON declarations is on of COMMON <dcl>[,<dcl>...] COMMON // <dcl>[,<dcl>...] COMMON /<base>/ <dcl>[,<dcl>...] . See 2.4.3. above for <dcl>. The first and second forms both denote 'blank COMMON'. The latter form denotes 'named COMMON' as usual, but in RTF is also used for 'absolute COMMON' and 'pointer-based COMMON', depending on the from of <base> which is one of: <name> or <integer constant> or @<name> . The first form results in standard 'named COMMON' and space is allocated by the linker which will normally give an absolute base address to the COMMON block at link-time. If the option COMMON=INDIRECT is chosen, the <name> form has the same effect as the @<name> form (see below). Still, the operating system takes care of space allocation, but this can then be delayed until load-time (necessary e.g. on the MacIntosh). The second form results in 'absolute COMMON'. Here, the programmer determines the base address of the COMMON block at programming time. The block can by this means placed into special hardware, e.g. an extra fast memory module. The integer constant may also be a PARAMETER declared before the COMMON declaration, e.g. Parameter (VectorBase='100000'X) Common /VectorBase/ IVector(0:255) or just Common /'100000'X/ IVector(0:255) . The system gets no notice about absolute COMMONs. The third form denotes 'pointer-based COMMON'. Here, the allocation of space for the COMMON block can be made more dynamically - at load-time or at run-time, depending on how the pointer is declared. If @<name> is not declared explicitely somewhere, then the system will take care of space allocation at load-time. For the user, this looks much like ordinary named COMMON, apart from the fact that the base address can still be modified at run-time. The <name> form with option COMMON= INDIRECT has the same effect as the @<name> form without explicit pointer declaration. In both cases, the base address is held in a storage word addressed relative to the program counter at the end of a program module. Thus, care must be taken when changing this storage word: the base address will be altered only in this program module. Other, separately compiled modules will not be affected (an appropriate waring is given by the compiler). Briefly speaking, @<name> without explicit pointer declaration is meant for load-time allocation of space as required by some operating systems, and not primarily for run-time allocation under the user's control. If, however, the pointer is declared explicitely before the COMMON declaration, the operating system does not get notice of the COMMON block, and the user has to allocate space at run-time. This is done with an assignment statement for the pointer, e.g.: Integer @Struct,CpuIndex,CpuBase Common /@Struct/ Xlow(1000),Xup(1000) ... @Struct=CpuIndex*'800000'X+CpuBase Xup(I)=XLow(I)+123.4 This can be applied for shifting data structures around in memory. The purpose of the RECORD statement (see below) is exactly the same. The 'scope' of the space allocation is the same as the scope of the declaration of the pointer; i.e. if @Struct in the above example is a local variable of a routine, the entire pointer-based COMMON is defined only in this routine. On the other extreme, if @Struct is itself in an ordinary COMMON block, then the definition is global - i.e., the effect of the @Struct=... statement is global. 2.4.6. SAVE declarations - - - - - - - - - - - - - According to the standard, the SAVE declaration is used to maintain the values of variables when leaving and reentering routines. For many Fortran implementations, this is achieved even without SAVE. In RTF, however, and in full accordance with the standard, SAVE is required if values of local variables of a routine are to be maintained during leave from that routine. Without SAVE, those variables would reside on the stack for re-entrancy and hence their values are lost on exit from the routine. COMMON variables are always maintained. The syntax of the SAVE declaration is: SAVE <dcl>[,<dcl>...] or SAVE /<name>/ . Note that local variables can be preset with DATA statements in RTF only if they appear in a SAVE declaration. There are no such restrictions for COMMON variables. A global SAVE for all local variables can be enforced with the option SAVE=ALL. This may be useful when porting programs from the usual (but non-reentrant) Fortran environments to RTF. 2.4.7. EQUIVALENCE declarations - - - - - - - - - - - - - - - - EQUIVALENCE declarations are the Fortran programmer's way to control storage allocation of variables. In the standard, they can be used to control memory addresses of variables relative to each other; e.g. variables of different data types and formats can share the same storage. As such, EQUIVALENCE is considered bad practice. In RTF, EQUIVALENCE can (and is meant to) be used to control the absolute positions rather than the relative positions of variables, although the latter is also possible. The syntax of EQUIVALENCE in RTF is: EQUIVALENCE (<name>,<base>) or EQUIVALENCE (@<name>,<base>) where <name> is the name of a variable to which storage is not allocated by other means than the EQUIVALENCE statement (e.g., COMMON variables not allowed here). <base> may be one of <variable ref> or <integer constant> or <register name> or @<name> . <variable ref> is the name of a simple variable, array, or an array element reference. This form is used for the standard EQUIVALENCE statements. Note that only pairs of arguments may appear in RTF. <integer constant> is used to give a fixed absolute address to a variable. This is useful for accessing special hardware locations easily, e.g.: Integer*1 IOReg Equivalence (IOReg,'FF0021'X) ... IOReg=15 . <register name> is used to force a variable in a register of the CPU or a coprocessor. The following symbols are reserved words when appearing as the second argument of EQUIVALENCE: D0, D1, D2, D3, D4, D5, D6, D7 for the data registers of the 68K processor A0, A1, A2, A3, A4, A5, A6, A7 or SP for the address registers of the 68K processor F0, F1, F2, F3, F4, F5, F6, F7 for the floating point registers - valid for the MC68881 coprocessor, the NS16081 processor, and software emulation SR for the status register of the 68K processor. Variables of any data type (1, 2, or 4 bytes long) except CHARACTER, as well as pointers, may reside in the data or address registers. Typically, values should be kept in data registers, and pointers in address registers. The floating point registers are useful only for variables of type REAL. Variables EQUIVALENCEd with the status register must be declared INTEGER*2. The purpose of putting values or pointers in registers is twofold: 1. a considerable gain in speed for frequently used variables or pointers, 2. a way of interfacing e.g. with operating systems that expect or deliver parameters in registers. Note that registers used by the programmer via EQUIVALENCE, unlike registers allocated automatically by the compiler, are not saved on entry and restored on exit from the routine that uses them. The KEEP directive (see below) has to be used for this purpose if it is desired to keep register contents unaltered for the outside world. This is a must for interrupt service routines and normally true also for usual routines. @<name> is used for EQUIVALENCEing with pointers. This is useful e.g. when making an equivalence with a formal argument which is passed by reference. Note that whenever variables are pointer-based, only their pointer may be used in EQUIVALENCE because the allocation of variables are not known at compile time. Examples of EQUIVALENCE: 1 ...the standard way: INTEGER*1 I1(4); INTEGER*4 I4 COMMON /XX/ I4 EQUIVALENCE (I1,I4) 2 ...with an absolute address: INTEGER*2 RegisterFile(0:15) EQUIVALENCE (RegisterFile,'3400006'O) ... RegisterFile(0)=1 3 ...with data registers: Function Dot(N,A,B) Integer A(N),B(N),N,I,S Equivalence (I,D7),(S,D6) Keep D7,D6 S=0 Do I=1,N S=S+A(I)*B(I) EndDo Dot=S End 4 ...with address registers in addition: Function Dot(N,A,B) Integer A(N),B(N),N,I,S,AA(N),BB(N) Equivalence (I,d7),(S,d6),(@AA,a4),(@BB,a3) Keep a3,a4,d6,d7 @AA=@A @BB=@B S=0 Do I=1,N S=S+AA(+)*BB(+) EndDo Dot=S End Note the special array indexing used in the formula. Writing S=S+AA(I)*BB(I) or S=S+A(I)*B(I) would both give exactly the same result, but the 'autoincrement' form chosen is by far the fastest. It is only available if the pointer to the array resides in an address register. The compiler generates the post-increment mode of addressing of the 68K processor for it. The code generated from S=S+AA(+)*BB(+) is (option CPU=68020): MOVE.L (A4)+,D2 MULS.L (A3)+,D2 ADD.L D2,D7 . Using as an array reference e.g. BB(-) results in the pre-decrement addressing mode. In the above example, it is in principle not necessary to copy the pointer of array A into an address register. The first two formal arguments of routines are copied to address registers automatically by the compiler in order to have fast access to them (unless option REGUSE= OFF is used). Thus also the special addressing mode is available for A. But beware: the pointer is of course changed by using the special addressing mode! This does no harm in the example because the pointer is no longer used afterwards. 5 ...with floating point registers: Equivalence (X,F7),(X2,F6) Keep F6,F7 ... X2=X**2 V=exp(X2-sin(X2)*cos(X)+sinh(X)) The code generated for the above formula is in the case of the 68881 coprocessor (option FLOAT=HARD): FMOVE.X FP6,FP0 FSIN.X FP6,FP1 FCOS.X FP7,FP2 FMUL.X FP2,FP1 FSUB.X FP1,FP0 FSINH.X FP7,FP1 FADD.X FP1,FP0 FETOX.X FP0,FP1 FMOVE.S FP1,<V> The advantage is obvious from this code: the coprocessor is utilized optimally. Apart from fetching the result, no operand moving between CPU and coprocessor takes place. 6 ...with the status register: Equivalence (CpuStatus,SR) Integer*2 CpuStatus ... If(CpuStatus.lt.0) ... ! check if in TRACE mode Note that access to the status register is only allowed in supervisor mode. The PRIORITY=... statement (see below) can be used if only the priority part of the status word should be affected. Also note that there is no KEEP for the SR. Explicit save/restore to e.g. a local variable must be used for temporary changes to the status register. In interrupt service routines, the status before interrupt occurance is restored automatically on routine exit. 7 ... with formal arguments: Subroutine S(X) Real X(100) Integer IX(100) Equivalence (@IX,@X) ... Subroutine T(%VAL(Y)) Real Y Integer IY Equivalence (IY,Y) ... Note that Equivalence (IX,X) which semantically looks the same would not be accepted by the compiler. Only items which occupy memory with addresses known at compile time can be equivalenced; in the first example above, those items are the pointers to X and IX, while in the second example, the values of Y and IY. As can be seen from the examples above, registers used by the programmer with EQUIVALENCE should be allocated starting from the upper end of the available sets of registers, i.e. starting with D7, D6... for the data registers, A4, A3... for the address registers, and F7, F6... for the floating point registers. A7, A6, and A5 are reserved for use by the system (thus there is often a shortage of address registers). The compiler starts allocating registers from the lower ends of the sets. If a clash between user's and compiler's allocation occurs, a warning is given. The restrictions in the standard use of EQUIVALENCE will be removed in a later release of RTF. 2.4.8. EXTERNAL declarations - - - - - - - - - - - - - - - EXTERNAL declarations are normally used to state that a name refers to a subroutine or function in cases where this is not obvious from the context, i.e. when such names are used as actual arguments of routines. This is also the case in RTF. In addition to that, also variables and even entire COMMON blocks can be made to refer to external objects (e.g., data which are located at an entry point of an assembler routine). This corresponds to the IMPORT and EXPORT variables known in Pascal. In order to distinguish those variables from standard EXTERNALs and to provide an access method by the standard linkers, they have to be pointer-based, i.e. their names have to be preceded by '@' in the EXTERNAL declaration. Examples: External @Iarr Integer Iarr(20,20) ... Iarr(5,5)=0 External @Com,@I Common /@Com/ X,Y,Iz(100) ... X=Y+Iz(I) In these examples, the addresses of the external variables (Iarr and X,Y,Iz,I) are equated by the linker to entry points in other code modules. 2.4.9. DATA statements - - - - - - - - - - - - DATA statements belong to the declarations part of a routine and are used to preset variables with values once prior to execution of the user's code. Values may be changed afterwards; if not, i.e. if the variables are in fact constants, PARAMETER declarations should be used whenever possible (e.g. not for arrays). DATA statements result either in blocks of data put into the appropriate storage location by the loader, or in pieces of code executed after loading and before the user's program is entered, depending on the operating environment. DATA statements must be put between the last of the other declarations and the first executable statement, statement function definition, or INTERRUPT attribute of a routine. In RTF, DATA statements have no purpose in addition to the standard one. Instead, there are some restrictions for the variable list. The syntax in RTF is: DATA <var>/<const list>/[[,]<var>/<const list>...] . <var> stands for a simple variable, an array name, an array element reference, or a pointer. Note that only one item, not a list, is allowed for <var>. Implied-Do lists are not allowed. Moreover, <var> must be declared SAVE if it is a local variable. Only global, COMMON, or SAVE variables can be preset with DATA. Example: Subroutine Junk Logical FirstTime Integer Arr(0:9,2) Save FirstTime, Arr Data FirstTime/.true./, Arr/10*0,10*1/ ... is legal in RTF, while omission of the SAVE or writing Data FirstTime,Arr/.true.,10*0,10*1/ are not. The restrictions will be removed in a later release of RTF. 2.4.10. INCLUDE directive - - - - - - - - - - - - - The INCLUDE directive is an extension to the standard present in most Fortran implementations. It is useful for inserting a piece of source text from a different file, especially one which contains all COMMON declarations used by a set of subroutines. The syntax in RTF is: INCLUDE '<filename>' (VAX style) or $INCLUDE <filename> (NORD style). The syntax and interpretation of <filename> depends on the operating system under which RTF is executed. E.g., under CP/M, the drive identifier of the main source file is prefixed to the file name of the INCLUDE file as the default drive. 2.4.11. Pointers - - - - - - - - - Pointers contain the addresses of variables rather than the values, which are carried by the variables themselves. The concept of accessing values indirectly through pointers is not unknown to Fortran users; it is used all the time when replacing the formal arguments of a subroutine definition with the actual arguments of a subroutine call. By means of the subroutine call, the programmer determines the addresses of the formal arguments. This is the only occurence of pointers in Fortran. The additional feature available with pointer variables as in RTF (or C, Pascal) is that the programmer has full control on the addresses of variables, not only of formal arguments. Many illustrations for pointers were used already on the previous pages; in summary, pointers are adequate for variables or data structures whose position in the 68K's address space is determined only at run-time. Paragraph 2.7. is devoted to the use of pointers. Only one important rule that must be obeyed for declaring pointer variables is stated here: If the address of a variable is to be changed in the executable part of a routine, the variable must either be declared as a pointer variable, or not declared at all. Examples: Integer @ITDC @ITDC=ICamAd(B,C,N,A,F,T) JTDC=ITDC or @ITDC=ICamAd(B,C,N,A,F,T) JTDC=ITDC are both correct and give the same result. It is not correct to declare ITDC without the pointer mark '@': Integer ITDC @ITDC=ICamAd(B,C,N,A,F,T) JTDC=ITDC The compiler gives an error message in this case for the second statement which tries to modify the address of a variable which is not pointer-based, but has a fixed address assigned by the compiler. (Semantic meaning: in the above examples it is assumed that the CPU has memory-mapped access to a CAMAC system, and that the function ICamAd returns the address of a CAMAC object with given branch B, crate C, station N, subaddress A, function F, and wordsize T. Once the address is assigned to the pointer variable ITDC, the access to it, i.e. the I/O to the CAMAC system, is as simple and as fast as the access to a normal variable in memory.) Note also that there exists in RTF an extension to the ASSIGN statement which is normally used to handle addresses of statement labels. Here it can be used for addresses of variables or array elements as well. This provides the func- tionality of the 'abbreviations' known from the transputer language OCCAM. For example, ASSIGN A(I,J,K) to B(0) B(0)=B(1)-B(-1) has the effect that a linear array B is defined which has the same data type as A and where the element B(0) has the same address as the element A(I,J). B can the be used as a shorthand form for A(I,J,K), B(1) for A(I+1,J,K), etc. This speeds up access very much in cases where A(I,J,K) and elements with constant offset relative to A(I,J,K) are accessed frequ- ently - especially if @B is EQUIVALENCEd with an address register. 2.4.12. RECORD declarations - - - - - - - - - - - - - - The pointer-based COMMON blocks have been described already in paragraph 2.4.5.; a RECORD is exactly the same as a pointer-based COMMON whose pointer is explicitely declared. Syntax: RECORD /<name>/ <dcl>[,<dcl>...] <name> is the name of a variable not declared so far, or declared as a pointer variable. Included in the latter case are formal arguments of routines. As mentioned earlier, the purpose of RECORDs is to have data structures that can be imposed on any block of storage, just by setting the address of the RECORD properly. Examples: 1 ...the pointer is not declared elsewhere, hence it is allocated on the stack (local variable): Subroutine Junk Record /D/ Flags,X,Y,Z Integer Flags; Real X,Y,Z @D=... ! set base address of RECORD If(Btest(Flags,0)) X=... 2 ...the pointer is declared as a COMMON variable: Subroutine Junk Common /Bases/ @D ! assume @D is set elsewhere Record /D/ Flags,X,Y,Z Integer Flags; Real X,Y,Z If(Btest(Flags,0)) X=... 3 ...the pointer is declared as a formal argument: Subroutine Junk(D) ! @D supplied by subroutine call Record /D/ Flags,X,Y,Z Integer Flags; Real X,Y,Z If(Btest(Flags,0)) X=... In the second example, it would be illegal to omit the '@' in the COMMON declaration. 2.5. Subroutines and Functions ------------------------------ This chapter is intended to provide details of the implementation with respect to subroutine and function definitions and calls rather than the language-guide type of information on this topic. Also, the extensions available for interrupt handling etc. are described. 2.5.1. General overview - - - - - - - - - - - - Passing of arguments from the calling program to a subroutine or function can be done in RTF by three different means: by VALUE - the value of a variable, constant, or expression is passed; by REFERENCE - the address of a variable, array, or auxiliary variable containing the value of a constant or expression is passed; by DESCRIPTOR - the address of a descriptor block which contains further information is passed (details are found below). The default method is passing by reference for all objects except for character variables or character expressions which are passed by descriptor. Arguments optionally may be passed by value. In the call as well as in the subroutine/function declaration, they have to be prefixed by a "#" for this purpose. This calling method is restricted to simple variables and constants. It is slightly faster than call by reference and may be necessary for compatibility with routines written in other languages calling or called by RTF routines. Trailing actual arguments may be omitted (short parameter list) if precautions in the called routine are taken. All arguments are transferred via the stack. Arguments are pushed on the stack before the call and removed from the stack after the call by the calling program. The first two arguments (i.e., their addresses when called by reference) are copied to address registers inside of the called routine in order to have fast access to them. With the option REGUSE=OFF, this optimization is switched off. This may be useful in cases where the compiler runs out of address registers within subroutines. Function results are returned via the register D0 if the value of the result fits into 32 bits (so far, everything except type CHARACTER). Otherwise, the stckframe contains an address, provided by the caller, which points to where the value has to go. When passed by reference, actual arguments may be simple variables, expressions, arrays (those three not of type CHARACTER), EXTERNALs, and constants (including string constants). Formal arguments may be simple variables, arrays (both not of type CHARACTER), or formal subroutines or functions. Passing by descriptor applies for actual arguments which are simple variables or expressions of type CHARACTER, and any formal arguments of type CHARACTER. When passed by value (i.e. with the '#' in front) actual arguments may be simple variables, constants, or expressions. Formal arguments may then only be simple variables. Type CHARACTER is not allowed for call by value. Side exits from subroutines are denoted in the standard way by "$" or "*". Secondary entry points are defined by ENTRY as usual. In addition, entry points coded with embedded assembler statements (and thus invisible to the compiler) can be declared with the "ASSEMBLER name" statement (see below). If inside of a function definition its declared name appears with an argument list, e.g in expressions, this results in a (legal) recursive call of the function. Occurence of the function name inside the function without an argument list is handled in the standard way, i.e. as the present value of the function identifier. All registers explicitely used by the compiler are saved during subroutine calls. This is required for reentrancy. The registers referenced by the user with EQUIVALENCE (variable,register) are, however, not automatically saved. The KEEP statement must be used to save these registers unless the routine is intended to change one or all of these registers. Subroutines may be statically designated to handle certain interrupts. This is requested with the "INTERRUPT vectornumber" statement which has to follow immediately all declaration statements of the subroutine. The specific meanings of vectornumber values is hardware dependent. The effect is to modify the appropriate interrupt vector cell during initialisation such that it points to the routine. The current priority of a task may be changed with the statement PRIORITY=prio, where prio is a constant between 0 and 7. The user should be aware of the effects resulting from priority changing. In routines serving interrupts, the priority is automatically reset to its value prior to the interrupt when returning from the routine. The code generated by RTF for subroutine definitions and calls is compatible with the 'CERN calling conventions for 68k processors' with the exception that registers are saved by default inside of the called routine instead of by the caller. This scheme is necessary for direct interrupt server routines. A compilation option, REGSAVE= OUTSIDE, forces RTF to stick to the CERN scheme also for register saving. In addition to, but compatible with the CERN scheme, the actual number of arguments is passed to the called routine via D0 to be able to handle short argument lists. For the option CODE=OS9, register assignments stick to OS9 rules instead of CERN conventions; the actual number of arguments may then be passed via the condition code register, CCR. 2.5.2. Stack Assignment during Calls - - - - - - - - - - - - - - - - - - - In the following, AG denotes the base register for the global/ static variables, and AL denotes the base register for local variables. For all options but OS9, AG is A5 and AL is A6. For OS9, ist is just the other way around: AG is A6 and AL is A5. Actual parameters (addresses, 32-bit values) are pushed on the stack by the calling program in the reverse order of the argument list. Note that pushing uses pre-decrement addressing mode which lowers the stack pointer. After that, the call (with assembler instruction JSR) pushes the return address on the stack. In the called routine, the LINK instruction saves the address register AL on the stack and reserves space for local variables. Local variables are addressed relative to AL, i.e., with the d(AL) mode. Then, the entry address to the routine and an all-zero word are written to locations -4(AL) and -12(AL) (-8(AL) is unused), resp., providing information for the symbolic debugger and traceback. This is done only if the option DEBUG=ON is chosen. Following these words, the contents of the necessary data-, address-, and floating point registers are saved within the local space. Local program variables are allocated from -204(AL) downwards. It is normally the calling program's responsibility to remove the arguments from the stack after return from the call, by moving the stack pointer with an LEA d(A7),A7 instruction. If the called routine takes a side exit, i.e. it does not return into the proper point of the calling sequence, it removes the arguments itself. In this case, short argument lists are not allowed, because the number of arguments stated in the routine declaration is removed. If the called routine is a function, it returns its result via D0. If the result occupies 4 bytes or less, the value is returned directly in D0. If the result occupies more than 4 bytes, the value is returned to the memory location pointed to by D0. This is the case for type CHARACTER functions only. Note that those functions are thus not able to obtain the actual number of arguments in D0. The stack layout after entry into the called routine (more precisely, after registers are saved and debug information is stored) is shown in the figure below. If you require access to the stackframe from Fortran, use the predeclared INTEGER*4 array STACKFRAME with indices given as below; e.g., STACKFRAME(21) contains the return address. For non-OS9: | | | stack before call | | | |---------------------------| +(n+1)*4 | parameter n | STACKFRAME(n+21) |- - - - - - - - - - - - - -| : : |- - - - - - - - - - - - - -| +8 | parameter 1 | STACKFRAME(22) |---------------------------| +4 | return address | STACKFRAME(21) |---------------------------| A6 --> | link location | STACKFRAME(20) |---------------------------| -4 | entry address | STACKFRAME(19) |---------------------------| -8 | (undefined) | |---------------------------| -12 | (zero) | STACKFRAME(17) |---------------------------| -16 | A7 (if requested) | : . data and address : : . register save area : : . 4 bytes/register : -76 | D0 (always saved) | STACKFRAME(1) |---------------------------| -80 | FP7 (if required) | : . floating point : : . register save area : : . 12 bytes/register : -172 | FP0 (if required) | |---------------------------| -176 | | : (reserved) : -200 | | |---------------------------| -204 | | : space for local variables : A7 --> | | |---------------------------| | | | free stack after call | | | For OS9: | | | stack before call | | | |---------------------------| \ +(n-1)*4 | parameter n | STACKFRAME(n+19) | only |- - - - - - - - - - - - - -| | : : | if |- - - - - - - - - - - - - -| | +8 | parameter 3 | STACKFRAME(22) | n>=3 |---------------------------| / .... parameter 2 in D1 ..... .... parameter 1 in D0 ..... +4 | return address | STACKFRAME(21) |---------------------------| A5 --> | link location | STACKFRAME(20) |---------------------------| -4 | entry address | STACKFRAME(19) |---------------------------| -8 | (undefined) | |---------------------------| -12 | (zero) | STACKFRAME(17) |---------------------------| -16 | A7 (if requested) | : . data and address : : . register save area : : . 4 bytes/register : -76 | D0 (always saved) | STACKFRAME(1) |---------------------------| -80 | FP7 (if required) | : . floating point : : . register save area : : . 12 bytes/register : -172 | FP0 (if required) | |---------------------------| -176 | | : (reserved) : -200 | | |---------------------------| -204 | | : space for local variables : A7 --> | | |---------------------------| | | | free stack after call | | | Summary of register usage in connection with calls: D0: scratch register; on entry, contains number of actual arguments, or address of first parameter if OS9. On exit, contains result if function call, else the value on entry. D1: \ .. | working registers. For OS9, D1 contains address of 2nd parameter. D7: / A0: address of first actual argument (if >= 1 formal arg. present and REGUSE=ON; else work register). A1: address of second " " (if >= 2 formal args. present and REGUSE=ON; else work register). A2: \ A3: | working registers. A4: / AG: global space pointer (global user variables and system variables, d(AG) ). AL: local space pointer (local user variables inside routine ,d(AL) with negative offset). A7: stack pointer. Format of character string descriptors: Whenever a character VARIABLE is referenced, a descriptor of this variable is set up in the following format (3 longwords occupied): < - - - - 32 bits - - - - -> +--------------------------+ |address of first character| Regardless of substring indices |------------+-------------| |lower index | upper index| These are the substring indices |------------+-------------| |pointer to next descriptor| Zero if nothing concatenated. +--------------------------+ For example, assume CHARACTER*10 A,B. Then the descriptors look as follows for various character expressions: +--------------------------+ | address of A(1:1) | for A |------------+-------------| | 1 | 10 | |------------+-------------| | 0 | +--------------------------+ +--------------------------+ | address of B(1:1) | for B(2:5) |------------+-------------| | 2 | 5 | |------------+-------------| | 0 | +--------------------------+ +--------------------------+ | address of A(1:1) | for A(1:5)//B(6:10) |------------+-------------| | 1 | 5 | |------------+-------------| | address of descr.2 |-- +--------------------------+ | -- - - - - - - < - - - - - - - - | +--------------------------+ ->| address of B(1:1) | |------------+-------------| | 6 | 10 | |------------+-------------| | 0 | +--------------------------+ In calls, the address of the descriptor of character variables or expressions is given, with the sign bit of this address set to 1. This allows to distinguish between descriptors and normal addresses. For character CONSTANTS, the address of the first character is given, with the sign bit cleared as usual. Character constants are null terminated. As soon as an expression is formed with constants, e.g. 'ABC'//'DEF', however, a descriptor is set up. 2.5.3. ENTRY specification - - - - - - - - - - - - - - The ENTRY specification is used to provide alternative entry points into subroutines and functions; the same is true for RTF. The formal arguments of an ENTRY can differ from those of the main entry point in number, names, and order. In RTF, when a formal argument has the same name as one of the main entry or a previous ENTRY, a warning is given. After the ENTRY, the ordering of arguments stated there is maintained statically. Example: Subroutine Alfa(A,X,Y) Real A,B,X,Y A=X-Y Entry Beta(B,Y,X) ! CAUTION X and Y change roles B=X-Y End In this odd example, the following happens when entry Alfa is called with CALL ALFA(O,P,Q). First, O is assigned the value P minus Q. Then (after skipping the non-executable ENTRY specification) O is assigned the value Q minus P. That is of course not what you want - so be aware of this implicit EQUIVALENCE scheme when using ENTRY! 2.5.4. ASSEMBLER entry specification - - - - - - - - - - - - - - - - - - - This extension to Fortran is provided for pieces of code written in embedded assembly code. Other than writing the entry point declaration also in embedded assembly, the RTF ASSEMBLER entry specification makes it an 'official' entry point which fully sticks to the rules imposed by the interactive linker, symbolic debugger, and traceback. Formal arguments are not allowed at ASSEMBLER entry points. Example: Subroutine Dummy Assembler Kill + ILLEGAL Assembler Fiddle + MOVE.L #4711,A7 + RTS End 2.5.5. Statement functions - - - - - - - - - - - - - - Statement functions conform to the standard. They are implemented not like macros which are inserted at every call point, but are entered with JSR and left with RTS much like external functions. Only the way of argument passing is faster: the arguments of a statement function as well as its value are local variables of the program unit containing the statement function. In effect, arguments are passed by value. Also, time for saving and restoring registers is minimized. 2.5.6. Intrinsic functions and subroutines - - - - - - - - - - - - - - - - - - - - - - The following paragraph describes the functions and subroutines which are recognized by the compiler as non-external and are handled in a special way. The purpose is twofold: a) run-time efficiency, and b) special handling of involved data types (generic functions). 2.5.6.1. Intrinsic functions Note that the routine names specified in this chapter cannot act as substitutions for dummy routines in subroutine or function calls; i.e. they cannot be declared EXTERNAL. For example, CALL SUB(X,ABS) is NOT allowed. They may, of course, be used in expressions which are used as actual arguments: CALL SUB(ABS(X)) is allowed. The reason is that fast in-line code is generated for the routines described in this chapter rather than subroutine/function calls. For some of the routines, this is only true if certain compiler options are chosen. 2.5.6.1.1. Bit manipulation functions i = IAND(j,k) or I is the bit-by-bit product of j and k. i = AND (j,k) Both must be INTEGER (any size), as is the result. Example: MANTISSA=AND(IREAL,'7FFFFF'X). i = IOR (j,k) or I is the bit-by-bit sum of j and k. i = OR (j,k) Both must be INTEGER (any size), as is the result. Example: PATTERN=IOR(PATTERN,ENDFLAG). i = IEOR(j,k) or I is the bit-by-bit difference of j and k. EOR (j,k) or Both must be INTEGER (any size), as is the IXOR(j,k) or result. XOR (j,k) Example: ITOGGLE=EOR(ITOGGLE,1). i = INOT(j) or I is the bit-by-bit inverse of j. i = NOT (j) The data type of the result is that of j. Example: ZERO=IAND(MASK,NOT(MASK)). i = IBSET(j,k) I is the same as j but the k-th bit set. The data type of the result is that of j. The data type of k must be INTEGER (any size). K is evaluated modulo 32. Example: REALMINUS=IBSET(REAL,31). i = IBCLR(j,k) I is the same as j but the k-th bit cleared. The data type of the result is that of j. The data type of k must be INTEGER (any size). K is evaluated modulo 32. Example: REALABS=IBCLR(REAL,31). i = IBCHG(j,k) I is the same as j but the k-th bit inverted. The data type of the result is that of j. The data type of k must be INTEGER (any size). K is evaluated modulo 32. Example: REALINV=IBCHG(REAL,31). i = ISHFT(j,k) I is j shifted by abs(k) places to the left (if k positive) or right (if k negative). The bits shifted out are lost and zeros are filled in. The data type of the result is that of j. The data type of k must be INTEGER (any size). K is evaluated modulo 32. Example: IEXPO=ISHFT(IREAL,-23). if (BTEST(j,k)) ... BTEST is a logical function which is TRUE if the k-th bit of j is set. The data type of k must be integer (any size). K is evaluated modulo 32. Example: IF(BTEST(STATUS,12)) CALL ALARM. 2.5.6.1.2. Other functions that produce in-line code i = MOD(j,k) I is j modulo k. If k is a power of two, a AND is carried out, otherwise a divide. J and k must both be INTEGER (any size), as is the result. Example: IAND255=MOD(I,256). i = IABS(j) I is the absolute value of j. J must be INTEGER (any size), as is the result. r = ABS (s) R is the absolute value of s. S may be of type INTEGER (any size) or REAL. The data type of the result is REAL. i = IFIX(j) I is j, converted to INTEGER by truncating to the nearest integer closer to zero. J must have INTEGER (any size) or REAL data type. The type of the result is INTEGER. Example: TRUNC=IFIX(REAL). r = FLOAT(s) I is s, converted to REAL. S must have INTEGER (any size) or REAL data type. The type of the result is REAL. Example: ISAME=FLOAT(I). i = ICHAR(c) I is the binary representation of the first ASCII character of the string c. The result is type INTEGER. C must have type CHARACTER. Example: LOWERA=ICHAR('a'). c = CHAR (j) C is the ASCII string representation of the binary value j. J must have type INTEGER (any size). The result is type CHARACTER*1. Example: CHARSET(I:I)=CHAR(I). if (Lcc(c,d)) ... Lcc is a logical value stating the result of lexical comparison of two CHARACTER strings c and d. cc may be one of LE, GE, NE, LT, GT, or EQ. Example: IF(LEQ(INPUT,'EXIT')) STOP. Note: Lcc(c,d) generates the same code as c.cc.d . if (OWNER(sema))... OWNER is a access control primitive which uses the TAS instruction of the processor to test and set a semaphore. It is a logical function which is .TRUE. if the semaphore was free, i.e. the calling program owns it after the call. Sema must be variable (not expression) of any data type; only its first byte is accessed. A natural choice would be INTEGER*1. Releasing the resource then works with sema=0. Example: DO WHILE(.NOT.OWNER(BRANCH)) CALL WAIT ENDDO ... CALL ACCESS ... BRANCH=0 2.5.6.1.3. Standard functions r = func(s) Func is the value of the standard function acting on the argument s. S must have INTEGER (any size) or REAL data type. Func has data type REAL. Func stands for: SQRT, SIN, COS, TAN, ASIN, ACOS, ATAN, EXP, EXP10, ALOG, ALOG10, SINH, COSH, TANH, ATANH. r = RNDM(j) RNDM is a high-quality random generator with uniform distribution in (0,1). J must be declared as INTEGER*4 J(2). The two values should be initialized and are updated in each RNDM call. They can be saved as a starting point for later use. The type of the result is REAL. RNDM provides a period length of 2**64. r = RNORM(j) RNORM generates random numbers with normal distribution (derived from 12 calls to RNDM). Call similar to RNDM. i = LEN(j) LEN calculates the number of characters in the character string expression j. Example: CHARACTER*128 CH ... NONBL=LEN(CH(1:-1)) ! length without ... ! trailing blanks i=INDEX(j1,j2) INDEX calculates the character index in the character string expression where the character string expression j2 starts. Zero is returned if j2 is not found in j1. Example: I=INDEX(FILE,'.') IF(I.NE.0) THEN FILE1=FILE(:I)//'FOR' ELSE FILE2=FILE//'.FOR' ENDIF i = INT(x) I is x, truncated to the nearest integer closer to zero. X must have REAL data type, the type of the result is INTEGER. Same as IFIX above, but not a generic function. i = NINT(x) I is x, rounded to the nearest integer. X must be type REAL. The type of the result is INTEGER. Not a generic function. Example: IROUND=NINT(R). i = INCHR(lu) Single character input without wait. Lu is the logical unit (INTEGER*4). I (INTEGER*4) is zero if no character is present; else it has the integer value of the ASCII character entered. Example: do forever i=inchr(1) if(i.ne.0) then call action(i) else call background ... 2.5.6.1.4. Word size modifiers These serve as a shorthand alternative for mixtures of data type and EQUIVALENCE statements (especially when accessing the same hardware in different modes). Note that, if used on the left side of assignments, they are outside the Fortran standard. i = BYTE(j) Regardless of the data type specification BYTE(j) = i of j, only its first data byte is read or written, resp.; i.e., the result is as if j were declared as INTEGER*1. If BYTE is used for reading (on the right hand side or in arithmetic expressions), its argument may be a constant. i = WORD(j) The same for the first 2 bytes (word) of WORD(j) = i j; like INTEGER*2 declaration. i = LONG(j) The same for the first 4 bytes (longword) LONG(j) = i of j; like INTEGER*4 declaration. CAUTION: note the quite different (peek/poke type) meaning of the above intrinsic names in ABSOFT Fortran. Use EQUIVALENCE with absolute addresses or pointer variables if the effect of the ABSOFT BYTE, WORD, or LONG is desired. 2.5.6.2. Intrinsic subroutines Some in-line procedures have been added which are invoked with CALL statements. They perform simple, often-used operations at maximum speed without any subroutine call overhead. - In particular, one class uses instruction sequences which have 'looping capability' on the 68010, acting on blocks of data. - Other classes act as 'hardware subroutines' by activating peripheral processors or 68020 co-processors. 2.5.6.2.1. Looping mode routines Note: Due to the nature of the DBcc instruction of the 68K processor the block length l is evaluated modulo 2**16; i.e., the values 1 to 65536 are possible. Also note that the word sizes may apply for various data types; e.g. _MOVL may be used for INTEGER*4, LOGICAL*4, and REAL*4 data, _CMPB may be used for INTEGER*1 and LOGICAL*1, etc. call _MOVL(s,d,l) Moves a block of l words (size L or 4 bytes, call _MOVW(s,d,l) W or 2 bytes, B or one byte each) from s call _MOVB(s,d,l) to d, starting from the low addresses. Example: CALL _MOVB(ARRY(1),ARRY(1001),1000). call _SETL(v,d,l) Writes the value v (word size as above) into call _SETW(v,d,l) all l components of the block d. call _SETB(v,d,l) Example: CALL _SETL(3.14,PIARR,LPIA) call _CMPL(s1,s2,l) Compares two blocks of l words each (word size call _CMPW(s1,s2,l) as above), s1 and s2. Comparison starts from call _CMPB(s1,s2,l) the low addresses and terminates when either two words are not equal or all l words are done. The result appears in the condition code register of the processor and can be checked subsequently. Example: CALL _CMPW(A,B,10000) IF(.EQ.) THEN CALL IDENTICAL ELSE IF(.LT.) THEN ! i.e. A < B ... As an alternate notation, one can also use the following aliases: MOV_L, MOV_W, MOV_B _MOVL, _MOVW, _MOVB SET_L, SET_W, SET_B instead of, resp. _SETL, _SETW, _SETB CMP_L, CMP_W, CMP_B _CMPL, _CMPW, _CMPB. 2.5.6.2.2. Hardware subroutines a) FASTBUS. A subset of the standard Fastbus routines is implemented as a 68020 coprocessor. They are enabled by the option FB=HARD (see above). Then statements of the following form result in generating essentially one F-line coprocessor instruction instead of a subroutine call: call FBxx(p1,p2,...) Please refer to Ref.2 for a complete list of FB hardware subroutines. Example: CALL FBBRD(PRIMA,SECA,DEST,LENGTH) b) As the source of the compiler is easy to modify, more sets of special hardware routines can be made available for Fortran use without runtime overhead. 2.5.7. Obtaining the actual number of arguments - - - - - - - - - - - - - - - - - - - - - - - - As inidcated above, the caller passes the number of actual arguments to the called routines if both - the calling routine is compiled with option NUMPAR=ON (this is the default) and - the called routine is not a type CHARACTER function. Then the number of arguments is in register D0.L after entering the called routine. The called routine may transfer the number of arguments to a variable by using CALL NUMPAR(var) or CALL NOARG(var) as the first executable statement. The compiler then generates inline code for that of the kind MOVE.L D0,var . If the call is made without parameters, i.e. CALL NUMPAR() or CALL NOARG() no code is generated. If the call is completely left out, however, RTF generates code to check if the actual number of arguments is the same as the formal number of arguments: CMP.W #formal,D0 BEQ *+4 JSR _BADPAR . This check may be suppressed with the option CHECK=OFF, or with a call to NUMPAR/NOARG without arguments as described above. Calling NUMPAR or NOARG other than in the first executable statement results in a normal subroutine call to a user-supplied routine. 2.5.8. KEEP directive - - - - - - - - - - - The KEEP directive should accompany EQUIVALENCE with registers. Other than registers which are allocated by the compiler itself, the user-declared registers are not automatically saved at entry and restored at exit of the routine using them. If the contents of user declared registers is to be preserved for the outside world (which is normally the case, and is a must for interrupt handler routines), the names of those registers have to appear in the KEEP directive. Otherwise, the routine exits with the value of the register(s) altered - maybe useful only as an odd way of passing results. Also when modifying registers in embedded assembly code parts, these registers should appear in KEEP. The registers accessible to the user by this means are the processor data registers D0-D7, the processor address registers A0-A7, and the floating point coprocessor data registers F0-F7. The processor status register SR, which is accessible with EQUIVALENCE, cannot appear in KEEP, however. Example: Function ISum(IA) Integer*2 I,IA(100); Integer ISum,IS Equivalence (I,d7),(IS,d6) Keep D7,d6 IS=0 Do I=1,100 IS=IS+IA(+) EndDo ISum=IS End If one omits the KEEP in this example, the function should be better called ITrouble - because it will affect the caller's registers D6 and D7 in an uncontrolled manner! 2.5.9. INTERRUPT and EVENTFLAG directives - - - - - - - - - - - - - - - - - - - - - As mentioned previously, RTF subroutines can be used as interrupt handlers without any operating system overhead. For this purpose, the entry address of the subroutine is written into the appropriate longword of the CPU's interrupt vector. On occurance of the interrupt signal (an external hardware signal or software trap), the CPU's firmware saves the current CPU status word and program counter (or more for some special interrupts) on the stack and jumps directly to the subroutine's entry point. The saving of the rest of the CPU's current context (registers) is done by the subroutine internally. Thus, it takes typically only 20 usec on a 10 MHz 68000 from the interrupt to the first useful instruction of the subroutine (i.e., after the register saving). The exit from an interrupt routine is special as compared to a normal subroutine: not only the old program counter, but also the old CPU status word are restored from the stack. Thus the return from interrrupt has to use the assembler instruction RTE instead of RTS. Both setting up the interrupt vector location and preparing the proper return instruction is done by the INTERRUPT directive. It has to be the last of the declaration statements, i.e. it has to be placed between DATA and statement functions (if any). After the keyword INTERRUPT, either a integer constant in the range 2 to 255 or a '*' must follow. In the first case, the interrupt vector location with the given constant as index is set to the routine's entry address during program initialization, much as with a DATA statement. The interrupt vector location is not touched during initialization in the second case, but this is then left for runtime (see subroutine ACTIVATE below). In both cases, the proper return from interrupt is generated. Examples: Subroutine Iresp Byte reg Equivalence (reg,'ff3000'x) Interrupt 200 ! set vector 200 to addr(Iresp) reg=0 ! 1st executable instruction ... End ! exit with RTE Program Main External Iresp1 ... Call Activate(200,Iresp1) ! set vector 200 to addr(Iresp1) ... End Subroutine Iresp1 Byte reg Equivalence (reg,'ff3000'x) Interrupt * reg=0 ! 1st executable instruction ... End ! exit with RTE In the first example, the interrupt vector is set automatically during initialization, while this is done explicitely by the main program in the second example. If one uses the keyword EVENTFLAG instead of INTERRUPT, the compiler generates an RTR instruction instead of RTE. A vector number is not allowed then, only the '*'. This may be useful for returning from asynchronous event handlers supported by some operating systems (OS9, VersaDOS). Example: Common /Flags/ IDone Program Main External Iresp2 ... Call Enable(Iresp2...) ! tell OS to enable handler ... End Subroutine Iresp2 Eventflag * IDone=1 ! 1st executable instruction ... End ! exit with RTR 2.5.10. CPU time required for subroutine calls - - - - - - - - - - - - - - - - - - - - - - - - The caller has to push the addresses of the N actual arguments onto the stack (PEA intructions), move N to D0 (MOVEQ), perform the jump to subroutine (JSR and JMP), and to clean up the stack after return (ADDQ). The called routine has to setup the local variable area (LINK), save registers (MOVEM), optionally check the actual number of arguments and stack limit (two times CMP, Bcc), then after execution of the user's code restore the registers (MOVEM), remove the local variable area (UNLK), and return (RTS). On a 68000 processor, a subroutine call requires thus the following number of clock cycles: 96 + 16*(n+m) + 28*c where n is the number of actual arguments, m is the number of registers to be saved inside of the routine, and c is 1 if the option CHECK=ON is chosen, 0 else. For n=2, m=4, c=0 this sums up to 192 clock cycles or roughly 20 usec for a 10 MHz 68000. 2.5.11. Type conversions for integer arguments - - - - - - - - - - - - - - - - - - - - - - - - There are cases where the conformation of actual and formal subroutine arguments is not evident: for integer constants and integer expressions containing INTEGER*2 parts. For example, in INTEGER*2 I,J ... J=I+1 ... the expression I+1 is treated as INTERGER*2, i.e. as a 16-bit word. The same is true in Options MULDIV=SHORT ... INTEGER*4 I INTEGER*2 J ... J=I/5 ... for the expression I/5 as a consequence of the 68000's DIVS instruction, while in the case I/4 (use of shift) the type is INTEGER*4. The features mentioned above are justified by optimizing criteria but are not at all obvious to the user, and hence could lead to arbitrary results if such expressions are used as actual subroutine arguments. Therefore the following scheme is used to arrive at definite data types for integer actual arguments. (1) Simple variables and array elements are passed with the type imposed by type declaration or implication as usual. (2) Integer constants and integer expressions other than (1) are passed as INTEGER*4 when the option INTSIZE=4 (default) is chosen. (3) Integer constants and integer expressions other than (1) are passed as INTEGER*2 when the option INTSIZE=2 is chosen. A similar difficulty occurs in principle also for LOGICAL variables. However, due to the representation of logical values with the topmost byte only, confusion of LOGICAL*1, *2, and *4 actual arguments has no effect. 2.6. Other language elements ---------------------------- 2.6.1. Arithmetic, logical, and character string expressions - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2.6.2. Assignment statements - - - - - - - - - - - - - - - 2.6.3. Label and variable ASSIGN statements - - - - - - - - - - - - - - - - - - - - - - Label ASSIGN statements are used in Fortran in connection with assigned GOTO statements. The syntax is: ASSIGN <label> TO <variable> where <label> is the label of an executable Fortran statement in the same program unit. <variable> is a simple variable or an array element of type INTEGER*4. The effect is that the address of the labeled statement is stored in the variable. Later, this address may be used in GOTO <variable> which results in an in- direct jump. In RTF, there exists an extension to the ASSIGN statement. It can be used to access addresses of variables or array elements, in addition to addresses of statement labels. It then acts as a dynamic EQUIVALENCE performed at run-time. Syntax: ASSIGN <variable> TO <base> where <variable> is a simple variable or array element reference of any data type. <base> has the form: <name> or <name>(<const>) . <name> may be a name not declared so far, or a name declared as a pointer variable or linear pointer-based array. The data type and array bounds of In both cases, a pointer-based, linear array of the same data type as <variable> is declared. In the first case, the element name(1) gets the same address as the variable, while in the second case, name(const) gets the same address as the variable. <const> is an integer constant. This provides the functionality of the 'abbreviations' known from the transputer language OCCAM. Examples: 1. ASSIGN A(I,J,K) to B(0) B(0)=B(1)-B(-1) B(2)=0 has the same effect as A(I,J,K)=A(I+1,J,K)-A(I-1,J,K) A(I+2,J,K)=0 2. EQUIVALENCE (@XX,A4),(I,D7); KEEP A4,D7 ... ASSIGN X(1,J,K) TO XX S=0 DO I=1,N S=S+XX XX(+)=0 ENDDO has the same effect as S=0 DO I=1,N S=S+X(I,J,K) X(I,J,K)=0 ENDDO In both cases, much speed is gained. In the first example, one can also speak of an 'abbreviation', while this is not really the case in the second example. In general, ASSIGN to variables pays in cases where the same array element or elements with constant offset relative to each other are accessed frequently. 2.6.4. DO loops - - - - - - - - Standard Fortran allows only one form of DO loops: DO <label> [,] <variable>=<startexpr>,<endexpr> [,<stepexpr>] [<statements>] <label> <statement> where <label> is the label of an executable Fortran statement later in the same program unit, <variable> is a simple variable or array reference of arithmetic data type, and <...expr> are arithmetic expressions. Note that RTF allows only integer data types here. The range of a DO loop may be empty, in which case the statements following the DO are not executed. The sign of the optional step expression determines whether the variable is increased or decreased on each iteration. There are additional forms of DO statements available in RTF: DO [FOR] <variable>=<startexpr>,<endexpr> [,<stepexpr>] [<statements>] END DO DO WHILE(<logical expression>) [<statements>] END DO DO UNTIL(<logical expression>) [<statements>] END DO DO FOREVER [<statements>] END DO Note that ENDDO can be used instead of END DO. The first form above (block DO) is an extension known from VAX or NORD Fortran and is useful for more stuctured programming, avoiding statement labels much like with the block IF. The second form is an extension known from VAX Fortran. The loop is executed while the logical expression is .TRUE. (maybe zero times). Loop control takes place at the beginning of the loop. The third form also allows loop control according to a logical expression, but here the loop is executed at least once until the logical expression is .TRUE.; loop control takes place at the end of the loop. The last from is equivalent to DO WHILE(.TRUE.) and also results in the same code. 2.6.5. IF statements - - - - - - - - - - - Three kinds of conditional statements are available in Fortran: arithmetic, logical, and block IF statements. The syntax is for arithmetic IF: IF(<arithmetic expr>) <label>,<label>,<label> logical IF: IF(<logical expr>) <statement> block IF: IF(<logical expr>) THEN [<statements>] [ELSE IF(<logical expr>) THEN [<statements>] ] [ELSE IF(<logical expr>) THEN [<statements>] ] ... [ELSE [<statements>] ] END IF where <arithmetic expr> is an expression of an INTEGER or REAL data type, <label> is a label of an executable Fortran statement in the same program unit, <logical expr> is an expression of type LOGICAL, <statement> is a legal Fortran statement and <statements> is one or more legal Fortran statements. Arithmetic IF is considered old-style Fortran and bad practice. In the case of block IF, one must be careful not to confuse an ELSE IF clause with ELSE, followed by a different IF statement on a new source statement line; even more so in the case of the potentially free-format RTF: ELSE IF(...) on one line is the end if clause, while ELSE followed by IF(...) on a new source line is an ELSE clause, containing a new IF statement. END IF and ENDIF, ELSE IF and ELSEIF are synonymous. 2.6.6. GOTO statements - - - - - - - - - - - - There are three kinds of GOTO stetements in Fortran: uncond- itional, assigned, and computed GOTO. The syntax is for unconditional GOTO: GOTO <label> assigned GOTO: GOTO <integer var> [ [,] <label>,<label>,... ] computed GOTO: GOTO (<label>,<label>...) [,] <integer expr> where <label> is a label of an executable Fortran statement in the same program unit, <integer var> is a simple variable or array element of type INTEGER*4 which previously became defined with a label ASSIGN statement, and <integer expr> is an expression of an integer data type. If a label list is provided in the assigned GOTO, it has only the purpose of documenting potential GOTO target labels in RTF; the compiler does not interprete the labels. The variable has to contain the address of the target label, which is then reached by an ind- irect JMP instruction. Assuming a list of n labels is provided in a computed GOTO, the integer expression should have a value between 1 and n. If the value is outside of this range, the GOTO is not executed, and the statement following the GOTO is executed next. GOTO and GO TO are synonymous. 2.6.7. Input/Output statements - - - - - - - - - - - - - - - - Formatted input: READ(unit,fmt[,ERR=label][,END=label]) [,] ilist READ fmt,ilist ACCEPT fmt,ilist Unformatted input: READ(unit[,ERR=label[,END=label]) [,] ilist Formatted output: WRITE(unit,fmt[,ERR=label][,END=label]) [,] olist PRINT fmt,olist TYPE fmt,olist Unformatted output: WRITE(unit[,ERR=label[,END=label]) [,] olist Unit is the logical I/O unit. It may be *, an integer constant or expression, an array name, character variable name, or a pointer. All of the latter three modes provide internal conversions. The * denotes the default I/O unit (e.g. the terminal). ilist contains simple variables or array elements, array names, or implied loops made out of them. olist contains, in addition to ilist elements, arithmetic, logical, or character string expressions (including constants). If the last character of a formatted output record is a "$", and the output goes to a terminal, the record is output without this last "$" and without CR/LF. If the last character is a "?", the action is similar but the "?" is still output. Both are useful when prompting for input from a terminal. FORMAT statements are standard. Static fromats (supplied at compile- time) and variable formats are supported. Format specifications, with parentheses and between quotes, may also be contained in the I/O statement itself. Example: type '(I5,Z9)',j1,j2 and type 1000,j1,j2 1000 format(I5,Z9) are equivalent. The following format field descriptors are supported: I decimal representation of integer data with default width (I12) on output or free-format input Iw same with width w on input and output Iw.n same, but at least n non-blank digits (leading zeros) on output Z hexadecimal representation of integer, real, or logical data with default width (Z9) on output or free-format input Zw same with width w on input and output O octal representation of integer, real, or logical data with default width (O12) on output or free-format input Ow same with width w on input and output B binary representation or integer, real, or logical data with default width (B33) on output or free-format input Bw same with width w on input and output L representation of logical data (T for .true. and F for .false.) with default width (T2) on output or free-format input Lw same with width w on input and output A ASCII representation of character, integer, real, or logical data. The width depends on the size of the data. Aw same with width w on input and output F fixed-point representation of real data with default width (F10.3) on output and free-format input Fw.d same with width w and d digits after the decimal point on input and output E normalized exponential representation of real data with width (E12.3) on output or free-format input Ew.d same with width w and d digits after the decimal point on input and output D or G same as E Dw.d or Gw.d same as Ew.d X one blank on output, skip one position on input Tn tabulation to column n; fills with blanks on output, skips on input '...' character constant (quoted) only for output nH... character constant (length n) only for output (..) encloses group of format descriptors All of the above descriptors may be preceded by a repetition factor. Further format elements are: , delimiter between format elements / end-of-record mark; serves also as delimiter OPEN and CLOSE have the following form: OPEN([UNIT=]lu,[FILE=]filename [,STATUS=status] [,CARRIAGECONTROL=carriagecontrol] [,ACCESS=access] [,FORM=format] [,ERR=errorlabel]) CLOSE([UNIT=]lu [,STATUS=status],[ERR=errorlabel]) Both forms result in according requests to a the local file system or a file system remote on the host computer. Filename, status, carriagecontrol, access, and form are character string expressions. The values of these expressions, even if constants, are not checked by the compiler. They are interpreted by the local or host computer's file system. Useful values are for Status: 'OLD', 'NEW', or 'UNKNOWN' ; 'KEEP' or 'DELETE' for Close Carriagecontrol: 'NONE', 'LIST', or 'FORTRAN' Access: 'READ', 'WRITE', or 'BOTH' Form: 'FORMATTED' or 'UNFORMATTED' . Defaults depend on the file system. The statements BACKSPACE lu REWIND lu ENDFILE lu complete the set of I/O statements available in RTF/68K. INQUIRE is not yet implemented. 2.6.8. PRIORITY statement - - - - - - - - - - - - - This statement may be used to change the running priority of the CPU, e.g. in order to allow/disallow interrupts up to a certain interrupt priority. The statement affects the priority part of the processor status register and is thus only allowed in supervisor mode. Form: PRIORITY = <octal constant> where <octal constant> is an integer constant in the range 0-7. PRIORITY is a reserved word in the above context. For example, the statement is useful to lower the priority to allow interrupts early in re-entrant interrupt handlers of certain device drivers. Another example is the protection of critical code parts by raising the priority to 7 temporarily (it should be kept in mind that this scheme is incomplete in multiprocessor setups where semaphores maybe required in addition or instead). 2.6.9. CACHECONTROL statement - - - - - - - - - - - - - - - This statement has been added to allow dynamic changes to the mode of the instruction cache of 68020 processors. This statement affects the cache control register and is only allowed in supervisor mode. Also, the option CPU=68020 must be selected. Form: CACHECONTROL = <control constant> where <control constant> is a 4-bit integer constant. Useful values are: 0 disable cache 1 enable cache 3 freeze and enable cache 8 clear and disable cache . Normally, the cache should always be enabled. 2.7. Use of pointers -------------------- 2.8. Registers and absolute addresses ------------------------------------- 2.9. Output from the Compiler ----------------------------- The compiler produces a list file and an assembler code output file. The list file contains for each line the line number, the statement nesting depth (zero outside program units, incremented by one for each level of program unit, DO, IF, and ALLOCATE), an indication whether declarations occured in the line, and the source text. The nesting depth helps to find begins and ends, e.g. of loops, which belong together. It is good practice to make the nesting levels visible already when typing the source file by indentations. The declaration indicator "$" serves as a warning against unintentional implicit declarations, e.g. against misspelt variables. The exact syntax of the generated assembly code can be controlled with the option CODE=... The assembler code file contains very readable code. If the option MIX=ON is chosen, declarations result in comment statements (beginning with "***") which give the name of the variable, its location, data type etc. This provides the information required for dumps on variable name level. 2.9.1. Sample Program Listing - - - - - - - - - - - - - - - The program has been compiled with OPTIONS LIST=ON (see next paragraph). The following is the contents of the file SINUS.LIS produced by the compiler: 1 0 OPTIONS LIST=ON 2 1 $ PROGRAM SINUS 3 1 $ PARAMETER (N=50) 4 1 $ INTEGER II(2) 5 1 $ TYPE *,'ONLY CONSOLE(0), ALSO FILE(1), ALSO VAX(3) ?' 6 1 ACCEPT *,MODE 7 1 $ TYPE *,'START !' 8 1 II(1)=1 9 1 II(2)=1 10 2 IF(BTEST(MODE,0)) 11 1 * OPEN(21,FILE='LOCAL::SINUS.DAT',STATUS='UNKNOWN') 12 2 IF(BTEST(MODE,1)) 13 1 * OPEN(22,FILE='SINUS.TST',STATUS='NEW',CARRIAGECONTROL='LIST') 14 2 $ DO I=1,N 15 2 $ X=RNDM(II)-0.5 16 2 $ X2=(1.-X)**2 17 2 TYPE 1000,I,SIN(I),X,X2 18 3 IF(BTEST(MODE,0)) WRITE(21,1000) I,SIN(I),X,X2 19 3 IF(BTEST(MODE,1)) WRITE(22,1000) I,SIN(I),X,X2 20 2 1000 FORMAT(I5,3F10.5) 21 1 ENDDO 22 1 IF(BTEST(MODE,0)) CLOSE(21) 23 1 IF(BTEST(MODE,1)) CLOSE(22) 24 0 END Total Errors 0 Warnings 0 The first column gives the source line number. The second column indicates the statement nesting level (nested DOs, IFs etc.). A '$' in the third column, if present, indicates that some declaration has been made in this line (e.g. for the new variables I, X, X2 which are undeclared so far in the lines 14-16); it serves as a sort of warning. If undeclared variables would appear on the right-hand side of expressions, an explicit WARNING was issued. 2.10. Compiler Directives ------------------------- Directives which control the listing, debugging, and code generation options can be given within the source file. 2.10.1 OPTIONS directive - - - - - - - - - - - - - This directive allows to select between various code generation options available in the RTF/68K compiler - regarding the syntax of the generated assembly code, the kind of floating point hardware/emulation used, the position-independence of the generated code, and debugging features. Furthermore, various listing options may be selected. Syntax: OPTIONS option,option,... where 'option' may be any of the following separated by commas (order irrelevant, but in case of contradicting options the last one is valid): SIZE = LONG | SHORT LONG has to be selected if the ----- code size of the compiled module exceeds 64 kB, i.e., no PC-relative addressing posible. SHORT (default) generates always position independent code, LONG only in the case of CPU=68020. LIST = ON | OFF | INCLUDE ON switches source-code listing --- on, but include files are not listed. INCLUDE lists source with include files. OFF (default) lists only errors and warnings. FLOAT = NS | NSAC | HARD | NS and NSAC generate special inline code for the NS16081 floating SOFT point chip - NS for the DATASUD CPUA1 ---- and NSAC for the ELTEC SAC, resp. HARD generates instructions for the MC68881 floating point coprocessor. It is only available for CPU=68020. SOFT (default) uses the software emulation package. - In all cases, IEEE number format is used. MIX = ON | OFF ON transfers the source lines and --- the memory allocation of the variables to the generated assembler code (as comment lines). OFF (default) generates no such comments. CODE = CPM | M68MIL | VERSA | CPM generates the assembly syntax - - - - - - - - of the CP/M-68K assembler, M68MIL AZTEC | OS9 the syntax of the M68MIL assembler - - - - - from CERN, VERSA the syntax of the Motorola assembler under VERSADOS, AZTEC the syntax of the assembler of the AZTEC C system, and OS9 the syntax of the assembler of the OS9 operating system. Note that 'syntax' also reflects partially the features of the native operating system. The default depends on the respective native or host system. DEBUG = ON | OFF ON generates additional code for --- comfortable debugging of the program which imposes a small runtime overhead. OFF (default) generates plain code. CHECK = ON | OFF ON (default) causes called routines -- to check the number of actual arguments they obtain as well as stack limits. OFF omits such checks which otherwise cost about 7 usec per call. COMMON = ABSOLUTE | INDIRECT ABSOLUTE (default) means that blank -------- and labeled commons are accessed by absolute addresses. With INDIRECT they are referenced indirectly via a PC-relative pointer. - Pointer based commons are always referenced indirectly, numerical commons are always referenced absolute, regardless of the COMMON option. LINK = ABSOLUTE | RELATIVE ABSOLUTE (default) means that absolute -------- references to external routines are generated. RELATIVE generates relative references, i.e. modules remain position-independent even after linking. It imposes some runtime overhead. RELATIVE offers a way to overcome the size limitation for position independent code for 68000/68010. CPU = 68000 | 68010 | 68020 Selects the CPU type for which code ----- ----- is generated. 68000/010 are treated identically and are the default; code generated this way runs also on the 020. In 68020 mode, additional instructions and addressing modes are utilized which speed up execution. FB = HARD | SOFT Selects if a subset of Fastbus routines ---- is executed by special hardware of software (default). LENGTH = FULL | 72 Selects truncation of the source line ---- input to 72 columns. The default FULL accepts all of the source. MULDIV = SHORT | LONG Selects whether INTEGER multiply and ---- divide are performed with full 32-bit arithmetics (default) or not. SHORT gives faster code (MULS/DIVS instructions on the 68000 instead of software emulation). For the 68020, this option has no effect; 32-bit precision is always used. SAVE = ALL | NORMAL With ALL, all variables are kept in ------ permanent storage, i.e. they will remain valid after routine exit and new entry; NORMAL keeps the variables which are not otherwise declared on the stack, thus providing re-entrancy (default). INTSIZE = 2 | 4 Determines whether INTEGER and LOGICAL - variables that are not explicitely sized occupy 2 or 4 (default) bytes. REGSAVE = NORMAL | OUTSIDE Determines whether the registers are ------ saved by the called routine (default) or by the caller. The latter is for compatibility with some other language implementations. NUMPAR = ON | OFF Determines whether the number of actual -- arguments is passed (default) to the called routines or not. INIT = ON | OFF Determines whether the code for system -- dependent initialization is generated (default) or suppressed. C_CALLS = ON | OFF Determines whether calling sequences -- are compatible with C (default) or not. Valid only for CODE = OS9. REGUSE = ON | OFF Determines whether A0 and A1 are used -- to hold the first two arguments within routines (default) or not. For example, OPTIONS LIST=OFF,SIZE=SHORT,MIX=OFF,FLOAT=SOFT,CODE=M68MIL, DEBUG=OFF,CHECK=ON,COMMON=ABSOLUTE,LINK=ABSOLUTE, CPU=68000,FB=SOFT,LENGTH=FULL,MULDIV=LONG,SAVE=NORMAL, INTSIZE=4,REGSAVE=NORMAL,NUMPAR=ON,INIT=ON,C_CALLS=ON, REGUSE=ON resembles the default for the cross compiler. More than one OPTIONS directive may appear in a program. The directive may be given at the very beginning of the module and in front of any program unit. It may start in column 1 or 7. 2.10.2 PUBLIC directive - - - - - - - - - - - - The PUBLIC directive controls the format of the compiled module and thus its adaptation to the operating system environment. - As stated above, RTF/68K code relies very little on an operating system. If PUBLIC is omitted, the stack and global space initialization is done by the generated module itself. If PUBLIC is given, this initialization is not done; instead, the module is then adapted to an environment with a minimum linker/loader and has dynamic linking capability (e.g. for interactive use). Syntax: PUBLIC entry,entry,... where 'entry' is the name of an entry point of one of the units contained in the module. A branch table is set up at the very beginning of the module pointing to these entry points. Only one PUBLIC directive is allowed. It must be either the first directive in a module or immediately follow OPTIONS directives if they are given at the beginning. The PUBLIC block may be in an INCLUDE file. PUBLIC may start in column 1 or 7. Please refer to the chapter on generated code for details on the module layout with/without PUBLIC option. 2.10.3 USE directive - - - - - - - - - - - Implementations under some operating systems (e.g. CP/M, OS9) require that all libraries used are made known to the system by the main program. This is the purpose of the USE directive. On other systems, this directive may act as a comment. Syntax: USE module,module,... where 'module' is the name of a program module. Typically, the module name is derived from the source file name of the module; e.g. module TEST from CP/M source file TEST.FTN . Subroutine calls are generated to the initialization entry points of all modules stated in USE. Only one USE directive is allowed. It is meaningful only in a file containing a main program. USE must be the first line of the file or must follow OPTIONS and PUBLIC if any of these is given. Please refer to the chapter on generated code for details on the module layout with/without USE option. 2.11. Compilation Error Messages and Warnings --------------------------------------------- RTF/68K gives message numbers together with message texts. Error messages are given in severe cases, warnings in cases which are not necessarily fatal. The format of the messages is as follows: ****** ERROR nnn IN LINE llll - LAST SYMBOL: sssss... ----- message text... listing of source line containing the error **** WARNING nnn IN LINE llll - LAST SYMBOL: sssss... ----- message text... listing of source line causing the warning nnn is the message number which corresponds to the message text in the second line. llll is the number of the source line where the error occured. The source symbol within the line which caused the error is also given; however, a symbol internal to the compiler may also show up here. When an error (not a warning) is detected, the compiler skips to the next source statement. However, due to the recursive compilation technique, recovery from errors is not always possible, and follow-up errors may be detected in subsequent statements. After a maximum of 20 errors, the compilation quits. There is no limit on number of warnings. The following is a table of the message numbers together with their meanings. 1: 'Internal compiler error - please contact expert' 2: 'Identifier list expected' 3: 'End of file expected' 4: 'Options list missing or illegal' 5: 'Unknown option' 6: 'Identifier expected' 7: 'END expected' 8: 'Data space too big for 68000 - use COMMON block' 9: 'Keyword misspelt' 10: 'Label doubly defined' 11: 'Error in FORMAT list' 12: 'Pointer must be address register here' 13: 'Indexing not allowed here' 14: 'All variables must be fully declared' 15: 'Variable array bounds illegal here' 16: 'Quoted string expected' 17: 'Data type specification expected' 18: 'IMPLICIT list misspelt' 19: 'This data type or size is not supported' 20: 'Data type or array bounds doubly defined' 21: 'Attempt to redeclare global variables' 22: 'Wrong size specification' 23: 'Not enough indices given for this variable' 24: 'Only five dimensions supported' 25: 'Array bounds required' 26: 'This variable cannot be EXTERNAL' 27: 'Slash or identifier expected' 28: 'Storage doubly allocated' 29: 'Parenthesis or identifier expected' 30: 'COMMON address affected only in current file' 31: '= constant expected' 32: 'Parenthesis expected' 33: 'Comma expected' 34: 'Only two EQUIVALENCE partners supported' 35: 'Illegal EQUIVALENCE partner' 36: 'Illegal formal argument' 37: 'Illegal DATA variable list' 38: 'Variable must be COMMON, SAVE, or global' 39: 'Only one variable per DATA value list supported' 40: 'Error in DATA value list' 41: 'Data type inconsistency in DATA list' 42: 'No statement after label' 43: 'Keyword used as variable name' 44: 'Illegal use of variable name' 45: 'Label expected' 46: 'Expression expected' 47: 'Illegal actual argument' 48: 'Data type inconsistency in assignment statement' 49: 'Illegal mixing of data types' 50: 'Constant too big or unsupported data type' 51: 'Unavailable index for side exit' 52: 'Control constant must be between 0 and 7' 53: '%VAL(...) or %REF(...) expected' 54: (unused) 55: (unused) 56: 'Syntax must be: ASSIGN label/variable TO variable' 57: 'Data type must be INTEGER*4' 58: 'Running out of registers - split expression' 59: 'Syntax must be: GOTO (label,...label) [,] expr' 60: 'Data type must be INTEGER' 61: 'Syntax must be: GOTO variable [,(label,...label)]' 62: 'Syntax must be: IF (expression) ...' 63: 'IF statement expected' 64: 'END IF expected' 65: 'Executable statement expected' 66: 'Syntax must be: IF (expr) label,label,label' 67: '(log.expr) expected' 68: 'END DO expected' 69: 'Assignment to loop variable expected' 70: 'Loop terminal or step expression expected' 71: 'Syntax must be: (unit[,form][,ERR=lab][,END=lab])' 72: 'Input/output list expected' 73: 'Syntax must be: ( [UNIT=] unit, [FILE=] file ...)' 74: 'Syntax must be: ( [UNIT=] unit )' 75: 'Format specifier expected' 76: 'String expected' 77: 'ERR=label expected' 78: '(formatlist) expected' 79: 'END=label expected' 80: '= expected' 81: 'Data type must be INTEGER or CHARACTER' 82: 'Unit specifier expected' 83: 'Variable expected' 84: '= expression expected' 85: 'Logical expression expected' 86: 'Character expression expected' 87: 'Arithmetic expression expected' 88: 'Why compare constants? ' 89: 'Concatenation operator // expected' 90: 'This operation is redundant' 91: '(variable) expected' 92: 'This variable is not specified so far' 93: '(expr,expr) expected' 94: 'Actual arguments list expected' 95: 'Autoincrement/decrement not allowed here' 96: 'Variable must be a pointer variable' 97: 'Integer constant expected' 98: 'Too many indices given for this variable' 99: 'Constant index expression expected' 100: '=expression expected - maybe keyword misspelt' 101: 'The generated code maybe not position-independent' 102: 'Register variables cannot be indexed' 103: 'Loop step expression is zero' 104: 'Only first array element allowed here' 105: 'Array bounds variable must be INTEGER*4' 106: 'Data type inconsistency in PARAMETER statement' 107: 'You cannot expect a result in this register' 108: 'Possible overlap with user-declared register' 109: 'Arguments of in-line routine must be declared' 110: 'Character string too long - truncated' 111: 'Incorrect FASTBUS coprocessor call' 112: 'Identifier illegal for statement function' 113: 'Substring operation (expr:expr) expected' 114: 'Syntax must be: ALLOCATE expr FOR var' 115: 'END BLOCK expected' 116: 'This label is undefined' 117: 'This DO loop label points backwards' 118: 'Available only on 68020' 119: 'Side exits not allowed for interrupt routines' 120: 'Formal parameter re-positioned at ENTRY' 121: 'Formal parameter doubly declared' 3. THE RUN-TIME LIBRARY ----------------------- 3.1. Main Features ------------------ - Floating point package which implements (in single precision) the basic algebra and the standard functions. The floating point number format is the 32-bit IEEE format. - Input/output to the host computer's file system, independent of the kind of hardware link between host and microprocessor. OPEN and subsequent WRITE e.g. to a magnetic tape connected to the host is possible. A small support program (which is contained in the RTF/68K package) has to run on the host for this purpose. - Routines for dynamic routing of interrupts to Fortran subroutines. They simply exchange entries in the interrupt vector. This provides very fast interrupt response directly by Fortran routines. The vector locations may be defaulted to a dummy routine which catches unassigned interrupts. 3.2. Details of Specific Routines --------------------------------- 3.2.1. Floating Point Package (FPP) ----------------------------------- The package implements arithmetics for single-precision (32 bit) floating point numbers. On such numbers, it performs load, store basic algebra type conversions standard arithmetic functions. The package resembles the NS16081 floating point chip. Two entry points, _F09 and _F11, are provided to emulate the NS16000 F9 and F11 instruction formats. Both are parametrized by 16-bit words which follows the JSR _F09/_F11 instruction in the code. The external number format is in IEEE 32-bit format : 31 30 23 22 0 +--+------------+----------------------------+ |sg| exponent | mantissa | +--+------------+----------------------------+ The number is negative if sg=1; exponent and mantissa are not complemented. The offset of the exponent is 127; i.e., $40000000 represents 2.0, $BFC00000 represents -1.5, etc. The most significant mantissa bit is always set and omitted in the representation. An all-zero number represents the floating point zero. If the exponent is zero, the entire number must be zero. This convention is chosen in order to recognize most integers (up to 2**23) that are by error handled as floating point numbers. Two entry points, _FSV and _FRS, are provided for saving and re- storing FP registers to the caller's save area, from -172(A6) on- wards. (Please replace 'A6' with 'A5' for OS9 implementations). The JSR _FSV/_FRS is followed by a word the n'th bit of which, if set, indicates that Fn should be saved/restored (n=0-7). A6 is the local base register as usual in the CERN conventions for 68k. The package is employed by the RTF/68K Real-Time Fortran-77 compiler for 68k processors. All processor registers remain unchanged unless an external result (not in FP register) is produced which is in D0. Arguments and results are passed via D0. Like the NS16081, only the FP compare generates the correct condition codes in the main processor's CCR. The format of the opcode word following the JSR _F09/_F11 is as follows. Note that the lower and upper byte are swapped! 7 3 2 0 14 13 11 10 9 8 For _F09: +----------+----------+------+--+----+ | gen1 | gen2 | op | f| i | +----------+----------+------+--+----+ The id-word is $3E. Gen1 and gen2 denote source and destination, resp., namely: 0 to 7 FP registers F0 to F7 15 data external to FPU (in D0) Op denotes the opcode: 0 for MOVIF, 5 for MOVFI. Only these two type-converting moves are implemented. F denotes the FP data type. Only f=1 (single precision) is im- plemented. I denotes the integer data type. Only i=3 (32-bit word) is im- plemented. 15 11 10 6 5 2 1 0 For _F11: +----------+----------+--------+--+--+ | gen1 | gen2 | op | 0| f| +----------+----------+--------+--+--+ The id-word is $BE. Opcodes are 1 for MOVEF 0 ADDF 4 SUBF C MULF 8 DIVF 5 NEGF D ABSF 2 CMPF . Please refer to the National Semiconductors NS16081 data sheet for further details. Error messages (floating point overflow, divide by zero, ALOG or SQRT negative argument, EXP overrange, non-implemented FP instr) appear on the terminal. The program is aborted. The internal (unpacked) representation of this package uses two longwords for the eight pseudo FP registers F0-F7. The FP regist- ers reside in the 32 bytes from -172(A6) onwards - save/restore of these software registers are thus dummy operations. 31 30 8 7 0 +--+-----------------------------+-----------+ | 1| mantissa |extra bits| +--+-----------------------------+-----------+ 31 16 15 14 8 7 0 +--------------------+--+--------+-----------+ | unused |sg| 0 | exponent | +--------------------+--+--------+-----------+ Execution times of the software package are about 60 usec for basic arithmetic and about 700 usec for the standard functions on a 10 MHz 68000. This is abou 20 times slower than the 16 MHz 68881 FP coproc- essor used with the 68020. As programs are seldom pure floating point, the overall speed remains still reasonable. For example, a matrix inversion algorithm (Gauss elimination without pivot search) executes 12 times slower on a 10 MHz 68000 than on a VAX 11/780. 3.2.2. Input/Output Package --------------------------- The input/output package provides the interface from the Fortran I/O statements to the hardware involved. This interface comprises I/O conversions, formatting, associating logical channels (units) with files via OPEN/CLOSE, routing of requests to the target hardware devices, queueing of requests if necessary, and performs the actions specific to the devices. The package is written in Fortran and also includes a small assembler part. 3.2.3. Dynamic Interrupt Handling --------------------------------- In addition to the static assignment of subroutines to interrupts during compilation described in 2.3., dynamic activation and de-activation at run-time is provided by two subroutine calls: CALL ACTIVATE(vectornumber,routine) CALL PASSIVATE(vectornumber). Vectornumber values are hardware-dependent as mentioned in 2.3.; routine is a subroutine name which is declared EXTERNAL or is a formal parameter of the routine calling ACTIVATE. The interrupt vector entry given by vectornumber is set to point to routine. The effect of ACTIVATE is cancelled by PASSIVATE. The interrupt vector entry is then reset to its default. This is represented by the library subroutine IDLE which just types the message "unexpected interrupt on line nnnn" in case of an interrupt not assigned to another routine. Exception: the trace interrupt (vectornumber 9) is defaulted to the standard tracing routine. This routine, TRACE, displays program counter, status, data and address registers, and then prompts the user for further action. It may be superseded by ACTIVATEing a user routine as described above. 3.2.4. CAMAC Address Function ----------------------------- The integer function ICAMAD calculates the address of a CAMAC module inside the 68K address space. Arguments are crate, station, subaddress, and function. Crate number zero means the local crate where the ACC-processor resides in (standard case), numbers 1-7 mean one of the crates along the branch if the processor acts as a branch controller via a branch interface. All parameters are checked for validity. Abort and traceback are initiated in case of error. Example: integer*2 @icam integer*4 @jcam @icam=icamad(b,0,n,a,f,1) ! calculate address of b,c=0,n,a,f ! .. the 1 stands for 16-bit size idat=icam ! read from that module ... idat2=icam ! second time with same address @jcam=icamad(b,1,m,a,16,0) ! calculate an address in crate 1 ! .. the 0 stands for 24-bit size jcam=0 ! write zero into module ... jcam=icam ! direct transfer between modules. 18 FEB 84 HVDS/CERN Description of Distribution Tape and Diskette for ------------------------------------------------- RTF/68K Fortran-77 Compiler and Runtime System ----------------------------------------------- 1. Scope -------- The information stored on this tape and/or floppy disk has been grouped into three categories: 1) A Fortran-77 CROSS COMPILER generating code for Motorola 68K family microprocessors, with or without use of a NS16081 floating point unit. 2) The same compiler as a NATIVE COMPILER for 68K family microprocessors. 3) A Fortran-77 RUN-TIME LIBRARY on which the code generated by the compiler(s) relies for execution on the 68K target system. The software can be executed WITHOUT MODIFICATIONS on the following systems: a) The CROSS COMPILER on any VAX running a VMS operating system. b) The NATIVE COMPILER on VME systems containing an ELTEC EUROCOM-3 (and eventually extra memory boards) with at least 3/4 MBytes of memory, running under CP/M-68K with floppies and/or Winchester disk. c) The same environment as under b) may be used as the target system. 2. Adaptation to other systems ------------------------------ One of the intentions behind RTF/68K is PORTABILITY. This applies for the compiler as well as for the runtime system. For this reason the distribution media contain SOURCES of various language levels, RELOCATABLE files, and EXECUTABLE files. In fact, the dependence of the runtime system on the environment is confined in one small assembler file. The entire runtime system is position-independent. The native compiler, interfacing to the environment through this same runtime system, needs little extra. Also, the implementor might like to modify the user and file-system interface of the compiler which is a small Fortran program completely separated from the internal action of the compiler which is confined in a 120-kBytes object. This object constitutes the body of the native compiler and is simply called as a Fortran INTEGER FUNCTION expecting line-by-line I/O from the main program. All sorts of tricks like interactive compiling, integration with an editor etc. can be played by modifying the small Fortran program. The cross compiler, finally, can be installed on any computer (32-bit) with a big enough Fortran-77 environment. It is actually running on VAX and NORD computers - and of course 68K ... 3. Media -------- The physical parameters of the distribution media, and the way they have been written, are as follows: a) The distribution tape is 1600 BPI, with the ANSI label RTF68K, and is written on a VAX with a series of COPY commands. b) The distribution diskette is 5 1/4 inch, 80 track, double side, double density, and has the CP/M-68K file structure. 4. Types of files ----------------- In the hierarchy of sources, the source of the compiler in the SYNTAX-ORIENTED language PRAGMA ranges highest. It is included on the tape for reference only as the compiler-compiler required to process them is normally useless and distributed on request only. This source form is contained in the files with extension ...PRA . - The source is the same for cross and native versions. Generated by the compiler-compiler are the derived Fortran sources of the compiler. They come together with the compile-time library which is also in Fortran. Here, the cross and native versions differ because the native version has been extensively speeded up in a way which is not available in standard Fortran-77 but in RTF/68K. The cross sources have names RTF...FOR and the native sources RTF...FTN . The COMMON block specifications are found in the files CPRAGMA.FOR, CPRAGMA.FTN, and NCPRAGMA.FTN . These sources are then further processed with Fortran compilers generating code either for the VAX (VAX/11 Fortran) or the 68K (RTF/68K cross compiler). For the VAX, the relocatable object files are skipped and the file RTF68K.EXE is given as the end product. This is invoked on the VAX with the command file RXFOR.COM . Implementors for other host systems might start their (usually minor) adaptation work with the Fortran sources. For the 68K, more levels of derived code are provided for ease of adaptation. The six files RTF...ASM contain the assembly code generated from RTF...FTN by the RTF/68K cross compiler. They can be further processed with the M68MIL assembler which then yields the files RTF...CUF which are ready to be linked with the run-time library. The CUFOM/PREPUSH/UNIPUSH package can be used for this purpose. NOTE: M68MIL, CUFOM, PREPUSH, and UNIPUSH may be obtained from CERN free of charge for non-profit organizations. The run-time library is made of two Fortran parts (RTFLIB.FTN and RTFCPM.FTN) and two assembler parts (RTFAUX.ASM and FPSOFT.ASM). All four are compiled together with the RTF/68K cross compiler, yielding RTFLIB.ASM, which by further processing with M68MIL yields RTFLIB.CUF as input to the CUFOM/PREPUSH/UNIPUSH chain. The executable native compiler, finally, may be obtained in various ways, differing in how the parts are arranged in memory. The executable file delivered here, RTF68K.COD, has been obtained by linking RTFMAIN.CUF, RTFSYM.CUF, RTFCOMP.CUF, and the four files RTF68Kn.CUF. The starting address of the module is 28000(hex), just above RTFLIB which is used and starts at 20100(hex). - RTF68K.COD consists of S-records. It can be downloaded in the standard way into any target system or in EPROMs if desired. Also, the way for importing S-record objects into the CP/M-68K environment described further below may be used, or the executable file RTF68K.68K on the floppy may be used directly. An alternative is to link a) RTFMAIN.CUF with XJFOR.CUF and RTFSYM.CUF (see below for these two files), b) RTFCOMP.CUF with the four RTF68Kn.CUF and RTFSYM.CUF, and c) RTFLIB.CUF separately and keep the three modules at different memory regions if desired. In addition to compiler and run-time library, the following information is found on tape and floppy. o SINUS.FTN is the Fortran source of a test program which does some arithmetic as well as I/O to the CP/M-68K file system. Its derivatives are SINUS.ASM and SINUS.CUF, which has to be linked with RTFLIB.CUF . o HOST.FTN and XFER.FTN are utilities for transparent mode and file transfer with the host computer, resp. o Various command files with extensions ...COM for VMS and ...SUB for CP/M. o Various documentation files with extension ...TXT, including the one you presently read. o A file RTFSYM.ASM which states all the entry points to RTFLIB separately. This is useful when the run-time library is loaded independent from the user programs (e.g. in EPROMs or in order to save space on the floppy). Note that the library is re-entrant, so one copy for all tasks is sufficient. Change the address in RTFSYM.ASM if you want to keep RTFLIB at a different address. RTFSYM.CUF is the derivative of this file and can be used alternatively instead of RTFLIB.CUF for linking. o A file XJFOR.ASM which states the single entry point to the body of the compiler, JFOR, separately. This is useful under similar circumstances as for RTFLIB, is you want to keep the I/O independent, big part of the compiler separately from the small main program. o A support program for file I/O on the host computer. 9 Oct 85 HvdS FILES USED IN CONJUNCTION WITH RTF/68K (ON VAX COMPUTERS) ========================================================= 0. Remarks ----------- 0.1. Preparation on the VAX In order to use the DCL commands described in this text, make sure that at login the following definitions are made: $ DEFINE RTF$DIR [VDSCHMITT] $ RTF :== "@RTF _ " $ RXASM :== @RXASM.COM $ CL*INK :== "@CLINK _ " $ SUP :== @SUP.COM and if you want to use the compiler-compiler, in addition: $ HPRAGMA :== @HPRAGMA.COM $ NPRAGMA :== @NPRAGMA.COM . 0.2. Different versions of the CERN cross assembler The old version M68MIL.EXE is not capable of handling the special 68020 addressing modes. This version uses the logical names SRCCODE for the source file and INCLUDE for the secondary source. It is bug-free. It is invoked by the command files described below. The new version M68MIL.EXE includes the extras for the 68020. It uses SRCFILE and INCFILE, resp. It is, however, not released officialy yet. 0.3. How to obtain the CERN cross software officially The cross assembler and cross linker package described below and associated documentation may be obtained officially from CERN via: J. Blake CERN Div. DD CH-1211 Geneva 23 Switzerland It is free for non-profit organizations. The cross Fortran (RTF/68K) package is available directly from the author: H. von der Schmitt Physikalisches Institut Philosophenweg 12 D-6900 Heidelberg W.-Germany It is free for non-profit organizations. 1. Compiler-compilers ---------------------- 1.0. 'Grandfather' PRAGMA.EXE this is an older implementation of PRAGMA which generates plain Fortran out of .PRA sources. It is used only to generate new compiler-compilers, but not new compilers any more (which are now table-driven, i.e. no longer plain Fortran). PRAGMA could be replaced soon by HPRAGMA, see below. PRAGMA.COM command file which invokes the former by the DCL command : $ PRAGMA PragmaSource where PragmaSource is a file which has the extension .PRA; but the extension is NOT given in the command. PRAGMA.RUN auxiliary file which just contains the name of the .PRA or .FTN file currently processed (used by compiler-compilers and compilers). 1.1. Compiler-compiler for generating host (cross) version of RTF/68K HPRAGMA.PRA this is the source of the compiler-compiler which generates host version compilers. HPRAGMA.FOR generated out of the former by PRAGMA.EXE for further processing with VAX Fortran. HPRAGMA.OBJ object code from HPRAGMA.FOR by using the DCL command: $ FORT HPRAGMA HPRAGMA.LIS listing generated by PRAGMA.EXE. If you request a listing in the above FORT, another file with this name will be generated. PRAGVXTAB.FOR Fortran source of the compile-compile time library used by HPRAGMA (and also NPRAGMA, see below. PRAGVXTAB.OBJ object code from PRAGVXTAB.FOR. HPRAGMA.EXE VAX executable file of the host version compiler-compiler, obtained by linking with the DCL command: $ LINK HPRAGMA,PRAGVXTAB HPRAGMA2.FOR Fortran source of pass 2 of the host version of the compiler-compiler. HPRAGMA2.OBJ object code from HPRAGMA2.FOR. HPRAGMA2.EXE VAX executable file of pass 2 of the host version compiler-compiler, obtained by linking with the DCL command: $ LINK HPRAGMA2 HPRAGMA.COM command file which invokes HPRAGMA and subsequently HPRAGMA2 to generate a new host version compiler by the DCL command: $ HPRAGMA PragmaSource 1.2. Compiler-compiler for generating native (running on 68K) version of RTF/68K (Note that there are only minor differences between the files described here and the files under 1.1.) NPRAGMA.PRA this is the source of the compiler-compiler which generates native version compilers. NPRAGMA.FOR generated out of the former by PRAGMA.EXE for further processing with VAX Fortran. NPRAGMA.OBJ object code from NPRAGMA.FOR by using the DCL command: $ FORT NPRAGMA NPRAGMA.LIS listing generated by PRAGMA.EXE. PRAGVXTAB.FOR \ see PRAGVXTAB.OBJ / above NPRAGMA.EXE VAX executable file of the native version compiler-compiler, obtained by linking with the DCL command: $ LINK NPRAGMA,PRAGVXTAB NPRAGMA2.FOR Fortran source of pass 2 of the native version of the compiler-compiler. NPRAGMA2.OBJ object code from NPRAGMA2.FOR. NPRAGMA2.EXE VAX executable file of pass 2 of the native version compiler-compiler, obtained by linking with the DCL command: $ LINK NPRAGMA2 NPRAGMA.COM command file which invokes NPRAGMA and subsequently HPRAGMA2 to generate a new native version compiler by the DCL command: $ NPRAGMA PragmaSource 2. Compilers ------------ 2.1. Host (cross) version compiler RTF68K.PRA is the source of the RTF/68K compiler. All changes to th compiler are made here. This source is common to the host and native version. RTF68K.MIX intermediate file (ASCII) generated from RTF68K.PRA by HPRAGMA. RTF68K.FOR Fortran generated from RTF68K.MIX by HPRAGMA2. RTF68K.DAT compiler tables (binary) generated from RTF68K.MIX by HPRAGMA2. Note that RTF68K.FOR and RTF68K.DAT must always be consistent, i.e. generated in the same compiler-compiler run. RTF68K.LIS listing file from RTF68K.PRA. RTF68K.OBJ object file generated from RTF68K.FOR by the DCL command: $ FORT RTF68K RTF.FOR Fortran source of the I/O interface of the host version compiler. RTF.OBJ object file of the former. RTFCOMP.FOR Fortran source of the compile-time library of the host version compiler. RTFCOMP.OBJ object file of the former. TCPRAGMA.FOR INCLUDE file used by RTF68K.FOR, RTF.FOR, and RTFCOMP.FOR. RTF.EXE VAX executable file of the host version compiler, obtained by linking with the DCL command: $ LINK RTF,RTFCOMP,RTF68K RTF.COM command file which invokes RTF and subsequently the CERN cross assembler by the DCL command: $ RTF/option/option... FortranSource where FortranSource is a RTF/68K source file with or without extension. The default extension is .FTN . If options are given, they must follow, with or without spaces inbetween, the word RTF. The SourceFile is required and must be the last item in the command. The following options are available: /[NO]LIST /LIST=ListFile /[NO]SHOW /SHOW=INCLUDE /[NO]OUTPUT /OUTPUT=OutFile /[NO]MACHINE_CODE /MACHINE_CODE[=AssListFile] as well as the specific options handled by the compiler itself rather than this command: /SIZE=SHORT | LONG /LIST=ON | OFF | INCLUDE /FLOAT=NS | NSAC | HARD | SOFT /MIX=ON | OFF /CODE=CPM | M68MIL | VERSA /DEBUG=ON | OFF /CHECK=ON | OFF /COMMON=ABSOLUTE | INDIRECT /LINK=ABSOLUTE | RELATIVE /CPU=68000 | 68008 | 68010 | 68020 /FB=HARD | SOFT /LENGTH=FULL | 72 /MULDIV=SHORT | LONG /SAVE=ALL | NORMAL /INTSIZE=2 | 4 /REGSAVE=NORMAL | OUTSIDE /NUMPAR=ON | OFF /INIT=ON | OFF Please refer to the RTF68K manual for details on these options. The /options may be missing; e.g.: $ RTF SINUS which is equivalent to $ RTF/NOLIST/NOSHOW/NOMACHINE_CODE - /SIZE=SHORT/FLOAT=SOFT/MIX=OFF - /CODE=M68MIL/DEBUG=OFF/CHECK=ON - /COMMON=ABSOLUTE/LINK=ABSOLUTE - /CPU=68000/FB=SOFT/LENGTH=FULL - /MULDIV=LONG/SAVE=NORMAL/INTSIZE=4 - /REGSAVE=NORMAL/NUMPAR=ON - /INIT=ON SINUS 2.2. Native version compiler (Note that there are only minor differences between the files described here and the files under 2.1.) RTF68K.PRA identical to the one under 2.1. RTF68K.MIX intermediate file (ASCII) generated from RTF68K.PRA by NPRAGMA. DIFFERENT from 2.1.! RTF68K.FTN Fortran generated from RTF68K.MIX by NPRAGMA2. RTF68K.TAB compiler tables (hex ASCII) generated from RTF68K.MIX by NPRAGMA2. Note that RTF68K.FTN and RTF68K.TAB must always be consistent, i.e. generated in the same compiler-compiler run. RTF68K.LIS listing file from RTF68K.PRA. RTF.FTN Fortran source of the I/O interface of the native version compiler. RTF.CUF object file generated from RTF.FTN by the DCL command: $ RTF RTF RTFCOMP.FTN Fortran source of the compile-time library of the native version compiler. RTFCOMP.CUF object file of the former (which INCLUDEs RTF68K.FTN). TCPRAGMA.FTN INCLUDE file used by RTF68K.FTN, RTFMAIN.FTN, and RTFCOMP.FTN. RTF.COD S-record absolute file obtained by the following DCL command: $ CLINK/START=StartAddress RTF,RTFCOMP . 3. Further tools used on the host --------------------------------- 3.1. CERN cross assembler M68MIL.EXE VAX executable file of the 68000/68020 cross macro assembler written by H. v.Eicken (see note 0.2. above). M68MIL.DAT \ auxiliary files M68MIL.TXT / used by M68MIL.EXE RXASM.COM command file which invokes the cross assembler alone (without Fortran) by the DCL command: $ RXASM AsmSource where AsmSource is an assembler source file with extension .ASM; but the extension is NOT given in the command. 3.2. CERN cross linker CUFLINK.EXE VAX executable file of the cross linker. PREPUSH.EXE VAX executable file of the cross tool which makes absolute addresses. UNIPUSH.EXE VAX executable file of the S-record generator. CLINK.COM command file which invokes the three tools described above in this order, by the DCL command: $ CLINK [/options] CufomFile,[CufomFile...] where CufomFile are object files produced by the CERN cross assembler. They have the extension .CUF by default. The options are: /SYM=file includes the Fortran library entry points specified in file.CUF /NOSYM omits inclusion of default entry point specification UNISYM.CUF /START=address or /START=(address,address,...) states the starting addresses of the various sections of the program. If START is not given, the addresses are entered interactively. 4. Runtime libraries -------------------- The RTF/68K runtime library covers a variety of 68K target systems which use the same processor type, but different serial I/O chips, address maps, and basic software environments. The runtime library smoothens out these differences and is thus necessarily slightly hardware dependent. The code sizes is about 28 Kbytes. The runtime library is reentrant (also sharable), and position-independent. One copy is thus sufficient for several programs. If the library is located in a global space which is accessible to several processor modules, even of different kinds (e.g. in a VME crate), still one copy is sufficient. For this reason, the library is normally linked and loaded separately from the user programs, while the user programs are linked only with the entry point specification of the library by: $ CLINK UserCufomFile,... The alternative of linking with the real code of the library exists also: $ CLINK/NOSYM UserCufomFile,...UNILIB UNISYM contains, apart from the definition of library entry points, also a system startup routine which is entered at the beginning of the user program. This routine allows to specify the hardware/software environment under which the program will run. UNILIB, universal in nature, can then adapt itself to this environment at runtime. CPU modules currently supported are: Datasud CPUA1 Motorola MVME130 Xycom IPROTO Eltec EUROCOM 3 Eltec SAC Microsys CPU06/07 NIKHEF FAMP ALEPH Event Builder Further hardware modules supported are (all VME): Datasud 4COM quad serial I/O Ikon DR11W DR11W emulation for link to DEC hosts . Note that by virtue of the 68K's memory mapped I/O, which is fully supported by the RTF/68K language, it is easy to access any hardware devices, e.g. : Datasud CAMAC CAMAC branch driver . It is forseen that the user extends UNILIB towards new processor modules. The files are therefore described here in terms of possible modification by the user. 4.1. Files that need no further modification COMLIB.FTN big, general part of the runtime library. FPSOFT.ASM floating point software emulation and NS16081 support routines (not used if MC68881 present). MDSOFT.ASM 32-bit MUL/DIV software emulation routines (not used by the 68020, or if compilation option MULDIV=LONG is NOT selected). RTFCPM.FTN interfacing routines to the CP/M-68K file system. DUMCPM.FTN dummy version of the former. 4.2. Files that need modification for extensions UNILIB.FTN framework of the runtime library which makes global declarations and INCLUDEs the files DRIVER.FTN, COMLIB.FTN, FPSOFT.ASM, MDSOFT.ASM, and DUMCPM.FTN (or RTFCPM.FTN if running under CP/M-69K). UNILIB.CUF object file generated from UNILIB.FTN by the DCL command: $ RTF UNILIB UNILIB.COD S-record absolute file generated from UNILIB.CUF by the DCL command: $ CLINK/NOSYM/START=StartAddress UNILIB DRIVER.FTN Fortran source of the set of driver routines for character and block oriented I/O, as well as for return to the software environment. UNISYM.ASM entry point specification for UNILIB, and system startup routine (INCLUDEs file UNINIT.ASM). It must be consistent with the addess given in the CLINK step above! UNISYM.CUF object file generated from UNISYM by the DCL command: $ RXASM UNISYM 5. Example of program preparation --------------------------------- Assume you have a MVME130 board together with a MVME222 memory board jumpered for continuous 2 Mbytes starting at 800000 (hex). You want to execute the Fortran program SINUS (source SINUS.FTN). 1) Prepare the runtime library (e.g.) for starting address 800000 as follows: $ RTF/LIST=TT: UNILIB $ CLINK/START=800000 UNILIB 2) Make the entry point specification consistent with this starting address and choose a proper initial stack pointer by editing UNISYM.ASM accordingly. The symbol START should by EQUed with 800000 and the symbol TOPn with (e.g.) A00000, using one full megabyte of the MVME222. Then: $ RXASM UNISYM 3) Prepare the user program SINUS for starting address 808000 (i.e. safely above the library) as follows: $ RTF SINUS $ CLINK/START=808000 SINUS 4) You can now download the S-record files for the library and for SINUS separately. Have the terminal connected to serial port 1 and the VAX connected to serial port 2 of the serial I/O board belonging to the MVME130. Be logged onto the VAX using TM (transparent mode). Caution: when the MVME130 has been reset or after power-off, initialize the MVME222 properly and set the VME bus size to 16 bits (38 in address FFFB003B of the MVME130). After leaving transparent mode, do: a) LO;X=TY UNILIB.COD to download the library, b) LO;X=TY SINUS.COD to download the user program. 5) You can now start execution of SINUS simply by: RM D0 2 (if you have assigned CPU ident 2 to this MVME130 in UNISYM) GO 808000 6) If you want to modify the user program, edit the .FTN source file, then repeat steps 3) and 4b) only; the library needs not be touched again (unless your program has overwritten it!). 7. Installing the software on the VME system from VAX ----------------------------------------------------- If it is not possible to use the floppy disk directly, follow the procedure described below to bootstrap the files from tape, via the host computer, into your VME system. 1) Copy the tape to disk on your host computer. 2) Have the second RS232 port of the VME system connected to your host computer. 3) Boot CP/M. 4) Go back to the monitor with the MONLOOP program which is on your CP/M system, or by pressing the SOFTWARE ABORT button. 5) Enter transparent mode with TM. 6) Login on your host computer and give the @SUP command. 7) Exit transparent mode. 8) Download the runtime library with the command LO2;=L RTFLIB.COD . 9) Download the file transfer utility with LO2;=L XFER.COD . 10) Execute the utility by GO 28000 . 11) The utility answers with (d is drive number, e.g. B:) : give local file name: enter d:XFER.68K give host file name: enter XFER.COD direction local to host? enter N conversion to command file format? enter Y 12) Some S-records will be displayed. After that, the dialog goes on with: want to merge with another file? enter N another file? enter Y give local... enter d:RTFLIB.68K give host... enter RTFLIB.COD direction... enter N conversion... enter Y (S-records) want to merge... enter N another... enter N 13) The utility finishes and goes back to CP/M. You have now the files RTFLIB.68K amd XFER.68K on your floppy and don't need manual downloading any more. 14) Instead, give the CP/M commands d:RTFLIB and d:XFER to invoke now the transfer utility in the CP/M fashion and use it to get the the files over. Always give the local extension .68K when the host extension is .COD . Use it also to get text files over, then with the approprite extensions, e.g B:SINUS.FTN locally and SINUS.FTN on the host. The loading of the compiler RTF68K.68K will take a long coffee break (about one hour at 9600 Baud). 15) Enjoy your system. And don't forget to have the RTFLIB always in memory before starting a program... either you keep it in a place not touched by CP/M, or you always load the library before starting your porgram, most conveniently by using RUN progname to start your program. 2. Using RTF/68K ================ 2.1 Cross-compilation on the VAX -------------------------------- The preferred method at CERN for generating downline-loadable code for microprocessors is via the universal relocatable object format CUFOM. Consequently, the assembler preferrably used as the second pass of a cross Fortran compilation is the CERN one, M68MIL. The set of command procedures for the VAX described below (and contained on the distribution tape) makes use of this software. - You could as well use the Motorola or comparable cross-assemblers on the VAX and change the command procedures accordingly. Note that M68MIL and the CUFOM linker package are property of CERN. Any non-profit organization can get this software free of charge from CERN. It is normally not contained on the RTF/68K distribution tape. 2.1.1 Editing - - - - - - - Use e.g. the standard full-screen editor EDIT/EDT on the VAX. 2.1.0 Fortran Cross-Compilation - - - - - - - - - - - - - - - - The following command invokes the compilation step as well as the assembly step: $ RTF file where file is the name of the source file. The extension is assumed to be .FTN by default. Files produced are file.ASM for the intermediate assembly code, file.LIS for the source listing, and file.CUF for the relocatable object. 2.1.3 Cross-Assembly Only - - - - - - - - - - - - - The following command invokes the assembler only: $ @RXASM file where file is the name of the assembler source file. The extension is assumed to be .ASM and must not be given. Files produced are file.LAS for the assembly code listing and file.CUF for the relocatable object. 2.1.4 Cross-Linkage - - - - - - - - - - The following command invokes the three linker-steps (Linker, Prepusher, and S-format pusher): $ CLINK file[,file...] where file (and optionally file1) are relocatable object files. The extensions are assumed to be .CUF by default. The file produced is essentially file.COD for the absolute S-record image. Various listings could be produced during linking but are suppressed in this command procedure. CLINK will ask you interactively for the starting addresses of all sections of your program, e.g. code section, all COMMON sections etc. 2.1.5 Cross File Access - - - - - - - - - - - - The RTF/68K runtime library provides a protocol for file access to the VAX file system from the microprocessor with OPEN/CLOSE/READ/WRITE (see SINUS example). The physical link used is a terminal line. The VAX responds in the same way as to terminal I/O. In order to handle the protocol on the VAX side, a program SUPPORT is provided. When you intend to start a microprocessor program which requires the VAX file system, simply do: $ @SUP on the VAX before (e.g. in transparent mode through the microprocessor). All you have to provide hardware-wise for this purpose is to put the microprocessor with its terminal- and host ports into the existing line between terminal and VAX. 2.2 Native software development on CP-M/68K systems --------------------------------------------------- 2.2.1 Editing - - - - - - - Use e.g. the full-screen editor described below under utilities. 2.2.2 Fortran Compilation - - - - - - - - - - - - - The following command invokes the compilation step as well as the assembly step: d> FOR file The compile step produces a file named file.S which contains the generated assembly code. If OPTIONS LIST=ON or INCLUDE is given in the source file, also file.LIS is produced. The assembler AS68 of the CP-M/68K system is invoked automatically if the compilation was error-free. AS68 produces the relocatable object file.O out of file.S . 2.2.3 Assembly Only - - - - - - - - - - The following commands invoke the assembler AS68 only: d> ASS file d> ASL file The file names you specify MUST have extension .S . AS68 produces the relocatable object file.O out of file.S . In the first form of the command, no listing is produced; in the second form, a listing is written to the terminal. 2.2.4 Linking - - - - - - - The following command invokes the linker LO68: d> LINK file1 [file2 ...] It links one or several relocatable object files together with the RTF/68K runtime library entry points. The absolute image file1.68K is produced. Note that the runtime library itself is NOT contained in the image file; only its entry points are made known. Thus the images generated in this way rely on the runtime library being present when execution starts. - The reason for this strategy is a) to save disk space, b) the runtime library is sharable, i.e. only one copy is required in the system, and this copy could even reside in a reserved memory region hidden to CP-M (e.g. in a EPROM module). 2.2.5 Program Execution - - - - - - - - - - - - According to what was said about the linking with the runtime library, the following command loads the library before it loads the user program and starts execution: d> RUN file This command looks thus like a VAX command, and unlike a CP-M command. The absolute image file.68K is loaded and executed. ONLY if in your implementation the runtime library would reside in reserved memory regions, you could avoid the loading of the library and immediadely load and execute the user program by typing: d> file . 2.2.6 Cross File Access - - - - - - - - - - - - What was said under 2.1.5 holds of course also for programs which have been developed on the CP-M system, i.e. they can access the Host's file system by means of a small protocol program SUPPORT on the Host. By the way, SUPPORT is written in Fortran and can be used in conjunction with Hosts other than VAXes if desired. 08 Dec. 86 HvdS Installation of RTF/68K on OS9 systems ====================================== This note describes briefly how to install Real-Time Fortran-77 for 68k processors, RTF/68K, on OS9-68000 systems. There are two possible starting points: (1) the easy one, a distribution diskette containing all the necessary files, (2) the other, still simple one, using OS9 assembler source files on a host, which have to be downloaded into the OS9 system by some means (e.g. the COM package). The equivalent of the distribution diskette is produced by procedure (2). Procedure (1) ------------- I. Contents os the distribution diskette: 0. Documentation: 1. install.txt (the present document) 1. Compiler code: 1. rtfos9.r Relocatable code of the compiler 2. rtf Executable module, generated from 1. and others by l68 (see below) 2. Compiler tables: 1. rtf68k.dat Binary form of parser tables 3. Runtime library: 1. os9lib.f Source code of runtime library 2. os9lib.r Relocatable code, generated from 1. by RTF and r68 (see below) 4. Other stuff: 1. testos9.f Fortran source of small test/verification program 2. rtfbinos.r Relocatable code of conversion program for parser tables (ascii to binary) 3. rtfbinos Executable module of 2. 4. fstart.a Assembler source of initialization routine for RTF programs 5. fstart.r Relocatable code, generated from 4. by r68 Finally required for running the compiler are only rtf (1.2.) and rtf68k.dat (2.1.). Required for running a RTF program are, in addition to the user program, only os9lib.r (3.2.) and fstart.r (4.5.), together with the os9 system library, sys.l. II. Installation of the RTF system from diskette: Assume the distribution diskette is on drive /dx. Then install the RTF system by: 1. loading the compiler with load /dx/rtf 2. copying the parser tables to the working device with copy /dx/rtf68k.dat rtf68k.dat 3. copying the initialization routine with copy /dx/fstart.r fstart.r 4. copying the runtime library with copy /dx/os9lib.r os9lib.r III. Running the RTF compiler: Assume your RTF source file is /dy/zzzz.f . Note that the extension of the source should be .f by convention. 1. execute the compiler, generating assembly code, with rtf /dy/zzzz or rtf /dy/zzzz option,option,... where the options are as described in the RTF manual, e.g. cpu=68020,check=off. This step leaves the generated assembly code in /dy/zzzz.a . It also generates a source listing on the terminal. 2. process it further with the assembler r68 /dy/zzzz.a -qo=/dy/zzzz.r Note that the -q option should be used here in order to suppress warnings for unnecessary long forward branches. IV. Preparing the executable module: Link the system library, the initialization routine, the user code, and the runtime library together. Assume that the relocatable code has come out of step III., on file /dy/zzzz.r . 1. invoke the linker with the following command: l68 -l=sys.l fstart.r /dy/zzzz.r os9lib.r -o=zzzz or, if the total code will exceed 32k, with l68 -l=sys.l fstart.r /dy/zzzz.r os9lib.r -a -o=zzzz If more, separately compiled user code is to be linked, insert the pathnames of the appropriate relocatable files after the /dy/zzzz.r . Now you can execute your program simply by the command zzzz . Example: use the source testos9.f to verify proper operation. For convenience, copy the source to the working device first (preferrably a RAM disk). copy /dx/testos9.f testos9.f rtf testos9 r68 testos9.a -qo=testos9.r l68 -l=sys.l fstart.r testos9.r os9lib.r -o=testos9 testos9 Compare the results with what you expect from the source. ------------------------------------------------- Special NOTE on the RTF implementation under OS9: ------------------------------------------------- The OS9 Assembler and Linker do not know a construct that would support Fortran COMMON blocks directly. Thus, RTF introduces them using when the program starts running issuing system memory requests. The code doing this is executed automatically during the initialization phase of the program. In cases where libraries etc. use COMMON blocks which are not also used in the mainline module (i.e. the one containing the main prog- ram), the mainline module must have knowledge of these libraries. A similar scheme has to be obeyed for DATA statements. The initialization values have to be placed into the variables when the program starts up. The loader does not do it for all cases (esp., not for COMMON variables). Thus, again, the mainline module has to know about the other modules with respect to DATA. The USE statement described in the manual is intended for these two purposes. Please refer to the RTF manual. Procedure (2): -------------- V. Contents of the host files (VAX as an example): 0. Documentation: 1. install.txt (the present document) 2. rtfman.txt RTF manual 1. Compiler code: 1. rtfos9.f Fortran source of compiler 2. rtf68k.f Include file to 1. 3. rtfos9.a Assembler code, generated from 1.,2. by rtf (cross version on the host) 2. Compiler tables: 1. rtf68k.tab Ascii form of parser tables 3. Runtime library: 1. os9lib.f Fortran source of runtime library 2. os9lib.a Assembler code, generated from 1. by rtf (cross version on the host) 4. Other stuff: 1. testos9.f Fortran source of small test/verification program 2. rtfbinos.f Fortran source of conversion program for parser tables (ascii to binary) 3. rtfbinos.a Assembler code, generated from 4.2. by rtf (cross version on the host) 4. fstart.a Assembler source of initialization routine for RTF programs VI. Transfer from host to the os9 system: Use your preferred utility to transfer all the files listed under V. to a fresh floppy in drive /dx. Most of the Fortran sources are optional, i.e., you can leave out 1.1., 1.2., 3.1., and 4.2. Also, it is not necessary to transfer rtfman.txt. Caution: the file rtf68k.tab contains long lines (80 char.). Make sure that your transfer utility does not split lines, nor add empty lines at the beginning of the file! Otherwise correct with your preferred editor. VII. Generate other files with os9 utilities: In order to make the missing relocatable files and executables, use the os9 assembler and linker. Note that <oskdesf> and sys.l are used in the following steps. 1. generate relocatable of initialization routine: r68 /dx/fstart.a -o=/dx/fstart.r 2. generate relocatable of compiler: r68 /dx/rtfos9.a -qo=/dx/rtfos9.r 3. generate relocatable of runtime library: r68 /dx/os9lib.a -qo=/dx/os9lib.r 4. generate relocatable of table conversion program: r68 /dx/rtfbinos.a -qo=rtfbinos.r 5. generate executable of compiler: l68 -l=sys.l /dx/fstart.r /dx/rtfos9.r /dx/os9lib.r -a -o=rtf -n=rtf 6. generate executable of table conversion program: l68 -l=sys.l /dx/fstart.r /dx/rtfbinos.r /dx/os9lib.r -o=rtfbinos 7. run rtfbinos to convert /dx/rtf68k.tab to /dx/rtf68k.dat: rtfbinos /dx Watch the checksums printed when rtfbinos finishes: the two numbers displayed must be identical! Now you have generated a complete diskette for RTF. You can continue with step II. of procedure (1). 5.4 Flow of Information ----------------------- The following diagram shows the files and operations involved in program preparation. +-----------------+ | RTF/68K source | +-----------------+ | /-------------\ + - - - - - - + ( RTF compiler )--> | RTF listing | \-------------/ + - - - - - - + | +-----------------+ | Assembler code | +-----------------+ | /-------------\ + - - - - - - + ( Assembler )--> |Ass. listing | \-------------/ + - - - - - - + | +----------------+ +-----------------+ |reloc. libraries| | reloc. code | +----------------+ +-----------------+ | | \ /-------------\ + - - - - - - + ----------> ( linker/loader )--> |Link listings| \-------------/ + - - - - - - + | +-----------------+ | executable code | +-----------------+ | /-------------\ ( load, execute ) \-------------/ The following file naming conventions have been chosen for the cross software implementation on the VAX. "Assembler" then stands for the M68MIL cross assembler, and "Link/Load" stands for the CUFOM cross linker package, consisting of CUFLINK, PREPUSH, and UNIPUSH. Assume that the RTF/68K source file is named file.FTN. The extension FTN is required. Then the Fortran list file is called file.LIS, the mnemonic assembler code file is called file.ASM, the assembler list file is called file.LAS, the relocatable code is called file.CUF, and the executable code (S-format) file is called file.COD. The following conventions hold for the native implementation on CP/M-68K systems. "Assembler" then stands for the AS68 assembler and "Link/Load" stands for the LO68 linker/loader. Assume that the RTF/68K source file is named file.FTN. The extension FTN is required. Then the Fortran list file is called file.LIS, the mnemonic assembler code file is called file.S, the relocatable code is called file.O, and the executable code file is called file.68K. The following conventions hold for the native implementation on OS9-68K systems. "Assembler" then stands for the r68 assembler and "Link/Load" stands for the l68 linker/loader. Assume that the RTF/68K source file is named file.f . The extension f is required. Then the Fortran list file is called file.lis, the mnemonic assembler code file is called file.a, the relocatable code is called file.r, and the executable code file is called file . 2.3 Utility Progams for Host System / CP-M System Connection ------------------------------------------------------------ The assumption about hardware made here are minimal: the CP-M system is inserted into the serial line between Host and terminal, using the two serial ports of the microcomputer module. 2.3.1 Transparent Mode - - - - - - - - - - - - You get transparent mode from the terminal through the CP-M system with the command: d> VAX You return to CP-M by typing CTRL-A on the terminal. Note that this way of transparent mode does no harm to the CP-M operating system, unlike the respective commands which are provided by the monitor software in the EPROMs of your microcomputer module. 2.3.2 File Transfer Utility - - - - - - - - - - - - - - It is assumed that the SUPPORT program is already running on the Host. Then, on the CP-M system, the command: d> XF invokes the file transfer program. The dialog which follows asks you for the type of Host computer, source and destination file names, and the direction of transfer (Y for CP-M to Host, N for Host to CP-M). Normally, ASCII files are transferred. Data are not changed during transfer in this case. If the direction is Host to CP-M, if the Host file extension is .COD (i.e. S-records from a cross compilation), and the CP-M file extension given is .68K (absolute image file), XF automatically reformats the S-record data into the CP-M executable image form. They can then later be executed as if they had been prepared under CP-M. 2.3.3 VAX-like full-screen editor on CP/M - - - - - - - - - - - - - - - - - - - - - A full-screen editor with similar functionality and keypad functions as the EDIT/EDT on VAX is available for CP-M/68K. It requires a VT100 compatible terminal. The line-mode commands of EDIT/EDT are not implemented. To invoke the editor, do: d> EDT The editor then asks for the file name. Press <return> if you are sure that the text you want to edit is still in the buffer (other TPA programs could already have destroyed it). - Please refer to the VAX editor manual for details. Here, only the display of the HELP command key (PF2) is reproduced: Cursor movement Keypad functions +-------+-------+-------+-------+ +-------+-------+-------+-------+ | | | | | | | | |FNDNEXT|DELLINE| | ^ | v | <-- | --> | | GOLD | HELP | - - - | - - - | | | | | | | | | | FIND |UNDLINE| +-------+-------+-------+-------+ +-------+-------+-------+-------+ up down left right | PAGE | SECT | |DELWORD| | - - - | - - - | - - - | - - - | |COMMAND| | |UNDWORD| Keypad: The second (lower on key) +-------+-------+-------+-------+ function is selected by pressing |ADVANCE|BACKUP | CUT |DELCHAR| GOLD before pressing the key. | - - - | - - - | - - - | - - - | |BOTTOM | TOP | PASTE |UNDCHAR| Further special keys used: +-------+-------+-------+-------+ DEL delete character left | WORD | EOL | CHAR | | from cursor | - - - | - - - | - - - | ENTER | BACKSPACE backup to begin of line |CHCASE |DELEOL | | | CTRL/W refresh screen +---------------+-------| - - - | | LINE |SELECT | | | - - - - - - - | - - - |SUBSTIT| To get help for the command mode, | OPEN LINE | RESET | | select this mode and type HELP. +---------------+-------+-------+ The following is the information you get when you press GOLD/COMMAND and the type HELP: HELP display help information EXIT or EX save file and exit from editor SAVE save file and stay in editor SET WRAP=n set right margin for wraparound/fill INCLUDE file insert contents of file at cursor WRITE file write selected range to file To get help on the keypad functions, go back to keypad mode and hit the HELP (=PF2) key. SYNTAX DEFINITION OF RTF/68K (NOT YET COMPLETED) ---------------------------- ------------------- The RTF/68K syntax definition is given here in a comprehensive form. It is a copy of the appendix of the ANSI standard which specifies the Fortran 77 language. Omissions from this standard are indicated by fat blobs blocking the respective 'railroads', while extensions are indicated by fat extra railroads and text. Current developments related with RTF/68K: ------------------------------------------ a) Implementation of REAL*8 and COMPLEX*8 data types. b) Implementation of bit-field instructions. Associated documentation ------------------------ a) PRAGMA - a Portable Compiler Writing System H.von der Schmitt, Internal Report PIB b) An Optimizing Compiler Kit for the MC68000 H.von der Schmitt, Internal Report PIB c) Performance of a VAX-supported MC68K V.Mertens, H.von der Schmitt, Note PIB d) 68CAMICRO - Ein leistungsfaehiger Experimentrechner auf Mikroprozessorbasis V.Mertens, BONN-IR-83-10, Bonn, May 1983 (in German) e) M68MIL, a cross assembler for the Motorola 68000 H.v.Eicken, CERN/DD/80/26, Sept. 1980 f) The CUFOM processors user manual