Computing: DOS, OS/2 & Windows Programming

An introduction to FORTRAN 77 (G77).

FORTRAN (FORmula TRANslation) is a programming language designed specifically for scientists and engineers. For over 50 years FORTRAN has been used for such projects as the design of bridges and aeroplane structures, it is used for factory automation control, for storm drainage design, analysis of scientific data and so on. FORTRAN continues to be used today, in particular to realize complicated scientific calculations that have to be executed at high speed, as for example in meteorology, or super-computer benchmarking. Intel has a FORTRAN compiler in their free development suite for the latest Windows releases; it may be fully integrated in the Visual Studio environment.

FORTRAN 77 was released in 1977, a time where computer data input was done using punch cards. This imposed strict limitations, such as a maximum of characters per line (actually 80 characters) and strict column indentation rules, what means that you cannot simply write your text anywhere in the line, but instead the F77 standard tells us where (i.e. at which line column) and how a specific information has to be written. This way to write a computer program is called fixed format coding.

G77 (formerly part of the GNU compiler collection) is a FORTRAN 77 with several extensions (mostly implementing FORTRAN 90 features). In particular, it allows you to use free format coding, i.e. you are free where within the line you write specific information (similar as you would do in Pascal, C, Perl, Python, etc). The two FORTRAN formats are not compatible! Thus using G77 to compile a free format program requires the special command line parameter -ffree-form to be specified. Note, that the sample programs in this text use the standard FORTRAN 77 fixed format.

This text is not a complete FORTRAN 77 tutorial. It's primarily 3 simple program examples, showing how variable declarations, calculation of expressions, arrays, decision making (IF statements), loops (DO statements), reading from the console (keyboard input), writing to the console (screen output), writing to a text file, and subroutines are coded in FORTRAN 77. The whole with comments and explanations. You may see it as a quick look-up for common FORTRAN statements, or as template code for your own first FORTRAN programs for G77. On which platform does this (very) old FORTRAN 77 run on, you may ask. It runs on DOS (cf. my tutorial Installing and running Gnu FORTRAN on FreeDOS), I suppose also on the first Windows versions, maybe even up to Windows 2000 (I did not try...). And the interest of using this obsolete programming language? Beside nostalgic considerations, there is one really good reason: There are thousands of FORTRAN 77 programs, mostly scientific applications, available; you can find lots of them on the Internet, download the code (and adapt it to your needs).

Program 1: Determine the date of Easter for a given year.

This program, taken from the book "Interactive FORTRAN 77 - A Hands on Approach", asks the user to enter a year from the keyboard, determines the date of Easter for this year (if you are interested in the algorithm to calculate the year of Easter, search the Internet) and outputs the date to the screen. The next paragraph shows the code (click the following link to download the source code of the 3 sample programs). If you have a basic knowledge of programming, you probably understand a more or less great part of the code. Detailed explanations will follow.

C A simple program to calculate the date of Easter
C From "Interactive Fortran 77 - A Hands on Approach"

      PROGRAM EASTER
      INTEGER YEAR, METCYC, CENTRY, ERROR1, ERROR2, DAY
      INTEGER EPACT, LUNA
      WRITE (*, "(A29, $)") ' Calculate Easter for year = '
      READ *, YEAR
C CALCULATING THE YEAR IN THE 19 YEAR METONIC CYCLE-METCYC
      METCYC = MOD(YEAR, 19) + 1
      IF (YEAR.LE.1582) THEN
        DAY = (5 * YEAR) / 4
        EPACT = MOD(11 * METCYC - 4, 30) + 1
      ELSE
C CALCULATING THE CENTURY-CENTRY
        CENTRY = (YEAR / 100) + 1
C ACCOUNTING FOR ARITHMETIC INACCURACIES
C IGNORES LEAP YEARS ETC.
        ERROR1 = (3 * CENTRY / 4) - 12
        ERROR2 = ((8 * CENTRY + 5) / 25) - 5
C LOCATING SUNDAY
        DAY = (5 * YEAR / 4) - ERROR1 - 10
C LOCATING THE EPACT (FULL MOON)
        EPACT = MOD(11 * METCYC + 20 + ERROR2 - ERROR1, 30)
        IF (EPACT.LT.0) EPACT = 30 + EPACT
        IF ((EPACT.EQ.25.AND.METCYC.GT.11).OR.EPACT.EQ.24) THEN
          EPACT = EPACT + 1
        ENDIF
      ENDIF
C FINDING THE FULL MOON
      LUNA = 44 - EPACT
      IF(LUNA.LT.21) LUNA = LUNA + 30
C LOCATING EASTER SUNDAY
      LUNA = LUNA + 7 -(MOD(DAY + LUNA, 7))
C LOCATING THE CORRECT MONTH
      IF (LUNA.GT.31) THEN
        LUNA = LUNA - 31
        PRINT *, 'For the year', YEAR
        PRINT *, 'Easter falls on April', LUNA
      ELSE
        PRINT *, 'For the year', YEAR
        PRINT *, 'Easter falls on March', LUNA
      ENDIF
      END

Fixed format coding rules.

First, note that FORTRAN 77 does not distinguish between upper and lower characters and so you are free to choose your own style of writing FORTRAN code. Older FORTRAN 77 programs are mostly entirely written in capital letters. Second, you can immediately see that this program uses fixed format, the FORTRAN code starting at column 7.

Here an overview of the FORTRAN 77 fixed format coding rules:

ColumnsUsageNotes
1 A C in this column indicates a comment, a space indicates that the line contains FORTRAN code Normally you can use any other non blank character to indicate a comment. Some programmers prefer to use an exclamation mark (!). It should also be possible to comment a part of a line that contains FORTRAN code by placing the comment (preceded by an exclamation mark, for example) behind the instructions.
2 - 5 Reserved for placing a numerical label. This allows you to request an unconditional jump to this line from anywhere inside the same program, function or subroutine. It is obvious that this "jumping around" (cf. GOTO in BASIC) makes it difficult to follow the instruction flow of the program and should be avoided!
6 Reserved for placing a character, usually a + sign, which designates a continuation of the previous line. You will often encounter a situation in which the code is too long to fit in the instruction columns of a single line. In this case, you'll need to break it into several lines, each continuation line requiring the character + in column 6.
7 - 73 Space where you write the FORTRAN 77 instructions. Many modern compilers can read the code beyond the 73th character, but it is recommendable to break long instructions in several lines (with a + in column 6). Instructions may be placed anywhere within theses column space, thus you can indent IF or DO blocks, the same way as you would do using a modern programming language.
74 - 80 Not considered by the compiler (may be line numbers?). Just leave them blank...

Program blocks.

Our example starts with the line PROGRAM EASTER and ends with the line END. This defines the main program block. If the program includes user defined subroutines or functions, they will have their own blocks, coded behind (not before, as in Pascal) the main program block.

Variable declaration.

Before you may use a variable, this variable has to be declared, i.e. you have to assign a given data type to it. Variable declaration is done at the beginning of the program (or subroutine/function) block; the syntax is similar as in C. There are two declaration lines in our sample program:
  INTEGER YEAR, METCYC, CENTRY, ERROR1, ERROR2, DAY
  INTEGER EPACT, LUNA
The variables YEAR, METCYC, CENTRY, ERROR1, ERROR2, DAY, EPACT, LUNA are all declared as belonging to the data type INTEGER.

Here a table with the FORTRAN 77 types:

TypeMeaningValuesNotes
INTEGER Number without decimal digits -231 to 231 - 1 Similar to the Pascal INTEGER type
BYTE Small INTEGER -128 to 127 Similar to the Pascal BYTE type
REAL Number with decimal digits approx. between 1.2 · 10-38 and 3.4 · 1038,
positive and negative
Similar to the Pascal REAL type
DOUBLE PRECISION Large REAL approx. between 2.2 · 10308 and 1.8 · 10308,
positive and negative
Similar to the Pascal DOUBLE type
CHARACTER One character One alphanumeric character Similar to the Pascal CHAR type; as in Pascal, constants are placed between single quotes
CHARACTER*N Several characters N alphanumeric characters Similar to the Pascal STRING[N] type; as in Pascal, string constants are placed between single quotes
LOGICAL False or true condition .FALSE. or .TRUE. Similar to the Pascal BOOLEAN type.
COMPLEX Complex number Two REAL numbers,
real and imaginary part
COMPLEX constants are written as (R, I), where R is the real, and I the imaginary part
DOUBLE COMPLEX Large complex number Two DOUBLE PRECISION numbers,
real and imaginary part
DOUBLE COMPLEX constants are written as (R, I), where R is the real, and I the imaginary part

FORTRAN 77 allows you to assign implicitly a particular data type to all variables whose symbolic names start with a common character. By default variables starting with the letters I - N are implicitly declared as INTEGER. It's not really recommendable to use this feature and you should declare all your variables explicitly at the beginning of the program. To be sure that there aren't any implicitly declared variables, add the instruction IMPLICIT NONE immediately after the PROGRAM statement.

Variable definitions and assignments.

Assigning a value to a variable is done using the = operator (as in most programming languages; not in Pascal where := is used). The variable is at the left side of the equality sign, the value (or the variable, or expression giving a value) on the right side. If a variable has to be assigned an initial value (variable initialization), then the assignment is called variable definition and should be done at the beginning of the program, after the variable declaration part.

The value assigned to a variable can be a constant value, the value of another variable or the result of an expression. Examples from our program:
  DAY = (5 * YEAR) / 4
  LUNA = LUNA - 31
  EPACT = MOD(11 * METCYC - 4, 30) + 1
All elements on the right side of the equality sign actually are expressions (cf. further down in the text). Be sure that the type of the value to be assigned is the same (and in FORTRAN "the same" really means "the same") as the type of the variable it is assigned to. Otherwise, you either get a compilation error, or a result that will not be what you expect, and in such a case, it may take some time to find the mistake in your source code (we'll see further down in the text how conversions, internally done by FORTRAN, may lead to "surprising" results of expressions).

Constants definitions.

Even though there aren't any constant definitions in the 3 sample programs, lets have a look at how they are coded in FORTRAN 77. From the point of view program structure, constant definition should be done between variable declaration and variable definition parts. Constants, called parameters in FORTRAN, are symbolic names which contain a value that cannot be changed during the run of the program. Their definition should contain two parts: 1. their type declaration (just as for variables); 2. the constant value assignment that is done using the instruction PARAMETER. Example:
  REAL PI, E
  PARAMETER(PI=3.2415927, E=2.7182818)

Arithmetic expressions.

Arithmetic expressions may be composed of numerical variables and parameters, arithmetic operators and functions. G77 arithmetic operators are the usual +, -, *, /, plus ** (exponentiation = power). Intrinsic mathematical functions include ABS, SQRT, SIN, COS, TAN, ASIN, ACOS, ATAN, EXP, LOG, LOG10, MOD (remainder of an integer division), MIN and MAX (with two or more arguments; minimum resp. maximum). INT and REAL may be used for conversion to integer, resp. real.

An important fact to know about G77 (FORTRAN in general?) is that the data type of an expression containing an integer is an integer! Thus, the expression 1/2 is not 0.25 as expected, but 0. To get the correct real result, you'll have to use 1.0/2.0. The same applies to the expression AVG = SUM / N. With AVG being real, SUM and N being integers, the expression will give an integer result! To get the correct result, you'll have to code AVG = REAL(SUM) / REAL(N); in the case where AVG and SUM are real, the correct expression would be AVG = SUM / REAL(N).

This is also true for functions. Normally a function has to get arguments of a precise data type and there are different functions for different data types. As an example the G77 function to calculate the absolute value of a number:

FunctionArgumentReturn typeStandard
ABS(N) INTEGER, REAL, COMPLEX same as argument, except COMPLEX argument returns REAL FORTAN 77 and later with GNU extensions overloads
IABS(N) INTEGER INTEGER FORTAN 77 and later
DABS(N) DOUBLE DOUBLE FORTAN 77 and later
CABS(N) COMPLEX REAL FORTAN 77 and later
CDABS(N) DOUBLE COMPLEX DOUBLE GNU extension
ZABS(N) DOUBLE COMPLEX DOUBLE GNU extension

Some examples of arithmetic expressions (and their assignment to a variable) in our sample program:
  DAY = (5 * YEAR) / 4
  CENTRY = (YEAR / 100) + 1
  EPACT = MOD(11 * METCYC - 4, 30) + 1

All variables and constants in these expressions, as well as the variables, that the value of the expression is assigned to, are integers, thus the result is an integer, too. In fact, with the first two expressions, we want to calculate the integer part of a division of integers (operator DIV in Pascal), and the third expression uses the function MOD to calculate the remainder of a division of two integers (11 * METCYC - 4 and 30).

Logical expressions.

Logical expressions may be composed of logical variables and parameters, logical and conditional operators and functions. G77 includes the following conditional operators:

OperatorExampleDescription
.EQ. X .EQ. Y true if X is equal to Y (X = Y)
.NE. X .NE. Y true if X is not equal to Y (X ≠ Y)
.LT. X .LT. Y true if X is less than Y (X < Y)
.LE. X .LE. Y true if X is less than or equal to Y (X ≤ Y)
.GT. X .GT. Y true if X is greater than Y (X > Y)
.GE. X .GE. Y true if X is greater than or equal to Y (X ≥ Y)

The logical operators .AND., .OR., and .NOT. act on logical expressions as in other programming languages.

Examples of logical expressions in our sample program:
  EPACT.LT.0
  LUNA.GT.31
  (EPACT.EQ.25.AND.METCYC.GT.11).OR.EPACT.EQ.24
where the first two expressions are true if EPACT is negative, resp. if LUNA is greater than 31; the third expression is true if either EPACT = 26 and METCYC > 11, or EPACT = 24.

Conditional statements.

Conditional statements (IF statements) test if a logical expression (such as a comparison) is true. The logical expression has to be placed between round brackets (as in most programming languages and as a difference with Pascal where there are no brackets needed). There are three forms of the IF statement in G77:

Keyboard input and screen output.

Unformatted input from the keyboard, more exactly from standard input, is done using one of the following statements
  READ (*, *) item1, item2, ...
  READ *, item1, item2, ...
Example from our sample program:
  READ *, YEAR

In the first statement, the first asterisk refer to the standard input unit (unit 5), the second one tells the compiler that no special formatting is used for the input, i.e. that unformatted input is used. The second statement is a simplification of the first one, the unit being omitted, standard input being used automatically.

Unformatted output to the screen, more exactly to standard output, is done using one of the following statements
  WRITE (*, *) item1, item2, ...
  PRINT *, item1, item2, ...
Example from our sample program:
  PRINT *, 'For the year', YEAR

With the WRITE statement, the first asterisk refer to the standard output unit (unit 6), the second one tells the compiler that no special formatting is used for the output, i.e. that unformatted output is used. The PRINT statement is a simplification of the WRITE statement, the unit being omitted, standard output being the only unit that may be used with this statement.

Formatted output to the screen, more exactly to standard output is traditionally done by specifying a numerical label, and this label is then used to define the output format using the FORMAT statement.
  WRITE (*, label) item1, item2, ...
  label FORMAT(item1-format, item2-format, ...)

Remember that in fixed format FORTRAN, labels are placed in columns 2 to 5!

In practice, the FORMAT statement isn't used anymore. Instead the output format is included within the WRITE statement. Here is how this form of the formatted output statement looks like:
  WRITE (*, "(item1-format, item2-format, ...)") item1, item2, ...
Example from our sample program:
  WRITE (*, "(A29, $)") ' Calculate Easter for year = '
where A29 (the format for the string literal) means 29 alphanumeric characters (with string values, the field width is often omitted; in this case the actual length of the string is used), and $ is a special formatting symbol, corresponding to advance="no" in later FORTRAN versions. We use it here to keep the cursor in the same line (no CR+LF) in order to enter the year in the same line as the text telling us to do so.

Formatting characters overview.

FormatUsageDescriptionExample
A Aw alphanumeric field with a width of w A5: "Aly" will be displayed as "  Aly"
I Iw integer numbers field with a width of w I5: "99" will be displayed as "   99"
F Fw.d real numbers fixed point field with a width of w and display of d decimal digits F6.2: "2.5" will be displayed as "  2.50", "3.14159" will be displayed as "3.1416"
E
D
Ew.d
Dw.d
real resp. double precision numbers exponent notation field with a width of w and display of d decimal digits E8.1: " 0.025" will be displayed as " 0.3E-01"
X nX horizontal skip (space); if n is omitted n = 1 I3 2X I3: "126" followed by "621" will be displayed as "126  621"
/ / vertical skip (new line); each slash corresponds to one newline I3 // I3: "126" followed by "621" will be displayed as "126", followed by an empty line, and finally "621" a further line down

To note that, as a difference with C, WRITE is by default followed by a CR+LF. To avoid this, use the special formatting symbol $, as described above.

The screenshot below shows the output of our first sample program.

Fortran 77 on FreeDOS: Determine date of Easter for a given year

Program 2: Determine the date of Easter for a given period of time.

The program calculates the date of Easter for a period from year 1 to year 2. The list with the Easter dates is written to a text file. The program uses the the program EASTER from "Interactive FORTRAN 77 - A Hands on Approach" (our sample program 1) as subroutine to calculate the date for one given year. The new FORTRAN elements introduced with this sample are loops (DO statements), subroutines and output to a text file.

C A simple program to calculate the date of Easter for a given period of time (year1 to year2)
C The list with the Easter dates is written to the file easter.txt
C Uses the program EASTER from "Interactive Fortran 77 - A Hands on Approach" as subroutine

      PROGRAM EASTER2
      INTEGER YEAR, YEAR1, YEAR2, MONTH, LUNA
      CHARACTER*5 SMONTH
      WRITE (*, *) 'Calculate Easter for a given period:'
      WRITE (*, "(A14, $)") ' First year = '
      READ *, YEAR1
      WRITE (*, "(A14, $)") ' Last year = '
      READ *, YEAR2
      IF (YEAR2.GE.YEAR1) THEN
        OPEN(UNIT=1, FILE='easter.txt')
        WRITE (1, "(A19, 1X, I4, 1X, A2, 1X, I4)")
     +    'Date of Easter from', YEAR1, 'to', YEAR2
        WRITE (1, *)
        DO YEAR = YEAR1, YEAR2
          CALL EASTER(YEAR, MONTH, LUNA)
          IF (MONTH.EQ.3) THEN
            SMONTH = 'March'
          ELSE
            SMONTH = 'April'
          ENDIF
          WRITE (1, "(I4, 5X, A5, 1X, I2)") YEAR, SMONTH, LUNA
        ENDDO
        CLOSE(UNIT=1)
        WRITE (*, *) 'List with Easter dates in file: easter.txt'
      ENDIF
      END
C *************************************************
C * Subroutine to calculate Easter for a given year
C *************************************************

      SUBROUTINE EASTER(YEAR, MONTH, LUNA)
      INTEGER YEAR, METCYC, CENTRY, ERROR1, ERROR2, DAY, MONTH
      INTEGER EPACT, LUNA
C CALCULATING THE YEAR IN THE 19 YEAR METONIC CYCLE-METCYC
      METCYC = MOD(YEAR, 19) + 1
      IF (YEAR.LE.1582) THEN
        DAY = (5 * YEAR) / 4
        EPACT = MOD(11 * METCYC - 4, 30) + 1
      ELSE
C CALCULATING THE CENTURY-CENTRY
        CENTRY = (YEAR / 100) + 1
C ACCOUNTING FOR ARITHMETIC INACCURACIES
C IGNORES LEAP YEARS ETC.
        ERROR1 = (3 * CENTRY / 4) - 12
        ERROR2 = ((8 * CENTRY + 5) / 25) - 5
C LOCATING SUNDAY
        DAY = (5 * YEAR / 4) - ERROR1 - 10
C LOCATING THE EPACT (FULL MOON)
        EPACT = MOD(11 * METCYC + 20 + ERROR2 - ERROR1, 30)
        IF (EPACT.LT.0) EPACT = 30 + EPACT
        IF ((EPACT.EQ.25.AND.METCYC.GT.11).OR.EPACT.EQ.24) THEN
          EPACT = EPACT + 1
        ENDIF
      ENDIF
C FINDING THE FULL MOON
      LUNA = 44 - EPACT
      IF(LUNA.LT.21) LUNA = LUNA + 30
C LOCATING EASTER SUNDAY
      LUNA = LUNA + 7 -(MOD(DAY + LUNA, 7))
C LOCATING THE CORRECT MONTH
      IF (LUNA.GT.31) THEN
        LUNA = LUNA - 31
        MONTH = 4
      ELSE
        MONTH = 3
      ENDIF
      END

The program consists of two blocs: the main program (that asks for the years, then, for each year, calls the subroutine to calculate the Easter date, and finally writes this date to the text file) and a subroutine called "EASTER" (that does the calculation of the month and day for a given year).

Declaring and calling subroutines.

A FORTRAN subroutine is similar to a Pascal procedure, some of its arguments being used to calculate or modify some others. Subroutines are declared using the reserved word SUBROUTINE, followed by the subroutine name and its arguments between round brackets. As a difference with Pascal or C, the data type of the arguments is not indicated within the subroutine declaration statement, but the arguments have to be declared as variables in the subroutine bloc. Example from our sample program:
  SUBROUTINE EASTER(YEAR, MONTH, LUNA)
  INTEGER YEAR, MONTH, LUNA

The code included within a subroutine is executed by using the instruction CALL, followed by the subroutine name and its arguments between round brackets.
Example from our sample program:
  CALL EASTER(YEAR, MONTH, LUNA)

Note that the variables used in the CALL statement are variables of the main program, whereas those used in the SUBROUTINE statement are local variables of the subroutine bloc. In this example, they have the same name as the main program variables that, of course, is not necessarily the case. Important to know: All arguments of a FORTRAN subroutine are passed by reference! This means that it is not the value of a variable, but a pointer to its address location that is passed. As a consequence, any changes to a local argument variable will change the corresponding variable in the main program. That's a major difference with, for example Pascal, where arguments of a procedure may be passed either by value (input arguments only), or by reference (input and/or output arguments).

I found in some FORTRAN books that procedures have to contain a RETURN statement. As my program works correctly, this seems not to be mandatory and the final subroutine END seems to implicitly include the return to the main program (or eventually other calling subroutine).

To note that beside subroutines, FORTRAN also allows to define custom functions. As in Pascal, they can only return a single value. Thus, if the subprogram calculates two or more values (as in our case), or doesn't calculate any value, a subroutine has to be used.

Loop statements.

For each year within the considered period, the program has to calculate the Easter date and write this date to a text file. This repetitive execution of a given bloc of statements is called a loop.

Traditionally, a FORTRAN 77 DO-loop has the following syntax:
  DO label counter = start, end, step
    ...
  label CONTINUE

This is similar to the FOR statement in other programming languages. The DO-bloc is executed with the value of the counter variable (that must be an integer and must not be changed within the bloc) varying from the start value to the end value, being incremented by the step value during each iteration (if the step value is omitted, +1 is used).

An alternative to the DO-loop with label is to use a DO ... ENDDO construct. This construct is not part of ANSI FORTRAN 77, but is implemented by most FORTRAN 77 compilers, as is the case for G77. Here an example of this construct from our sample program:
  DO YEAR = YEAR1, YEAR2
    ...
  ENDDO

The loop above is a simple counter loop. A conditional loop is a loop where the statements of its bloc are executed as long as a given logical expression is true. In ANSI FORTRAN 77, this kind of loop is coded using IF and GOTO:
  label IF (logical expression) THEN
    ...
    GOTO label
  ENDIF

Lots of FORTRAN 77 compilers offer an extension that allows the usage of DO WHILE or WHILE ... DO constructs to implement conditional loops.

It's obvious that with these statements, except if you want an infinite loop, the logical expression has to be modified within the bloc.

File input-output.

In FORTRAN each file is associated with a unit number, an integer between 1 and 99. Remember that unit 5 and 6 are reserved for standard input resp. standard output.

Before being able to read from or write to a file, you must open it. When you have done with the file, you should close it.

The basic syntax for opening a file is one of the following:
  OPEN(UNIT=unit-number, FILE=filename)
  OPEN(unit-number, FILE=filename)
Example from our sample program:
  OPEN(UNIT=1, FILE='easter.txt')

Besides UNIT and FILE, the instruction may contain several other specifiers, among others IOSTAT = ios, where "ios" is the I/O status identifier (integer variable), that upon return is zero if the statement was successful and returns a non-zero value otherwise, and STATUS = sta, where "sta" is one of NEW, OLD, and SCRATCH (file that is created and that will be deleted when it is closed).

The basic syntax for closing a file is one of the following:
  CLOSE(UNIT=unit-number)
  CLOSE(unit-number)
Example from our sample program:
  CLOSE(UNIT=1)

As with OPEN several other specifiers (in particular IOSTAT) may be part of the instruction.

After a file has been opened (and until it is closed), you may read data from it, or write data to it. The general format with all possible specifiers looks like this:
  READ ([UNIT =] UNIT, [FMT =] format, IOSTAT = ios, ERR = error-label, END = eof-label)
  WRITE ([UNIT =] UNIT, [FMT =] format, IOSTAT = ios, ERR = label1)
where "format" is the output-format as described further up in the text ("*" for unformatted output), where "ios" is the I/O status identifier, where "error-label" is a label where to jump to if there is a file error (the ERR specifier can also be used when opening or closing a file), and where "eof-label" is a label where to jump to when the end-of-file is reached.

Examples from our program:
  WRITE (1, "(A19, 1X, I4, 1X, A2, 1X, I4)") 'Date of Easter from', YEAR1, 'to', YEAR2
  WRITE (1, *)

Note: In the sample program, the first of these instructions spans over two lines (usage of a "continuation character" in column 6). The second instruction writes nothing but a CR+LF (empty line); this had better been done using the "/" format symbol in the write operation before.

The screenshot shows a part of the program output, opened in Notepad (on my Windows 10).

Fortran 77 on FreeDOS: Determine the date of Easter for a given period of time

Program 3: Determine the frequency of the date of Easter for a given period of time.

The program calculates the frequency of the date of Easter for a period from year 1 to year 2. The list with the Easter date frequencies is written to a text file. The program uses the program EASTER from "Interactive FORTRAN 77 - A Hands on Approach" (our sample program 1) as subroutine to calculate the date for one given year, and the subroutine SORT to sort the frequencies, descending by their values. Beside nested DO statements and the usage of the EXIT instruction to exit from a DO-bloc, the only new FORTRAN element introduced with this program is the usage of one-dimensional arrays.

C A simple program to calculate the frequency of the date of Easter for a given period of time (year1 to year2)
C The frequency values are written to the file easterfq.txt
C Uses the program EASTER from "Interactive Fortran 77 - A Hands on Approach" as subroutine

      PROGRAM EASTER3
      INTEGER YEAR, YEAR1, YEAR2, MONTH, LUNA, I, J
      CHARACTER*5 SMONTH
      INTEGER FREQ(61), FREQX(61)
      REAL FRQ
      DO I = 1, 61
        FREQ(I) = 0
      ENDDO
      WRITE (*, *) 'Calculate frequency of Easter for a given period:'
      WRITE (*, "(A14, $)") ' First year = '
      READ *, YEAR1
      WRITE (*, "(A14, $)") ' Last year = '
      READ *, YEAR2
      IF (YEAR2.GE.YEAR1) THEN
        OPEN(UNIT=1, FILE='easterfq.txt')
        WRITE (1, "(A19, 1X, I4, 1X, A2, 1X, I4)")
     +    'Frequency of Easter for a period from ', YEAR1, 'to', YEAR2
        WRITE (1, *)
        DO YEAR = YEAR1, YEAR2
          CALL EASTER(YEAR, MONTH, LUNA)
          IF (MONTH.EQ.3) THEN
            FREQ(LUNA) = FREQ(LUNA) + 1
          ELSE
            FREQ(LUNA + 31) = FREQ(LUNA + 31) + 1
          ENDIF
        ENDDO
        DO I = 1, 61
          FREQX(I) = I
        ENDDO
        CALL FREQSORT (61, FREQ, FREQX)
        DO K = 1, 61
          IF (FREQ(K).EQ.0) EXIT
          I = FREQX(K)
          IF (I.LE.31) THEN
            SMONTH = 'March'
            J = I
          ELSE
            SMONTH = 'April'
            J = I - 31
          ENDIF
          FRQ = 100 * (REAL(FREQ(K)) / REAL((YEAR2 - YEAR1 + 1)))
          WRITE (1, "(A5, 1X, I2, 5X, F5.2, A1)") SMONTH, J, FRQ, '%'
        ENDDO
        CLOSE(UNIT=1)
        WRITE (*, *) 'Easter frequency values in file: easterfr.txt'
      ENDIF
      END
C *************************************************
C * Subroutine to calculate Easter for a given year
C *************************************************

      SUBROUTINE EASTER(YEAR, MONTH, LUNA)
      INTEGER YEAR, METCYC, CENTRY, ERROR1, ERROR2, DAY, MONTH
      INTEGER EPACT, LUNA
C CALCULATING THE YEAR IN THE 19 YEAR METONIC CYCLE-METCYC
      METCYC = MOD(YEAR, 19) + 1
      IF (YEAR.LE.1582) THEN
        DAY = (5 * YEAR) / 4
        EPACT = MOD(11 * METCYC - 4, 30) + 1
      ELSE
C CALCULATING THE CENTURY-CENTRY
        CENTRY = (YEAR / 100) + 1
C ACCOUNTING FOR ARITHMETIC INACCURACIES
C IGNORES LEAP YEARS ETC.
        ERROR1 = (3 * CENTRY / 4) - 12
        ERROR2 = ((8 * CENTRY + 5) / 25) - 5
C LOCATING SUNDAY
        DAY = (5 * YEAR / 4) - ERROR1 - 10
C LOCATING THE EPACT (FULL MOON)
        EPACT = MOD(11 * METCYC + 20 + ERROR2 - ERROR1, 30)
        IF (EPACT.LT.0) EPACT = 30 + EPACT
        IF ((EPACT.EQ.25.AND.METCYC.GT.11).OR.EPACT.EQ.24) THEN
          EPACT = EPACT + 1
        ENDIF
      ENDIF
C FINDING THE FULL MOON
      LUNA = 44 - EPACT
      IF(LUNA.LT.21) LUNA = LUNA + 30
C LOCATING EASTER SUNDAY
      LUNA = LUNA + 7 -(MOD(DAY + LUNA, 7))
C LOCATING THE CORRECT MONTH
      IF (LUNA.GT.31) THEN
        LUNA = LUNA - 31
        MONTH = 4
      ELSE
        MONTH = 3
      ENDIF
      END
C ***********************************************
C * Subroutine to sort the Easter frequency array
C ***********************************************

      SUBROUTINE FREQSORT(N, ARRAY, INDEXES)
      INTEGER N, ARRAY(N), INDEXES(N)
      INTEGER F, X, I, J
      DO I = 1, N - 1
        DO J = I + 1, N
          IF (ARRAY(I).LT.Array(J)) THEN
            F = ARRAY(I)
            ARRAY(I) = ARRAY(J)
            ARRAY(J) = F
            X = INDEXES(I)
            INDEXES(I) = INDEXES(J)
            INDEXES(J) = X
          ENDIF
        ENDDO
      ENDDO
      DO I = 1, N - 1
        DO J = I + 1, N
          IF (ARRAY(I).EQ.Array(J).AND.INDEXES(I).GT.INDEXES(J)) THEN
            F = ARRAY(I)
            ARRAY(I) = ARRAY(J)
            ARRAY(J) = F
            X = INDEXES(I)
            INDEXES(I) = INDEXES(J)
            INDEXES(J) = X
          ENDIF
        ENDDO
      ENDDO
      END

The program consists of three blocs: 1. The main program (that asks for the years, calls a subroutine to calculate the Easter date for each year, incrementing the corresponding per date counter (actually an array element). When all dates are calculated, another subroutine is called to sort the per date counter array, and finally, the counts are transformed into frequencies and written to the text file. 2. The subroutine "EASTER" (that does the calculation of the month and day for a given year; exactly the same as in the preceding program). 3. The subroutine "SORT" that sorts the array containing the per date counters.

Declaring an array and accessing its elements.

A one-dimensional array is just a linear sequence of elements stored consecutively in memory. Such an array may be declared using one of the following statements:
  data-type array-variable(array-length)
  data-type array-variable(first-element-index:last-element-index)

Important to know that an array declared by indicating its length is a one-based array, i.e. the first element has the index 1 (and not 0 as in most programming languages). Array declaration example from our sample program:
  INTEGER FREQ(61), FREQX(61)
This declares two arrays with 61 integer elements, the first element having an index of 1, the last one an index of 61.

Each element of an array may be seen as a separate variable. A given element is accessed by the array name followed by the index in round brackets (that's also a difference with most programming languages that use square brackets in this case). Example from the sample program:
  DO I = 1, 61
    FREQ(I) = 0
  ENDDO
This statement sets all array elements to 0 (initialization of the array).

Some explanations concerning sample program 3.

The aim of the program is to create a list showing, for the time period entered by th user, how many times Easter felt on a given date. As the dates to be considered range from 1st March to 30th April, we can use an array with 61 elements, one element for each day of this period and the elements being used as "Easter counters".

First, we initialize the array, setting all counters to zero, as shown in the previous paragraph. Then for each year, we increment by 1 the counter corresponding to that date. For March, we can use the day returned by the EASTER subroutine itself as index of the counter array. For April, we have to adjust the index value: As March has 31 days, the value of the index is equal to the day returned by the subroutine plus 31. Here is the corr. code:
  DO YEAR = YEAR1, YEAR2
    CALL EASTER(YEAR, MONTH, LUNA)
    IF (MONTH.EQ.3) THEN
      FREQ(LUNA) = FREQ(LUNA) + 1
    ELSE
      FREQ(LUNA + 31) = FREQ(LUNA + 31) + 1
    ENDIF
  ENDDO

The list should however not be ordered by date, but by the frequency of Easter, those days where Easter felt the most often being at the top of the list. This requires to sort the counter array. However, just sorting the array elements is not enough! As the position of an element in the array actually corresponds to a given date, we have to create a second array and sorting it the same way than we sort the counts. What has this other array to contain? The different dates, you will probably say, and that is right. But, there is another possibility. As there is a direct correspondence between the counter array indexes and the dates, our second array can actually contain these indexes. That's why, before calling the SORT subroutine, we initialize the second array as follows:
  DO I = 1, 61
    FREQX(I) = I
  ENDDO

When the sort is done (the elements in FREQX having been sorted the same way as the elements in FREQ), we have everything in the order we want. All that remains to do is to transform the index values in FREQX to dates. This follows the same logic as the one used when storing the Easter counts into FREQ; here is the corresponding code:
  DO K = 1, 61
    IF (FREQ(K).EQ.0) EXIT
    I = FREQX(K)
    IF (I.LE.31) THEN
      SMONTH = 'March'
      J = I
    ELSE
      SMONTH = 'April'
      J = I - 31
    ENDIF
    FRQ = 100 * (REAL(FREQ(K)) / REAL((YEAR2 - YEAR1 + 1)))
    WRITE (1, "(A5, 1X, I2, 5X, F5.2, A1)") SMONTH, J, FRQ, '%'
  ENDDO

Note the usage of the EXIT statement, that terminates the DO loop (resuming execution with the instruction following ENDDO). This statement is executed if the count for a given date is zero; as the counts are sorted in descending order, this means that with the first count being zero, the loop is terminated, in other words, only frequencies (these are calculated from the counts) greater than zero will be written to the list.

Also note the usage of the REAL function in the expression to calculates the frequency. If you do not use it, all frequencies will be zero!

The "SORT" subroutine is a simple bubble sort. As already said, the important thing here, is not only to sort the array with the counts, but also the array with the indexes.

If you look at the subroutine, you can see that the code to implement the bubble sort is followed by a portion of code, that seems to be another bubble sort. And it is another bubble sort. I let it as a little knowledge test to the reader to find out, what this second sort is for. Here is a hint: It is not mandatory, but it ensures that the list looks "prettier" if it is there than if it wasn't. If you don't find an answer, just rebuild the program without this portion of code and compare the result with the original one...

And, to be complete, here is the screenshot with a part of the program output, opened in Notepad (on my Windows 10).

Fortran 77 on FreeDOS: Determine the frequency of Easter for a given period of time

If you find this text helpful, please, support me and this website by signing my guestbook.