Table of Contents
Table of Contents
Most of this document is a faithful transcription of the reference manual for MAD (Michigan Algorithm Decoder), an early academic computer language from 1962. The manual should be accompanied by an implementation; if not, it can be found at the Retrocomputing Museum. MAD has been revived as a work of reconstructive archeology, so that language connoisseurs of the present and future will be able to appreciate what computing life was like back in that day.
MAD was an extended dialect of ALGOL 58, first released in 1959 near the beginning of the great age of invention in compiled languages that stretched from FORTRAN in 1955 until the rise to dominance of C after 1985. Study of it teaches a great deal about the uses and background assumptions of computing in 1962 and earlier.
Some features of MAD are only superficially odd. The all-caps listings look strange to modern eyes; the IBM 704 on which it was implemented had a pre-ASCII, pre-EBCDIC six-bit character set with no lower-case letters and lacks several punctuation marks that later became standard. The limitation to six characters in variable names, labels, and strings reflects the compiler's primitive memory management; it meant all of these could fit in one 36-bit machine word.
A deeper difference from modern languages is the complete lack of interactive I/O. MAD was designed to run in a batch environment, with programs submitted via card decks and sending output to line printers or a card punch. This limitation is a bit obscured in the implementation that goes with these notes, because the card punch is identified with Unix standard input and the line printer with Unix standard output. Thus, MAD I/O accidentally looks more flexible than it was — modern MAD programs (assuming anyone were perverse enough to write any new ones) could actually be part of pipelines!
Punched-card input also explains some syntactic features that will seem bizarre to modern eyes. MAD statement labels lack a delimiter like C's trailing colon because they were distinguished by position (columns 1 to 10) on the card. The line continuation syntax, a digit in column 11, also relies on character position. In the late 1960s and 1970s, when punched cards coexisted with interactive timesharing, the term “free-format” was commonly used to describe languages with the newer-style syntax that used whitespace-delimited tokens without relying on card position, and languages like MAD were called “fixed-field”. When batch computing vanished in the 1990s, both terms passed out of use.
By modern standards, the list manipulation statements (SET LIST, SAVE DATA, SAVE RETURN, RESTORE DATA, RESTORE RETURN) are downright peculiar. We are accustomed to having a similar stack be managed automatically by procedure entry and exit and the binding of local variables, but that was an innovation of ALGOL 60 which was not present in ALGOL 58.
Perhaps strangest to the modern eye is the exclusiveness with which MAD is focused on numerical computations in applied mathematics. String handling is rudimentary, and data structures other than arrays are absent. These gaps reflect not only the non-interactive style of computing back then but the fact that what we would today call systems programming (which drove the incorporation of features like pointers, structures, unions, and typedefs in C) was not yet done in non-assembler languages. The fact that the if statement is called WHENEVER and the goto called TRANSFER is a mere cosmetic trivium by comparison.
The expository style of the manual conveys as clearly as do the language features that at this date the infant discipline of computer science had yet to separate itself from applied mathematics. All the impedimenta of mathematical notation are present not just in the formulas, but in the language syntax descriptions where script letters are used as metavariables. The whole is written in the rather stilted over-formal prose one finds in the ordinary run of mathematical papers.
Certain phrasings in the manual suggest that the authors thought of flowcharts as the primary notation for programs, and considered MAD code a sort of hand-compiled derivative of it. This made sense at the time, though it would have made much more sense if the authors had actually settled on a consistent notation (the MAD manual uses at least three conflicting styles). Programming languages were clumsy and primitive, resources were scarce, and the typical complexity of programs was very low. By five years later (roughly three or four Moore's-Law doublings) flowcharting was going out of fashion, probably for the simple reason that flowcharts were getting too large to draw and read!
There are some significant respects in which MAD was actually rather advanced for its period. The multiline WHENEVER statement anticipated the free-format syntax of ALGOL 60's descendants, including Pascal and C. The DEFINE statement, which supports syntactic extension of the language, was an interesting experiment not emulated in most later designs. And, as the Simpson's Rule example illustrates, MAD even supported what was later called “higher-order programming” — passing around function-valued variables!
Also, MAD took care of details of running a job in ways that few other languages of the time could do. MAD was what came to be called a “load-and-go” compiler; unlike other languages of the period in which jobs had to be launched by the operator after the card deck had been loaded, the MAD system would ship either a diagnostic listing or program output to the line printer immediately after the deck compiled. This fast turnaround was essential for a teaching environment. MAD also featured good diagnostics to help students find and correct errors. MAD's combination of a compiler with the simple supervisor program that enabled load-and-go operation was considered quite cutting-edge at the time and inspired a paper in the very first issue of ACM Computing Surveys (Supervisory and Monitor System, R.F. Rosin, March 1969).
On the other hand, there are some features of MAD which make one truly wonder what the designers were thinking — the handling of array indexing, for example, with two different addressing systems one of which could mutate at runtime. If 1962 weren't a couple of years too early to attribute that to an LSD overdose it would make a tempting hypothesis.
In most of the respects we've described, however, MAD is very representative of its time. This will become apparent from a look at the Retrocomputing Museum implementation of CORC/CUPL, which is rather similar (though less powerful). MAD's grammar — ad-hoc and festooned with many more keywords than a modern language would employ — is very typical. This entire family of languages became extinct in the 1970s, snuffed out like dinosaurs by the meteoritic impact of time-shared computing and teletypewriter-like interactive terminals.
MAD was named after Mad Magazine, which in those more innocent days of the late 1950s and early 1960s was one of the cutting edges of American humor. Batch compilers of the period sent their error listings to a line printer to be printed on fanfold paper. It was normal for such listings to begin with a cover page giving, among other things, the time and date of compilation. We are informed that the original MAD compiler also included on the cover page an image of the MAD Magazine mascot, Alfred E. Neumann. It must have looked rather like this:
.M .:AMMO: .:AMMMMMHIIIHMMM. .... .AMMMMMMMMMMMHHHMHHMMMML:AMF" .:MMMMMLAMMMMMMMHMMMMMMHHIHHIIIHMMMML. "WMMMMMMMMMMMMMMMMMMH:::::HMMMMMMHII:. .AMMMMMMMHHHMMMMMMMMMMHHHHHMMMMMMMMMAMMMHHHHL. .MMMMMMMMMMHHMMMMMMMMHHHHMMMMMMMMMMMMMHTWMHHHHHML .MMMMMMMMMMMMMMMMMMMHHHHHHHHHMHMMHHHHIII:::HMHHHHMM. .MMMMMMMMMMMMMMMMMMMMMMHHHHHHMHHHHHHIIIIIIIIHMHHHHHM. MMMMMMMMMMMMMMMMMHHMMHHHHHIIIHHH::IIHHII:::::IHHHHHHHL "MMMMMMMMMMMMMMMMHIIIHMMMMHHIIHHLI::IIHHHHIIIHHHHHHHHML .MMMMMMMMMMMMMM"WMMMHHHMMMMMMMMMMMLHHHMMMMMMHHHHHHHHHHH .MMMMMMMMMMMWWMW""YYHMMMMMMMMMMMMF""HMMMMMMMMMHHHHHHHH. .MMMMMMMMMM W" V W"WMMMMMHHHHHHHHHH "MMMMMMMMMM". "WHHHMH"HHHHHHL MMMMMMMMMMF . IHHHHH. MMMMMMMMMM . . HHHHHHH MMMMMMMMMF. . . . HHHHHHH. MMMMMMMMM . ,AWMMMMML. .. . . HHHHHHH. :MMMMMMMMM". . F"' 'WM:. ,::HMMA, . . HHHHMMM :MMMMMMMMF. . ." WH.. AMM"' " . . HHHMMMM MMMMMMMM . . ,;AAAHHWL".. .:' HHHHHHH MMMMMMM:. . . -MK"OTO L :I.. ...:HMA-. "HHHHHH ,:IIIILTMMMMI::. L,,,,. ::I.. .. K"OTO"ML 'HHHHHH LHT::LIIIIMMI::. . '""'.IHH:.. .. :.,,,, ' HMMMH: HLI' ILTT::"IIITMII::. . .IIII. . '"""" ' MMMFT:::. HML:::WMIINMHI:::.. . .:I. . . . . ' .M"'.....I. "HWHINWI:.'.HHII::.. .HHI .II. . . . . :M.',, ..I: "MLI"ML': :HHII::... MMHHL ::::: . :.. .'.'.'HHTML.II: "MMLIHHWL:IHHII::....:I:" :MHHWHI:...:W,," '':::. ..' ":.HH:II: "MMMHITIIHHH:::::IWF" """T99"' '"" '.':II:..'.'..' I'.HHIHI' YMMHII:IHHHH:::IT.. . . ... . . ''THHI::.'.' .;H.""."H" HHII:MHHI"::IWWL . . . . . HH"HHHIIHHH":HWWM" """ MMHI::HY""ML, ... . .. :" :HIIIIIILTMH" MMHI:.' 'HL,,,,,,,,..,,,......,:" . ''::HH "HWW 'MMH:.. . 'MMML,: """MM""""MMM" .'.IH'"MH" "MMHL.. .. "MMMMMML,MM,HMMMF . .IHM" "MMHHL .. "MMMMMMMMMMMM" . . '.IHF' 'MMMML .. "MMMMMMMM" . .'HMF HHHMML. .'MMF" IHHHHHMML. .'HMF" HHHHHHITMML. .'IF.. "HHHHHHIITML,. ..:F... 'HHHHHHHHHMMWWWWWW::"...... HHHHHHHMMMMMMF"'........ HHHHHHHHHH............ HHHHHHHH........... HHHHIII.......... HHIII.......... HII......... "H........ ......
We cannot certify that this is the original image; we found it with a web search on a cartoon site, with no mention of the MAD language nearby. But the character set it uses is limited enough that it could be the real thing.
The Retrocomputing Museum implementation of MAD translates MAD code into compileable C. It has been tested on the examples in the manual, which are included with the distribution. This section is a summary of differences: more details are given in notes attached to the relevant sections.
Card images are read from standard input and printing is to standard output. Line printer control codes are partly but not exactly simulated (see Note). Tape and drum devices are not supported.
The 704 had 36-bit words; MAD floats are translated to C doubles, which on modern machines have 32 or 64 bits. Precision and overflow of numeric results may therefore differ.
Character READ FORMATs into arrays cause their equivalents in the generated C code to be declared with a base char type as would be usual in C. Perfect fidelity to the MAD behavior would have required the data to be unpacked into int or double arrays, but that code would have been far more complicated to generate and hard to read.
In original MAD arguments were passed by name. This is difficult to simulate in C, so the generated code passes arguments by value. None of the examples given in the manual rely on pass-by-reference, and given that all variables are global it is difficult to imagine code that would.
Label arrays, array-mode variables, and the associated computed-transfer statement are not implemented (mainly because the manual did not include programs with which they could be tested). Neither are multiple ENTRY points, EQUIVALENCE, ERASABLE, COMMON, TAPE, or DRUM statements.
More seriously, though array references and assignments are implemented, array I/O is not (except for the special case of character arrays which are handled as strings). The fact that the size of arrays is dynamic appears to make this feature impossible to compile; I conjecture that in the original MAD format strings were actually interpreted at runtime by a service routine compiled into the code.
Only the no-argument case of ERROR RETURN is supported. The language describing the more general case of this feature is deeply confusing, and I was unable to extract a coherent semantics for it. None of the examples use the general case.
On the other hand, some limits in original MAD are not present in this implementation, mainly because it would have required actual effort to enforce them. Names and string constants may be longer than 6 characters, and may contain underscores and lowercase letters. Source lines are not limited to 72 chars long.
All external function definitions must precede the program main line. It is unclear whether or not this was true in original MAD.
The generated C is self-contained and does not require any dedicated runtime support, except that the stdio and math libraries must be linked with it.
The remainder of this document, following this section) is an exact transcription of the original Michigan Algorithm Decoder manual dated February 1962. Catherine Raymond typed it in from the original velo-bound typed manual; Eric Raymond hand-corrected it, and moved the result into PIC and XML-DocBook markup so both HTML and PostScript can be generated from it.
The philosophy of this transcription is that the semantic content of the manual is what is essential and is what should be presented by rendering into HTML; the accidents of literal content have in some cases been changed but should be reconstructable by looking at the XML source transcription. Thus, typos have been fixed in the visible text, but the XML source includes a note adjacent to each one explaining the fix. We have also corrected other minor errors like off-by-ones in the indexing, but those fixes too are explained in the XML masters.
We have been (necessarily) a little more relaxed about changes in whitespace distribution, as DocBook doesn't give us the ability to control that exactly. Other notes in the XML source describe the few places where we were not able to reproduce the original layout.
Redactor's comments in the body of the text are decorated like this.
Typewriter underlining in the original is expressed by DocBook <emphasis> tags in this version, which will probably render as italics.
The original hyphenation will probably not be discernible in the visible rendering you see, though hyphen characters in the original have been preserved as ISO soft-hyphen entities.
The program listings in the original failed to distinguish between 0 and O, and between 1 and l; this does not reflect incompetence but rather the fact that although computer line printers of the period had separate forms for these character pairs, typewriters of the period did not have. This distinction has been added in the present text.
Most of the original listings were typed so there was whitespace between remark or continuation punches and following statements or comments. The language's card-based, column-sensitive format did not require this, however, and a few listings had a remark or continuation character stuck to the first statement token. These have been whitespace-separated so that a modern token-oriented parser can read them.
In the original, the description of flowcharting conventions in Chapter 1 include small hand-drawn geometrical shapes after each of the items A, B, and the words "diamonds" and "rectangles" in item D were corresponding shapes.
In the original, each example in the first two parts of Chapter 3 was introduced by a centered numbered header "Example N" with no section numbering; examples in the third part had no headings at all. These headers have been massaged or added to produce second-level sections, and descriptive titles added because the Contents page looked awful without them. To remind the reader that these are additions by the redactor, they are highlighted like redactor's notes.
The PIC diagrams preserve the logic of the original hand-drawn ones, but are not exact geometrical duplicates. The proportions of the flowchart elements are slightly different. One detail we have not preserved is that in Chapter 3, PRINT-statement boxes are left-right reversed from their orientation in Chapter 2 (this shows in the bottom curve.) Where the original used monospaced fonts and hand-drawn Greek characters, PIC and EQN give us typeset formulas quite a bit better looking.
In the original, sections were numbered only down to three levels deep. No per-chapter tables of contents were present.
The item lists bulleted a., b., c... and 1., 2., 3.... in this transcription were bulleted (a), (b), (c)... and (1), (2), (3)... in the original. There does not seem to be a good way to generate the original style in DocBook.
Special characters like white square (□), back arrow (←), and the Greek letters were hand-drawn in the original (except for the capital thetas, which were typed capital Os with bars hand-drawn across them). The original also some contained hand-drawn script characters:
in the original: | used for: | represented as: |
---|---|---|
script-A | address metavariables | A |
script-F | format metavariables | B |
script-L | list metavariables | L |
script-M | general metavariables | M |
script-N | general metavariables | N |
script-S | statement metavariables | S |
script-V | vector metavariables | V |
Footnotes were tagged with asterisks in the original rather than being numbered. Section references to section numbers have been replaced with xref tags in order to generate hyperlinks in the HTML rendering; as a result, they may render slightly differently than in the original. Notably, "section 1.1" becomes "Section 1.1" (capitalized) and expressions like "sections 1.1.4 and 3.2" become "Section 1.1.4 and Section 3.2".
The reference structure of the index has been faithfully reproduced; that is, for each index reference a DocBook <indexterm> tag was placed within the span of text defined by the original page numbering (two off-by-one errors were corrected). However, DocBook renders the index differently from the original. The original lacked the canned section headings, and references were by page number rather than section name.
EPSLON and COMPLX and the like are not typos. As pointed out earlier, MAD variable names are limited to the 6 characters in a six-bit character set that would fit in the IBM 704's 36-bit word.
A few typos in the original have been fixed; if you're really curious, comments in the XML source describe them. Variant spellings such as “imbedded” have been preserved.
The original was not continuously page-numbered. Instead the pages were numbered within each chapter with the chapter number as a prefix; thus, page 6 of Chapter I was I.6.
In the Retrocomputing Museum implementation, statement boundaries are detected by a preprocessing stage that places a marker before every newline and then detects and erases markers that precede whitespace followed by a continuation digit punch. This is strictly conformant with the manual says in text, but some of the listings in the manual lacked continuation punches that would be needed under this rule. Either these were transcription errors, or the original parser had an undocumented capability to detect most continued statements automatically. We have assumed the former and restored needed continuation punches in several listings.
The above notes fail to properly convey just how primitive the original document looks to a modern eye, and this is worth noting because transcription into DocBook makes it look a lot less so. Standards have risen in a world saturated with the Web and cheap page printers. The original manual's monospace fonts on yellowing paper with hand-drawn figures and special characters look quaint and ugly by contrast.
The Michigan Algorithm Decoder (MAD) is a computer program which translates algebraic statements describing algorithms to the equivalent machine instructions. This descriptive language – also called MAD – is explained in this manual. The language was patterned after ALGOL 58, a proposed standard language for the description of algorithms, with certain extensions and adaptations which the authors believe make tbe language more useful. It should be undergtood in what follows that a computer program may consist of several sections utilizing the language described herein or other languages if desired. The sections can be translated independently and are linked together just prior to the actual execution of the program.
The original translating program was written for an IBM 704 computer with 8192 words of magnetic core storage, 8192 words of magnetic drum storage and 6 magnetic tapes. The program was subsequently written for the IBM 790/90 computers with 32768 words of core storage. The features described in the appendices are included only in this later version. The programming and the preparation of this manual were done at the University of Michigan Computing Center, and the language has been widely used by University of Michigan students and staff.
B. Arden
B. Galler
R. Graham
In order to present a problem to a digital computer for solution, it is necessary to transmit to the machine a statement of the problem, a procedure for solving it (usually called an algorithm for the solution of that problem), and the data which is needed in the solution. In fact, a statement of the problem is not really necessary, since the algorithm and data are sufficent for a computer, provided the algorithm is stated unambiguously and completely.
As a simple example, let us consider the problem of determining the largest number in a collection of n + 1 numbers A = { a0, a1, a2, ... an } with n ≥ 1. A verbal description of the procedure (algorithm) might be:
Pick up the first number.
Compare it with the second number.
If the first is larger or if they are equal, keep the first one.
If the second is larger, keep the second one.
Whichever one was saved from this comparison is now compared with the third number.
Continue to repeat steps 2 through 5 (each time moving down the list) until the n+1st number has been included in the comparison.
The number which has been finally saved is then the largest number in the collection A.
Unfortunately, this method of description is not very precise. Such words as "compare", "moving down the list", and "finally saved" should really be spelled out more exactly.
The following restatement of the procedure would probably be more suitable:
Set Z = a0.
Let j = 1.
If J > n, the problem is done[1] go to step 7; otherwise go on.
If Z < aj, let Z = aj; otherwise, go on.
Let j increase by 1.
Return to step 3.
Z is the answer.
This may be illustrated by following "flow diagram":
Note that the following conventions have been used here:
Computation of subscripts or countiong occurs in diamond-shaped boxes.
Actual computation occurs in rectangular boxes.
Decisions, based on comparisons occur in triangular boxes.
The "=" inside computation boxes (both diamonds and rectangles) is meant in the dynamic sense: "Compute the value of the expression on the right and let that now be the value of the variable whose name appears on the left."
Although this problem does not require it, we frequently ask a question like "Does j = n?" The "=" in this context is a relation and yields either "YES or NO" when placed between two arithmetic expressions.
We return to the example problems. The algorithm exhibits an important concept, which occurs in a great many procedurs; namely, it contains a loop. A loop has the following characteristic properties:
It is repeated over and over until some conditions is satisfied (occasionally this may be a very complex condition). In the example, the condition was "j > n".
Before the first "iteration," some initialization may be performed. In the example, Z - a0 and j = 1.
After the "body" of the loop is performed, (the comparison, in the example), some variable is incremented, and the termination conditions is tested again. In our example, j is increased by 1, and if j ≤ n, the "body" of the loop is computed again - with the new value of j.
It is often convenient to take advantage of this standard structure of a loop, and use an "iteration box" in the flow diagram. This box would have to indicate the "scope"; i.e., the extent of the body of the loop, the variable which is to be initialized and later incremented, and the condition which will determine the number of iterations in the execution of the loop. The contents of a typical iteration box, (not related to the example above) might be:
Repeat the computation through the box labelled (for example) "BACK", starting with the variable α having the value 12, and increasing it by the amount 3 after each time until either α > 90, or |x + y| ≤ ∊.
We shall abbreviate this by the form:
THROUGH BACK, FOR ALPHA = 12, 3 ALPHA .G. 90 .OR. .ABS. (X+Y) .LE. EPSLON
Here we have substituted ".G." for ">", ".LE." for "≤", ".ABS. (X+Y)" for "|x + y|". (The reason for these simple substitutions is the lack of characters such as >, ≤ as input to the computer. Such details are fully presented in Chapter II of this manual.)
The IBM 704 on which MAD was implemented had a very primitive pre-ASCII character set. It did not even include lowercase letters, let alone fancy punctuation like < and >. ASCII still lacks ≤ and ≥, but writing these as <= and >= became universal in later ASCII-based languages.
The flow diagram for our previous example, then, may be rewritten as follows: (In order to keep the notation familiar when possible, the above-mentioned character substitutions will not be made in diagrams.)
The three operators specified in a "THROUGH" statement are written in a single box to indicate the correspondence to a single statement.
The question still remains: How does one communicate the algorithm to the computer? A translator such as MAD is designed according to the philosophy: Once the algorithm has been stated, as in a flow diagram, it should be presented to the computer directly in that form, or as near to it as possible. The "translator" then has the job of producing a translation from a flow diagram in a form acceptable as input to the translator, the user's work ends with the diagram itself.
It should be remembered that the MAD language was designed with several important criteria in mind:
Speed of translation - the choice of some words of more than six characters (e.g. WHENEVER, TRANSFER, THROUGH) enables the translator to recognize the statement type with a minimum of analysis.
Generality - a few restrictions on the construction of statements and expressions have been introduced as possible.
Ease of adding to the language - desired additions can be made easily, since most of the necessary information can be stored in tables during translation.
It is obvious that a different set of criteria could lead to a different language. The details of the form of statements in this "input language" are the subject of Chapter II.[2]
We are concerned here with introducing some of the basic ideas, such as the loop, etc. The input form of our example would be:
DIMENSION A(100) Z = A (0) THROUGH BACK, FOR J = 1, 1, J .G. N BACK WHENEVER Z .L. A(J), Z = A(J) INTEGER J, N END OF PROGRAM
The DIMENSION statement assigns a block of storage in the computer large enough to handle a0, a1, a2, ... a100, if necessary. The INTEGER statement declares J and N to be integers. We have already seen that different shaped boxes are used for operations on integers, such as subscript modification and counting. The reason for this is that integer arithmetic can be done more simply and efficiently, usually with less round-off error, than arithmetic on non-integers. [Numbers which may have fractional parts are usually written in the so-called "scientific notation", such as 3.1 × 10-6 are called floating point numbers.] Numbers are assumed to be in the floating-point mode unless otherwise declared, as in the INTEGER statement in the example. The WHENEVER statement above is to be interpreted in the sense: Whenever the following condition (in this case Z < aj) is satisfied, do the specified action (Z = aj), otherwise just go on.
It is interesting to ask just how complicated a condition can be used in making decisions. We have seen that such conditions may occur in iterations statements, and "WHENEVER" statements, etc., for the purose of making binary (i.e., "yes" or "no") choices. An expression which can be labelled "True" or "False" is exactly what is needed here. Such expressions are called Boolean[3] expressions, and usually involve "and", "or", "not", and possibly other such words connecting shorter expressions involving <, ≤, =, ≠, >, and ≥. For example, the following is a Boolean expression:
((x - 3)3 < y and i ≤ j) or x ≥ 3
This will be "true" for some values of x and y, and "false" for others. It might then occur in statements such as:
WHENEVER ((X - 3) .P. 3 .L. Y .AND. I .LE. J) .OR. X .GE. 3, TRANSFER TO AGAIN
or in the iteration statement
THROUGH ALPHA, FOR BETA = 1, 1, (X - 3) .P. 3 .L. Y .AND. I. .LE. J) .OR. X .GE. 3
where .P. denotes exponentiation (i.e., "to the power").
Returning again to the example problem on the largest of a set of numbers, we observe that no provision was made for obtaining the values of n, a0, a1, ..., an on which to perform our computation, nor was any provision made for producing an answer. Normally, each program would contain suitable input and output statements, such as are described in Chapter II and illustrated in Chapter III. Let us assume instead that we are interested in making out little algorithm available for use in any other program, as a pre-packaged "function", in the sense that, given n and a0, ..., an, this function computes as its value the largest of a0, ..., an. In this case we shall call our algorithm an EXTERNAL FUNCTION, and give it a name, say MAX., since it will be written and translated externally with respect to the program which will later call upon it. The program will now be written:
EXTERNAL FUNCTION (N,A) ENTRY TO MAX. Z = A(0) THROUGH BACK, FOR J = 1, 1, J .G. N BACK WHENEVER Z .L. A(J), Z = A(J) FUNCTION RETURN Z INTEGER J,K END OF FUNCTION
The first statement specifies the inputs to the function to be N and A, the second statement indicates the point of entry, the FUNCTION RETURN statement specifies the value of Z as the desired value of the function, and the other statements are as before. Any program using the function now need only call upon it by name, as in the statement:
LARGEQ = 1. + MAX. (6,Q)/3.
Note that the set (in this use of MAX.) whose largest element is desired is called Q, and N has the value 6. No DIMENSION statement is needed for A is the EXTERNAL FUNCTION definition program above, since A is there only as a "dummy variable", anyway. When used, with a concrete set Q, we would expect a DIMENSION statement for Q in the program that calls on MAX. for a value.
For a second example consider the problem of repeatedly solving, by Newton's method , of the equation f(x) - ax + x = 0, taking a different value for a each time but with the restriction that a ≥ 1. This method involves the repeated evaluation of the formula
(the prime denotes the derivative with respect to x) until xi+1 is a root - i.e., until f(xi+1) = 0. Actually, in the numerical solution of equations where we deal with approximate numbers the latter condition becomes: until |f(xi+1)| < ∊, where ∊ is a small positive number.
To evaluate the iterative formula above the first time it is necessary to have an initial approximation, x0, to the desired root. The use of the index, i, as well as the initial subscript zero suggests that we will produce a sequence, x0, x1, ... xn, of approximations to the root. However, from the computational point of view we do no need all of these values simultaneously, since to evaluate the formula it is sufficient to know only the last value, x, produced. We can say
x is replaced by x - f(x)/f'(x)
Often a left arrow (←) is used to mean "is replaced by" but in the actual statements produced for the computer the "=" symbol conventionally has this meaning.
It is important to realize what the "=" means in this context; it is different from the usual use of the symbol where it indicated a relation. When the "=" symbol is used as such a "replacement operator" the item on the left of the "=" is always the name of a variable. The variable may have a complicated subscript but nevertheless it is not an expression, but the name of a variable. The item on the right of the symbol is an expression involving one or more constants, variables, etc. The operation implied is simply that the value of the express on the right becomes the value of the variable whose name appears on the left. This is referred to as substitution.
For our specific example, then, the evaluation of the iterative formula could be described as
although we will for convenience break this into two statements. The entire computational procedure can be represented by the following flow diagram.
The corresponding statements are:
ST READ FORMAT ALPHA, A, X, EPS WHENEVER A .L. 1.0, TRANSFER TO PRT REPEAT F = A .P. X + X X = X - F/(ELOG. (A)*A .P.X + 1.0) WHENEVER .ABS.F .GE. EPS, TRANSFER TO REPEAT PRINT FORMAT ALPHA, A, X, EPS TRANSFER TO ST PRT PRINT FORMAT BETA, A, EPS TRANSFER TO ST VECTOR VALUES ALPHA =$S1,3F15.6*$ VECTOR VALUES BETA =$17H0A TOO SMALL, A =2F15.8*$ END OF PROGRAM
The first statement, labelled ST, causes a value for a,x, and ∊ to be read into computer storage. A block of adjacent storage location in the computer (a "vector") named ALPHA is designated as containing a description of how the three numbers were punched on the card. (This description – which is the value of ALPHA – is described in a later statement; see (9) below.)
The second statement is a simple conditional which causes the statement labelled PRT to be the next one executed if a < 1. Otherwise, the next one in ssequence (labelled REPEAT in this case) will be executed.
The statement labelled REPEAT computes ax + x using the current value of x, and places the result of this computation in a storage location named F.
The next statement divides the value F of the function by the derivative of the function (loge a · ax + 1.0), evaluated using the current value of x, subtracts this quotient from the current value of x and the resulting difference is stored as the current value of x. The name ELOG. is the name for the function loge and the item in parentheses following this name indicated the variable whose natural logarithm is desired.
The following statement is a simple conditional which causes the function and iterative formula to be evaluated again if |f(x)| ≥ ∊. Otherwise, (i.e., |f(x)| < ∊), the next statement in sequence is executed.
The PRINT statement causes three numbers - the current values of a, x and ∊ to be printed. The arrangement and form of the numbers on the printed page are controlled by the format description which is the value of the vector named ALPHA.
The TRANSFER statement following causes the statement labelled ST to be the next one performed.
The statement labelled PRT is the one executed immediately after the first statement whenever a < 1.0. This statement causes the current value of a to be printed (here a must be < 1.0) according to a format description stored in BETA. The format description also causes the printing of some constant alphabetic information preceding thenumber; namely, the remark "A TOO SMALL, A = ".
The next two statements following the last TRANSFER TO ST set the initial values of two vectors named ALPHA and BETA. Although VECTOR VALUES statements can be used to preset vectors to specified numeric values, in both instances here the values are format descriptions. The strings of characters between the dollar signs (MAD quotation marks!) are to be taken literally as they appear and stored in designated vectors. The actual format specifications are described in more detail in Chapter II, but, for example, the first description: S1,3F15.6* means that the first item is to be a space (S1), followed by three (3) numbers printed with a decimal point but no exponent (F), each occupying fifteen card or print positions (15), and that there will be six positions to the right of the decimal point (.6). The asterisk (*) indicates the termination of the description.
The final statement indicates the end of the statements and is the last statement executed when a definite termination to the problem is known. In our particular example, the computer would continue until it has exhausted the given input values of a, x, ∊.
The final example before the description of the language in Chapter II is a more general and more elaborate illustration of Newton's method. Here, we consider the solution of the equation x3 + ax2 + bx + c = 0, starting with some input value for x0, using the iterative formula:
which is obtained from the standard Newton's Method formula
for the problem f(x) = 0. (Here f(x) = x3 + ax2 + bx + c). We shall specify quantities ∊1 and ∊2, and stop the iteration when the following condition is satisfied:
(|xi+1 - xi| < ∊1 and |f(xi)| < ∊2) or i ≤ n0
where n0 is some upper limit to the number of equations which we can tolerate. The inequaliies involving ∊1 and ∊2 may be illustrated by the graph below, We are requiring that at xi the value of the function f(xi) be between the upper and lower bounds ∊1 and -∊2. Also the distance d between xi+1 and xi should be less than ∊1. Observe that we are never concerned with more than two successive aproximations to the root — say x and xm (for next x) — and that the incrementing of i is not really necessary.
GAMMA READ FORMAT CARD, A, B, C, XZERO, EPS1, EPS2, NZERO PRINT FORMAT TITLE, A, B, C, XZERO, EPS1, EPS2, NZERO X = XZERO NEXTX = D.(X) INTERNAL FUNCTION D.(Z) = (2.*Z.P.3 + A*Z.P.2 1 -C)/(3.*Z .P.2 + 2.*A*Z + B) ALPHA THROUGH BETA, FOR I = 0, 1, (.ABS.(NEXTX-X) .L. 1 EPS1 .AND. .ABS. F.(X) .L. EPS2) .OR. I .GE. 2 NZERO X = NEXTX BETA NEXTX = D.(X) WHENEVER I .GE. NZERO, PRINT FORMAT REMARK PRINT FORMAT RESULT, I, X TRANSFER TO GAMMA INTERNAL FUNCTION F.(Y) = Y .P.3 + A * Y.P.2 1 +B*Y+C INTEGER I, NZERO VECTOR VALUES CARD = $6F10.5, I4*$ VECTOR VALUES TITLE = $27H1SOLUTION OF CUBIC EQUATION 1 /4H0A = F8.3, S8, 3HB = F8.3, S8, 3HC = F8.3 2 S8, 7HXZERO = F8.3/12H0EPSILON 1 = F8.3, S8 3 11HEPSILON 2 = F8.3, S8, 3HN = I4*$ VECTOR VALUES RESULT = $20H0NO. OF ITERATIONS = I4 1 S20, 3HX = F8.3*$ VECTOR VALUES REMARK = $15H0NO CONVERGENCE*$ END OF PROGRAM
The first statement, labelled GAMMA, would cause the program to read from the data card the numbers a, b, c, x0, ∊1, ∊2, and n0. The format of the input information is to be found in a block of storage labelled "CARD" whose values are set later in the program by a VECTOR VALUES statement. The vector CARD actually has as values the format information in alphabetic form (sometimes called Hollerith, or binary-coded decimal (BCD) form). The construction and analysis of format information is thoroughly discussed in Chapter II.
The second statement in the program causes the inout information, properly labeled., to print out, preceded by the title "SOLUTION OF CUBIC EQUATION". The appropriate alphabetic information is contained in the format information which is t be found in the vector TITLE, as set by a VECTOR VALUES statement later in the program.
Two functions are defined in this program, and each is designated as an INTERNAL FUNCTION. The statement following that whuich is labelled BETA illustrates a conditional output statement. If the iteration is terminated because i ≥ n0, the alphabetic information NO CONVERGENCE is printed before the values of I and X are printed, otherwise that remark is not printed. The final transfer to GAMMA cause the program to start over with a new set of data, if additional data is present, otherwise, the computation is automatically terminated.
The VECTOR VALUES statement, illustrated here, may be used to cause vectors (and matrices) to be initialized to any desired value (even alphabetic values) before the program computation is begun. Of course, these values could be computed or read in as input, if desired, so that the data for a problem could be preceded by its own description of format, in problems where the format may change from one set of data to another.
Table of Contents
There are four classes of constants: Integer, floating point, alphabetic, and Boolean.
Integer constants contain from 2 to 10 decimal digits. The decimal point is assumed to be immediately to the right of the rightmost digit, but is always omitted. Integers may be positive or negative, and while the "+" sign may be omitted, the "-" sign must be present if the number is negative (e.g., 2, -2, 0, +0, -0. 100 are all integers). Leading (but not following) zeros may be omitted (e.g., 5 and 005 represent the same integer, but 3 and 300 do not.
There is a subtle difference from modern practice here. In 2004, major contemporary languages (perhaps unfortunately) follow C's lead and interpret leading 0 as a directive to evaluate the following digits in octal. The Retrocomputing Museum implementation of MAD implements the original MAD behavior.
Floating point constants may be written with or without exponents. If written without an exponent, the constant contains from one to eight digits and a decimal point ".", which must be written, but which may appear anywhere in the number. Thus, 0., 1.5, -0.05, +100.0, .1 and -4. are all floating-point constants. If the number is written with an exponent, it must contain from one to eight digits with or without a decimal point, followed by the letter "E", followed by the exponent of the power of 10 that multiplies the number. (If the decimal point is omitted, it is assumed to be immediately to the left of the letter "E".) The exponent m consists of one or two digits preceded by a sign (although a "+" sign may be omitted), and must satisfy the condition -38 ≥ m ≥ 38. Examples of floating-point constants with exponent are; .05E-2(= .05 × 10-2) -.05E2(= -.05 × 102), 5E02(=5.0×102), 5.E2(-5.0×102).
Negative floating point constants must be preceded by a "-" sign. Positive constants may be preceded by a "+" sign.
An alphabetic constant consists of from one to six admissible characters preceded and followed by the character "$". The admiss- ible characters include all letters of the alphabet, the digits 0 through 9, the special characters +, -(minus sign), -(dash), *, /, =, ). (. ., the comma "," and the blank space, to be represented here occasionally (but not punched on input cards), as the character "□". Thus the following are alphabetic constants: $ABCD$, $TO BE$, $DEC. 4$, $5 +3=8$. Note that blank spaces , while ignored elsewhere in the language, count as characters in alphabetic constants. An alphabetic constant is stored internally as an integer, and any constant containing fewer than six characters will be extended to six characters by adding blanks on the right; thus $ABCD$ will appear internally as $ABCD□□$
The Retrocomputing Museum implementation neither enforces the six-character limit nor blank-pads constants. These features were an artifact of packing six-bit characters into the IBM 704's 36-bit machine word.
The way the original is worded suggests that minus sign and dash were different characters in the character set that the Michigan 709 was using. They are rendered with the same glyph in the original, but this may be a collision enforced by period typewriters (like 0 and O being indistinguishable).
The name of a variable consists of one to six letters or digits, the first of which must be a letter. If the variable is defined as an n-dimensional array variable (see Section 3.3, “Dimension Declaration”) then the name of an element of the array consists of the variable name, (i.e one to six letters or digits, starting with a letter), followed by the appropriate subscripts separated by commas and enclosed in parentheses. Thus the following are "single variables:": X51, ALPHA6, LAMBDA, GROSS, while the following are elements of arrays: BETA(C1, C2, 6), X15(Y, Z1), J(6), J(Z1 + 5 *Z2, 5). (See Section 1.11, “Subscript Expressions” for the description of subscripts.) Parentheses enclosing subscripts may not be omitted.
A statement may be labelled or unlabelled. Labels are used to refer to a statement in other statements. A statement label consists of from one to six letters or digits, the first of which must be a letter, e.g. IN or BACK. A statement label may be an element of a statement label vector, in which case the vector name is followed by a constant integer subscript enclosed in parentheses, e.g. S(2) or LBL(3). A statement label appears in the label field (i.e. columns 1-10) of the statement it identifies. When a statement extends to additional cards (i.e. cards identified by a digit punched in col. 11) the statement label need not be punched on the additional cards.
The name of a function consists of one to six letters or digits followed by a period "." which must be written. The first character of a function name must be a letter. If the function is single-valued, then the value of the function is represented by following the function name by the proper number of arguments (see Section 3.8, “Function Definitions” for the definition of function) separated by commas and enclosed in parentheses. Thus, ADD51., COS., POLY., and FUNCT3. are function names, while ADD51. (X,Y,ADD.), POLY. (N, VJ, 7) and COS. (X) are values of functions.
The following arithmetic operations are available:
Subtraction, written as "-", e.g., Z5 - D. (Note, both the minus sign and the dash are accepted.)
Multiplication, written as "*", e.g. Z5*D. (Note that the "*" may not be omitted. It is illegal to write Z5D, since it would be impossible to distinguish such a product from the variable Z5D.)
Division, written as "/"; e.g., Z5/D. If both Z5 and D are integers, the result is again an integer; e.g., the "fractional part" of the true quotient is truncated (not rounded). For example, if Z5 = 7, and D = 3, the Z5 = D will have the value 2.
Exponentiation, written as ".P.", e.g., Z5.P.D, and meaning (Z5)D, i.e., Z5 raised to the power D.
Negation, written as "-", e.g., -ALOHA, meaning the "negative of ALOHA." Thus -X.P.-.5 means -(X-.5).
Arithmetic expressions are defined inductively as follows:
All integer, floating point, and alphabetic constants, integer and floating point individual variables, subscripted integer and floating point array variables, and integer and floating point values of functions are arithmetic expressions.
If E and F are any arithmetic expressions and V and individual or subscripted array variable, then the following are also arithmetic expressions: +E, -E, .ABS.E, E + F, E - F, E * F, E/F, E.P.F, and (E).
The only arithmetic expressions are those arising in (a) and (b).
The following Boolean, or logical, operations are available in the language (where M and P are boolean expressions):
(M) has the same value as M.
M.OR.P has the value 0B if and only if both M and P have the value 0B.
M.AND.P has the value 1B if and only if both M and P have the value 1B.
M.THEN.P has the value 0B if and only if M has the value 1B and P has the value 0B.
M.EQV.P has the value 1B if and only if M and P have the same values.
Thus, .NOT., .OR., .AND., .THEN., AND .EQV. correspond to the usual logical operations, ~, ∨, ∧, ⊃, and ≡.
Boolean expressions are defined inductively as follows:
Boolean constants, Boolean individual variables, Boolean subscripted array variables and Boolean-valued functions are Boolean expressions. (see Section 1.4, “Functions” and Section 3.2, “Mode Declaration”).
If H and F are arithmetic expressions: then H.L.F., H.LE.F, H.E.F., H.NE.F, H.G.F., H.GE.F, are Boolean expressions, where the meanings are H < F, H ≤ F, H = F, H ≠ F, H > F, and H ≥ F, respectively.
If M and P are Boolean expressions, then the following are also Boolean expressions: .NOT.M,(M), M.OR.P, M.AND.P, M.THEN.P, and M.EQV.P.
The only Boolean expressions are those that arise in (a), (b), and (c).
Examples of Boolean expressions are: (X .G. 3 .AND. Y .LE. 2) .OR. (GAMMA .L. EPSILN), and ((P .AND. Q) .THEN. Q) .EQV. (P .OR. .NOT.P), where P and Q are Boolean variables.
Parentheses are used in the same way as in ordinary algebra and logic to specify the order of the computation. Also, certain conventions are used to allow deletion of parentheses. The conventions used here are the same as in ordinary algebra and logic, namely: Parentheses may be omitted, subject to the rules (A) and (B) below, but redundant parentheses are allowed.
(A) Within any expression the sequence of computation, unless otherwise indicated by parentheses, is:
.ABS., + (as unary operations)
.P.
- (as a unary operator)
*, /
+, - (as binary operations, i.e., addition and subtraction)
.E., .NE., .G., .GE., .L., .LE.
.NOT.
.AND.
.OR.
.THEN.
.EQV.
, (as used to separate function arguments)
=
Examples:
.ABS.(B - C) means |B - C|, while .ABS.B - C means |B| - C.
-B + C means (-B) + (C), while -(B + C) means the negation of the sum.
B.P. - X + Y means B-X + Y, while B.P.(-X + Y) means B-X+Y.
K2/Z - 3 (K2/Z) - 3, while K2/(Z - 3) implies that Z - 3 is denominator.
A * B + C means (A * B) + C.
A.P.3/J means (A3)/J
X.L. Y + 3 means (X) .L. (Y + 3).
P.AND..NOT.P .EQV.Q means (P.AND.(.NOT.P)).EQV.Q.
Z = X + Y /QA means Z = (X + Y/QA))
A = -B.P.X means A = -(BX).
(B) Within an expression operations appearing on the same line of the list in (A) are to be performed from left to right, unless otherwise indicated by parentheses.
Examples:
A + B - C + D - E means (((A + B) - C) + D) - E.
X/Z * Y/R * S means (((X/Z) * Y/R) * S.
The kind of arithmetic performed on a constant, variable or function value is determined by its mode. There are five modes in MAD; floating point, integer, Boolean, statement label, and function name. Floating point, integer, and Boolean constants were described in Section 1.1, “Constants”. Alphabetic constants are assumed to be of integer mode. Section 3.2, “Mode Declaration” describes how the modes of variables and functions are specified.
If an expression consists entirely of one constant, one variable, or one function value, the mode is that of the constant, variable, of functional value itself. If the expression is a compound expression; i.e., consists of two or more subexpressions joined by logical or arithmetic operations, the following rule applies:
If an expression is a Boolean expression as defined in Section 1.8, “Boolean Expressions”, then its mode is Boolean. An arithmetic expression is considered to be in the floating point mode if any operand of any arithmetic expression in the expression is in the floating point mode. If all operands are integer (or alphabetic), then the expression is considered to be in the integer mode. In this determination arguments, though not values, of functions are ignored.
Thus, if Y, Z, and W are floating point variables, while the function GCD. and the variables I and J are in the integer mode, then the expressions
Y = GCD.(I, J)
Y + Z - I
I + 1.
GCD. (I, J)/Z
are all floating point expressions while the expressions
I + GCD.(I, J)
(I + J)/3
I + 1
GCD.(I, J)/I
are all integer expressions.
If an expression has subexpressions of different modes, a conversion may be necessary before some of the operations can be performed. Thus, in the expression
Y + 3
if Y is in the floating point mode it cannot be added directly to the integer 3. But for one precaution the user need not be concerned with this since the instructions necessary for the conversion of the integer to floating point form before adding are automatically inserted by the translator during the translation process. The precaution is that if the integer being converted is greater than 134,217,728 (i.e. 227) then an improper conversion will take place.
In some cases, however, the user must understand the sequence in which the conversions will be made. Consider the expression
( Y + 7/3) + (I * J/K)
where Y is in the floating point mode, and I, J, and K are in the integer mode. According to the parenthesizing conventions, the computation will proceed in the following order (where the T's are temporary locations):
T1 = I * J
T2 = T1/K
T3 = 7/3
T4 = Y + T3
T5 = T4 + T2
and T5 will be the value of the expression.
Now, since both I and J are integers, the first multiplication will be integer multiplication, and T1 will be an integer. Since the following involves two integers, it will be integer division, and, if K happens to have a larger value than T1 the quotient is 0. Similarly, T3 will have the value 2 because of the division of two integers. In the computation of T4, however, we have "mixed modes", since Y is floating point and T3 is integer. There I3 will be automatically converted to floating point before adding. Likewise, in the next step, the integer T2 will be converted to floating point before adding to the floating point number.
In other words, although the mode of the expression is floating point because of the presence of the floating point variable Y, some of the computation (until Y is involved) is performed in integer arithmetic, and this may occasionally cause the final value to be different from the value one might expect from a different analysis.
In the example above, the divisions would be performed in the floating point mode if the expression were written:
(Y + 7./3) + (I * J)/(K + 0.)
Of course, many times the expression will be written as originally stated just to achieve the "truncation" effect.
A statement label (appearing in the label field of a statement) is a constant of statement label mode. A function name with no arguments (e.g. SIN.) is a constant of a function name mode. A single constant, variable, or function value of statement label (function name) mode is an expression of statement label (function name) mode. There are no other expressions of statement label (function name) mode. If A and B are both statement label (function name) expressions then A.E.B. and A.NE.B are Boolean expressions except when A or B are elements of a vactor which has been preset by a VECTOR VALUES statement (see Section 3.7, “Presetting Vectors”). In this exceptional case A.E.B. and A.NE.B are not expressions.
Any arithmetic expression may be used as a subscript expression for an element of a linear array. If the value of the expression is in the floating point mode (see Section 1.10, “Mode of Expressions”), it is truncated to integer form before being used as a subscript. The expressions for subscripts of elements of an array whose dimension is two or greater must be of integer mode. Moreover, for arrays of dimension three or greater the use of subscripts having other than integer mode will not be caught as an error. Subscript expressions may contain variables with subscripts, etc.
Examples of subscripted variables: J(3), K10(Z + 5 * XY/T), MP(A, B + 6 * J, I * 6/TDX), MA(K(Z + 5) + T(1) + 6).
This statement has the form illustrated by
ALPHA = Y + Z + F.(X, Y, Z)
That is, the left side of the "=" sign consists of the name of a variable (either an individual variable or a subscripted array variable), and the right side consists of any expression of the same mode. The only exceptions to this mode requirement are the cases: (1) If the variable on the left is of integer mode then the value of a floating point expression of the right will be converted to integer mode. (2) if the variable on the left is of floating point mode then the value of an integer expression on the right will be converted to floating point mode.
The substitution statement is to be interpreted as "(1) Compute the value of the expression on the right, (2) convert it, if necessary, to the mode of the variable on the left of the "=" sign, and (3) give the variable on the left the value which results from steps (1) and (2)." (See Section 1.10, “Mode of Expressions” for mode of expressions.)
Thus, if Y is a floating point variable, then the statement
Y = 1
will cause the integer 1 to be converted to floating point and then stored in the location called "Y"; i.e., Y will now have the value 1. (as a floating point number). If the statement were written
Y = 1.
then the floating point number 1. would be stored in the location "Y"; i.e., Y would again have the floating point value 1., but in this case the conversation of the integer is unnecessary, thus speeding up the computation.
When a floating point number is to be converted to an integer, it is first expressed as a number with both an integer part and a fractional part, and then the fractional part is truncated. Thus, the following floating point numbers:
3E5, .3E0, .34568127E2, - .345681E10
would convert to the following integers, respectively:
300000, 0, 34, -3456810000.
This statement has the form: TRANSFER TO S
Here S may be any statement label or any expression in statement label mode which labels an executable statement. Execution of this statement causes the computation to continue from the statement whose label is the value of S. Examples: TRANSFER TO SUMX, TRANSFER TO SWTCH (K + 2) (if K=4 then the value of SWTCH(K+2) is SWTCH(6)).
There are two types of conditional statements.
(a) Simple Conditional
WHENEVER B,Q
Here B is a Boolean expression and Q any executable statement except the following: end of program, another conditional, iteration, and function entry. If at the time of execution of this statement, the expression B has the value 1B, the statement Q is executed. If, however, B=0B, then Q is skipped and computation continues from the next statement in sequence. The comma in this statement, as in other statements containing punctuation, must be written.
Examples:
WHENEVER XM.LE.1, TRANSFER TO END WHENEVER I.GE.N.AND.J.NE.I-1, I=0
(b) Compound Conditional
S1 WHENEVER B1 ... ... Θ1 S2 OR WHENEVER B2 ... ... Θ2 ... ... Sk OR WHENEVER Bk ... ... Θk Sk+1 END OF CONDITIONAL
Often the last condition Bk is always true. This may be expressed by the statement
OR WHENEVER 1B
or alternately the statement
OTHERWISE
The Si are statement labels which need not be used unless desired. k may equal one (if no "OR WHENEVER..." statements are used). Here B1, ..., Bk are Boolean expressions, and the execution of this block of statements is as follows.
Each B1 is tested in turn, starting with B1. It B1 has the value 0B, then B2 is tested, etc. As soon as some expression, say Bj, has the value 1B, then the statements between (but not including) Sj and Sj+1 (i.e. Θj) are executed. At this point the execution of the entire block is considered ended, and computation continues from the first statement after the declaration labelled Sk+1. In other words, at most one of the alternative computations is performed; e.g., that one which immediately follows the first expression B1 which has the value 1B. If none of the Bi has the value 1B, none of the computation in the scope of these statements is performed.
Example: The evaluation of the function whose graph is
might be given by the section of the program:
WHENEVER X .LE. 0. .OR. 1. .LE. X .AND. X .L. 2. .OR. X .GE.3 Y = 0. OR WHENEVER 0 .LE. X .AND. X. .L. 3. Y = 1. END OF CONDITIONAL
This section of program could be rewritten in another way.
WHENEVER 0. .LE. X .AND. X .L. 1. Y = X OR WHENEVER 2. .LE. X .AND. X .L. 3. Y = 1. OTHERWISE Y = 0. END OF CONDITIONAL
The indentation of the statements between the conditional statements is not required but contributes to the readability.
This statement has the simple form:
CONTINUE
It serves as an entry point in the program, and causes no computation to be performed by its presence or absence.
The following program segment illustrates the use of this statement.
. . . A = D(1, 1) THROUGH ST1, FOR I = 1, I .G. 10 THROUGH ST1, FOR J = 1, J .G. 10 WHENEVER A .LE. D(I, J), TRANSFER TO ST1 K = I L = J ST1 CONTINUE . . .
This statement causes the block of statements which follows immediately afterwards to be repeatedly executed, each time varying the value of some variable, until the specified list of values for that variable is exhausted, or until some specified condition is satisfied. The THROUGH statement may take either of the following two forms.
(a) THROUGH S, FOR VALUES OF V = E1, E2, ..., Em
Here S is the statement label of the last executable statement in the block to be repeated. The block of statements following (and not including) the THROUGH statement, up to and including the statement whose label is S, will be called the "scope" of the THROUGH statement. Following the word OF appears the name of the iteration variable (in the illustration: V), which may be either an individual variable or subscripted array variable of any mode. To the right of the "=" sign may appear any number of expressions E1, ... Em. The modes of the E1 bear the same relationship to the mode of V as they would in the statement V = E1 (see Section 2.1, “Substitution Statement”).
The execution of this statement causes the statements within its "scope" to be executed, first with V = E1, then again with V = E2, and so on, until the list of expressions is exhausted. Computation then proceeds with the statement immediately following statement S. At this time the iteration variable will have the value of the expression Em unless its value was changed during the final iteration. An example of this type of statement is:
THROUGH A, FOR VALUES OF BETA = 3, 4, X5, Y(6 + I) + 3 J = 5 = BETA + 6 J1 = J .P..5 - 1 A X(BETA) = J1 * COS.(2. * THETA)
(b) THROUGH S, FOR V = E1, E2, B
Here S is a statement which defines the "scope" exactly as in (a) above (with the exception: If S is the label of the THROUGH statement itself, the iteration will proceed as described below, but the scope will be empty, and the iteration will consist only of the incrementing of V by E2 and the testing.) Following the word FOR is the name of the iteration variable (in the illustration: V), which may be an individual variable or subscripted array variable of any mode. The modes of E1 and E2 are subject to the rules which would apply to the statements V = E1 and V = V + E2. B is a Boolean expression.
The execution of the statement proceeds as follows: The variable V is set equal to E1. If the value of B = 1B, the scope of the THROUGH statement is not executed. If the value of B = 0B, the scope is executed. V is then incremented by E2, and B is tested again. In general, as soon as B = 1B, the scope is not executed, and the computation proceeds from the statement immediately following statement S!. Each time B = 0B, the statements in the scope are executed, then V is incremented by E2, and B is tested again. Thus, when the iteration is finished and B = 1B, V has the value used during the last computation of the scope, incremented by E2. The scope will not have been executed for this value of V. (The value of V will be E1, of course, if B = 1B before the scope is executed at all.) If, at any time, the computation transfers out of the iteration to another part of the program, the value of V will be the current value at the time the transfer was made.
In all cases, every reference to an expression E1 will involve its current value at the time of reference. Moreover, the variable V may have its value changed at any time during the execution of the scope. In a statement of the form (a), the value of V will be reset by the value of the next E1 for the next computation of the scope. In a statement of form (b), the current value of V will be used for incrementing, testing, etc.
Examples:
(i) To evaluate the polynomial cnxn + cn-1xn+1 + ...+ c1x + c0 using the formula (...((cnx + cn-1)x + cn-2)x + ...+ c1)x + c0 (nested multiplication), we may write the program:
INTEGER J,N Y = 0 THROUGH POLY, FOR J = N, -1, .L. 0 POLY Y = X * Y + C(J)
(For the meaning of the statement INTEGER J,N see Section 3.2, “Mode Declaration”).
(ii) A Newton's Method solution (Xk+1 = xk - f(xk)/f'(xk)) of the equation f(x) = cos x - x = 0 could be written as a single statement, using the criterion "|f(x)| < ∊ and |xk - xk+1| = |f(xk)/f'(xk)| < | ∊" for stopping the iteration:
NEW THROUGH NEW, FOR X = X0, (COS.(X)-X)/(SIN.(X) + 1.), 1 .ABS.(COS.(X) - (X) .L. EPSLON .AND. 2 .ABS.(COSX.(X) - (X)/(SIN.(X) + 1.) ) .L. EPSLON
where X0 is the initial guess.
(iii) If the transformation of the iteration variable is to be performed within the scope of the iteration, one may use a zero increment. E.g., if J is an integer variable, and the scope of the iteration is to be performed for those multiples of 2 which are not multiples of 5 and which are less than the value of the integer K, one might write the iteration as follows:
THROUGH END, FOR J = 2, .GE. K ... ... J = K J + 2 END WHENEVER J .E. (J/5) * 5, J = J + 2
(iv) A table-look-up procedure using an interation statement. Suppose that a string of alphabetic (or numeric) characters (i.e., a "sentence") has been decomposed into single characters stored in C(1), C(2), ..., C(K), where K is the length of the string. Then the first occurrence of a comma could be found as follows:
LOOK THROUGH LOOK, FOR I = 1, 1, C(I), .E. $, $ .OR. I .G. K WHENEVER I .G. K, TRANSFER TO NOCOMA
As indicated in Section 2.5, “THROUGH (Iteration) Statement”, the "scope" of an iteration statement is the block of statements designated for repeated execution, indicated by ... below:
THROUGH END, FOR V = E1, E2, B ... ... END ...
Some of the statements within the scope of an iteration may, themselves, be iteration statements. However, if iteration statement b is in the scope of iteration a, then the scope of b must be entirely within the scope of a. The following diagrams represent some valid configurations:
(1) THROUGH K, FOR ... (Iteration Statement a) ┌──────────────────────────────── │ Scope │ THROUGH M, FOR ... (Iteration Statement b) of a │ ┌──────────────── ... │ Scope of │ ... │ │ ... │ b │ ... │ │ ... │ └──────────────── ... │ ... └──────────────────────────────── ... THROUGH K, FOR ... (Iteration a) ┌──────────────────────────────── (2) │ │ THROUGH K, FOR ... (Iteration b) Scope of │ ┌──────────────── ... │ Scope of │ ... a │ │ ... │ b │ ... │ │ ... └───────────────┴──────────────── ...
Here, although the scopes of a and b both end on statement K, iteration b is incremented and tested first. Therefore, iteration b is completed before iteration a is incremented:
(3) THROUGH K, FOR ... (Iteration a) ┌──────────────────────────────── │ │ THROUGH M, FOR ... (Iteration b) │ ┌──────────────── ... │ Scope of │ ... │ b │ ... │ └──────────────── ... │ ... │ THROUGH N, FOR ... (Iteration c) │ ┌──────────────── ... │ Scope of │ ... │ c │ ... │ └──────────────── ... │ ... └──────────────────────────────── ...
The following diagram represents an invalid configuration:
(4) THROUGH K, FOR ... (Iteration a) Scope of ┌──────────────── │ a │ THROUGH M, FOR ... (Iteration b) ┌───────────────┼──────────────── ... │ │ ... Scope │ │ ... of b │ │ ... │ └──────────────── ... │ ... └──────────────────────────────── ...
When iteration statements occur in the scope of other iteration statements, they are said to be "nested". The "nesting depth" of an iteration statement is the number of iteration statements in whose scope it appears. The nesting depth of an iteration may not exceed 50. For example:
(5) THROUGH K, FOR ... (Iteration a) ┌──────────────────────────────────────── │ │ THROUGH M, FOR ... (Iteration b) │ ┌──────────────────────── ... Scope of │ Scope of │ ... a │ b │ THROUGH N, FOR ... (Iteration c) │ │ Scope ┌─────────────── ... │ │ of c │ ... │ │ └─────────────── ... │ │ ... │ └──────────────────────── ... │ ... └──────────────────────────────────────── ...
In example (5), iteration a has a nesting depth 0, iteration b has nesting depth 1, and iteration c has nesting depth 2. In example (3), both b and c have nesting depths of 1.
There are no restrictions on jumping into or out of the statements in the scope of an iteration.
PAUSE NO. n
This statement indicates a breakpoint in the program, and it causes the computer to stop in such a way that the operator can manually start it and automatically go on to the next statement in the program. The number n is an octal integer containing up to 5 digits, which will be displayed on the computer console for the operator to note when the computer stops, thus indicating the point in the program at which the stop occurred.
The Retrocomputing Museum implementation handles this by generating code that prints the octal number to standard output, then waits for the user to press Enter.
Normally, the value of a function will occur as part of an expression, as in the statement:
Z = COS.(X)/SIN.(X + 2.)
Certain functions, however, may stand alone as separate statements, as a statement calling for the sorting of a list, etc. This would appear as:
EXECUTE LSORT.(ARRAY, MAP, N)
(See Section 3.8, “Function Definitions” for definition of functions). Here ARRAY, MAP, N are the "arguments" of the function (subroutine) LSORT. Arguments and parentheses may be omitted in an EXECUTE statement if the function has no arguments.
Provision is made for including an error return in function definition programs (see Section 3.8, “Function Definitions”). The form of the statement is simply:
ERROR RETURN
(An example of its use is shown near the end of Section 3.8.3, “Internal and External Functions”).
In order to use it in a function evaluation, the last (right-most) argument of the function must be the label of a statement or a variable in statement label mode, whose value is the label of a statement to which a transfer is made in case the ERROR RETURN statement is executed.
This language is confusing, and doesn't seem to match the actual uses of ERROR RETURN in the examples. The Retrocomputing Museum implementation ignores it.
Even though an error return has been provided in the definition of a function, in the use of the function the last argument may be omitted. If it is omitted, execution of the ERROR RETURN statement will cause control to be transferred to the operating system in which the translated program is imbedded, with an error indication.
The Retrocomputing Museum implementation generates code that prints ERROR RETURN and terminates the program.
This statement has the form: END OF PROGRAM.
This statement must be the last statement in the program, both physically (i.e., the last card) and in the sequence of computation. Execution of this statement will transfer control to the operating system in which the translated program is imbedded. An alternate way of terminating a program - i.e., returning to the operating system - is to attempt to execute an input statement when the data has been exhausted.
FUNCTION RETURN N
This statement is used in a function definition to indicate a return to the calling program. N is an expression whose value is to be the output of this function considered as a single valued function. N need not be specified if outputs are specified in the argument list (see Section 3.8, “Function Definitions”).
Entry points to a function being defined are indicated by
ENTRY TO N
where N is the name of this entry (see Section 3.8, “Function Definitions”).
The Retrocomputing Museum implementation supports only one entry point per function. Attempting to specify more than one will raise a compile-time error.
These statements facilitate the writing of recursive internal and external functions. (see Section 3.8, “Function Definitions”) They cause the designation and use of a vector for the temporary storage of data and action transfer instructions. (i.e. function returns).
SET LIST TO L
L is the name of an array element designated as the initial location to be used for temporary storage. Consecutive elements will be used as required by the following statements.
SAVE DATA L
L is a list of: 1) single or subscripted variable names, 2) array segments of the form A(i1, ..., in) ... A(j1, ..., jn) where the i, j subscripts may be any valid subscript expression and n ≥ 1, 3) constants, or 4) expressions—separated by commas. This statement causes the current values of the elements of the list as consecutive elements of the currently designated temporary storage vector—starting with the first currently available element. L is the same as any input-output list (see sec. 2.14).
SAVE RETURN
This statement will appear in the scope of the definition of a function (see Section 3.8, “Function Definitions”) and cause the reentry point to the calling program to be stored as the next available element of the currently designated temporary storage vector.
RESTORE DATA L
L is a list of single or subscripted variable names or array segments of the type described in (b). If the list designates n names (not necessarily n items in the list) the values of the last n elements of the currently designated temporary storage vector become the values of the list variables. The order of this value assignment assuming k used locations in temporary storage vector, left to right in the list correspond to k, k-1, ... k - n + 1 elements of the temporary storage vector are then made available for use by successive SAVE statements.
RESTORE RETURN
This statement will appear in this scope of a function definition and causes the current last element in the currently designated temporary storage vector to be used as the reentry point to the program calling the function. The last element of temporary storage is then made available for use by successive SAVE statements.
The Retrocomputing Museum implementation does not implement TAPE and DRUM statements.
In this section the follwing definition the following definitions will hold. B is the location of a format specification vector (see Section 2.15, “Format Specifications”) which must be an integer variable name. N is an expression whose value is to be either a tape number or a drum number. L is an input-output list. Elements of L may be: 1) single variable names or array names with subscripts;, 2) blocks of the form A(ii, ..., in) ... A(ji, ...jn) where the i's and j's may be any subscript expression and n ≥ 1. In addition, when L designates an output list, the elements of L may be: 3) constants or 4) expressions. Elements of a list are separated by commas. Example of an output list: AB, D, 2.5, MTX(1) ... MTX(N), P(14), J(I, K). Example of a list which may be ysed either for input or output: A, B, K(3), J(25*I - L), A(K+1)... A(L*2). To completely understand the operation of statements which make an explicit reference to "TAPE" or "DRUM" it is advisable to read the sections of the IBM 704 Reference Manual which describes the operation of these units (See Restrictions below).
Prints L according to format B on the on-lin printer. This statement is used for comments to the operator. After L has been printed a skip of 1/6 page is automatically produced, allowing the operator to read the comment.
Punches L on cards according to format B.
Transmits L to or from tape N in BCD form according to format B.
Transmit to L from tape N until L is exhausted or a complete type record has been read.
Transmits L to or from drum N starting with drum address A (where A is an integer expression).
Writes an end of file mark on tape N.
Move tape N back to beginning of last record.
Tape N is moved backward until an en-of-file mark, the load point gap, or the load point is encountered. If an end-of-file mark has been written previously, executing this statement backspaces over the end-of-file mark and stops just in front of it (since the end-of-file mark must be passed over to be recognized). If the tape is already at the load point, the program goes to the executable statement labelled S (where S is a statement label or variable of statement label mode.) If the phrase ", IF LOAD POINT TRANSFER TO S" (including the comma) is omitted no transfer is made even if at load point.
If an error (improperly formed format specifications, invalid data, a tape check, etc.) occurs during any input-output statement except READ DRUM or WRITE DRUM, the subroutine ERROR. is automatically entered. The subroutine ERROR. sets an error flag and returns control to the system in which the translated program is imbedded.
If an end-of-file is encountered while executed a READ FORMAT, READ BCD TAPE, or READ BINARY TAPE statement the subroutine SYSTEM. (a subroutine which returns control to the system in which the translated program is imbedded) is automatically entered. This action can be changed by executing the statement EXECUTE SETEOF. (S). The subroutine SETEOF. sets the read routines to transfer to the executable statement labelled S when an end-of-file is encountered. If the statement EXECUTED SETEOF. (0) is executed the read routines will be reset to enter SYSTEM. when an end-of-file is encountered.
Normally, if an end-of-tape is encountered while executing a WRITE BCD TAPE or WRITE BINARY TAPE statement, the tape is rewound, a comment indicating this is printed for the operator, and the computer is halted so that the operator can replace the tape. The program is then continued from where the end-of-tape was detected. Thus, no information is lost and the new tape is a continuation of the old one. This action can be changed by executing the statement EXECUTE SETETT. (S). The subroutine SETETT. sets the write tape routines to transfer to the executable statement labelled S when an end-of-tape is encountered. The tape is not rewound nor is any comment printed for the operator and the computer does not stop before this transfer occurs. Executing EXECUTE SETETT.(0) will reset the write tape routines to the normal action.
The subroutines SETEOF. and SETETT. may be executed as many times as desired. Only one setting is in effect for end-of-file (that specified by the latest execution of SETOFF.) and end-of-tape (that specified by the latest execution of SETETT.), i.e., each setting cancels the previous one.
When information is read from (or punched in) a card into (or from) a computer, it is necessary to know how this information has been allocated among the available columns of the card. Similarly, whenever information is to be printed by a printer (either on-line of off-line), it is necessary to know how this information has been allocated among the columns available on the printer. A description of each allocation is called a format specification. Usually, but not always, such a specification is accompanied by a list of variables whose values are to be printed or punched or whose values are to be read. (Occasionally, the information is contined entirely in the format specification, so the list may be empty.) A format specification is a string of alphabetic characters (as described below) terminated by an "*". This string is stored six characters per computer word in a vector with integer mode. This vector may be preset (see Section 3.7, “Presetting Vectors”), computed, or read in as data.
Every format specification consists of a description of the allocation of available columns, and the form in which the particular information is to appear. Specifications for reading or punching cards and printing of information are identical, whith the following exceptions:
(a) A card has only 72 available columns[4] while a line of print has 120 available columns. Any attempt to allocate more than the available number of columns will cause an error return (see Section 2.9, “Error Return Statement”).
(b) The first columnof a card has no special significance. The first (left-most) character of a line of print is treated differently. This character governs printer carriage control, much as skipping to a new page, double spacing, etc. and should not contin information to be printed. The user has effectively 119 available columns on a line of print, but he must always include the first character as a code to control the vertical spacing for that line.
For example, the specification
S16, 6HBETA =I2*
will indicate that the line (or card) starts with a skip of 16 columns and then prints (or reads or punches) the characters "BETA =" followed by an integer field of two columns. If a card is involved, the letter "B" in "BETA" will be found in column 17, due to the skip of 16 columns. However, if a line is to be printed, the "B" will print in column 16 of the printed page, since the first character of the line (in this case, the first of the 16 columns to be skipped) is detached, to be interpreted as carriage control leaving an effective skip of 15 columns. The effect of a "blank" character on carriage control is to move to the next line before printing, as will be seen from the following table. In each case the carriage control character is effective before the rest of the specification is carried out.
Blank | Single space |
0 | Double space |
+ | No space |
1 | Skip to next page |
2 | Skip to next half-page |
4 | Skip to next quarter page |
These were the standard control codes for IBM line printers, which assumed they were printing to 132x66 fanfold paper. There isn't a lot of point in simulating them on a modern page printer. The Retrocomputing Museum implementation implements them by prefixing the output string:
Blank | No prefix |
0 | \n |
+ | Not supported, no prefix. |
1 | Form feed |
2 | Two vertical tabs |
4 | One vertical tab |
As another example, the specification
6H1PHI = F6.3^*
would cause a skip to the next page (because the first character is a "1"), and cause "PHI =" to be printed, followed by a fixed point number. As will be explained below, the "6H" that appears in the specification indicates that 6 Hollerith (or BCD) characters follow, e.g., "1PHI =". Note that blanks are counted as characters here. It should be understood that this special behavior with regard to column 1 pertains to lines that are to be printed (either on-line or off-line), and not at all to cards.
Each specification describes successive fields across the available columns, starting fromthe left. If the specification describes fewer than the total number of available columns, the line (or card) will automatically be filled in with blanks. But, as indicated above, if more than the total number of available columns is included in the specification, an error return (see Section 2.9, “Error Return Statement”) will result.
The basic field description consists of a letter followed by an integer. The letter indicates the form of the information in the external medium as follows:
S | Skip (or blank information) |
I | Integer |
F | Fixed point number (Internally floating point) |
E | Floating point number |
K | Octal number |
C | Characters |
(For the purposes of input-output the Boolean values 0B, 1B are considered as integers and will be punched in cards and appear in print as 0, 1.) The integer indicates the size of the field; i.e., the number of available columns to be used. For example, K3 indicates a 3-column octal field, C23 indicates a 23-column character field, andf S31 indicates a blank field of 31 columns.
For F and E fields, i.e., fixed and floating point numbers, there is always the question of the placement of the decimal point, the form of the numbers, etc. in addition to the field size.
For this reason, the basic field description for E and F fields requires an additional integer giving the number of places after the decimal point that are to be rounded and printed (or read, or punched). This integer is always taken mod 10. For example, the specificaiton (F20.3) and the specification (F20.13) both describe a twenty column fixed point field in which a number will occur with three digits to the right of the decimal point. The decimal point in the specification itself must always be present for E and F fields.
Within the numeric type fields, E, F, I, and K, "+" signs are not printed or punched, nor are they necessary on input cards. However, "-" signs may not be omitted on input, and are always printed and punched. If the field description gives a field size larger than the number requires, the number is pushed as far right as possible. If the field size given in a specification is too small, information is printed, punched, or read from the right end of the field until the field is exhausted, and the left end of the number is truncated, including sign. It is important, therefore, to be sure to provide a large enough field to handle the information required. In fact, some spacing can be achieved by giving large field sizes, since blanks automatically occur to the left of a number pushed to the right end of an over-sized field.
Integers (I fields) and octal numbers (K fields) are printed, punched, etc., directly, without any decimal point. Numbers printed or punched in E fields have the form (if 5 decimal places are requested, for example):
+0.x1x2x3x4x5E±n1 n2
where n1n2 is the exponent. On input cards numbers in E fields must have an exponent of the form
E±n1n2
although if the sign is punched, the "E" may be omitted. Similarly, if the "E" is punched, a "+" may be omitted, as well as a leading zero in the exponent. The exponent must be counted in the field size.
Numbers to be printed or punched in F fields have the form (if 5 decimals digits are requested, for example):
±x1x2x3x4x5x6x7x8
although "+" signs are not printed. On input cards "+" signs may be omitted, but not "-".
On both E and F input data on cards, blanks are regarded as zeros, and any number of digits may be used, but only 8 digits of accuracy are retained. Moreover, E and F input data need not have the decimal point punched. If the decimal point is not punched, the field specification determines its position. For example, the punched number +9032 described by the specification F10.2 would be understood to be the number +90.32 because the 2 in the specification indicates 2 decimal places to the right of the point. Similarly, the punched number +9032E3 described by the specification E10.4 would be understood to be the number +.9032E3. If the decimal point is punched, however, it completely overrides the setting of the point by the specification. The entire specification must be present, however.
It should be understood that there must be a relationship between the form of a number inside the computer and its external form. In other words, a number described by an I field specification is assumed to be an integer in storage. A number described by E or F specification is assumed to be in floating point form in storage. Similarly a number govern by a K specification will be handled by direct binary-octal conversion. Information decribed by a C specification is assumed to be in alphabetic (BCD) form both inside the computer and outside.
If several consecutive fields can be described by the same basic specification, reptition may be avoided by prefixing the basic specification by its multiplicity. For example, the specification
3F10.3, E18.4, 2E9.1, 3I2*
is a short way of writing:
F10.3, F10.3, F10.3, E18.4, E9.1, E9.1, I2, I2, I2*.
Either specification may be used, of course. A group of basic specifications may be repeated by enclosing the group in parentheses and preceding the left parenthesis by the multiplicity. Thus
3E10.3, 2(I2,3F10.1), 2C5*
would be equivalent to the following:
E10.3, E10.3, E10.3, I2, F10.1, F10.1, F10.1, I2, F10.1, F10.1, F10.1, C5, C5*
Such grouping parentheses may not be nested.
One extra feature is allowed when reading or printing E and F fields. A "scale factor" may be applied to an F number according to the formula
Printed number = Internal number x 10Scale factor
(where the scaling is accomplished before the conversion is done). The "scale factor", followed by the letter "P" is prefixed to the basic field specification, as in the example
-2P2F7.3, F7.3*
Thus, three numbers which would print 0.522 -1.567 93.671 according to the specification 3F7.3* would print instead 0.005 -0.16 93.671 if the specification -2P2F7.3, F7.3* were used. It must be noted that for F fields this scale factor actually changes the values of the numberse to which it applies. It affects only those numbers to which it is directly applied, however. For E fields , the "scale factor" causes the number itself to be modified, but the exponent is correspondingly modified so the true value of the number remains unchanged . Thus the number 0.9321E-3 would print as 0.9321E-03 according to the specification E18.4*, but it would print as 93.2100E-05 according to the specification 2PE18.4*. Unlike an F number, the value is the same in either case.
Several specifications may be condensed into one larger one by the use of the character "/". Each appearance of "/" (except as part of a Hollerith field-see Section 2.15.8, “Hollerith fields”) indicates that the specification of a new line (or card) is to be started with what follows. A pair "//" causes a blank line (or card), three "///" cause two blank lines (or cards), etc. Thus, the specification
3F10.3/I2, K18, 2C5/7I3*
implies that one line (or card) is described by the specification 3F10.3, and the next line (or card) is described by the specification I2,K18,2C5, and the next by 7I3. It must be noted that each line (if printing is being described) is described individually here, and must include its own carriage control (see Section 2.15.1, “Carriage Control”).
Although the C specification is available for transmitting characters (Hollerith information) to and from storage, it is often convenient to include strings of Hollerith characters directly in the format specification. This is done by means of a basic Hollerith field specification consisting of the string of characters to be transmittted, preceded by a count of these characters and the letter "H". Thus, if the specification
1H1 F10.3, 6HBETA = E10.2
were used in printing one would obtain a new page skip, because of the one column Hollerith field containing the character "1". Then a ten column F number would print, followed by the characters "BETA =" and a floating point ten column field. Note that blanks are completely ignored throughout all format specifications except when they occur as characters in a Hollerith string. Note also, that while every field specification of types S, I, E, F, K, and C must be followed by commas, the comma may be omitted after a Hollerith string of the type described here. The comma may be used, however, if desired.
It has been stated above that a number appearing in an oversize field is pushed as far as possible to the right. In the case of C information (characters) , any information occurring in an oversize field is pushed to the left , while in either case, unused columns of the field are filled with blanks. Similarly, in the case the specification describes too small a field, characters are taken from the left end of the field until the field is exhausted.
Thus, if a card contins the characters: ABCDEFGHIJK in column 1 through 11, and it is read according to the specification 2C3*, the two six character words that are read into the computer are:
ABC□□□
DEF□□□
(where "□" denotes "blank"), while the specification C6* would cause a single word to be read:
ABCDEF
and C7, C3* would cause the words:
ABCDEF and HIJ□□□
to be read (since at most six characters can go into one word of storage).
The "list" consists of a set of names of variables to or from which information is to flow. Except for Hollerith strings imbedded in the format specification itself (see Section 2.15.8, “Hollerith fields”) and the S fields, each field in the specification refers to one item on the list. For this purpose, a regional entry on the list, such as A(6) ...A(8), counts as several names of variables (in this case, the three variables A(6), A(7), and A(8).) During the transmission of information, the input or output subroutine scans both the list and the specification simultaneously, correlating corresponding entries, and associating a field size, a type of conversion, etc. to each variable. If a Hollerith string is encountered in the specification, it is immediately transmitted, and it is not associated with any item on the "list."
For example, if the list consisted of:
A, B(1,1), I, K, where I and K were integers, and the others floating point, and the specification was
1H1, F11.3, -2PE14.4, S13, 3HM =I3, S9, 3HJ =I3*
we might find a printed line something like the following (at the top of the next page because of the 1H1):
456.010 -16.1251E+02 M = 50 J = 17
The same list would look the following way with the specification 1H1, 2F11.3, S16, 3HM =I3, S9, 3HJ = 13*:
456.101 -1612.510 M = 50 J = 17
As stated above, a specification may not account for more than 72 columns on a card. It may happen, however, that a list calls for more information than can appear on a single card. Or perhaps only a certain part of each card is to be read. The determining factor in every case is whether or not the entire list has been accounted for. After each cards is read according to the format specification, the list is consulted; if not yet satisfied, another card is read, and so on. It is important to realize that the specification is not necessarily scanned from the beginning when a new card is read. In fact, the specification scanner moves to the left from the end of the specification (the *) until it hits a left parenthesis not in an H field. (If there is no left parenthesis, it will move to the beginning of the specification). It then examines the characters just to the left of this left parenthesis to see if they are a multiplicity indication (see Section 2.15.5, “Repetition of Basic Field Specifications”). From this left parenthesis (together with the multiplicity, if any) to the end of the specification now becomes the format specification until the list is satisfied. A similar statement may be made for printed or punched output.
Thus, in the specification:
3F10.3 /4(F10.3, 6HBETA = I2)*
the first line printed (or read) would have three fixed point numbers, subsequent lines would all be printed (or read) according to the specification
4(F10.3, 6HBETA =I2)*
As an example, one might have an integer on a first data card, followed by many cards, each with six floating point numbers. The specification might then be:
I6/(6E10.5)*
Only the first six columns would be read on the first cards, and only 60 columns would be read on subsequent cards. The remaining columns are ignored and may contain any legitimate Hollerith characters.
If a specification contains a Hollerith string of the form nHa1a2...an, certain conventions are observed. (1) If the list is satisifed, but the next field specification is a Hollerith string, the string is transmitted, anyway. (2) On input, i.e., reading from cards, when a Hollerith string is encountered in the specification, the information in the corresponding columns of the input card will be brought in and will replace the BCD string itself within the format specification. This can then be used as a specification for output, for example. This is useful for labelling a set of data and causing the label to appear on the output along with a date, etc.
Thus, a card punched as follows:
1 DATA SET NO. 3-A JULY 31, 1959 J. DOE
might be read in with a format specification
72H (72 blank spaces) *.
Later, this specification could be used to print the same information as a heading for the results. Note the "1" provided for carriage control for the printing.
WARNING: The specifications S72* and 72(1H )*, while indicating 72 blank spaces, do not allow the reading in of an entire card, as indicated above, since they do not really provide a storage region of 72 characters in length into which the information on the card may be read and stored until needed.
Declarations are non-executable statements, and, except for function declarations, they may occur anywhere in the program. Their purpose is to furnish information to the translator program or to the reader of the program. Declarations may have statement labels, but these labels are ignored by the translator, and may not be referred to in other statements.
A remark declaration consists of any string of characters acceptable to the computer. This statement is completely ignored by the translator, and furnishes information to the reader of the program. Every card of the remark declaration must have an "R" in column 11.
All variables and function values are assumed to have the normal mode unless declared otherwise. Any of the other modes may be specified as the normal mode by writing the following declarations:
NORMAL MODE IS M
where M is one of the following phrases; INTEGER, BOOLEAN, STATEMENT LABEL, FUNCTION NAME, or FLOATING POINT. Only one such declaration may appear in a program and it is in effect for the whole program, no matter where it occurs in the program. If a variable or function value is to have a mode different from the normal mode then its modes must be declared in a declaration of the following form:
M V, L, ..., F., ..., BFN.
where M is one of the phrases; INTEGER, BOOLEAN, STATEMENT LABEL, FUNCTION NAME, or FLOATING POINT; for example, BOOLEAN P,Q,DIGIT.TRUTH. Following M is the list of variables and functions whose values are to be of mode M. A program may contain any number of such mode declarations but a name may not be declared to be of two different modes.
All constants are automatically assigned modes by the translator (see Section 1.1, “Constants” ). Other automatic assignments of modes are:
A name appearing in the statement label field is assigned statement label mode (see Section 1.3, “Statement Labels”).
A function name constant is assigned function name mode (see Section 3.8, “Function Definitions”).
A vector appearing as the dimension vector of some array in a dimension declaration is assigned integer mode (see Section 3.3, “Dimension Declaration”).
A vector which is preset by a vector values declaration is assigned a mode consistent with the assigned values (see Section 3.7, “Presetting Vectors”).
Variables may be declared to be array variables, such as vectors or matrices of various dimensions, rather than individual variables.
Each array is stored in a region whose elements may be referred to by means of a single subscript, such as A(0), A(1), B(16), etc. Even elements of higher dimensional arrays may be referred to in this way at any time. We shall call the subscript thus associated with each element of an array its "linear subscript." If A is an array then the name A is equivalent to A(0), e.e., the name A written without a subscript is assumed to have a subscript of zero.
Two dimensional matrices are stored internally by rows, i.e., an m × n matrix will be stored in the order: a11, a12, a13, ..., a1n, a21, ..., a2n, ... amn. In general, matrices are stroed in the order determined by varying the right-most subscript first, then the next right-most, etc. Thus, an m × n × p × q matrix would be stored in the order: a1111, a1112, ..., a111q, a1121, a1122, ... a112q, ..., a11pq, a1211,..., a1npq, ... amnpq.
Elements of higher dimensional arrays may be referred to not only by their linear subscripts but also by the usual matrix notation, such as A(1,1), A(I,J), B(I+6,J/3+3).
Although any number of dimension declarations may be used in a program, as many variables as desired may be "declared" in any dimension declaration. The declaration consists of the word DIMENSION, followed by the variables to be declared. Each variable is immediately followed by one or two arguments (separated by a comma) enclosed in parentheses, as follows:
The first argument is a positive integer constant, specifying the largest linear subscript which an element of the array is expected to assume during the execution of the program. The size of the region allocated is this number plus one, regardless of whether the program actually makes use of this amount or not.
The second argument, given only for arrays with dimension n > 1, is the name of the first element in the "dimension vector" for this array. If this name is D(k) (k must be an integer constant), the vactor D is automatically assigned integer mode. D may not be declared to be other than integer. In addition, D must itself be dimensioned, either by declaration or by presetting its values (Section 3.7, “Presetting Vectors”) to have its largest linear subscript ≤ k+n. Assuming that elements of the array to be dimensioned are to be referred to as A(I1, I2, In), the elements of the linear array D, i.e. D(k), D(k + 1), ...D(k + n), must be preset (or set during computation) to the values
D(k) = n
D(k + 1) = the linear subscript which corresponds to A(1,1, ...1) - commonly set to 1
D(k + 2) = current upper bound of I2 (i.e., the current highest value of I2 during the computation)
D(k + 3) = current upper bound of I3
D(k + 4) = current upper bound of I4
.
.
.
The lower bound of each subscript is assumed to be unity.
Thus, in the statement: DIMENSION XA(400, V), YT(300, K(3)) where V(0) = 2,V(1) = 6, v(2) = 13, K(3) = 3, K(4) = 4, K(5) = 8, and K(6) = 9, the variables XA and YT are designated as 2-dimensional and 3-dimensional arrays respectively. Considering the array element XA(I,J), the range of J may be expressed by 1 ≤ J ≤ 8 and 1 ≤ K ≤ 9. The array XA will have a storage region of length 401 set aside for it, e.g., XA(0), ...XA(400), and the 395 elements XA(6), XA(7), ..., XA(400) may also be referred to as XA(1,1), XA(1,2), ...XA(31,5) respectively. The matrix will be located at various places in the XA region depending on the value of V(1) at the time a reference is made ot a matrix element during execution of the program. Observe that this ability to vary the location of the initial array element (i.e., the array element with unit indices) within the linear array allows the effective use of negative subscripts - in spite of the fact that the defined ranges of the subscripts are positive.
Similarly, the matrix YT will have 301 locations set aside for it, although these do not all have to be used. The base element of YT, i.e., YT(1,1, 1) will correspond to the element YT(4) since K(4) = 4.
If the array is a vector, i.e., one-dimensional, a special convention is adopted. Only the first argument is given to the DIMENSION declaration. Thus, in the statement
DIMENSION V(10)
the array V is specified as consisting of the eleven elements V(0), ..., V(10). If a variable appears in more than one dimension declaration, the highest linear subscript will be used; however, the dimension vector must be the same in all occurrences. Negative subscripts are not available for vectors.
Dimensioning is automatic in two situations
(a) In case a statement label vector, say L, is used (see Section 1.3, “Statement Labels”) and n is the highest subscript used on L in the statement label field (i.e., if L(m) appears then m ≤ n), then n + 1 locations are reserved for L. L may also appear in a dimension declaration in which case the highest linear subscript is used.
(b) If part of all of a linear array is set by a VECTOR VALUES declaration the array need not appear in a dimension statement unless the maximum linear subscript implied by the initial values is not sufficiently high.
This declaration has the form
EQUIVALENCE (A,B), (MATRIX, XARRAY), (C, D(3))
and implies that the variables A and B are to represent the same storage location throughout the program, that MATRIX and XARRAY are to represent the same storage location through the program, etc. (Two variables which represent the same location always have the same value at any given time.) Thus, any number of equivalences may be established by one EQUIVALENCE declaration, and any number of such declarations may occur (at any place) in a program. They will be treated as one combined declaration, however.
Variables whose names appear within the same set of parentheses need not have the same mode. The mode must be established by the appropriate MODE declaration for each of the variables. Occurrences within an EQUIVALENCE declaration do not establish mode.
A nonsubscripted array variable name in an EQUIVALENCE statement represents that element of the array (considered as a one-dimensional vector) whose subscript is zero. Reference in an EQUIVALENCE statement to an array element of any number of dimensions may be made by linear-subscript only (i.e., as an element of a vector). Note that occurrence of any elements from any two arrays in the same perentheses implies equating the entire arrays accordingly.
The Retrocomputing Museum implementation does not implement EQUIVALENCE.
This declaration has the form
ERASABLE MATRIX, XARRAY, YARRAY
and implies that the arrays and individual variables listed after the word ERASABLE (which need not have the same number of dimensions) are disjoint (i.e., non-overlapping in storage), but are assigned to a special section of storage which is separate from the usual storage of variables and arrays. Each ERASABLE declaration eliminates the effect of previous assignments to this special section of storage, thus allowing several arrays to occupy the same storage at different times. It should be understood that this storage is accessible to, andmay be used by, subroutines.
This declaration is a memory-footprint optimization hack. The Retrocomputing Museum implementation does not implement it.
This declaration has the form
PROGRAM COMMON MATRIX, X, Y1, BC
and implies that the arrays and individual variables listed after the words PROGRAM COMMON (which ned not have the same number of dimensions) are disjoint (i.e., non-overlapping in storage), but are assigned to a special section of storage which is separate from the usual storage of variables and arrays, and separate from ERASABLE storage (see Section 3.5, “ERASABLE Declaration”).
One use of this statement provides for several sections or a program to refer to variables and arrays by the same names, while being translated and checked out separately. A program divided up this way would have the form of a main program and several external function programs, with the main program being used primarily to call on each of the external functions in turn. Although variables and arrays to be used jointly by several external functions can be communicated as arguments to the functions, assigning them to PROGRAM COMMON makes them available to all sections.
Note that storage is actually reserved for PROGRAM COMMON only in the main program (not in external functions, although the proper address assignment occurs there, also). Any such declarations occurring in external function definitions must therefore occur also in the main program.
Another use for the PROGRAM COMMON assignment is in the case of a segmented program, i.e., a program so large that it is written in blocks which will occupy the same section of storage and be brought in one at a time. If one block of a program is to use the results of the previous block's computation, the variables involved should be specified as being in the PROGRAM COMMON region. Then they will not be destroyed in the process of bringing in the new block of program. The PROGRAM COMMON and DIMENSION declarations which set up this storage allocation must be identical in all blocks in which these arrays and variables are to be used, except that later segments may add additional names at the end of the list .
PROGRAM COMMON declarations do not affect variables and arrays which have already been assigned to this common section of storage. If another such statement occurs in a program, the arrays and variables listed therein are considered appended to the previous list of PROGRAM COMMON arrays and variables. If a program is broken up into a main program and several external functions, the main program must be loaded into the computer first if PROGRAM COMMON is used at all.
The Retrocomputing Museum implementation does not implement COMMON.
Any vector or portion of a vector (or array when considered as a vector, i.e., using linear subscripts) may be preset by a declaration of the following form:
(a) VECTOR VALUES V = c0, c1, ... cL
Here V is of the form N(n) or simply N, if n = 0. The elements N(n), ... N(n + L) are preset to the values c0, c1 ..., cL, where the ci may be any constants. N is automatically set to have the same mode as c0 and given a storage reservation of n + L + 1 locations, i.e., this is equivalent to writing DIMENSION N(N + L). N may appear in a mode declaration provided it is consistent with the mode of c0. If N appears in other VECTOR VALUES or DIMENSION declarations the maximum length given or implied for N is used for storage assignment. The c1 must all be of the same mode.
(b) VECTOR VALUES V = $ k1 ... km$, $km+1... kp$, ...
Here the ki may be any valid characters (including blanks but excluding "$") and L (used as in (a) above) is equal to the smallest integer ≥ m/6.
This declaration is useful for presetting dimension vectors and and format descriptions. The presetting is done at the time of translation. The constants ci are loaded (as part of the translated program) into V. This declaration produces no computation at execution time. However, the values of V may be modified by other statements in the program during execution.
Vectors which have been assigned to ERASABLE storage (see section Section 3.5, “ERASABLE Declaration”) may not be preset by a VECTOR VALUES statement. Vectors which have been assigned to the PROGRAM COMMON section of storage (see section 3.6) can be preset with numerical or alphabetical information only (not statement labels or function names) and then only if it is a single section program (i.e., storage is loaded with instructions only once) or, if it is a multiple section program, the PROGRAM COMMON region is identical in all sections of the program.
There are two main types of functions: The internal function and the external function. Since these are quite similar in many ways, that part of the description which is specific to external functions will be given in Section 3.8.1, “External Function Definitions”, that which is specific to internal functions in Section 3.8.2, “Internal Function Definitions”, and that which is common to both types will follow in subsection Section 3.8.3, “Internal and External Functions”. Throughout this section a single-valued function will be called a "function", and a function with multiple outputs and/or multiple exits will be called a "procedure". For the purposes of this manual a recursive function is one whose definition contains calls for the function being defined -- or calls for functions which ultimately call the one being defined. Recursive functions have the same structure as other functions although, in general, they will include statements of the type described in Section 2.13, “List Manipulation Statements”, and certain considerations should be borne in mind when constructing such functions. These considerations arise from the fact that names, instead of values, are used as function arguments. The problem is considered in more detail in the examples in Chapter III.
These statements define functions not yet available in the language or as standard package programs ("subroutines"). The designation "EXTERNAL" implies that the statements which follow are to be translated indpendently of the main program in which they are to be used. (Because of this independence this block of statements is to be considered an entirely separate program, and must have its own DIMENSION and MODE declaration, etc.) The word "EXTERNAL" also implies that occurrences of variables and functions in the current function definition program have no relation whatever with occurrences of similarly named variables and functions in the main program (or other definition programs), and no difficulties will be encountered because of the use of similar names. The modes of the arguments (if other than the normal mode), must be declared in the usual way (see Section 3.2, “Mode Declaration”), but arrays which are arguments need not be dimensioned.
These statements define functions not yet available in the language or as standard package programs ("subroutines"). The designation "INTERNAL" implies that the definition program which follows is to be translated as part of the main program. The word "INTERNAL" also implies that any variables or functions not listed as arguments in the definition of the function (but used in its evaluation), are understood to be the same as elsewhere in the main program, and the current values of these variables and functions will be used. Names of variables and functions which occur as arguments of the function being defined are designated "dummy arguments" (or "bound variables") . They must be distinct from those appearing elsewhere in the program. The modes of dummy arguments (if other than normal mode) must be declared in the usual way, but arrays which are arguments need not be dimensioned.
One form of internal function definition (not available as an external function definition because the latter must be a complete, independent program) is the one sentence definition, which has the form:
INTERNAL FUNCTION SUMSQ.(X,Y,Z) = X * X + Y + Z * Z - T * T
As indicated above, X, Y, and Z, as they occur here, are dummy variables, and (X, Y, Z) is the dummy variable list. The current value of T, however, will be obtained and used each time the value of the function is needed. An example of the use of the function so defined would be:
A = 1.-SUMSQ. (U, V + 3, W) .P..5
Only nonsubscripted names of variables (either individual or array) or names of functions (without arguments) may appear in the dummy variable list. For this type of function definition no END OF FUNCTION statement is required. In the user of the function is an expression, the arguments may be any expressions that agree in mode with the corresponding argument in the declaration.
In the example:
INTERNAL FUNCTION POLY. (N, X, FN.) = FN. (J*X).P.N - X/XBAR
which might be used in the statement
BETA ZQ = POLY.(M + 1, Y, SIN.) + POLY.(M -1, Z, COS.)
it is understood that if N is in the integer mode, then so are Y and Z. Both M and N would have had to be declarted to be in the integer mode, of course. Similarly, the values of SIN. and COS. must be the same mode as the values of FN. Moreover in the use of functions this mode correspondence cannot be checked by the translator.
The function POLY. has as one of its arguments the name of a function. In the statement BETA the function used in the first term to the right of the "=" sign is SIN. and in the second term COS.. Hence statement BETA is then equivalent to:
ZQ = SIN.(J * Y).P.(M + 1) - Y/XBAR + COS.(J*Z).P.(M-1)-Z/XBAR.
Each function definition (except single statement definitions described in Section 3.8.2, “Internal Function Definitions”) may define any number of functions and/or any number of procedures. Within one function definition all functions and procedurews defined must use exactly the same set of arguments. In other words, the functions CPADD.(X,Y,A,B)and CPMPY.(X,Y,A,B) may be defined, if desired, by one definition, but the functions SIN.(X) and ARCTAN.(X.Y) would require separate definitions. Similarly, the procedures RXSUB. and ADAMS. could be defined by the same definition if they have the same outputs, say Z and W, and the same input parameters, say X, Y, T, and N. However, if they did not have exactly the same outputs and the same inputs, they would require separate declarations.
In the use of a function (i.e., the call for it) the arguments may be constants, variables, or expressions. However, if one of the arguments appears to the left of an "=" sign in a substitution statement in the defining program it is not meaningful to use a constant or an expression for that argument in the call. As mentioned earlier, the arguments in the call for a function cannot be checked for correspondence in mode and number to arguments in the definition.
The general form of all function definition programs is as follows:
INTERNAL FUNCTION (X) ENTRY TO COS. ... ... ... ENTRY TO SIN. ... ... ... FUNCTION RETURN ALPHA + J - 3 ENTRY TO TAN. ... ... ... FUNCTION RETURN BETA/K5 - 4 * D END OF FUNCTION
The first statement (INTERNAL FUNCTION (X)) is a function declaration (i.e., declares that the following statements define a function). If this is defining an external function, the declaration would be EXTERNAL FUNCTION (X). Following the words INTERNAL (or EXTERNAL) FUNCTION is the dummy variable list ((X) in this example). The END OF FUNCTION declaration is the last statement in the function definition program. (In an external function definition this is also the last statement in the program.) An entry must provided for each function being defined, but several functions may share any number of FUNCTION RETURN statements. An entry statement merely marks a point of entry, and does not effect the sequence of computation in any way. The expression after the word "RETURN" indicates that on this return the value of the function is to be the value of that expression. This expression must agree in mode with the function whose value it supplies; this agreement is not checked. The definitions of functions whose calls are intended to be included in expressions must have an expression following the FUNCTION RETURN statement. If the calls for a function are to appear in an EXECUTE STATEMENT (generally such functions have multiple outputs) the FUNCTION RETURN statement may appear without an accompanying expression.
An example of a definition for procedures is as follows:
EXTERNAL FUNCTION (M, N, I, P, Q) STATEMENT LABEL N, I ... ... ... FUNCTION RETURN ... ... ... WHENEVER B, TRANSFER TO I ... ... ... TRANSFER TO N END OF FUNCTION
In this example, N and I are actual alternate exits, and B represents some Boolean expression.
It is important to note that internal function definitions of any kind whatever (including the single statement definition of Section 3.8.2, “Internal Function Definitions”) may occur anywhere in the program, except within another internal function definition. Internal function definitions may occur within external function definitions, however. External function definitions may not occur within any other programs, not even within other external function definitions. Each external function definition must be a complete, self-contained program.
The following is an example of a function whose value is 1/x if 0 < x ≤ 1 and 1/x^2 if x > 1. If x ≤ 0, one obtains an error return (see Section 2.9, “Error Return Statement”).
A EXTERNAL FUNCTION (X) J ENTRY TO INVSF. G WHENEVER X.G.0 .AND. X .LE. 1 C FUNCTION RETURN X .P. -1 H OR WHENEVER X .G. 1 D FUNCTION RETURN X .P. -2 I OTHERWISE E ERROR RETURN K END OF CONDITIONAL B END OF FUNCTION
(Here the statments are all labelled only for reference in what follows.)
The list of dummy variables in the declaration statement itself (statement A in the preceding paragraph) may contain only nonsubscripted variable names (either individual or array) or function names (without arguments). Within the definition program itself (the statements between statement A and statement B), a function name will usually occur with arguments, and an array variable will usually occur with subscripts.
A few comments about the last example. This definition program defines a single-valued function of X, called INVSF.. Since no mode declaration is given it is assumed by the translator that X is floating point. The value of INVSF.(X) is computed by the use of a compound conditional. If 0 < x ≤ 1, (statement G) then statement C is executed, causing a return to the calling program with the value 1/x. If the condition 0 < x ≤ 1 is not true, then the condition X > 1 is tested (statement H). If X > 1, statement D is executed. Finally, if neither of the conditions 0 < x ≤ 1 or x > 1 is true, then statement I finds that X ≤ 0 and statement E (the error return) is executed. If
A = B - D Z = T(I) + INVSF.(Y) *T(I-1) Y(I) = Z + R(J) * 2.5
is part of a program and the error return statement is executed during the evaluation of INVSF.(Y) (i.e., Y ≤ 0), then control is returned to the system in which the translated program is embedded, with an error flag set. If
A = B - D F Z = T(X) + INVSF.(Y, ER)* T(I-1) S Y(I) = Z = R(J) * 2.5 ... ... ER Z = 0 L Y(I) = 1. ...
is part of a program and Y ≤ 0, then when the ERROR RETURN statement is executed control transfers to statement ER (then goes on to L), instead of finishing the execution of statement F (and then going on to S). Note that the END OF FUNCTION statement will never be executed, but must be present in the definition.
Variable names appearing as dummy arguments in any function declaration may not appear in a PROGRAM COMMON, , ERASABLE, or EQUIALENCE declaration.
The name of a function with the period deleted must not be used as the name of a variable or as a statement label. A statement label must not be identical with any variable name.
The translator automatically calls on some subroutines, hence the following names may not be used as variable names or statement labels in a MAD program. They may be used as function names only when referring to these subroutines. (These subroutines will be described in Chapter 4).
SYSTEM. SETEOF. PRINT. READ. ERROR. SETETT. COMMNT. TAPERD. TAPEWR. PUNCH. SETERR.
If a block designation A(i1,...,in)...A(ji,...,jn) is used in an input-output list L for reading or writing binary tape or drum , the linear subscript corresponding to ii,...in may not be greater than the linear subscript corresponding to J1,...,jn. While the list for a binary tape or drum statement is transmitted in the sequence written, a block A(i1,...,in)...A(j1,...,jn) within the list is actually transmitted in reverse order, i.e., in the sequence A(j1,...,jn) to A(i1,...,in).
[4] Actually, 80 columns may be used, provided the proper computer hardware configuration is available. The use of 72 columns is always safe, however, and strongly recommended.
Table of Contents
Note:The following illustrates how some programs may be writtten in the MAD language. Since they were written to illustrate as many features of the language as possible, they are not necessarily the most efficient or elegant programs which could have been written. They have all been tested on the computer, however, and are correct.
The flow chart notation used here varies somewhat from the illustrations of Chapter 1. The variation is consistent with present computing practice where many individualistic forms are encountered and, moreover, causes no difficulty due to the essentially graphic nature of charts. In particular, iterations are present by the form:
Here the scope of the iteration lies between the hexagonal box and diamond box A, and the calculation proceeds from A when the stated relation is true.
Problem: To solve the quadratic equation ax2 + bx + c = 0 for various sets of coefficients a, b, and c.
Analysis: Let x1 and x2 be the two roots of the equation. Then their values are found by the formulas,
whenever a ≠ 0. The single root x1 of the equation when a = 0 is x1 = -c/b. The input values of a, b, and C are printed immediately after they are brought in to help in finding trouble spots during the development of the program (not as necessary here as in longer problems, but a good idea!).
Flow Diagram:
The Program:
R R MAIN PROGRAM R GAMMA READ FORMAT INPUT,A,B,C PRINT FORMAT CHECK,A,B,C WHENEVER A .NE. 0,TRANSFER TO ALPHA2 ALPHA1 PRINT FORMAT LINEAR,-C/B TRANSFER TO GAMMA ALPHA2 D = B .P.2 -4.*A*C WHENEVER D .L. 0, TRANSFER TO BETA2 BETA1 PRINT FORMAT REAL,(-B+SQRT.(D)/(2.*A)),(-B-SQRT.(D)/(2.*A)) TRANSFER TO GAMMA BETA2 PRINT FORMAT COMPLX,-B/(2.*A),SQRT.(-D)/(2.*A), 1 -B/(2.*A),-SQRT.(-D)/(2.*A) TRANSFER TO GAMMA R R FORMAT SPECIFICATIONS R VECTOR VALUES INPUT = $ 3F10.4*$ VECTOR VALUES CHECK = $ 4H0A = F10.4,S8, 1 3HB = F10.4,S8,3HC = F10.4*$ VECTOR VALUES LINEAR = $21H0LINEAR EQUATION, X =F10.4*$ VECTOR VALUES REAL = $21H0REAL SOLUTIONS,X1 = 2 F10.4,S8,4HX2 = F10.4*$ VECTOR VALUES COMPLX = $19H0COMPLEX SOLUTIONS, 1 S4,7HR(X1) = F10.4,S8,7HI(X1) = F10.4,S8, 2 7HR(X2) = F10.4,S8,7HI(X2) = F10.4*$ END OF PROGRAM *DATA 4. -8. 4. 0 5. 10. 1. 1. 1.
Problem: A logical (Boolean) expression such as (P .AND. Q) .OR.(.NOT. P .AND. R .AND. S).OR.(R .OR. P) will have a value TRUE or FALSE (represnted here by 1B and 0B, respectively) depending on the "input values" of the variables involved: P,Q,R,S. Thus, if P = 1B, Q = R = S = 0B, then the total expression T will have the value 1B. The entire table of outputs for all possible inputs would be as follows:
P | Q | R | S | T |
---|---|---|---|---|
0B | 0B | 0B | 0B | 0B |
0B | 0B | 0B | 1B | 0B |
0B | 0B | 1B | 0B | 1B |
0B | 0B | 1B | 1B | 1B |
0B | 1B | 0B | 0B | 0B |
0B | 1B | 0B | 1B | 0B |
0B | 1B | 1B | 0B | 1B |
0B | 1B | 1B | 1B | 1B |
1B | 0B | 0B | 0B | 1B |
1B | 0B | 0B | 1B | 1B |
1B | 0B | 1B | 0B | 1B |
1B | 0B | 1B | 1B | 1B |
1B | 1B | 0B | 0B | 1B |
1B | 1B | 0B | 1B | 1B |
1B | 1B | 1B | 0B | 1B |
1B | 1B | 1B | 1B | 1B |
The problem is to write a program to generate the entire "truth table" for the given expression T.
Flow Diagram:
The Program:
PRINT FORMAT HEADER BOOLEAN P,Q,R,S THROUGH A, FOR VALUES OF P = 0B,1B THROUGH A, FOR VALUES OF Q = 0B,1B THROUGH A, FOR VALUES OF R = 0B,1B THROUGH A, FOR VALUES OF S = 0B,1B A PRINT FORMAT TABLE,P,Q,R,S,(P .AND. Q) .OR.(.NOT. P .AND. R 1 .AND. S).OR.(R .OR. P) VECTOR VALUES HEADER = $1H1,S10,1HP,S10,1HQ,S10,1HR,S10,1HS, 1 S15,1HT*$ VECTOR VALUES TABLE = $1H0,4(S10,I1),S15,I1*$ END OF PROGRAM
Note: Although it would have meant only a slight change in the format information, no attempt was made here to label the "0" and "1" that print as values in the table as Boolean, i.e., "0B" and "1B". This points up the fact that internally 0B and 1B are stored as 0 and 1, respectively. Also, the statement
NORMAL MODE IS BOOLEAN
could have been used as tthe second statement of this program instead of the BOOLEAN declaration.
Problem: To approximate a∫b f(x) by Simpson's Rule, for an arbitrary interval [a, b] using N equal subintervals (where N is an arbitrary even integer).
Analysis:By Simpson's Rule
where yi = f(xi), and a = x0, x1, ... xN = b are the partition points of the interval [a,b].
Method: We shall write the program in the form of an external function, so that it could be used with any other program. The evaluation of f(x) could be accomplished by another external function or an internal function.
Flow Diagram:
The Program:
EXTERNAL FUNCTION (A,B,N,F.) INTEGER N ENTRY TO SIMPS. H = (B-A)/N S1 = 0 S2 = 0 THROUGH ALPHA,FOR X = A+H, 2.*H, X .G. B S1 = S1 + F.(X) ALPHA S2 = S2 + F.(X+H) FUNCTION RETURN H*(F.(A)+4.*S1+2.*S2-F.(B))/3 END OF FUNCTION
If, for some reason, the integral of sin 3x - cos(rx + 1) were needed if 0 ≤ x ≤ 3, and the integral of sin 3x - cos(rx) otherwise, the program might then be as follows:
The Program:
READ READ FORMAT INPUTS,A,B,N,R INTEGER N WHENEVER 0. .LE. R .AND. R .LE. 3 PRINT FORMAT RESULT,A,B,N,R,SIMPS.(A,B,N,F1.) OTHERWISE PRINT FORMAT RESULT,A,B,N,R,SIMPS.(A,B,N,F2.) END OF CONDITIONAL TRANSFER TO READ R R DEFINITION OF FUNCTIONS R INTERNAL FUNCTION F1.(X) = SIN.(3.*X)-COS.(R*X+1.) INTERNAL FUNCTION F2.(X) = SIN.(3.*X)-COS.(R*X) R R FORMAT SPECIFICATIONS R VECTOR VALUES INPUTS = $2F12.4,I6,F12.4*$ VECTOR VALUES RESULT = $23H1 FOR THE INTERVAL FROM 1 F12.4,3H TO F12.4,5H WITH I6,38H EQUAL SUB-INTERVALS AND 2 PARAMETER F12.4/29H0THE VALUE OF THE INTEGRAL IS F12.4*$ END OF PROGRAM
External Functions:
EXTERNAL FUNCTION (A,B,N,F.) INTEGER N ENTRY TO SIMPS. H = (B-A)/N S1 = 0 S2 = 0 THROUGH ALPHA,FOR X = A+H, 2.*H, X .G. B S1 = S1 + F.(X) ALPHA S2 = S2 + F.(X+H) FUNCTION RETURN H*(F.(A)+4.*S1+2.*S2-F.(B))/3 END OF FUNCTION *DATA 0 2. 10 10.
An alternate way to write the first eight lines of this program, illustrating one use of the FUNCTION NAME mode, would be:
READ READ FORMAT INPUTS,A,B,N,R INTEGER N WHENEVER 0. .LE. R .AND. R .LE. 3 S = F1. OTHERWISE S = F2. END OF CONDITIONAL PRINT FORMAT RESULT,A,B,N,R,SIMPS.(A,B,N,S) TRANSFER TO READ FUNCTION NAME S
Problem: To find a real solution (if it exists) of the equation f(x) = 0 (where f is a continuous function) obn an arbitrary interval [a, b], provided the roots (if there are more than one) are at least ∊ apart.
Analysis: We specify a, b, and ∊ as parameters. The method used will be "half-interval convergence," in which the function is evaluated at x -a, and then the interval is scanned for a change of sign[5] in the value of f(x). If no change odf sign is found, the scanning is repeated with a step size for searching half of the previous size. If the step size becomes smaller than ∊, and no change of sign is found, the process is terminated, and comment is printed: "NO SOLUTION".
If a change of sign is found between xL and xR, the value of f is computeed at xM = (xL + xR)/2, i.e. the midpoint of the interval of uncertainty [xL, xR]. We then determine which one of the intervals [xL, xM]. [xM, xR] now contains a change in sign. We then compute the value of f at the midpoint of that smaller interval, etc., until the interval being considered finally has length less that ∊, at which time either end may be taken as the solution with an error less than ∊.
The method used here to handle the xM cpomputation is perhaps not the most obvious one. It consists of a simple loop in which the value of x is adjusted by h′ = -h/2, then h′′ - h′/2 = h/4, etc., until h is small enough. The adjustment of x is either to the left or to the right, depending on the occurrence or non-occurrence, respectively, of a change of sign between f(a) and f(x).
It should be understood that this method may not find a root which is one of a pair of roots which either coincide or are less that ∊ apart.
Flow diagram:
Program: It is assumed here that f (referred to as F. in the program) will be defined as an internal function.)
Definition: SIGN.(Z) = Z/|Z|
A sample F. is actually specified in the listing. Specifying F this way makes more sense if one recalls that MAD programs were punched-card decks. An internal function definition was typically a single card that could be slipped anywhere into the program deck.
INTERNAL FUNCTION F.(Z) = Z .P. 2 - 2 PSI READ FORMAT ABEPS, A,B,EPS READ FORMAT INVAL, A,B,EPS YA = F.(A) THROUGH ALPHA,FOR H = (B-A)/2.,-H/2.,H .L. EPS THROUGH ALPHA,FOR X = A+H,H, X .G. B WHENEVER F.(X) .E. 0,TRANSFER TO ETA ALPHA WHENEVER YA*F.(X) .L. 0,TRANSFER TO DELTA PRINT FORMAT NOROOT TRANSFER TO PSI R R THE NEXT SECTION IS ENTERED WHEN A CHANGE OF SIGN IS FOUND R DELTA THROUGH SIGMA,FOR H=H/2.,-H/2.,H .L. EPS SIGMA X = X+SIGN.(YA*F.(X))*H ETA PRINT FORMAT ROOT,X TRANSFER TO PSI R R DEFINITION OF SIGN. FUNCTION R INTERNAL FUNCTION SIGN.(Z) = Z/.ABS.(Z) R R FORMAT SPECIFICATIONS R VECTOR VALUES ABEPS = $ 3F12.4*$ VECTOR VALUES INVAL = $18H1INPUT VALUES, A = F12.4,S3, 1 3HB = F12.4,S3,5HEPS = F12.4*$ VECTOR VALUES NOROOT = $12H0NO*SOLUTION *$ VECTOR VALUES ROOT = $14H0SOLUTION, X = F12.4*$ END OF PROGRAM *DATA 1. 2. .01
Problem: Find the transpose A' of an n×n matrix A = (aij).
Analysis: If we write A' = (bij) then bij = aji. We shall interchange symmetrically-placed pairs of elements, leaving untouched elements on the main diagonal. The program will be in the form of an external function.
Flow diagram:
Program:
EXTERNAL FUNCTION (A,N) ENTRY TO TRANS. THROUGH BETA, FOR K=1,1, K .GE. N THROUGH BETA, FOR I = K+1,1, I .G. N Z = A(I,K) A(I,K) = A(K,I) BETA A(K,I) = Z FUNCTION RETURN INTEGER N,K,I END OF FUNCTION
Note: No dimension information is needed for A, since it is an argument in a function definition program. The function would be called with a statement of the form EXECUTE TRANS.(A, N).
The Retrocomputing Museum implementation cannot yet compile this example. The problem is the array I/O.
Problem: Multiply the matrix A = (aij) by the matrix B = (bij) to produce the matrix X = (cij), i.e. C = A · B. Assume tha A has dimensions m × n with m · n ≤ 1500, B has dimensions n × p with n · p ≤ 1500, and C has dimensions m × p with m · p ≤ 1500.
Analysis: An element cij of C is computed by the formula
Flow diagram:
The Retrocomputing Museum implementation cannot yet compile this example. The problem is the EQUIVALENCE declaration; there is no general way to implement this in generated C.
The Program:
DIMENSION A(1500,ADIM),B((1500,BDIM).C(1500,CDIM) EQUIVALENCE(N,ADIM(2)),(P,BDIM(2)) INTEGER I,J,K,L,M,N,P VECTOR VALUES ADIM = 2,0,0 VECTOR VALUES BDIM = 2,0,0 VECTOR VALUES CDIM = 2,0,0 READ READ FORMAT INPUT,M,N,P,A(1,1)...A(M,N),B(1,1)..B(N,P) PRINT FORMAT INVAL,M,N,P,A(1,1)...A(M,N),B(1,1)..B(N,P) CDIM(2) = P THROUGH Q, FOR I = 1,1, I .G. M THROUGH Q, FOR J = 1,1, J .G. P C(I,J) = 0 THROUGH Q, FOR K = 1,1, K .G. N Q C(I,J) = C(I,J) + A(I,K)*B(K,J) PRINT FORMAT RESULT,C(1,1)...C(M,P) TRANSFER TO READ R R FORMAT SPECIFICATIONS R VECTOR VALUES INPUT = $3I4/(6F122.4)*$ VECTOR VALUES INVAL = $13H0INPUT*VALUES/4H0M = I6,56,3HN = I6 1 ,S6,3HP = I6//(1H0,8F13.4)*$ VECTOR VALUES RESULT = $9H1C MATRIXC//(1H0,8F13.4)*$ END OF PROGRAM * DATA 2 3 3 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15.
The Retrocomputing Museum implementation cannot yet compile this example. The problem is the array I/O.
Problem Solve a system of n ≤ 20 simultaneous linear equations in n unknowns, assuming that one does not encounter a zero on the main diagonal of the coefficient matrix during the solution process.
Analysis: We shall use a Jordan Elimination Method, in which each diagonal coefficient is used to "clear" all other coefficients in its column to zero by appropriate multiplications and subtractions. Since we shall divide the "clearing row" by the diagonal element in that row before clearing t;he column, we shall finish the process with only a diagonal of ones and the solution to the problem as the resulting right hand side of the equations.
We denote the system of equations to be solved by:
a11x1 + a12x2 + ... + a1nxn = a1,n+1 (1) a21x11 + a22x2 + ... + a2nxn = a2,n+1 . . . . . . . . . . . . an1x1 + an2x2 + ... + annxn = an,n+1
We divide the first row by its diagnoal element a11. Then to clear a21 ato zero we substract a21 times the first row from the second row, and so on. In general, to clear aik to zero (after row K has been divided by akk), we subtract aik times row k from row i (i≠k). A typical element aij is thus transformed each time by the formulas:
(2) akj = akj/akk (3) aij = aij-aikakj (i≠k)
where the value of akj in (3) is the result of (2). These transformations are performed for k = 1, 2, ..., n. For each (fixed) k, we will let i = 1, 2, ...., k-1, k+1, ..., n, so as to operate on all rows except i=k. While transforming each row we will cycle on j from right to left; i.e., j = n+1, n, n-1, ... k, and we stop at j = k since for j < k there is no change in the matrix.
The array
a11a12 ... a1,n+1 . . . . A = (aij) = . . . . an1an2 ... an,n+1
is called the "matrix of coefficients" of the system (1).
It should be understood that this method, involving the assumption of no zeros on the diagonal and not searching for the largest element of a row to use as a divisor (to minimize round-off error), is not satisfactory from a mathematical point of view. It could serve as a basis for a larger, more complete program, however, and serves here only as an example problem.
Flow diagram:
The Program:
DIMENSION A(420, ADIM) VECTOR VALUES ADIM = 2,0,0 DELTA READ FORMAT NVAL,N ADIM(2) = N+1 READ FORMAT INPUT, A(1,1)...A(N,N+1) PRINT FORMAT INVAL,N,A(1,1)...A(N,N+1) THROUGH B, FOR K = 1,1,K .G. N THROUGH C, FOR J = N+1,-1,J .L. K C A(K,J) = A(K,J)/A(K,K) THROUGH B, FOR I = 1,1,I .G. N WHENEVER I .E. K, TRANSFER TO B THROUGH D, FOR J = N+1,-1, J .L. K D A(I,J) = A(I,J)-A(I,K)*A(K,J) B CONTINUE THROUGH E, FOR I = 1,1,I .G. N E PRINT FORMAT RESULT,I,A(I, N+1) TRANSFER TO DELTA INTEGER I,J,K R R FORMAT SPECIFICATIONS R VECTOR VALUES NVAL = $ I4* $ VECTOR VALUES INPUT = $ 6F12.4* $ VECTOR VALUES INVAL = $7H1 INPUT//4H N = I4// 1 7H MATRIX//(1H0,8F12.4)*$ VECTOR VALUES RESULT = $ 1H0,S20,2HX(,I2,3H) = F12.4*$ END OF PROGRAM * DATA 3 1. 1. 1. 6. -1. 0 0. -1. -1. -2. -9. -32.
The Retrocomputing Museum implementation cannot yet compile this example. The problem is the array I/O.
Problem: Compute the social security deduction and accumulated gross pay. The program should read a card containing: (a) the employee's name, (b) his payroll number, (c) his gross pay for the current week, and (d) his accumulated gross pay for the current year (but not including item (c)). For each card read, the program should print (a) and (b) from the card, and, in addition, print (e) the updated gross pay, (f) the social security deduction for the current week, and (g) the net pay for the current week, taking into account only the social security deduction.
Analysis: The social security deduction is currently 3% of the gross pay until the accumulated gross pay for the year exceeds from $4800.00. The updated gross pay can be computed from the formula, (e) = (c) + (d). The social security deduction has already been made on (d). There are thus three cases to consider:
(d) ≥ 4800.00, in this case (f) = 0.
(d) < 4800.00 and (c) + (d) > 4800.00, in this case (f) = 3% of 4800.00 - (d)
(c) + (d) > 4800.00, in this case f = 3% of (c).
The information on the cards to be read will be in the following format:
Card Columns | Information |
---|---|
1-30 | (a) employee's name |
31-38 | (b) payroll number |
39-44 | (c) gross pay for the current week in the form XXX.XX |
45-52 | (d) accumulated gross pay for the current year in the form XXXXX.XX |
The printed output will be in the following format:
Line Columns | Information |
---|---|
Line Columns | Information |
1 | Carriage control for printer |
2-31 | (a) employee's name |
32-34 | Blank |
35-42 | (b) payroll number |
43-45 | Blank |
46-53 | (e) updated gross pay for current year in the form XXXXX.XX |
54-56 | Blank |
57-61 | (f) social security deduction for current week in the form XX.XX |
62-64 | Blank |
65-70 | (g) net pay for current week in the form XXX.XX |
Flow chart: We will use the following abbreviations:
NAME | for employee's name (a). |
PAYNR | for payroll number (b). |
GROSSW | gross pay for current week (c). |
AGROSY | accumulated gross pay for current year (d). |
UGROSY | updated gross pay for current year (e) |
FICA | social security deduction for current week (f). |
NET PAY | net pay for current week (g). |
The Program:
START READ FORMAT IN,NAME(1)...NAME(5),PAYNR,GROSSW,AGROSY VECTOR VALUES IN = $5C6,I8,F6.2,F8.2*$ DIMENSION NAME(5) INTEGER PAYNR, NAME WHENEVER AGROSY .GE. 4800,TRANSFER TO BIGGRS WHENEVER GROSSW+AGROSY .G. 4800.,TRANSFER TO BIGTOT FICA = .03*GROSSW TRANSFER TO UPDATE BIGGRS FICA = 0. TRANSFER TO UPDATE BIGTOT FICA = .03*(4800.-AGROSY) UPDATE UGROSY = AGROSY+GROSSW NETPAY = GROSSW-FICA PRINT FORMAT OUT,NAME(1)...NAME(5),PAYNR,UGROSY,FICA,NETPAY TRANSFER TO START VECTOR VALUES OUT = $1H0,5C6,S3,I8,S3,F8.2,S3,F5.2,S3,F6.2*$ END OF PROGRAM * DATA GEORGE WASHINGTON 12345678 100. 4800. JOHN ADAMS 12345679 200. 4900. THOMAS JEFFERSON 12345680 200. 4600. JAMES MADISON 12345681 200. 4700. JOHN QUINCY ADAMS 12345682 100. 300.
Alternate Program:
START READ FORMAT IN,NAME(1)...NAME(5),PAYNR,GROSSW,AGROSY VECTOR VALUES IN = $5C6,I8,F6.2,F8.2*$ DIMENSION NAME(5) INTEGER PAYNR, NAME WHENEVER AGROSY .GE. 4800 FICA = 0. OR WHENEVER GROSSW+AGROSY .G. 4800. FICA = .03*(4800.-AGROSY) OTHERWISE FICA = .03*GROSSW END OF CONDITIONAL UGROSY = AGROSY+GROSSW NETPAY = GROSSW-FICA PRINT FORMAT OUT,NAME(1)...NAME(5),PAYNR,UGROSY,FICA,NETPAY TRANSFER TO START VECTOR VALUES OUT = $1H0,5C6,S3,I8,S3,F8.2,S3,F5.2,S3,F6.2*$ END OF PROGRAM * DATA GEORGE WASHINGTON 12345678 100. 4800. JOHN ADAMS 12345679 200. 4900. THOMAS JEFFERSON 12345680 200. 4600. JAMES MADISON 12345681 200. 4700. JOHN QUINCY ADAMS 12345682 100. 300.
1. The maximum number of characters which can be stored in one machine word is six. Hence, we need five machine words to store the 30 characters allowed for the employee's name. We need to give a dimension declaration stating that NAME is actually be a block and that NAME(5) is the last word of this block. In the read and print statements we specify that the whole block is to be read or printed by writing NAME(1) ... NAME(5) and giving the format specification 506, i.e., 5 words of 6 characters.
2. Since the payroll number is an integer (i8, i.e., and 8 digit integer) we give an integer mode declaration stating that PAYNR is an integer. Similarly, since alphabetic information is assumed to be in the integer mode, NAME is also declared to be integer.
Problem: Assume that a master tape is available containing basic information for each employee: (1) The employee number, (2) his hourly rate, (3) gross pay to date, (4) amount of withholding tax withheld to date, (5) social security deduction withheld to date, (6) net pay to date, and (7) the number of exemptions. Input will be in the form of m cards representing the current pay record, containing the employee's number and the number of hours worked during the current week. Pay is to be copmuted at time and a half for any hours worked over forty. (We shall assume that the input deck is already sorted according to increasing employee number, but we shall provide for cards which may be out of order. The last input card must have an employee number greater than the last employee number of the master tape.
The withholding tax W is to be computed by the formula:
W = .18(Gross pay = 13 n)
where n is the number of exemptions. If n is negative, we set W = 0 (see Note 2 below). The social security deduction FICA is 3 per cent of gross pay up to $4800, with no deduction for gross pay over $4800.
A program is desired which will produce a listing (for each input card) of (a) employee number, (b) gross pay this week, (c) withholding tax, (d) FICA, (e) net pay for the week. Moreover, a new updated master tape shojuld be prepared, with provision for saving the previous master tape as well. As much checking as possible should be incorporate, including specifying to the operator the number of the master tape needed, and the number to be assigned to the new tape produced by the program, and the automatic checking that the correct tape has been mounted on the unit.
Note 1: Abbreviations used here are outlined in Example 1, except for the following new terms:
AWITHY | accumulated withholding tax for year |
AFICAY | accumulated social security deduction for year |
ANETY | accumulated net pay for year. |
EXEMPT | no. of exemptions |
Note 2: In the computations of the gross pay for the current week we shall find it useful to be able to compute a function (which we shall call EXCESS.) of two numbers, say a and b, whose value is 0 if a ≤ b, and a-b if a > b. A formula for this function is
where | | denotethe usual "absolute value". In fact, by using this function, a simple one-line formula for this function is:
FICA = .03 EXCESS.((GROSSW - EXCESS.(AGROSY, 4800.)),0)
where AGROSY is assumed to already contain GROSSW, i.e., to have been updated already. We shall also apply this function in the case of the withholding tax to guarantee that we do not make a negative deduction. Thus
W = .18*EXCESS. (GROSSW, 13*EXEMPT)
Note 3: To check the order of input cards (normally in order of increasing employee number with a large employee number greater than the last employee number on the master tape) the program uses the subroutine SETEOF.(LABEL), where LABEL is the statement label of a statement to be executed if an end of file condition is detected during reading.
Since the last input card has a large employee number the first end of file condition is normally detected at the end of processing, but an illegal input card may also exist with a high employee number. After the first end of file is detected the end of file return is changed and the input tape checked for end of file. If no end of file exists a comment is printed to change tapes and processing begins again.
Flow diagram:
The Program:
START READ FORMAT IDENT,TAPENO INTEGER TAPENO,PAYNR,NUMB,J,OLTAPE PRINT ON LINE FORMAT OPER,TAPENO PAUSE NO. 1 REWIND TAPE 4 TEST REWIND TAPE 3 READ BINARY TAPE 3,OLTAPE WHENEVER OLTAPE .E. TAPENO,TRANSFER TO MAIN PRINT ON LINE FORMAT WRONG PAUSE NO. 3 TRANSFER TO TEST MAIN CUMGRS = 0. CUMFIC = 0. CUMNET = 0. CUMW = 0. REDO EXECUTE SETEOF.(M FILE) WRITE BINARY TAPE 4,TAPENO+1 READ(1) READ FORMAT EMPLOY,PAYNR,HOURS READ(2) READ BINARY TAPE 3, NUMB,RATE,AGROSY,AWITHY, 1 AFICAY,ANETY,EXEMPT WHENEVER NUMB .E. PAYNR GROSSW = RATE*HOURS+.5*RATE+EXCESS.(HOURS,40) AGROSY = AGROSY+GROSSW W = .18*EXCESS.(GROSSW,13.*EXEMPT) FICA = .03*EXCESS.((GROOW-excess.(AGROSY,4800.)),0) NETPAY = GROSSW-W-FICA AWITHY = AWITHY+W AFICAY = AFICAY+FICA ANETY = ANETY+NETPAY CUMGRS = CUMGRS+GROSSW CUMFIC = CUMFIC+FICA CUMNET = CIMNET+NETPAY CUMW = CUMW+W WHENEVER .ABS. (AGROSY-ANETY-AFICAY-AWITHY) .GE. .005, PRINT 1 FORMAT ERROR,PAYNR PRINT FORMAT OUTPUT,PAYNR,GROSSW,W.FICA,NETPAY J = 1 OR WHENEVER NUMB .G. PAYNR PRINT FORMAT ORDER,PAYNR BACKSPACE RECORD OF TAPE 3 TRANSFER TO READ(1) OTHERWISE J = 2 END OF CONDITIONAL WRITE BINARY TAPE 4,NUMB,RATE,AGROSY,AWITHY,AFICAY,ANETY, 1 EXEMPT TRANSFER TO READ(J) M FILE END OF FILE TAPE 4 REWIND TAPE 3 REWIND TAPE 4 EXECUTE SETEOF.(C FILE) READ FORMAT EMPLOY,DUMMY,DUMMY PRINT FORMAT NOMAN,PAYNR,TAPENO PRINT ON LINE FORMAT NOMAN,PAYNR,TAPENO PAUSE NO. 4 TAPENO=TAPENO+1 BACKSPACE RECORD OF TAPE 7 READ BINARY TAPE 3,DUMMY TRANSFER TO REDO C FILE PRINT ON LINE FORMAT OFF,TAPENO,TAPENO+1 PAUSE NO. 2 PRINT FORMAT TOTALS,CUMGRS,CUMFIC,CIMNET,CUMW EXECUTE SYSTEM. R INTERNAL FUNCTION EXCESS.(X,Y)=(X-Y+ .ABS.(X-Y))/2. R R FORMAT SPECIFICATIONS R VECTOR VALUES IDENT = $I8*$ VECTOR VALUES OPER =$15H4MOUNT TAPE NO. I8,S2,30HON TAPE UNI 1 T NO. 3,PRESS START*$ VECTOR VALUES WRONG = $48H4THE WRING TAOE HAS BEEN USED. PLEA 1 SE TRY AGAIN.*$ VECTOR VALUES EMPLOY = $I8,F10.2*$ VECTOR VALUES ERROR = $37H0ERROR IN CHECKING TOTALS FOR MAN N 1 O. I8$ VECTOR VALUES OUTPUT = $1H0,I8,4F20.2*$ VECTOR VALUES OFF = $24H4REMOVE TAPE LABEL 3,LABEL IT I8,S4,233HREMO 1 VE TAPE 4, LABEL IT I8*$ VECTOR VALUES NOMAN = $38H0THERE IS NO MASTER RECORD FOR MAN 1 NO. I8/22H0PULL TAPE 3. LABEL IT I8/51H0RESELECT TAPE 4 AS TAP 2 E 3 AND HANG BLANK TAPE ON 4/16H0THEN PUSH START*$ VECTOR VALUES ORDER = $8H0MAN NO.I8,43H IS OUT OF ORDER OR NO 1 MASTER RECORD EXISTS*$ VECTOR VALUES TOTALS = $13H1CUM. GROSS =F10.2/12H0CUM. FICA = 1 F10.2/11H0CUM. NET = F10.2/23H0CUM WITHHOLDING TAX = F10.2*$ END OF PROGRAM
The Retrocomputing Museum implementation cannot yet compile this example. The problem is the TAPE statement.
Problem: Mortgage Payment. The type of mortgage we consider is the fixed principle type for which each installment consists of an interest payment, a fixed amount to be deducted from the outstanding principal, and an additional amount to be placed in escrow--to be used to make insurance and tax payments.
Assume that a master card file is available containing the following information for each mortgage: (1) the mortgage number; (2) amount of outstanding principal; (3) annual payment on principal; (4) interest rate; (5) annual escrow payment; and (6) current escrow balance. There is also a file of cards available containing the current payment record consisting of mortgage number and amount of payment received. The master file and current payment file are assumed to be in order of increasing mortgage number.
The program is to read a card from the current payment record and check to see if it is acceptable. A payment is deemed acceptable if it consists of a single normal payment (i.e., a payment consisting of a single principal payment, a single escrow payment, and an interest payment for a single period) or if it consists of exactly two normal payments and any number (i=0, 1, 2, ...) of principal payments.
Note 1: Since the Michigan Algorithm Decoder uses the dollar sign as the delimiter for characters it is not possible to print out the dollar sign in a hollerith field. Using the - $=$ sets up the proper bit configuration for the dollar sign in storage since single characters appear in the left most position of the word. This character constant can be named and put on the list of items to be printed out.
Note 2: The current payments are processed until the file is exhausted. The detection of the end of file on reading tranfers control to the section of the program which punches the new master file.
Flow diagram:
The Program:
DIMENSION RECORD(1400, DIM) INTEGER I, NUMB VECTOR VALUES DIM = 2,1,7 VECTOR VALUES DOLLAR = -$=$ START READ FORMAT SIZE, NUMB THROUGH A, FOR I = 1,1, I .G. NUMB A READ FORMAT MASTER,RECORD(I,1)...RECORD(I,6) EXECUTE SETEOF.(UPDATE) I = 1 CARDS READ FORMAT PAYMT, IDENT,AMOUNT THROUGH B, FOR I = 1,1,I .G. NUMB WHENEVER RECORD(I,1) .E. IDENT DUE1 = RECORD(I,3)+RECORD(I,5)+RECORD(I,4)+RECORD(I,2) WHENEVER .ABS. (AMOUNT-DUE1) .L. .005 RECORD(I,2) = RECORD(I,2) - RECORD(I,3) RECORD(I,6)=RECORD(I,6)+RECORD(I,5) TRANSFER TO CODE OTHERWISE DUE2 = 2.*DUE1 - RECORD(I,4)*RECORD(I,3) END OF CONDITIONAL WHENEVER .ABS. (AMOUNT-DUE2) .L. .005 RECORD(I,2) = RECORD(I,2)-2.*RECORD(I,3) TRANSFER TO ESCROW OR WHENEVER AMOUNT .G. DUE2 THROUGH C, FOR PAY = RECORD(I,3),RECORD(I,3), 1 AMOUNT .L. DUE2+PAY C WHENEVER .ABS. (AMOUNT-DUE2-PAY) .L. .005, TRANSFER TO 1 PAID TRANSFER TO OVERPAY PAID RECORD(I,2)=RECORD(I,2)-2.*RECORD(I,3)-PAY ESCROW RECORD(I,6)=RECORD(I,6)+2.*RECORD(I,5) CODE RECORD(I,7)=1. OTHERWISE OVRPAY PRINT FORMAT REJECT,IDENT,DOLLAR,AMOUNT END OF CONDITIONAL OR WHENEVER RECORD(I,1) .G. IDENT PRINT FORMAT ORDER,IDENT,DOLLAR,AMOUNT OTHERWISE B CONTINUE I=1 PRINT FORMAT NONE,IDENT END OF CONDITIONAL TRANSFER TO CARDS UPDATE THROUGH D, FOR I=1,1,I .G. NUMBER D WHENEVER RECORD(I,7).G. 0.,PUNCH FPRMAT MASTER,RECORD(I,1)... 1 RECORD(I,6) R R FORMAT SPECIFICATIONS R VECTOR VALUES SIZE=$I10*$ VECTOR VALUES MASTER=$F10.0,5F10.2*$ VECTOR VALUES PAYMT = $F10.0,F10.2*$ VECTOR VALUES REJECT = $20H0PAYMENT ON MORTGAGE,F10. ,3H, ,C 1 11,F10.2,19H IS UNSATISFACTORY*$ VECTOR VALUES ORDER = $26H0PAYMENT CARD FOR MORTGAGE,F10.0,3H 1 ,C1,F10.2,44H IS OUT OF ORDER OR NO MASTER RECORD EXISTS*$ VECTOR VALUES NONE $1H0NO MASTER RECORD EXISTS FOR MORTGAG 1 E NO.,F10.0*$ END OF PROGRAM
Problem: Computation of actuarial commutation columns based on an arbitrary set of mortality rates and an interest rate, as an external function to be used by another program.
Analysis: Commutation columns, which are very important tools in actuarial problems are generated very easily by means of the formulas given below. The quantities Mx, Nx, and Dx in these formulas occur most often in combination, as in the computation of Px. Assuming a population of some initial size (at x = b0) (here 1,000,000), lx is the number living a t age x (so that lbo = 1,000,000), qx is the mortality rate, and dx is the number of deaths at age x. Thus d x = qx·lx. The quantity Dx is computed by the formula Dx = lx(1+i)-x. It can be used, for example, to compute the cost of term insurance, since Cx/Dx is the premium for one-year term insurance of $1 at age x.
The sums Mx and Nx are obtained by the formula
We note that for some w. we always have qx = l, so that lw+1 = 0 (since lw+1 = lw-dw = lw-lw = 0), therefore Dw+1 =0, dw+1 = 0, Cw+1 = 0, and the sums for Mx and Nx are actually finite sums.
The three most useful quantities computed here are (1)Px = Mx/Nx, which is the annual premium payable for an entire life for $1 of whole life insurance, (2)Ax = Mx/Dx, which is the single premium payable at age x for $1 of whole life insurance, and (2)ax = Nx/Dx, which is the present value at age x of a whole life annuity of $1, first payment at age x.
Printing of results is under the control of an input variable PRINT. Certain relationships must hold between some independently computed values, and these are used as checks on the compuatation:
Mb0 + Nb0+1 = Nb0+1 = Nb0/(1+i)
Px = 1/ax - i/(1+i)
These cannot be expected to come out exactly equal, because of round-off, but they should differ by very little.
Flow diagram:
The Program:
R R SAMPLE CALLING PROGRAM R READ FORMAT IN,Q(0)...Q(100) VECTOR VALUES IN =$12F6.5* $ DIMENSION Q(120),L(120),SMALLD(12),BIGD(120),C(120),N(120), 1 M(120),BIGA(120),SMALLA(120,P(120) EXECUTE COMFCN.(Q,0,099,.03,L,SMALLD,BIGD,C,N,M,BIGA,SMALLA, 1 P,1) INTEGER PRINT END OF PROGRAM
The External Functions:
R COMMUTATION TABLE FUNCTION R IF PRINT = 0,SUPPRESS PRINTING EXTERNAL FUNCTION(Q,BZERO,OMEGA,I,L,SMALLD,BIGD,C,N, R M,BIGA,SMALLA,P,PRINT) ENTRY TO COMFCN. INTEGER BZERO,OMEGA,PRINT,X L(BZERO) = 1E6 V = (1.+ I) .P. -BZERO THROUGH A,FOR X = BZERO,1,X .G. OMEGA SMALLD(X) = Q(X)*L(X) BIGD(X) = L(X)*V V = V/(1. + I) C(X) = SMALLD(X)*V A L(X + 1) = L(X)-SMALLD(X) N(OMEGA) = BIGD(OMEGA) M(OMEGA) = C(OMEGA) THROUGH B, FOR X = OMEGA-1,-1, X .L. BZERO N(X) = N(X+1) + BIGD(X) B M(X) = M(X+1) + C(X) WHENEVER .ABS.(M(BZERO)+N(BZERO)/(I+1.)) .G. 1., 1 TRANSFER TO MNERR E THROUGH G ,FOR X = BZERO,1,X .G. OMEGA BIGA(X) = M(X)/BIGD(X) SMALLA(X) = N(X)/BIGD(X) P(X) = M(X)/N(X) WHENEVER .ABS. (P(X)-1./SMALLA(X) + I/(I+1.)) .G. R 1E-4,TRANSFER TO PERROR G CONTINUE WHENEVER PRINT .E. 0, FUNCTION RETURN R R OUTPUT GENERATOR R PRINT FORMAT HEAD01,I VECTOR VALUES HEAD01 = $1H1,4HI = F5.4// 1 4H X,S13,4HQ(X),S18,4HL(X),S15,10HSMALL D(X), 1 S8,1HX*$ THROUGH BETA,FOR X = BZERO,1,X .G. OMEGA BETA PRINT FORMAT F1,X,Q(X),L(X),SMALLD(X),X VECTOR VALUES F1 = $1H0,I3,3E22.9,I7*$ PRINT FORMAT HEAD02 VECTOR VALUES HEAD02 = $1H1,4H X,S11,8HBIG D(X), 1 S16,4HC(X),S184HM(X),S18,4HN(X),S11,1HX*$ THROUGH GAMA,FOR X=BZERO,1,X .G. OMEGA GAMMA PRINT FORMAT F2,X,BIGD(X),C(X),M(X),N(X),X VECTOR VALUES F2 = $1H0,I4,4E22.9,17*$ PRINT FORMAT HEAD03 VECTOR VALUES HEAD03 = $4H1 X,S11,8HBIG A(X), 1 S13,10HSMALL A(X), S15,4HP(X),S11,1HX*$ THROUGH DELTA,FOR X = BZERO,1,X .G. OMEGA DELTA PRINT FORMAT F3,X,BIGA(X),SMALLA(X),P(X),X VECTOR VALUES F3 = $1H0,I3,3E22.9,17*$ FUNCTION RETURN PERROR PRINT FORMAT PERR,P(X),SMALLA(X),I VECTOR VALUES PERR = $27H0ERROR ON P CHECK. P(X) = E18.9, 1 S10,13HSMALL A(X) = E18.9,S10,4HI = F5.4*$ TRANSFER TO G MNERR PRINT FORMAT MNERR1 VECTOR VALUES MNERR1 = $19H0ERROR ON M,N CHECK*$ TRANSFER TO E END OF FUNCTION * DATA 2258 577 414 338 299 276 261 247 231 212 197 191 192 198 207 215 219 225 230 237 243 251 259 268 277 288 299 311 325 340 356 373 392 412 435 459 486 515 546 581 618 659 703 751 804 861 923 991 1064 1145 1232 1327 1430 1543 1665 1798 1943 2100 2271 2457 2659 2878 3118 3376 3658 3964 4296 4656 5046 5470 5930 6427 6966 7550 8181 8864 9602 10399 11259 12186 13185 14260 15416 16657 17988 19413 20937 22563 24300 26144 28099 30173 32364 34666 37100 39621 44719 54826 72467100000
The Retrocomputing Museum implementation cannot yet compile this example. The problem is the array I/O.
Problem: Find the first occurrence of an arbitrary word in a given text.
Analysis: Let N be the number of characters in the text and T(1)...T(N) be the text stored one character per word. Let L be the number of letters in the word which is stored one character per word in W(1)...W(L).
DIMENSION T(720),W(30) NORMAL MODE IS INTEGER READ FORMAT CNT,N READ FORMAT TXT,T(1)...T(N) ALPHA READ FORMAT CNT,L READ FORMAT TXT,W(1)...W(N) THROUGH SCAN, FOR I=1,1, I .G. N-L+1 THROUGH TST, FOR J=0,1,J .GE. L TST WHENEVER T(I+J) .NE. W(J+1),TRANSFER TO SCAN PRINT FORMAT OUT,I,W(1)...W(L) TRANSFER TO ALPHA SCAN CONTINUE PRINT FORMAT NOT TRANSFER TO ALPHA VECTOR VALUES CNT=$I3*$ VECTOR VALUES TXT=$72C1*$ VECTOR VALUES OUT=$11H0CHARACTER I3,13H IS START OF 30C1*$ VECTOR VALUES NOT=$15H0WORD NOT FOUND*$ END OF PROGRAM
Problem: Evaluate the recursive function,
f(0) = 1
f(n) = f(n-1) * n
Due to a reproduction error in the original, the Analysis paragraph below is somewhat mangled (the right-hand side sliced off). Italicized text in the paragraph is a best guess as to what it actually said.
Analysis: This is the definition of n! Although it could have been implemented directly using a THROUGH statement, instead the function will be evaluated using its recursive definition to illustrate how recursive functions can be handled.
EXTERNAL FUNCTION (N) NORMAL MODE IS INTEGER ENTRY TO FACT. WHENEVER N .E. 0, FUNCTION RETURN 1 SAVE RETURN SAVE DATA N T1 = FACT.(N-1) RESTORE DATA N RESTORE RETURN FUNCTION RETURN T1*N END OF FUNCTION
In order to use this function, the calling program would have to specify a list for use in the SAVE and RESTORE stayements. The following is an example of a program which uses FACT.
DIMENSION LIST(100) NORMAL MODE IS INTEGER SET LIST TO LIST BACK READ FORMAT IN, NR PRINT FORMAT OUT, NR, FACT.(NR) TRANSFER TO BACK VECTOR VALUES IN = $I2*$ VECTOR VALUES OUT = $4H0N= I3,14HN FACTORIAL= I11*$ END OF PROGRAM
Problem: To find the greatest common divisor of two integers Y and Z.
Analysis: The greatest common divisor is defined recursively by three equations
Where REM.(A,B) is the remainder of A/B. This function expects the arguments to be found on the temporary storage list as the two most recent additions. The use of the list as a parameter list makes the establishment of dummy variables unnecessary. This is less efficient than the usuall way of defining functions, but serves to remove many pitfalls encountered in using dummy variables with recursive functions.
EXTERNAL FUNCTION INTERNAL FUNCTION REM.(A,B) = (A/B)* B ENTRY TO GCD. NORMAL MODE IS INTEGER RESTORE DATA Z,Y WHENEVER Y .G. Z SAVE RETURN SAVE DATA Z,Y X = GCD.(0) RESTORE RETURN FUNCTION RETURN X OR WHENEVER REM.(Z,Y) .E. 0 FUNCTION RETURN Y END OF CONDITIONAL SAVE RETURN SAVE DATA REM.(Z,Y),Y RESTORE RETURN FUNCTION RETURN X END OF FUNCTION
Note: When called upon for a value, a function such as GCD, must have at least one argument (in this example a dummy argument of zero is used) even though the argument is never called upon. This is because GCD. is the name of the function, wgile GCD.(...) is the value of the function.
Note: The SET LIST TO statement need be executed only once, either in the main program or in a subprogram(but before any use of SAVE or RESTORE), since the SAVE and RESTORE statements always refer to the current list.
An example of a program using GCD. is:
NORMAL MODE IS INTEGER SET LIST TO LIST DIMENSION LIST(50) S READ FORMAT IN,M,N SAVE DATA M,N PRINT FORMAT OUT,M,N,GCD.(0) TRANSFER TO S VECTOR VALUES IN = $2I6*$ VECTOR VALUES OUT = $1H0,3HN= ,I7,S10,5HGCD = I7 1 *$ END OF PROGRAM
Problem: To evaluate Tschebychev polynomials.
Analysis: The Tschebychev polynomial T(N,X) is defined recursively as follows:
It is important to understand that when an expression is written as an argument of a function its value is computed and stored in a temporary location. It is this location (or address) which is actually used as the argument of the function. The implication of this use of a temporary location is that often expressions cannot be used as arguments of recursive functions.
EXTERNAL FUNCTION (N,X) ENTRY TO TSCHEB. INTEGER N,Z WHENEVER N .E. 0, FUNCTION RETURN 1. WHENEVER N .E. 1, FUNCTION RETURN X SAVE RETURN SAVE DATA N-2 Z = N-1 Y = 2.*X*TSCHEB.(Z,X) RESTORE DATA Z SAVE DATA Y M=TSCHEB.(Z,X) RESTORE DATA Y RESTORE RETURN FUNCTION RETURN Y-M END OF FUNCTION
A program which uses TSCHEB. is:
SET LIST TO LIST DIMENSION LIST(50) BEGIN READ FORMAT INPUT,N,X PRINT FORMAT OUTPUT,N,X,TSCHEB.(N,X) TRANSFER TO BEGIN VECTOR VALUES INPUT = $I6,F10.2*$ VECTOR VALUES OUTPUT = $1H0,4HN= ,I6,4H X= F10.23, 1 11H0FUNCTION= F15.6*$ END OF PROGRAM
MAD Statements are punched according to the following card format:
1.1 Statement labels may be punched anywhere in cols. 1-10. Spaces are not relevant.
1.2 Column 11 is used to designate a remark (R) or, alternatively, a continuation of a statement (0, 1, 2, ..., 9). The digits used to indicate continuation cards need not be in any particular order but there may be at most 10 cards in a statement.
1.3 The statement may start anywhere in cols. 12-72. With the exception of characters enclosed between "$" spaces are not relevant. (Spaces may not be relevant even if they are so enclosed (see section 2.15) on format specifications).
1.4 The identification information in cols. 73-80 is not used by the computer for any purpose but printing during translation and is arbitrary. Actually, it is good practice to use the first four of the available eight columns for the first four digits of the project number (i.e., for R10AN use R10A). Presumably, the last four would contain some sequence code.
During the process of translation many kinds of errors in the formation of statements and the allocation of storage can be detected. To understand this error detection and the subsequent printing of diagnostic comments some knowledge of the structure of the translator is helpful. The translation from statements to machine code is accomplished in three major sections:
The decomposition of the original statements into arrays of binary operations and pseudo-operations.
The analysis of all of the declarative information in order to allocate variable storage and identify the arithmetic types (i.e. modes) of variables.
The combination of the information produced from (1) and (2) to translate the arrays to relocatable binary programs.
When an error is encountered in one of these sections the translation does not proceed to the next section. However, insofar as possible, the entire set of statements is processed through the section in which the error is detected and therefore more than one error may be detected. It should be understood, then, that not all detectable errors may be found because:
They are detectable only in a later stage of the translation.
Some types of errors make it impossible to attempt further detection within the section in which it occurs.
One error may actually obscure another error.
Occasionally, an error in one statement may be such that it causes the translator to misinterpret a second statement, thus giving an error indication even though no error exists in the later statement.
The printed diagnostic comment may very often have an alternative or ambiguous form. This results from the fact that it is frequently not possible to determine what form was intended--merely that the present structure is not admissible--and therefore some of the alternative possibilities are suggested by the comment.
This section describes the mechanics of writing MAD-accessible subroutines in IBM 709 assembler. For tolerably obvious reasons, the Retrocomputing Museum implementation does not support this.
The information in this and the following sections of Chapter IV are in much greater detail in other Computing Center write-ups. However, the following sections should be sufficient for the general use of MAD.
Subroutines which are written for use by MAD programs--whether written in MAD as functions or in SAP[6] --must be relocatable and must operate from the calling sequences the translator produces. Consider, for example the function call - FN. (A,B,C) - which might appear in the body of a statement. Assume that B is an array which has an associated dimension vector BDIM. Using SAP notation for illustrative purposes, the calling sequence produced would be
TSX FN,4 TXH A TXH B,0, BDIM TXH C
Input-output routines utilize two types of parameters--the regional and single variable types. In addition an error return is given as well as a format specification location. The parameter operation code used is TXI and the end of the parameter list is indicated by a TXI operation with a blank address. Thus the statement
READ FORMAT FMT, BETA, X(1) ... X(100), K
would produce the calling sequence
TSX READ,4 TXI ERROR TXI FMT TXI BETA TXI X-1, 0, X-100 TXI K TXI
On occasion it is useful to use the regional notation in subroutines which are not in the input-output category, for example, G. (GAMMA, DELTA, Z(10)...Z(20)). The calling sequence would be
TSX G, 4 TXH GAMMA TXH DELTA TIX Z-10, 0, Z-20
It is important to notice that in this example, as well as in the first, the parameters--if executed as instructions--would produce no operation.
It is beyond the scope of this manual to discuss the structure of relocatable programs (see IBM FORTRAN II Manual). It is sufficient to say that a relocatable program must contain--in adidtion to the actual instructions in the program--information as to which addresses must be relocated at the time of loading for execution and which addresses must not. In addition, the first card (or record) of such programs must contain information about the size of the program, the number of subroutines it calls on, the amount of stroage it will share with other subroutines, the location of the list of subroutines it calls on, and the names by which the routine itself is referred to. The symbolic names of the subroutines called on must appear as the first words after this information.
The execution of MAD programs requires the use of a loading routine to relocate and store the program and subroutines. A slightly modified BSS FORTRAN loader is automatically produced by the University of Michigan executive routine. Also there are certain subroutines which may be automatically called for by a MAD program without an explicit reference to them in the source program.
The Retrocomputing Museum implementation does not support these routines or reserve these names.
The use of the following names for functions (subroutines) should be avoided except where the operation is the one indicated here.
Entry to this routine causes a return to the operating system. The END OF PROGRAM statement produces a call for this routine.
Entry to this routine also causes a return to the operating system. However, if a dump of storage was requested of the operating system such a print of storage will be produced before the return to the system. The ERROR RETURN statement may produce a call for this routine.
Entry to this subroutine causes BCD information to be written on tape. The arguments are: (1) location of the format specification, (2) tape number, and (3) a list of variables to be written. The WRITE BCD TAPE ... statement produces a call for this subroutine.
Similar to TAPEWR above except that the use of tape 6 (the output tape) is implied. The PRINT FORMAT ... statement produces a call for this subroutine.
Entry to this subroutine causes BCD information to be read from tape. The arguments are: (1) location of the format specification, (2) tape number, and (3) a list of variable to be read. The READ BCD TAPE . . . statement produces a call for this routine.
Similar to TAPERD above except that the use of tape 7 (the input tape) is implied. The READ FORMAT . . . statement produces a call for this subroutine.
Entry to this subroutine causes BCD information to be written on tape 5 (punch output type). The arguments are the same as PRINT and the PUNCH FORMAT . . . statement produces the call.
Entry to this subroutine causes BCD informtion to be printed on the attached (on-line) printer. The arguments are the same as PRINT except that the line spacing is not given in the format specification since an automatic 1/6 page skip is produced. The statement producing the call is PRINT ON LINE . . . .
This name may appear explicitly in the form SETEOF.(S). where S is an expression in the statement label mode designating the point of return when an end-of-tape is encountered during the reading or the writing of magnetic tape.
This name may appear explicitly in the form SETETT.(S). where S is an expression in the statement label mode designating the point of return when an end-of-tape is encountered during the reading or the writing of magnetic tape.
This name may appear explicitly in the form SETERR.(S), where S is an expression in statement label mode designating a point of return after an illegal data character or illegal format specification has been encounterered by an input-output subroutine.
It should be understood thatthe subroutines described above may be called by other subroutines as well as statements. Thus, for example, SETEOF is called by TAPERD, TAPEWR calls PRINT, and all of the input-output routines call ERROR.
[6] SAP is the SHARE Assembly Program which permits the writing of actual machine instructions in symbolic form.
Table of Contents
DEFINE is not supported in the Retrocomputing Museum implementation.
The following describes a procedure by which new operators and mode conversion operations may be introduced into the MAD language and used immediately thereafter in the rest of the program. As each definition is processed, the sequence of machine-like operations defining the behavior of the operator becomes part of the translator, just as the sequence for any already existing operator in the MAD language. The definition is "temporary", in that it applies only the program or external function definition which contains the definition program. In every case, the definition program must precede the first use of the operator or conversion being defined. A definition may refer to previously defined operators and conversions, but not to operators or conversions not yet defined. An example is given immediately below, to which reference may be made while reading the complete explanation which follows it.
Example: DEFINE BINARY OPERATOR .LOGADD., PREDECENCE SAME AS .OR. MODE STRUCTURE 2 .LOGADD.2 TO 2, 1 .LOGADD.1 TO 1 JMP *+1, MQ, *+7 STQ T CAL A ORA B SLW T+1 CLA T+1 OUT AC STO T JMP *-6 END
This example illustrates some, but not all, of the pertinent facts about definitions.
The name of the operator being defined consists of a sequence of letters (at most six), preceded and followed by periods (e.g. .LOGADD.).
The modes already existing in MAD are referred to in operator and conversion definition programs by numeric codes:
Floating Point 0 Integer 1 Boolean 2 Function Name 3 Statement Label 4
The user may find it necessary to introduce new modes to accompany newly defined operators. These are introduced via mode declarations similar to those of the form
INTEGER J,M,P,GCD.,E
except that the new modes are designated by number, as, for example,
MODE NUMBER 5 MP, GR, V., T
The available numbers are 5, 6, and 7, and these may represent any modes desired. This mode declaration does not carry any information as to the nature of the arithmetic implied by the mode; it merely declares certain variables and function values to be of that mode. The interpretation of the mode is implicit in the operator and conversion definition programs. Mode declarations may occur anywhere, as before, except within operator and conversion definition programs.
The first statement of an operation definition program is
where .NAME. is the name of the operator being defined (the operator may be either binary or unary), and .OPER. is the name or symbol of some existing or previously defined operator. This statement establishes the precedence (relative to the missing-parenthesis convention) of the operator .NAME. as being (1) immediately above that of .OPER. (but below all those already above .OPER.), (2) equal to that of .OPER., or (3) immediately below that of .OPER. (but above all those already below .OPER.). The name .OPER. could also be a single character, such as *, +(binary), -(binary), or /. Unary minus can be referred to in such statements by the name -U. Unary plus should not be used.
The choice of an appropriate sequence of instructions to perform a given operation assumes knowledge not only of the operator in question, but also of the modes of the operands. Thus A + B will be computed via one sequence if A and B are both floating point numbers, and via the same floating point sequence preceeded by an integer-to-floating point conversion sequence if one of the operands is an integer and the other is a floating point number. The specification of an operator together with the modes of its operands (or operand, if unary) will be called a context, and indicated by expressions such as 1 + 1, 0 .ADD. 0, .ABS. 0, 1 .P. 0. In these expressions, the integers represent the modes of the operands, and if the operator is unary, the mode is given only for the right-most (or b) operand position. The left operand will be referred to as the a operand.
A mode scheme may be described as being of one of two types:
(1) M TO r, used for operator definitions, or M TO N, where M and N are contexts containing the same operators but different in exactly one operand mode, and r is a mode number. Examples of mode schemes are 2 .LOGADD .2 to 2, 1 + 1 TO 1, 1 .ADD. 0 To 1 .ADD. 1. In the first case the operator .LOGADD. is applied to two Boolean arguents to produce a Boolean result. In the second case we have ordinary integer addition, producing an integer result. The third example illustrates a mode conversion. The operator .ADD. has occurred with an a operand in floating point mode and a b operand in integer mode. The mode scheme given describes the conversation of the b operand to floating point mode. These schemes are used in MODE STRUCTURE statements (see below) to indicate the effect of the associated sequence on the operands involved. Reference will be made below to schemes of "type 1" and "type 2" as indicated above, and to the M and N contexts and the mode number r which appear in these schemes.
After the DEFINE statement (but not necessarily immediately after) must be one or more sequence definition programs. Each of these programs consists of one MODE STRUCTURE statement followed by a "sequence". In case a new mode is created, one may find occasion to write definition programs without any new operators being defined.
Immediately after the words MODE STRUCTURE is a list of one or more mode schemes, separated by commas. In any one MODE STRUCTURE statement all mode schemes must be of the same "type" (see above), which will then be referred to as the "type" of the MODE STRUCTURE statement itself, and as the "type" of the sequence definition program which contains this statement. A statement of type I will precede a sequence which will accept as input one or two operands and produce a result. (See "sequence" below.) The "sequence" will be inserted in the object program, however, only when the operator and modes of the operands correspond to a context appearing in one of the mode schems in the MODE STRUCTURE statement. Thus, in the example above, if the operator .LOGADD. appears between two operands of Boolean or integer mode, the sequence which follows will be processed as outlined below. If .LOGADD. appears between two operands, either (or both) of which is of a different mode, this sequence will not be applicable. If no other sequence definition programs are provided, the context being translated is illegal, and an error indication is produced. the mode number r is taken as the mode of the result of the computation which results from the sequence. This is needed in establishing contexts of succeeding operations.
Since schemes of type 2 require their M and N contexts to agree in the operator they contain and one of the mode numbers, they have the form: 2.LOGADD. 1 to 2 .LOGADD. 2. The sequence which follows a MODE STRUCTURE statement containing this scheme will be written so as to perform the conversion of the second operand of .LOGADD. from integer to Boolean form in some manner.
The following general rules apply to all sequence definition programs:
(1) A context appearing in an M position may not already exist in the MAD language or appear in the M position in another MODE STRUCTURE statement.
(2) Every context appearing in the program containing the definition program must appear in an M position in some MODE STRUCTURE statement. This applies in particular to contexts occurring in N positions in MODE STRUCTURE statements of type 2.
Occasionally the sequence which would follow a particular MODE STRUCTURE statement is known to agree exactly with some existing sequence in MAD or some previously defined sequence. In this case, the entire sequence may be replaced by the statement SAME SEQUENCE as [script]S, where [script]S is some mode scheme for which a sequence is already available in MAD or has been previously encountered. For example, one might write:
MODE STRUCTURE 5 + 5 TO 5 SAME SEQUENCE AS 1 + 1 TO 1
for some new mode numbered 5 for which addition happens to coincide with integer addition. (Presumably, some other operator will differ in its mode 5 behavior from its integer behavior.) When a new sequence is needed, it must follow immediately the MODE STRUCTURE statement to which it belongs.
There are five kinds of instructions which may occur in the sequence: (1) END - this must be the physically last instruction of the sequence. It has no significance except to terminate the definition sequence. (2) OUT AC and OUT MQ - these terminate the processing of the sequence and indicate that the result of carryin gout the object program computation will be left in the accumulator or MQ register, respectively. (3) JMP - unconditional transfer during processing of the sequence. This is illustrated in the example by the introduction of the form
JMP *-6
It consists merely of a local reference (* ± integer constant), and refers to the processing of the sequence during compilation. This instruction will not appear in the object program generated. The * may only occur in JMP instructions. (4) JMP - conditional transfer during processing of the sequence. This is illustrated in the example by the instruction
JMP *+1, MQ, *+7
This refers to the symbol MQ, which is one of four logical propositions:
The result of the previous operation was left in the MQ register.
The result of the previous operation was left in the accumulator.
The current a operand is the result of the previous operation.
The current b operand is the result of the previous operation.
(Note: The a and b operands refer to the context: A .NAME. B, where A is omitted for unary operators.)
The conditional transfer instruction indicates in the "tag" position the logical proposition (e.g., MQ) in the "address" (left-most) position the local reference to which the processing next proceeds if the proposition is true, and in the "decrement" (right-most) position the local reference to which the processing next proceeds if the proposition is false. (The symbol "*" refers to the location in the sequence in which the "*" itself is found.) Thus, in the example, if the result of the previous operation was in the MQ register, processing moves next to the STQ T instruction while if it was not in the MQ register, processing moves next to the STO T instruction. This illustrates the need for always saving the previous result if it is not used immediately. (5) Object program instructions in the form of a 3-letter mnemonic operation code (taken from the list given in the Appendix), followed by the usual address, tag, and/or decrement parts as in the standard 704 codes. (SAP extended operations codes are not included i the Appendix and may not be used.) In object program instructions the tag, if any, must be one of the digits 0 through 7, and if there is a decrement part, the tag digit may not be omitted and the address part may not be omitted. Address and decrement parts must be one of the following:
refers to the a operand (not used for unary operations)
refers to the b operand Note: A and B may not appear in instructions such as STO, itc., which change their values.
refers to the temporary in which the result of the previous operation must be stored if not used. This temporary may not be used for any other purpose.
refers to other temporaries which may be used in the sequence. There must be exactly two occurrences of any temporary name so used. One may not assume these temporaries are located contiguously in storage.
local object program reference, i.e., a reference in the object program, effective at the time of execution of the object program, not during translation. (There is no provision here for a "location field" such as columns 1-6 in SAP.) "LOG" refers to the location in which "LOG" occurs, and may not occur in a JMP instruction.
After the definition program has been absorbed into the translator, the sequence is processed interpretively at each occurrence of the defined operator in some legal context, i.e., the first instruction is examined and if it is an unconditional transfer or conditional transfer (JMP), the transfer is made to the appropriate instruction in the sequence. If it is an object program instruction (operation code other than JMP, OUT, or END), the appropriate translation of the instruction is included in the machine-language version of the object program, which is being generated. If it is an OUT instruction, the procerssing of the sequence stops, with the setting of a switch in the translator to indicatte whether the result of this operation remains in the accumulator or MQ register. (The END instruction should never be encountered during processing of the sequence.)
(1) The following program could be used to define floating point multiplication if it were not already in the MAD language. The three sequence definition programs define floating point multiplication and two conversions, respectively.
DEFINE BINARY OPERATOR .FMPY., PRECEDENCE SAME AS *( MODE STRUCTURE 0 .FMPY.0 TO 0 JMP *+4,AC,*+1 JMP *+1,MQ,*+4 JMP *+4,AT,*+1 JMP *+7,BT,*+5 STO T LDQ A FMP B OUT AC STQ T JMP *-4 FMP A OUT AC END MODE STRUCTURE 1 .FMPY. 0 TO 0.FMPY.0 JMP *+5,MQ,*+1 JMP *+1,AC,*+5 JMP *+5,AT,*+1 STO T JMP *+2 STQ T CLA A ORA CF FAD CF OUT AC END MODE STRUCTURE 0 .FMPY. 1 to 0.FMPY.0 JMP *+5,MQ,*+1 JMP *+1,AC,*+5 JMP *+5,BT,*+1 STO T JMP *+2 STO T JMP *+2 STQ T CLA B ORA CF FAD CF OUT AC END
(2) The unary operator .SHIFT., which shifts an integer in the address part into the decrement part of a word, would be defined as follows (where mode 5 indicates an integer in the decrement part of the word):
DEFINE UNARY OPERATOR .SHIFT., PRECEDENCE SAME AS .ABS. MODE STRUCTURE .SHIFT. 1 TO 5 JMP *+3, MQ, *+1 JMP *+1, AC, *+3 JMP *+3, BT, *+5 STQ T CLA B ASL 18 OUT AC STO T JMP *-4 END
One would also have to provide for all contexts in which mode 5 variables or constants might appear. In particular, one might needf the following (in the case of "*", the "TO r" in a type 1 scheme may be omitted.):
MODE STRUCTURE 5 = 5 JMP *+1, BT, *+2 JMP *+2, AC, *+4 CLA B STO A OUT AC STQ A OUT MQ END
Of course, this could also have been written:
MODE STRUCTURE 5 = 5 SAME SEQUENCE AS 1 = 1
(3) Another example, illustrating the use of the local object program reference (LOC) is the operator .BIT., a Boolean binary operator whose value is 1B if A has a 1 in bit position B, and 0B otherwise. (Bits are numbered here from the left, 0-35.)
DEFINE BINARY OPERATOR .BIT., PRECEDENCE SAME AS .E. MODE STRUCTURE 1 .BIT.1 TO 2, 2.BIT.1 TO 2, 0.BIT.1 TO 2 JMP *+6, MQ, *+1 JMP *+1, AC, *+7 JMP *+1, BT, *+13 LDQ A STA LOC+1 <———————————— JMP *+6 JMP *+3, AT, *+1 STQ T LDQ A CLA B STA LOC+1 RQL 0 PXD LGL 1 OUT AC STO T JMP *-8 MODE STRUCTURE 1 .BIT.0 TO 1.BIT.1, 1 2 .BIT. 0 TO 2.BIT. 1, 0 .BIT. 0 TO 0 .BIT. 1 SAME SEQUENCE AS 1 = 0
Note that in the instruction indicated by the arrow the address (LOC+1) is that which will appear in the object program, not that which is in the sequence definition program. This sequence could be written more elegantly but was written in this way to illustrate the use of LOC.
The DEFINE and MODE STRUCTURE statements are punched (as any other MAD statements) starting in (or after) column 12. Instructions in the sequence must have the "operation code" in columns 8-10, and the rest of the instruction in (or after) column 12. Blanks are ignored throughout, except in operating codes.
ACL LDA STA ADD LDQ STD ADM LFM STO ALS LGL STP ANA LLS STQ ANS LRS STZ ARS LTM SUB BSF LXA SXD BST LXD TIX CAD MPR TLQ CAS MPY TMI CHS MSE TNO CLA NOP TNX CLM ORA TNZ CLS ORS TOV COM PAX TPL CPY PBT TOC DCT PDX TQP DVH PSE TRA DVP PXD TSX EFM RDS TTR ETM REW TXH ETT RND TXI FAD RQL TXL FDH RTT TZE FDP SBM UFA FMP SLQ UFM FSB SLW UFS HPR SSM WEF HTR SSP WRS LBT
Table of Contents
READ DATA is not supported by the Retrocomputing Museum implementation, mainly because we have no test programs for it.
This statement causes information to be read from cards; no list of variable names or format specification is necessary. The values to be read and the variable names are punched in the data cards in a sequence of fields of the form:
v1 = n1, v2 = n2 , v3 = n3, ...., vk = nk *
The v1, ..., vk are the variable names and n1, ... , nk are the corresponding values. Reading is continued from card to card until the terminating mark * is encountered. Fields cannot be divided between cards, so the last character in a card not terminated by an asterisk would normally be a comma. However, as a convenience, the end of the card is treated as an implied comma and hence this final comma may be omitted. The variable names may designate single variables or elements of linear and two-dimensional arrays. The subscripts on the array vaiables must be integer constants. The values may be floating point, integer, octal, Boolean, or alphabetic with the forms descibed in the section on constants in the MAD manual (See section Section 1.1, “Constants”).
For convenience in entering values of array elements it is possible to designate only one variable name and have successive numbers, written without names, interpreted as the consecutive values of the array, i.e.,
V(j) = n1, n2, n3 . . . , nk
would be the same as
V(j) = n1. V(j + 1) + n2, ..., V(j + k - 1) = nk
For 2-dimensional arrays successive numbers will be entered in succeeding columns of the designated row until the row - as determined from the current value of the dimension vector - is filled, and then the next row will be started.
Zeros must be punched; adjacent commas (,,) are simply skipped. Blanks are ignored throughout except between dollar signs (which are used only to delimit a string of Hollerith characters).
Six or less Hollerith characters - delimited by dollar signs - may be values of single integer variables. Longer strings of Hollerith characters may be entered as elements of arrays. Such strings are divided into six character groups for storage.
As an example illustrating many of the features described herein consider the data card set:
X1 = 1.2, Y1 = -6.8, INDEX = 4, A(4) = 3.1, -10.93, 12.6, MATRIX (2,1) = 25 E-2, 1.8 E-10, 3.14 E-8, STRING (1) = $ END OF PROBLEM $ *
It is important to remember that, since such cards are data cards, they should not be included as an integral part of the MAD statements but handled as ordinary data.
PRINT COMMENT is not supported by the Retrocomputing Museum implementation, mainly because we have no test programs for it.
Here S designates a string of no more than 120 Hollerith characters. These characters may not include dollar signs and here blanks are valid characters. The string, delimited by dollar signs as indicated, will be printed. The first character will be interpreted as a carriage control code and will not be printed. For convenience the carriage control code is reproduced here:
Character | Result |
---|---|
blank | single space |
+ | no space |
- | triple space |
0 | double space |
1 | space to top of next page |
2 | space to next half-page |
4 | space to next quarter page |
8 | space to next one sixth page |
An example statement is:
PRINT COMMENT $1 JOHN PUBLIC, MATH 373 PROBLEM 1$
PRINT RESULTS is not supported by the Retrocomputing Museum implementation, mainly because we have no test programs for it.
Here L designates a list of variable names or block designations. Expressions may not appear in the list although subscripts of variables in the list may be expressions. The printed output is analogous to the input in that the numbers printed are preceded by the appropriate variable name and an equal sign - e.g., X = -12.4 - and only the initial elements of arrays are so labelled. Elements of three and higher demensional [typo?] arrays will be labelled with the equivalent linear subscript. If dummy variables (in a function definition) are included in the list the specific values assigned to such variables during execution will not be labelled but simply preceded by ... .
An example statement is:
PRINT RESULTS X1, Y1, Z(1) . . . Z(N + 1), MTX (1,1) ...MTX(M,N)
November 15, 1960
These extended features are not supported by the Retrocomputing Museum implementation, mainly because we have no test programs for them.
The notes which appear below describe several new features which have been added to MAD. Existing programs need not be changed in any way. Most programs will be reduced in size and execution time, however, if they are recomplied to take advantage of the internal improvements that have been made with regard to the translation of Boolean expressions and which are described below (see (8)). Included here, also, are some comments on reducing the size of some subroutines.
(1) The Appendix to this supplement describes the new DEFINE facility. By means of this facility, new operators can be added temporarily to MAD, as well as new modes of arithmetic.
(2) Along with integer, floating point, Boolean, and alphabetic constants, one may now write octal constants. These constants are written as twelve digit octal integers followed by the letter K, except that leading zeros may be omitted. If one or more decimal digits follow the letter K, this is interpreted as an octal scale factor. This 127K2 would be the octal integer 000000012700, and 1K10 would produce the octal number 010000000000.
(3) Any constant (integer, floating point, Boolean, alphabetic, or octal) may be declared to be of a mode other than its normally assigned mode by following the constant by the letter M and the digit code for that mode. The digit codes are:
0 Floating point 1 Integer 2 Boolean 3 Function Name 4 Statement Label
The new modes 5, 6, and 7, which may be defined as described in the Appendix, may also be used. The constant is converted as usual, but then is assigned the indicated mode, if the letter M is used. Thus, if some new mode numbered 6 were defined for certain integers, then the appearance of the constant 32M6 would assign mode 6 to the decimal integer 32. The appearance of 32KM6 would assign mode 6 to the octal constant 32, and the appearance of $AB$M6 would assign the mode 6 to the constant $AB$. Similarly, the appearance of 32.1E-1M6 would assign mode 6 to the number 3.21 which appears in storage in the usual floating point form.
(4) A subroutine is not available, called ZERO., which will accept as arguments single variables or blocks (i.e., an "input list") and very efficiently (i.e., using STZ and TIX operations) store +0 in these locations. This saves writing an iteration loop.
(5) In addition to the usual notation for a block: e.g., A(6) ...A(N), MAD will now accept the traditional use of commas as well:
A(6), ... , A(N)
(6) A list of variables used in the program, but to which only one reference is made, is printed out for each MAD program. This list does not include any variables appearing in PROGRAM COMMON, ERASABLE, DIMENSION, VECTOR VALUES, or EQUIVALENCE statements. Variables appearing in this list are all assigned to the same location, under the assumption that they are not purposely used for anything except perhaps redundant labelling of statements. This list has proven to be a valuable debugging aid, since misspelled names almost invariably show up on this list. It should therefore be checked carefully whenever it appears.
(7) In order to conserve storage, it may be desirable to store, for example, only one half of symmetric matrix or an upper (or lower) triangular matrix, etc. Perhaps only the non-zero elements of a sparse matrix might be stored, also. For such arrays, it is necessary to use a subscription subroutine other than the standard one supplied by MAD which expects to find the entire matrix stored by rows (in the two-dimensional case). Such special subscription subroutines may now be written as any other internal or external functions in MAD or in SAP, as usual. The arguments must be the array name, followed by the n subscripts (integer expressions) in the usual order. The value of the function which is to be computed is the linear subscript to be used to obtain the value desired. For example, if one wished to store only the upper triangle (plus diagonal) of an upper triangular two-dimensional array (by rows, starting as A(2)), the subscription function F, could be defined as follows: (We shall assume a constant zero stored at A(1))
where n is the nubmer of columns in the matrix A, and b is the base point. Of course, if n (and/or b) is not a constant, it will have to be made available to the subscription subroutine, probably via PROGRAM COMMON, as will the base point, if used. It will automatically be available, of course, if the subroutine is written as an internal function. Note that in the case i > j, we give as the value of F.(A,i,j) the linear subscript 1 of the value 0, rather than the value itself.
In order to notify MAD that such a subscription function is to be used, one merely inserts the name of the function as the first entry of the dimension vector of the appropriate array, moving the number of dimensions, etc., down by one in the dimension vector. Thus, the above example might be indicated as follows:
DIMENSION A(100, ADIM) VECTOR VALUES ADIM = F.,2,2,10
if there were ten columns. Although dimension information could always be computed or brought in as data instead of via a VECTOR VALUES declaration, the one exception to this is now the name of the subscription function. If used, it must occur as the first entry in a VECTOR VALUES declaration, and it must be followed immediately by at least one integer in the same declaration. It should be understood that if this feature is used, references to matrix elements by means of subscripts are made as if the matrix were stored as originally described in the MAD manual. Thus, subroutines do not need to know whether a calling program is or is not using such storage conservation functions.
Two special subscription functions are available from the subroutine library: (a) SYMM. will handle two-dimensional symmetric arrays for which only the upper half (plus diagonal) is stored by rows. (b) TRANSP. will handle a two dimensional array stored by rows (in full) but considered to be in transposed form. In other words, if a matrix B is designated as having subscription function TRANSP., then a call of B(6,2) will, in fact, produce the linear subscript of B(2,6).
Both SYMM. and TRANSP. automatically have access to the dimension vector (including the base point), so no provision need to be made for putting any information in PROGRAM COMMON, for example. In fact, if the subscription function is written in SAP, it may be able to use the fact that MAD provides the address of the dimension vector in the decrement part of the parameter which contains the address of the array, in this case the first parameter. Note, however, that the dimension information has been moved down by one because of the presence of the name of the subscription function; and this must be accounted for in any references to the dimension vector.
In order to accommodate this new feature, the MAD library subroutines (03310, (03311, and (MTX have been modified. Anyone using special subscription subroutines should discard any previously punched versions of these library subroutines. Note, also, that TRANS and TRANS1 may not be used for transposing matrices stored in short form, since they were not written to recognize special subscription fuctions.
(8) A Boolean expression usually contins one or more atomic Boolean expressions consisteing of two arithmetic expressions on either side of a relation. Exampls of atomic Boolean expressions are X + 3 .LE. Y, I, .E. 1, and so on. It has been found possible to save as many as 60% of the object program instructions formerly generated for atomic Boolean expressions. Moreover, the object programs now produced will skip the evaluation of the remaining terms of a disjunction (an "or" expression) as soon as one terms has the value 1B, and a similar statement holds for conjunctions ("and" expressions). These changes are entirely internal to MAD, and require no change in MAD source programs. A recompilation will be necessary, however, to obtain the shorter, faster object program which results. In order to obtain the maximum benefit from the skipping behavior indicated above, it is necessary to understand that the atomic Boolean epxressions in a complex Boolean expression are evaluated from right to left, and the one most likely to be "true" in a conjunction, and the one most likely to be "false" in a conjunction, should be placed as far toward the right end of the expression as possible.
Thus, if one were testing for values of X between 0 and 2 and between 5 and 6, one might write
WHENEVER 0 .L. X .AND X .L. .OR. 5 .L. X .AND X .L. 6
If one knew that for the data expected, the values of X would occur most often between -1 and 2, one would do better to write the above as follows:
WHENEVER X .L. 6 .AND. 5 .L. X .OR. X .L. 2 .AND. 0 .L. X
(9) There is a table within MAD in which are recorded all occurrences of parameters within an external or internal function definition. Since all such occurrences must be initialized on each entry to the subroutine, it is very desirable that the number of entries in this table be kept as small as possible. This is especially important when a diagnostic comment is generated saying that this table has in fact overflowed! (The diagnostic comment produced in this case is PARAMETER USE TABLE EXCEEDED.)
There are several ways to cut down the entries in this table and thus speed up the execution of the subroutine and shorten its length as well. (1) If X is an input to the subroutine and its value is used several times, start off the subroutine's computation with the substitution statement Y = X and use Y instead of X everywhere. Only this substitution statement will then need to be initialized with the address of X. (2) If the address of a variable is needed, as in the case of an output argument, or an argument which is an array name, one cannot use the method just given in (1). One can instead put the variable or array in PROGRAM COMMON by means of (identical) declarations in both the main program and the subroutine. Then the variable or array should not be used as an argument at all, and the initialization is thus avoided. Note that in this case (identical) dimension declarations are necessary for such arrays in both the main program and the subroutine.
(10) A new logical operation is now available in MAD, the "exclusive or", written as .EXOR.. The precedence of .EXOR. is the same as the precedence of .OR., and its definition is: M .EXOR. N has the value 1B if and only if either M or N has the value 1B, but not both.
(11) Most diagnostic comments printed for errors in part I of the translation now include references to the appropriate section of the MAD manual. These references appear in parentheses following the comment. For example, (II.1.1.4) refers to Chapter II, section 1.1.4 which is titled Boolean Constants.
(12) Each use of the subscript notation A(i,j) where A is an m x n array, involves evaluation of the expression n(i - 1) + (j - 1) + b where n = the number of columns in A and b = the base point, i.e., A(1,1,) ≡ A(b). A method which is much more efficient is to preset or compute at the beginning of the program, the vector: V(1) = b-1, V(2) = b-1+n, V(3) = b=1+2n, ..., V(m) = b-1+(m-1)n. The ith element in the vector is the linear subscript in A of the element in A immediately preceding the first element in the ith row. To refer to A(i,j) one now writes A(V(i)+j). The base point and number of columns may be changed during computation, but each time either is changed the elements of V must be recomputed. The usual subscript restrictions still hold, i.e. i ≤ 0, (V(i)+j) ≤ 0. For example, if A is a 4 x 3 matrix (m = 4, n = 3) and b = 1, then V(1) = 0, V(2) = 3, V(3) 6, and V(4) = 9.