dos_compilers/RHA (Minisystems) ALGOL v55/ALGOL60.TXT

4466 lines
195 KiB
Plaintext
Raw Normal View History

2024-07-06 18:53:43 +02:00
1 Introduction
PLEASE NOTE - This document has been generated by scanning old printed documents.
It can therefore be expected to contain errors. If you find any, please edit the document
and email it to rhaminisys@aol.com. There is no need to say where the changes are,
we will find your corrections by comparing the original with your version.
Please visit our web site to find out about our current software.
Thanks!
1.1 Licence agreement
The Rogalgol Algol60 system is copyright by RHA (Minisystems) Ltd.
Freeware conditions
-------------------
The components distributed in Algol60.zip are free for personal use. For any other
use, including but not limited to educational use and any form of mass distribution,
please contact RHA (Minisystems) Ltd.
You may give copies of Algol60.zip to your friends. However, we would prefer
everybody to download it from our web site at http://www.angelfire.com/biz/rhaminisys
to be sure of getting the latest version. Only the downloaded ZIP file may be
distributed, not the individual files.
The software is provided "as is" without any warranty whatsoever. No support for the
free software is guaranteed, although you are welcome to email us and we will help
if circumstances allow.
Educational and commercial use
------------------------------
All requests to use or distribute the software are treated on an individual basis.
Customization of the run time program can be undertaken, including porting to different
processors and operating systems.
Contact information
RHA (Minisystems) Ltd.,
83 Gidley Way,
Horspath,
Oxford OX33 1TQ,
England
e-mail RHAMinisys@aol.com
Web pages http://www.angelfire.com/biz/rhaminisys
1.2 History
The RHA (Minisystems) Ltd Algol60 system began life in the early 1970s and
was sold until the mid 1980s. Known as Rogalgol from the start, it was
designed to run with minimal resources, just 8K of memory and paper tape
input and output. In its final state a complete interface to the MSDOS
operating system had been added. Sub-directories were a new idea at the
time, the programs only recognize file names, not paths.
The first Z80 version was developed in association with computer
manufacturer Research Machines Ltd of Oxford, who also produced the Z80 User
Manual. RML dropped their support for the language when they moved to 80x86
based machines. RHA (Minisystems) Ltd continued to develop the Z80
implementation and was solely responsible for the 80x86 versions.
The Algol subset is the ultra lean language used to write the full Algol-60
compiler. Written in a language devoid of floating point, nested procedure
declarations, stack checking and other things, the full compiler at 17K
bytes is only just larger than the full virtual machine interpreter. The
output of the subset compiler is code for the subset virtual machine,
expressed in assembly language.
Miniaturisation has progressed to such an extent that the system may have
another lease of life as a language for microprocessor controls. It would
not be difficult to change the front end of the compiler to make it compile
something more modern looking than Algol-60. The virtual machine is simple
and easily ported. The input/output system is modular, uses a generic way of
dropping down to machine code, and is isolated from the rest of the virtual
machine. For this reason we are not releasing the virtual machine sources.
1.3 Document overview
Document overview
The remainder of this document consists of two main sections, the Z80 manual
and a supplement for 80x86 operating systems. Everything in the Z80 section
applies to the 80x86 as well. All Z80 specific material in the original
manual has been removed, it applies to patching the runtime interpreter,
which is not supported on the 80x86 and is being reserved for commercial use
on the Z80. Enhancements made since the involvement of RML have been
included in the main body of the text, because they apply to the Z80 CP/M
version also.
2 Z80 User manual
2.1 Z80 manual introduction
Rogalgol is an implementation of the Algol 60 language designed especially
for small computers. Almost all the features of Algol 60 are implemented
together with a significant number of extensions. The system is essentially
portable; the compiler, which is itself written in Algol, has been "burned
in" over a period of several years on PDP8 and PDP11 computers. Rogalgol for
the Z80, running under the CP/M operating system, consists of a one-pass
compiler and a runtime program. The compiler translates the Algol source
program into a machine independent intermediate code which specifies the
sequence in which a number of subroutines is to be obeyed and which contains
arguments for these subroutines.
Each intermediate code occupies only one byte of memory, resulting in a very
compact object code. Roughly 10 bytes are required to store an average
statement. The runtime program contains a loader for the compiler output and
all the routines required to run the Algol program.
The compiler determines the minimum memory requirements of the system. The
compiler and its runtime system together occupy about 12K bytes. Program
work space and the CP/M operating system bring the total memory requirement
to about 21K.
The runtime system requires about 8K bytes of memory, which together with
program, data, and CP/M requirements allows sizable programs to run in as
little as 16K bytes.
[Reserved for Z80 commercial use only]. The availability of the same
compiler for 80x86, Z80, PDP11 and PDP8 computers makes it possible to
develop programs on larger computers and to run high level programs on
microcomputers which do not in themselves have the I/O or memory capability
to support CP/M or the compiler. The modular construction of the programs
allows the user to optimise the runtime system to meet individual
requirements. In particular users can add I/O handlers, assembly code
routines and error handling without needing to become involved in the inner
workings of the system.
2.2 Language elements
2.3 Identifiers and symbols
An Algol program consists of a sequence of symbols which are printing
characters on a standard 'Teletype' terminal or its equivalent. Editing and
layout characters are generally ignored by the compiler except where
indicated in the following sections. The symbols are frequently grouped into
units which the compiler treats as a single entity. These groups are the
numeric constants just described, the language key words such as BEGIN and
TRUE (which are strings of letters enclosed in single quotes or in upper
case depending upon the convention being used), and identifiers. Only the
first two letters of a language key word are significant. An identifier is
the name given by the programmer to a variable, label, switch, array or
procedure. There is a small group of Algol symbols which are made up of two
characters. These are the assignment operator (:=), greater than or equals
(>=) and less than or equals (<=). Identifiers consist of any number of
alphanumeric characters of which the first must be a letter. Letters are in
upper or lower case depending upon the convention being used. Examples are
x, fred, ml, m2, abc123, abc1234.
Identifiers may have up to 99 characters, all significant. To prevent an
excessive slowing of the compiler, the 99 character limit is not checked.
However, the total number of identifiers and the byte array storing the
names are checked. The tables are optimised for an average identifier length
of 5 characters. If the average is longer, the name array will overflow
first; if it is less the limit on the number of identifiers will be reached
first.
All identifiers referring to variables and switches must be declared before
they are used so that the compiler knows to what type of object they refer.
Labels and procedures may be used before they are declared because the type
of the identifier can be deduced from the context in which it is used.
2.4 Key words
Some convention is required in Algol to distinguish between the language key
words, e.g. BEGIN, END, ELSE, etc., and identifiers. In textbooks this is
usually done by printing the key words in bold type. In actual compilers
some other convention is needed depending upon the i/o hardware available.
This compiler accepts two possible conventions. The first must be used where
the i/o is restricted to upper case characters only. The second may be used
where both upper and lower case are available. Two small utilities are
provided to convert between these conventions.
CONVENTION 1. Upper case only. All basic language words must be enclosed
within single quote marks, e.g.
SUM:=0;
'FOR' I:=1 'STEP' 1 'UNTIL' MAX 'DO'
SUM:=SUM+(X[I]-XM[I])^2;
RMS:=SQRT(SUM/MAX);
CONVENTION 2. Upper and lower case. All basic language words must be in
upper case and all identifiers must use only lower case. The above example
now becomes:
sum:=0;
FOR i:=1 STEP 1 UNTIL max DO
sum:=sum+(x[i]-xm[i])^2;
rms:=sqrt(sum/max);
The compiler decides which of the two conventions is being used from the
first word of the program, which must be BEGIN, COMMENT or DIRECTIVE. Once
the convention is selected, the compiler expects the remainder of the
program to continue in the same manner. Internally the compiler converts all
identifiers to upper case (which is how they appear if the identifier tables
are printed). The compiler only checks the first 2 letters of the language
key words, then skips until a suitable terminator is found (a closing quote
or a non upper case letter). This implies that in the second convention
adjacent language key words must be separated, e.g.
THEN GOTO label; and not THENGOTO label;
Note: In the first convention, decimal exponentiation in the
representation of real numbers is represented by 'E' e.g. X := 1.234E-5,
whereas in the second case a small 'e' must be used e.g. x := 1.234e-5. Note
that when data is being read by the program then a capital 'E' is always
used.
Note: When switches are applied to file names they are case sensitive in
the same way. Use lower case letters if the program being compiled uses the
upper/lower case convention.
Users if they so wish may use any of the language key words for the names of
identifiers. As an example the following declaration is perfectly
acceptable:
BEGIN INTEGER real, begin, end;
For the sake of readability the examples given in this manual will for the
most part use the upper/lower case convention.
2.5 Pre-declared identifiers
Certain procedure names are recognized by the compiler without declaration.
Such identifiers may be regarded as having been declared in a fictitious
outer block enclosing the entire program and thus in scope everywhere except
where masked out by a local redeclaration. These procedures include the
input/output routines which are discussed later and the standard functions
as defined in the Algol 60 Report. In addition to these there is the
procedure 'ioc' which takes a single integer parameter, e.g.:
ioc(n) ;
This routine serves a variety of purposes depending upon the value of n.
These include input/output selection, format control and so on. These uses
will be discussed in the following sections to which they apply. Another use
of this procedure is as a simple way of linking to code routines within the
runtime system. An inspection of the file 'ALIB.ALG' on the distribution
disk shows that the majority of the procedures consist simply of a formal
definition of their parameters with a single call to 'ioc' as the body of
the procedure. Commercial Z80 users may link in their own code routines by
this same method.
2.6 String literals
A string in Algol consists of a sequence of characters enclosed within
double quotes, e.g.
text(1, "Hello Dolly");
All characters within the quotes with an ASCII value less than 32 are
ignored. This includes carriage return, tab, and control characters but
space is permitted. In order that these and other characters may be included
within strings, the following convention is used. The characters '*'
(asterisk) and '^' (circumflex) have special significance and are always
considered in conjunction with the character which immediately follows.
Control characters
The sequence ^X will insert CONTROL-X into the string. In general '^' has
the effect of stripping off all but the 5 least significant bits of the
following character.
Layout characters
*N Inserts CR-LF (carriage return-line feed) into string.
*C Inserts CR (carriage return).
*I Inserts LF (line feed).
*S Inserts SP (space). A literal space is also allowed.
*T Inserts TAB.
*P Inserts FF (form feed--new page).
All other characters following * are taken literally; in particular,
** Inserts *
*^ Inserts ^
*" Inserts "
For example
text (1, "An example *"STRING*"
*SMight look lik
e this*NX*^2+Y*^2=");
will print on the console as:
An example "STRING" might look like this
X^2+Y^2=
The internal representation of a string is a series of characters stored in
sequential bytes terminated by a zero value.
2.7 Character literals
The literal value of any character may be found using an ampersand "&"
character followed immediately by the required character. For example, to
convert a character digit to a number between 0 and 9 we have
i:=chin(1) - &0;
or to output an X then
chout(1,&X);
The convention described above involving * and ^ also applies, so to check
for end of file (CONTROL-Z):
i:=chin(dev);
IF i=&^Z THEN ...
or to check for a carriage return character:
IF i=&*C THEN ...
The two exceptions are &*N and &*" which will lead to the wrong result. The
first generates two characters and the second is written &".
Character literals are of type integer.
2.8 The structure of an Algol program
2.9 Program structure
One of the most important features of the Algol language is that it is
structured. Just as round brackets define sub-expressions within
expressions, so the brackets BEGIN and END enclose a set of statements which
are treated syntactically as a single statement, These bracketed statements
are used in controlling the order of execution of the program and largely
replace the use of labels and GOTOs, since they are obeyed as a whole or not
at all. Such a statement is known as a compound statement or, if it contains
any declarations apart from labels, a block. Statements within a compound
statement are separated by semicolons. Strictly speaking, an Algol program
is one statement, because it must start with BEGIN and end with END. A
complete Algol program can be represented thus:
BEGIN s1; s2; a3; .......... sn END FINISH
The statements s1, s2, etc. may be of any type, including compound
statements and blocks. The closing FINISH must be present. It is used by the
compiler to check that there are the same number of BEGINS and ENDs.
For users not familiar with the use of structured programming it is worth
explaining why the 'converted' consider this such an important feature. An
examination of any Algol program reveals the modular nature of its
structure, each block containing specific declarations of the variables,
arrays and procedures relevant to its operation. Many users new to such
languages are at first irritated by the need to declare explicitly every
identifier in a program. While there may be some justification for such
criticism when considering trivial program examples, the advantages become
obvious as the programs become larger and the declarations become a small
percentage of the program. The block in which an identifier is declared
defines the 'scope' of that identifier. Outside that block the identifier
has no meaning and occupies no memory resource. The system thus takes on the
role of resource management. The allocation of memory to variables is done
dynamically as the program is being executed. On entering a block the system
makes available those resources defined in the declarations and upon exit
from the block these resources are reclaimed and made available for other
uses; thus the memory required is always minimised. The penalty in terms of
runtime speed is negligible as most of the organisation is done by the
compiler.
The total declarations throughout the program may in fact be in excess of
the memory of the computer, provided it is not all in scope at once. In
languages such as Fortran, on the other hand, the nearest one can get to
this feature is to work out some cumbersome EQUIVALENCE statements which
probably take longer to define than the corresponding Algol declarations.
Economy of memory can also be achieved using 'dynamic bounds' on array
declarations. The declared bounds can be defined in terms of arithmetic
expressions evaluated at runtime on entering a block. These bounds may be
different each time the block is entered. The size of the arrays can be
chosen to be the smallest that will do the required task.
Because memory is allocated dynamically in this way, it is important that
programs make no assumptions on entering a block about the initial values of
variables declared therein. Between leaving a block and re-entering it, the
memory used by such variables may have been re-used for other purposes and
indeed, in the case of procedures, the variables need not even occupy the
same memory addresses each time the block is entered. If it is important
that a particular variable should preserve its value between leaving and
entering a block then its declaration should be removed to an outer block
such that it remains 'within scope.'
The localisation of the scope of variables to that of the block in which
they are declared also economises on the use of identifiers. The same names
can be declared within several blocks without ambiguity. This also helps
when program segments from various sources are combined without leading to
major problems with conflicting identifiers.
2.10 Blocks and declarations
It has been mentioned already that variables must be declared before being
used and that the presence of such declarations makes a compound statement
into a block. All declarations must be placed immediately after a BEGIN or
another declaration. This does not apply to labels, which are set by placing
an identifier terminated by a colon just in front of the statement to be
labelled. The form of a declaration of a set of unsubscripted variables of
the same type is:
Type ident1, ident2, ......, identn;
'Type' may be REAL, INTEGER, or BOOLEAN. The declarations ident1 etc.
represent the names of the identifiers. Variables may be used within the
block in which they are declared from the point of their declaration up
until the corresponding END. Outside this range they do not exist and are
said to be 'out of scope.' Procedures and labels may be used before
declaration, but the declaration must still be such that they are within
scope at the point they are used. Variables and switches must be declared
before they are used. This is so that the compiler can distinguish them from
functions, which may be used before declaration. This restriction is not
made in full Algol, in which variables may be referenced before declaration
as long as they are within scope. To each identifier used in a given block
there must be a corresponding declaration. It follows that all identifiers
declared in a block must be unique. The same identifiers may however be
re-used within other blocks. Where an identifier is used within an inner
block and also an enclosing outer block, then the inner declaration takes
precedence for the duration of the inner block. The outer declaration is
effectively 'masked out' but its value is preserved and again becomes
accessible on leaving the inner block. Consider the following:
BEGIN REAL x,y,p; INTEGER i,p;
s1; s2;
BEGIN REAL x,z;
s3;
END
END
Statements s1 and s2 cannot refer to z because it is out of scope; s3 cannot
refer to the first x because the inner declaration will take precedence.
When s3 refers to x it will always be the one declared with z, but s3 may
refer to y and to i. The identifier p is declared twice within the same
block and will generate a compile time error.
2.11 Program layout and style
The layout of an Algol program is almost entirely within the hands of the
programmer. Such concepts as line number or column number have no meaning in
Algol. Layout characters such as new lines, spaces, and tabs are generally
ignored except for the following cases:
1. Spaces are significant within strings.
2. Language key words and double character symbols should contain no
embedded layout characters, e.g.
BEGIN
a>=b
a:=b
and not:
BE GIN
a> =b
a: =b
In all other contexts the programmer is free to lay out the program as he
wishes. The resulting text may thus range from the very elegant to the
totally unintelligible. As intelligent layout requires no additional work
and results in programs far easier to follow and modify, users are urged to
develop a good layout style which should reflect the block structure
inherent in an Algol program. Use tab stops to indent text with BEGIN and
END pairs aligned as in the examples given in this manual. Labels should
start on a new line to the left of the program statements. Statements are
separated by semicolons or language key words and not by new line
characters, so that if necessary expressions may extend over several lines
of text. Similarly one line may contain several shorter statements.
Identifiers should reflect the nature of the quantities they represent;
spaces may be included within identifiers if it makes the resulting text
more readable. They will be ignored by the compiler. Frequently used loop
variables and array subscripts are often most conveniently represented by a
single letter identifier, e.g.
end of file:=char=&^Z ;
total:=total+term;
FOR i:=lower STEP interval UNTIL upper DO ....
volume:=height*length*width;
Comments should he included where they make the workings of the program more
understandable and are described later.
2.12 Conditional compilation
There are two new Algol basic symbols: CC and EC. CC stands for Conditional
Code and EC for End Conditional. By default these symbols and all
intervening code are ignored. If the compiler is given the directive D
(diagnostics) then the code between the new symbols is compiled. Because the
code is scanned as basic symbols and not as characters, it should not
contain any unmatched single quote characters.
2.13 Algol program variables
2.14 Data types
The data in the computer memory which may be manipulated by the program is
either numeric or Boolean. Numeric values may be real or integer. The
difference is that integers have no fractional part and occupy 2 bytes of
memory, while real quantities are held in exponent and mantissa form and
occupy 4 bytes. The Algol system converts numbers from one type to the other
whenever necessary. Arithmetic expressions may contain a mixture of real and
integer quantities.
Numerical and Boolean data may appear within the program as literal values.
An integer literal is one which has neither a decimal point nor a decimal
exponent part. Examples are 3, -200, +1234. The range of integers is -32768
to 32767. Real numbers contain either a decimal point or a decimal exponent,
indicated by E or e (depending upon which convention is in use), or both.
Examples are 0.1, -2.345, 1.2E3, 25.7e-7. Real numbers use one byte for the
exponent and three for the mantissa, giving a range of magnitudes from
approximately E-38 to E+38 and between 6 and 7 decimal digits of precision.
Boolean literals are the language key words TRUE and FALSE.
An alternative runtime program ARUNL has 32 bit integer operations instead
of floating point.
2.15 Arrays
Subscripted variables in Algol are known as arrays. Real, integer, and
Boolean variables may be subscripted, while byte variables must be
subscripted. Like other variables, arrays are declared at the start of a
block, REAL ARRAY and ARRAY are equivalent. The declaration of an array with
one subscript has the form:
ARRAY ident [ae:ae];
The ae's represent arithmetic expressions defining the bounds of the
subscript. Either or both bounds may be negative or zero, as long as the
second one is not lower than the first. Real ae's will be rounded towards
the nearest integer. If there is more than one subscript, the bound pairs
are separated by commas; there is no limit to the number of subscripts other
than the amount of available memory. If more than one array is to have the
same bounds, the bounds need only be specified after the last one. One
declaration may contain any number of array names:
INTEGER ARRAY ia[1:30,1:5] ;
BOOLEAN ARRAY ba1, ba2 [1:n,1:3], ba3 [0:20],
ba4, ba5 [1:2*n] ;
When arrays are declared with variable bounds, care should be taken that the
variables have defined values. This means that they should be declared in an
outer block and have been assigned values. This feature is used to partition
the available storage according to the data.
When an array is used, an arithmetic expression is put in each subscript
position. The following are possible statements, using the examples above.
Conditional expressions are described later.
ia[1,4]:=7;
ia [n,m]:=ia[ia[n,2], ia[n,m]] ;
Finally a complex example to show what is possible
ia[n,3]:=IF ba2[3,1] AND ba3[n] THEN
ia[ia[3*n,1], IF ba3[0] THEN 2 ELSE n]
ELSE a;
2.16 Array memory layout and bound checking
The elements of an array occupy a contiguous region of memory. In
multidimensional arrays the last subscript varies most rapidly. For example,
consider a two dimensional array declared as
a[m:n, p:q]
The address of element s[i,j] would be given by an equation of the form
base + size * ((i-m)*(q-p+1)+(j-p))
where size is 1, 2 or 4 depending on the type of the array (byte, integer,
or real). At runtime a check is made that the address computed lies within
the limits allocated for the storage of the array. Note that for
multidimensional arrays this does not necessarily mean that all subscripts
lie within the declared bounds. For example, the element a[-1,15] would he
acceptable for an array declared as a[0:9, 0:9].
Boolean arrays are stored 8 bits per byte.
2.17 Byte arrays
The introduction of BYTE arrays in Rogalgol is an extension to the language
as defined in the Algol 60 Report. BYTE arrays allow for the efficient use
of memory when string manipulation or small integer values are required.
Within expressions BYTE array elements are treated as type INTEGER, and may
be used in any context where am integer is allowed.
Within expressions the contents of a byte array element become the integer
value, and the 8 most significant bits of the integer are set to zero. Thus:
i:=b[n] ;
will always yield a positive value for i in the range 0 to +255.
When assigning to a byte array element, the expression on the right hand
side is first converted to type integer if necessary, the eight least
significant bits of which are then assigned to the byte array element, the 8
most significant bits being discarded without any checking. Thus
b[1] := -1;
i := b[1] ;
will assign to i the value +255. The only time the compiler distinguishes
between an INTEGER ARRAY and a BYTE ARRAY is at the declaration. In all
other contexts the two may be used interchangeably. In particular, a formal
procedure parameter specified as type INTEGER ARRAY or BYTE ARRAY will
accept either as the actual parameter, e.g.,
BEGIN INTEGER ARRAY i[0:100];
BYTE ARRAY b[0:100];
PROCEDURE xx(a); INTEGER ARRAY a;
BEGIN ...
END;
xx(i); xx(b);
will be accepted.
2.18 Expressions
2.19 Simple expressions
An expression is a section of program which delivers a result. The result
may be of type REAL, INTEGER, BOOLEAN, LABEL or (an extension to Algol 60)
the address of a procedure.
The result of a numerical expression is real unless all the variables and
literals within it are integer and it contains no real operators. The real
operators are exponentiation (denoted by ^) and real division %. Arithmetic
expressions are evaluated with due regard to operator priority, and from
left to right where these are equal. Parentheses may be used to change the
order of evaluation. The following is a list of tho arithmetic and logical
operators together with their priorities,
Operator with their priority and meaning
(highest priority)
^ 3 exponentiation
* 2 multiplication
/ 2 real division
% 2 integer division
MOD 2 integer modulus
+ 1 addition
- 1 subtraction
MASK 1 logical AND
DIFFER 1 logical EXCLUSIVE OR
! 1 logical OR
(lowest priority)
The operators MOD, MASK, DIFFER, and ! are additional to those defined in
the Algol 60 Report.
The operators %, MOD, MASK, DIFFER, and ! take two integer operands and
deliver an integer result. The result of integer division % is truncated
towards zero. The result of integer modulus (MOD) is the remainder lost by
integer division. Note that
i MOD 0
will always return the value zero while
i%0 or x/0
will give a division by zero runtime error.
The logical operators MASK, DIFFER and | consider each of the two integer
arguments as a pattern of 16 bits thus:
3 ! 5 = 7
3 MASK 5 = 1
3 DIFFER 5 = 6
Apart from the cases just discussed, expressions may contain any mixture of
real and integer quantities, and conversion between types will occur
automatically as context dictates. For example
BEGIN REAL x,y;
INTEGER i,j;
...
i:=x*y; x:=x*i; i:=x^y;
i:=x^i; x:=i%j;
are all valid operations.
In conversion from real to integer the result is rounded towards the nearest
integer value.
Boolean expressions are made up of Boolean variables, the literals TRUE and
FALSE, arithmetic relations, Boolean procedures, and Boolean operators. The
Boolean operators are in order of precedence from highest to lowest
priorities.
NOT NOT b: Is FALSE if b is true and TRUE if b is FALSE.
AND b AND c: Is TRUE if both b and c are TRUE otherwise it is
FALSE.
OR b OR c: Is TRUE if either b or 0 is TRUE (regardless of
the other) otherwise it is FALSE.
IMPLIES b IMPLIES c: Is FALSE only if b is TRUE and a is FALSE,
otherwise it is TRUE.
EQUIVALENT b EQUIVALENT c: Is TRUE if b and c have the same truth
value and FALSE otherwise.
The relational operators are:
= equals
> greater than
>= greater than or equal
< less than
<= less than or equal
# not equal
Relational operators are dyadic. Care should be taken with the use of = and
# where either argument is of type real. In this case am exact equal occurs
only if both arguments have exactly the same bit pattern, which in the
context of real quantities involving rounding may not be very meaningful. A
more sensible test to make may be to check whether the absolute difference
is less than (or greater than) some small quantity, e.g.
replace IF x=y THEN ....
with IF abs(x-y) < 0.0001 THEN
As with arithmetic expressions, the order of evaluation may be changed by
the use of parentheses to group terms. Examples of Boolean expressions are:
NOT x < 5 OR bv1 AND (y = 0 OR bv2)
x # (y - 5)
+(x-5)^2 <= 20
where x and y are numeric variables or procedures and bv1 and bv2 are
Boolean variables or procedures.
Note the plus sign before the parentheses in the last example. If this had
been absent, the compiler would have assumed that the bracket enclosed a
Boolean expression and would have indicated an error on finding the closing
bracket instead of the expected relational operator. In the first example
the brackets do enclose a Boolean expression. (This way of forcing the
compiler to recognize a bracketed arithmetic expression within a Boolean
expression is not necessary in full Algol 60). The Rogalgol compiler can
deal with expressions such as x - 5 >= 20 correctly because x must be
numeric. Similarly, in the second example, the expression after the # must
be numeric, so the compiler does not need a plus sign. In this case the
brackets are not essential either.
2.20 Conditional expressions
A conditional expression is one that takes one of several values depending
on the result of one or more Boolean expressions. The general form is:
IF be THEN se ELSE e
where be stands for Boolean expression (which may itself be conditional),
se stands for simple expression, which must not be conditional, and e for
any expression. Because e may also be conditional the form can be extended:
IF be THEN se ELSE IF be THEN se ELSE e
The expressions must all be numeric, all Boolean or all designational (these
are described later). A conditional expression is made unconditional by
enclosing it in round brackets. The following is legal Algol:
IF IF bv1 THEN x#3 ELSE y=0 THEN
(IF bv2 THEN 25 ELSE 30) ELSE x+y
The first be is conditional. The ae (arithmetic expression) within brackets
is conditional and would not be allowed in that context without its
brackets. It is a general rule of Algol that IF must not follow immediately
after THEN. This is because it can result in ambiguous code. Examples of
conditional expressions are:
a:=IF a>0 THEN a*a ELSE 0;
large:=IF a>b THEN a ELSE b;
max:=IF a>=b AND a>=c THEN a
ELSE IF b>=a AND b>=c THEN b ELSE c;
a:=IF IF x>0 THEN y>0 ELSE y<50 THEN 3*x ELSE 0;
2.21 Statements
2.22 Conditional statements
These have the same form as conditional expressions, except that it is not
necessary for there to be an ELSE part. There are consequently two forms of
conditional statement:
IF be THEN s1
IF be THEN s1 ELSE s2
The statement s1 must not be conditional but s2 may be. In the first case no
statements are obeyed if the be delivers a FALSE result. In the second case
s1 is obeyed if the result is TRUE; otherwise s2 is obeyed. As s2 may be
conditional this form can be extended indefinitely:
IF be1 THEN s1 ELSE IF be2 THEN s2 ELSE s3
Just as expressions are made unconditional by enclosing them in round
brackets, so statements are unconditional if they are enclosed in the
brackets BEGIN and END. Examples of conditional statements are:
IF a>0 THEN sum:=sum+a;
IF char=&^Z THEN close(dev);
IF samp>max THEN max:=samp
ELSE IF samp<min THEN min:=samp;
IF a>b AND c>d THEN
BEGIN ...
END ELSE
BEGIN ...
END;
2.23 Assignment statements
The general form is:
variable:=expression
The variable on the left hand side is assigned the value of the expression
on the right. Note that in Algol the assignment operator is the double
character symbol ':=' and not the equal sign '=' which is a relational
operator. If the variable is Boolean, the expression must also be Boolean.
If the variable is numeric, the expression may deliver an integer or a real
result; it will he converted to the type of the variable if necessary, real
numbers being rounded towards the nearest integer. Examples:
i := 3+j ;
x := IF be THEN J%5 ELSE 36.75;
bv := i%7
The full Algol-60 multiple assignment syntax is supported. All array
subscripts are evaluated left to right first, then the value of the right
hand side (after the last ':=') and finally this value is assigned to all
the variables to the left of a ':='. All variables assigned to must be of
the same type: INTEGER, BOOLEAN or REAL.
Examples:
a := array[i,j] := b := c := (100 * x);
bool1 := bool2 := i = j;
2.24 FOR statements
A FOR statement allows the repeated execution of a statement with different
values of a variable known as the controlled variable. The general form is:
FOR variable:=fle, fle, ... fle DO s1
The statement s1 may be conditional. The controlled variable must be real or
integer and not subscripted. (In full Algol 60 subscripted variables are
allowed.) fle stands for "for list element". Lists containing only one
element are allowed. There are three types of for list element: (1) an
arithmetic expression (2) a STEP element and (3) a WHILE element.
A STEP element has the form 'ae STEP ae2 UNTIL ae3'. After each execution of
the controlled statement the value ae2 is added to the variable. Before each
execution of s1, including the first, the variable is tested against ae3. If
it is greater than ae3 and ae2 is positive, or less than ae3 when ae2 is
negative, then the element is said to be exhausted. It should be noted that
ae2 and ae3 are evaluated each time they are used, so that the value may be
changed by the execution of the controlled statement. A STEP element may
result in the statement not being executed at all, far example if ae>ae3 and
ae2>0.
A WHILE element has the form 'ae WHILE be'. On each iteration the arithmetic
expression is evaluated and assigned to the variable. The Boolean expression
is then evaluated and if the result is TRUE the statement is executed,
otherwise the element is exhausted. Examples of FOR statements are;
FOR i:=min STEP 1 UNTIL max DO sum:=sum+s[i];
FOR i;=1 STEP i UNTIL 1024 DO ....
FOR i:=1, 3, 99, j, -6, 11 DO ....
FOR x:=1, x*2 WHILE x<1O25 DO ....
FOR i:=100 STEP -1 UNTIL -100 DO
FOR x:=0.1, 1, x*5 WHILE x<1000, 20 STEP -5 UNTIL 0 DO..
FOR loops can be nested as deeply as desired. For example:
FOR 1:=1 STEP 1 UNTIL max DO
FOR j:=1 STEP 1 UNTIL i DO a:=a+b[i+j]^2;
Matrix multiplication might look like:
FOR i:=1 STEP 1 UNTIL m DO
FOR j:=1 STEP 1 UNTIL n DO
BEGIN x:=0;
FOR k:=i STEP I UNTIL p DO x:=x+a[i,k]*b[[k,4l;
c[i,i]:=x;
END;
The body of the FOR loop may be a dummy statement, for example to skip to
the start of a new line.
FOR i:=chin(dev) WHILE i#*C DO ;
The loop variable may also be a dummy;
FOR i:=0 WHILE test DO body ;
In this case body may be a procedure or block which sets a Boolean variable
test, or test could itself be a Boolean procedure. It would be preferable to
use a WHILE statement in this case.
FOR i:=body1 WHILE test DO body2 ;
In this example body1 could be an integer or real procedure, test a Boolean
procedure, and body2 a procedure or black, giving several possibilities for
loop construction.
A FOR loop is not a simple statement and cannot be called following the THEN
part of a conditional statement unless enclosed within a BEGIN and END. It
may however follow the ELSE clause without needing an enclosing BEGIN and
END.
2.25 The CASE statement
This allows the execution of just one of a number of statements, depending
on the value of an arithmetic expression. The statements are labelled by one
or more positive integer labels. The result of the selecting arithmetic
expression is rounded down to an integer. The statement is terminated by an
END, or by ELSE <Statement>;. If none of the statements is selected and the
terminator is END, program execution continues with the next statement; if
terminated by ELSE then the <Statement> is obeyed. Note that there is no ';'
before the END or ELSE.
Example:
CASE i+j OF
0: x := 3.4 ;
3:4: BEGIN x := 3; y := 4; END;
10: y := 10
END end comment;
CASE n OF
2: GOTO fred;
1234: GOTO jim
ELSE GOTO bert;
2.26 The WHILE statement
The syntax is:
WHILE <Boolean expression> DO <Statement>;
The <Statement> may be any type. The Boolean expression is first evaluated
and if true the statement is executed. This process is repeated until the
Boolean expression is false, when program execution continues at the next
statement.
2.27 The REPEAT statement
The syntax is:
REPEAT <Statement> (; <Statement>) UNTIL <Boolean expression>;
The syntax is not usual for Algol; it has been made identical to the Pascal
REPEAT statement. (Algol would require a BEGAN after the REPEAT and an END
before the UNTIL if more than one statement were to be repeated.) The
statement or statements are obeyed at least once. They are obeyed
repetitively until the Boolean expression is true, when execution continues
with the next statement.
2.28 Dummy statements
A dummy statement is one in which there is nothing before the terminating
END, ELSE or semi-colon. Examples are:
BEGIN END
IF be THEN ;
; ;
BEGIN s1; END
IF be THEN ELSE ;
PROCEDURE dummy; ;
2.29 Comments
Rogalgol allows three types of comment. Any symbols appearing after an END
until the first occurrence of semicolon, FINISH, END, or ELSE are ignored.
These are known as END comments, e.g.
END this is ignored;
END so is this ELSE
END and this also FINISH
The other form is
COMMENT any sequence not containing semicolon;
This form is allowed after a semi-colon or after a BEGIN. Within comments
single quotes must be matched.
An alternative to using the key word COMMENT is to enclose text within
braces. This is an extension to the Algol 60 Report. Such comments may
contain embedded matching braces, or embedded unmatched single quotes.
This form of comment may be used anywhere that COMMENT may be used. e.g.
{this {comment} is ignored}
Full Algol 60 also allows an additional type of comment within procedure
calls and declarations.
2.30 Labels, switches and GOTO statements
Any statement may be labelled by preceding it with an identifier and a
colon. The scope of the label is the block in which it occurs. Program
control is transferred to a labelled statement by a GOTO statement.
BEGIN REAL x;
s1; s2;
GOTO lab; s3;
lab: s4
END
The following is not allowed because the label is not within scope:
GOTO lab;
BEGIN REAL x; s1; s2;
lab: s3
END
Labels in an outer block may however be accessed from within an inner block,
e.g.
BEGIN REAL q;
lab: s1; s2;
BEGIN REAL y;
s3; GOTO lab;
END
END
It should be noted that a compound statement does not become a block because
there is a labelled statement within it. The second example would have been
allowed but for the declaration of x. For the same reason labels in
different compound statements but within the same block must have different
names.
A switch is a list of labels declared at the start of a block. All the
labels must be within scope at the declaration.
SWITCH s:=lab1, lab2. lab3;
The simplest use of a switch is in a GOTO statement
GOTO s[ae]
The ae is evaluated and is used as an index to the list of labels in the
declaration. If for example the ae has the value 2, the effect of the
statement is the same as GOTO Lab2. If the value of ac is either less than
1 or greater than the number of labels in the declaration, then the effect
is that the statement is treated as a dummy statement. Example:
BEGIN SWITCH sw:=case1, case2, case3;
try: text(1,"casenunber=");
GOTO sw[chin(1)-&0] ;
text(1,"*Nillegal value"); GOTO try;
case1: ....
case2: ....
case3: ....
2.31 Designational expressions
Designational expressions are like arithmetic or Boolean expressions. In a
designational expression the elements may be labels or switch elements. The
full definition of a GOTO statement is:
GOTO de
where de stands for designational expression. An example:
GOTO IF x=0 THEN lab1 ELSE IF b THEN s[i+3] ELSE lab2
Designational expressions may also result in the address of a procedure, as
will be described in the section on procedure parameters. This is an
extension of Algol 60, in which designational expressions are only allowed
for labels.
2.32 Procedures
2.33 Standard functions
sin(x) x is in radians
cos(x) x is in radians
arctan(x) the result is in radians in the range -pi/2 to +pi/2.
ln(x) natural logarithm
exp(x) e to the power x
sqrt(x) square root of x
abs(x) absolute value of x
sign(x) delivers -1, 0, or +1 according to whether x is negative,
zero or positive.
entier(x) returns the largest integer less or equal to x. Thus if x
= 3.3 the result is 3. If x = -3.3 the result is -4.
Note: As the result is integer the value of x must lie within the valid
integer range of -32768 to 32767.
In each of the above procedures x is called by VALUE and thus the actual
parameter may be an expression.
2.34 Operating system identification
INTEGER PROCEDURE opsysidcode(n) is a new pre-declared identifier. If n = 0
the value returned is as follows
1 if running under CP/M-80
2 if running under CP/M-86
3 if running under MSDOS or PC-DOS
If n is non-zero the version number word is returned. This word has a
different format under CP/M or MSDOS.
2.35 Procedures without parameters
A procedure is a statement which is declared at the start of a block, but it
is not executed when the block is entered. It is given an identifier and the
appearance of its name causes the statement to be executed. The simplest
type of procedure has no parameters and does not deliver a result.
BEGIN
PROCEDURE dothis; stat;
s1; s2; dothis; s3; dothis
END
The statement stat is executed when dothis appears in the program. It is
known as the body of the procedure. Even if stat has the form of a compound
statement or a single statement, it is treated as a block. This is to
prevent GOTO statements from leading into a procedure which is not active.
A procedure may deliver a result of type REAL, INTEGER, or BOOLEAN. Such a
procedure is known as a 'type procedure' or function. Its name can then be
used in expressions, which will cause the procedure to be executed and the
result to be used in evaluating the expression. Within the procedure body
the value which will be returned is set by assigning it to the name of the
procedure. Such an assignment statement may occur anywhere within the
procedure body and there can be any number of them. Execution of the
procedure continues until either the end is reached or a GOTO leads out of
it. If the name of the procedure occurs within the procedure body itself,
except on the left of an assignment as just explained, then the procedure
will call itself and is said to be recursive. The following example
illustrates several points:
BEGIN INTEGER i;
INTEGER PROCEDURE j;
IF i<0 THEN GOTO nogood
ELSE IF i=1 THEN j:=0 ELSE
BEGIN i:=i-1;
j:=j+1
END procedure j;
i:=10; i:=j;
nogood:
END
FINISH
The procedure refers to variable i which is declared in the main program in
the same block as the procedure. The declaration of i must come first or the
compiler would have assumed that it referred to an as yet undeclared
procedure of type Boolean (lines 3 and 4) or integer (line 6). Line 5 would
have failed because the identifier to the left of := must be already
declared. There must always be some condition which causes a recursive
procedure to deliver a result or exit without recursing, as on lines 3 and
4. If this had not been done, or if i had not been decremented on line 5,
the procedure would have called itself until the available storage was used
up. The label nogood is attached to a dummy statement. Note also that the
body of the procedure does not have to be enclosed by BEGIN and END; in this
case it is a conditional statement.
2.36 Procedures with parameters
The action of a procedure can be made to depend upon data supplied to it
through a list of parameters at the time it is called. The procedure
declaration contains a list of formal parameters. These are the names which
are used within the body of the procedure. The type of each formal parameter
is given in a specification, which looks rather like a set of unsubscripted
variable declarations. The list of formal parameters is enclosed in round
brackets and is placed immediately after the name of the procedure. The
identifiers are separated by commas. Only the names are given, not
subscripts or procedure parameters. For example:
REAL PROCEDURE p(x,y,a,r,lab);
VALUE y; REAL x,y;
REAL ARRAY a;
REAL PROCEDURE r;
LABEL lab;
In full Algol 60 a more complicated type of parameter separator (the 'fat
comma') is also allowed.
2.37 Numeric and Boolean parameters by value
This is the simplest type of parameter. When the procedure is called the
actual parameters are evaluated and the value is passed to the procedure.
Within the procedure body a parameter called by value acts in every way like
a variable declared within the procedure, except that it is assigned an
initial value when the procedure is entered. The value may be changed within
the procedure but the new value is not accessible once the procedure has
finished.
PROCEDURE p(i,x,b); VALUE x,b,i;
REAL x; BOOLEAN b; INTEGER i;
BEGIN IF b THEN s:=x+i ELSE a:=x-i;
x:=2*i; a:=x+i;
END
The variable 'a' has been previously declared outside the procedure. Note
that the VALUE specification must come before the part specifying the types
of the parameters. The Rogalgol system converts between INTEGER and REAL if
the types of the actual and formal parameters are not the same. No other
type conversions are allowed. A possible call of this procedure is:
p(5.32, 2.5*y, z>0)
where x and z are numeric variables, the last parameter is Boolean by value.
It is a restriction is that a Boolean expression or variable should not be
enclosed in brackets when used as a procedure parameter. The brackets are
redundant, so the restriction is not a real one. This is due to the compiler
making only one pass through the source code.
2.38 Variables called by name
Any formal parameter which is not specified to be VALUE is said to be called
by NAME. Instead of a value being passed to the procedure, the address of
the variable is transmitted. It follows that the actual parameter must be
the name of a variable of the correct type. (In full Algol 60 an expression
is allowed and the address of a routine to evaluate it is transmitted.) When
a variable called by name is assigned to within the procedure body the
variable specified in the call is changed. Thus, variables called by name do
not act like locally declared variables. The formal name stands for the
actual name used in the call. The actual parameter is brought within the
scope of the procedure body. In Rogalgol, array parameters must be called by
name. (The full Algol 60 call of array names by value involves making a
local copy of the whole array.) The actual parameter is the name of the
array, without subscripts. Within the procedure body the formal array name
is used with subscripts. There must be the same number of subscripts as in
the original declaration of the array whose name was used as a parameter.
Array elements (an array name followed by subscripts) may be used within or
as an actual parameter, but only when the formal parameter is by value. This
compiler accepts the use of BYTE ARRAY and INTEGER ARRAY interchangeably in
procedure calls. (See the section on byte arrays).
Objects called by name may be passed on from procedure to procedure through
the parameter list. Unsubscripted variables called by name may be used as
the controlled variable in a FOR statement.
BEGIN ARRAY ar[1:20];
REAL x; INTEGER i;
REAL PROCEDURE rp: rp:=i*2+2;
PROCEDURE p(a,k,z); VALUE a;
REAL a; INTEGER k; ARRAY z;
FOR k:=1,2 DO z[k]:=rp*a;
ar[1]:=10;
p(ar[l],i,ar) ;
END
FINISH
When procedure p is called, the value of the parameter a is initialised to
be 10.0 and k within the procedure becomes equivalent to i in the main
program. When rp is called for the first time, i has the value 1 so the
result of tire expression which is assigned to rp is 3.0. The value of
3.0*10.0 is assigned to z[1], which means that ar[i] becomes 30.0. Next
time, when i=2, tire value of rp is 6.0 but a still has the value 10.0 so
the effect of the statement is ar[2]:=60.0.
2.39 String and switch procedure parameters
When the formal parameter is a string, the actual parameter may be either a
string of characters enclosed in double quote marks or, if the call is
within a procedure having a string parameter, the name of such a string.
String parameters cam only be used as actual parameters in further procedure
calls, so it follows that the information in the string (as opposed to the
address of the string, which is the information transmitted to the called
procedure) can be used only by machine code called from within the
procedure. This may be done by using the pre-declared procedure
text(device,string), or string handling procedures in ALIB.ALG e.g.
PROCEDURE moan(message,num);
VALUE num; INTEGER num; STRING message;
BEGIN text(1,"*NError at line ");
write(1,num); text(1,message);
END;
The use of switch parameters is straightforward. The actual parameter is the
name of a switch. When this name is used within the procedure body (with a
subscript) the effect is as if it had been used in the block in which the
switch was declared. However, in Rogalgol (but not in full Algol 60) if the
execution of a parametric switch leads to within a procedure which has been
called recursively, then the return is to the most recent call of the
recursive procedure. If this consideration is important, the Rogalgol
programmer should use several label parameters instead of a switch. Rogalgol
finds the correct incarnation of a recursive procedure if it is jumped into
through a label parameter. A procedure has been called recursively if there
is more than one call in force. If A calls B, B calls C, and C calls A, then
A has been used recursively. In practice this restriction is unlikely to
prove to be a limitation.
2.40 Labels and procedures as parameters
The treatment of parameters of type LABEL, PROCEDURE, REAL PROCEDURE,
INTEGER PROCEDURE and BOOLEAN PROCEDURE is similar. The actual parameter is
a designational expression which in Rogalgol (but not in full Algol 60) must
be preceded by a type specification. This requirement is included so that
the one-pass Rogalgol compiler may allow as yet undeclared procedures and
labels to be used in procedure actual parameters. As with variables called
by name, arrays, and switches, it is the address of the label or procedure
which is passed to the procedure being called. The actual parameter must be
in scope at the point of call but need not be within the scope of the called
procedure; its use as a parameter effectively brings it within scope. If a
procedure body contains a label declared with the same identifier as a
formal parameter of the procedure, then the formal parameter will take
precedence until after the declaration of the label. (In full Algol 60 the
block structure always determines the order of precedence.)
The pre-declared functions and input/output names cannot be used as
procedure parameters, where the formal parameter is a procedure. A dummy
procedure which calls the pre-declared one must be used. This is because the
pre-declared procedures are not treated in the same way as those declared by
the user, in order to shorten compiled programs and to increase the speed of
execution. (In full Algol 60 the pre-declared procedure names can be used in
this way.) The pre-declared procedures can of course be used in expressions
where the formal parameter is a value parameter; sin(cos(3)), for example,
is allowed.
The Rogalgol compiler does not differentiate between name and value calls of
parameters which are switches, strings, labels, and procedures. Where the
actual parameter is a designational expression (only allowed for labels and
procedure types) the value is calculated on procedure entry only, and not
each time the parameter is used within the procedure body. The calls of all
these parameters are therefore by value, although the compiler does not
force the user to specify this.
To illustrate these points, suppose that two procedures have the following
headings:
PROCEDURE p(s,lab,rp,at);
SWITCH s; STRING st; LABEL lab;
REAL PROCEDURE rp;
REAL PROCEDURE x(y,st); VALUE y;
REAL y; STRING st;
A possible call] is
p(sw, LABEL IF be THEN labl ELSE lab2,
REAL PROCEDURE x ,"abc")
A designational expression has been used as an actual parameter of type
LABEL. As with arrays and switches only the name of the real procedure is
used as a parameter. The parameters of the parametric procedure are included
when the procedure is actually called, and not otherwise. A possible call of
x within the body if p is:
rp (rp(3,"DEF"),st)
2.41 Summary of points on procedures
On entering a procedure the memory required is allocated dynamically
according to the declarations. It follows that procedures are intrinsically
recursive in nature, the limit on the depth of recursion being set by the
available memory.
The body of a procedure is a 'statement'; this may range from a simple (even
dummy) statement to a compound statement or block. Within such a block there
may of course be further procedure declarations, so that the following is a
valid structure.
PROCEDURE tom;
BEGIN
PROCEDURE dick;
BEGIN
PROCEDURE harry;
BEGIN
s1; ...
END;
s2; ...
END;
s3; ...
END;
The scope of these procedures follows the normal rules of scoping, so that
statements s1 and s2 may refer to tom, dick or harry; statement s3 may refer
to tom and dick but not harry.
Statements within a procedure may make reference to any variable that is 'in
scope', not just those passed through the parameter list. In Fortran a
COMMON statement would be necessary. It is also possible to jump out of a
procedure by means of a GOTO statement to any label that is within scope.
There is a problem regarding the scoping of procedures in the case where the
user declares a procedure of the same name as one of the pre-declared ones.
This results from the fact that these pre-declared functions are compiled
differently from those declared by the user, to make them faster and to
economise on memory.
BEGIN INTEGER i;
PROCEDURE abc;
BEGIN ...
z:=sin(y);
...
END;
REAL PROCEDURE sin(x);
VALUE x; REAL x;
BEGIN
...
END ;
END
FINISH
According to the Algol 60 Report the scope of the two procedures 'abc' and
'sin' is the block in which they are declared. The statement 'z:=sin(y)' in
the first procedure is referring to the second procedure in the block. In
this compiler however the statement will generate code corresponding to a
built-in procedure identifier that it already knows about. No error message
is given. The problem could be avoided in this case by simply reversing the
order of the two procedures, or better still changing the name so that no
ambiguity can exist. There is no problem with procedures having names
different from the built-in ones. As a general rule all procedure names are
best kept unique amongst themselves and also from the variables.
2.42 Differences from the Algol 60 report
2.43 Differences from the Algol 60 report
The Rogalgol language is Algol 60 with a few restrictions. Some of these are
a result of the one-pass nature of the compiler. For example, variables must
be declared before use. In other possible ambiguous situations that a
multi-pass compiler could resolve, this compiler may require a 'clue' as to
the type of object being translated. These differences are described in the
following sections. A number of extensions to the language have been
introduced. These include the data type BYTE ARRAY, logical and MOD
operators, and a significant number of functions. Appendix 3 gives a summary
of the differences from the Algol 60 Report.
This manual describes the syntax of the language. The distribution kit
includes a number of example programs which are described later. Users new
to structured programming may find it beneficial to refer to an introductory
manual on the language and also to the Algol 60 Report.
2.44 Language restrictions
No OWN variables.
No integer labels except in the CASE statement.
Variables must be declared before use.
Call by NAME is restricted to the case where the actual parameter is a
variable name, i.e. as per call by reference in Fortran or call by location
in CORAL 66.
Array parameters must be called by name.
The controlled variable in a FOR statement must not be subscripted.
The 'fat comma' is not implemented.
Boolean procedure parameters may not be enclosed entirely in round brackets.
An opening bracket enclosing an arithmetic expression at the start of a
Boolean expression must be preceeded by a plus or minus sign.
2.45 Language extensions
Data type: BYTE ARRAY.
Operators: MOD, !, DIFFER, MASK.
Comments may be enclosed within matching braces e.g. {like this}.
Procedure names may be the result of designational expressions.
The CASE, WHILE and REPEAT statements.
Additional functions include string handling, direct disk i/o, block move,
clear array, etc.
2.46 The input/output mechanism
2.47 Stream or device numbers
In Algol the exact form of Input/Output is not strictly defined but left up
to the implementer to make best use of whatever facilities are available.
Input/Output takes place through a series of procedures built into the
runtime system.
Input/Output is device independent and is associated with a stream or device
number, which is the first parameter of all the built in input/output
procedures. These numbers are in turn associated with a device name or a
file name. The usual way of obtaining the stream numbers is to parse a
command line with the procedures INPUT and OUTPUT. For disc files the number
is 64 or greater and is an index to a CP/M file control block. Non
file-structured devices such as the console and printer have lower numbers
and Input/Output can be performed by simply choosing the appropriate device
number.
In the case of disk files some dialogue is necessary to open or create a
specified named file. In this case the corresponding stream numbers are
allocated dynamically by the system. The procedures to perform this dialogue
are described in a later section. In the case of disk files we also have the
choice of serial or random access.
The command line parser maps all other device names to a device number, the
one you can also use directly in the i/o procedures. The parser makes an
additional check that the named device is suitable for the operation (input
or output) requested. For example, the command line CON:=LST: is in error as
LST: cannot be an input device. This will give runtime error 27.
2.48 ALGOL fixed stream numbers
0 Dummy. Output is thrown away, input always returns ^Z.
1 Output goes direct to the console screen via CP/M function 2. Input
comes from the console keyboard one character at a time, but is checked for
(a) ^C which causes a warm boot and (b) carriage return which causes a
linefeed to be echoed as well. CP/M function 1 is used which echoes all
characters typed.
2 PUN: on output and RDR: on input, AUX under MSDOS. Uses CP/M
functions 3 and 4.
3 On output this goes to LST:, PRN under MSDOS, via CP/M function 5.
Used for input it reads the keyboard without echo via CP/M function 6. Zero
is returned If no character is waiting. Under CP/M V1.x ^Z is always
returned.
4 This Is nearly the same as device 1. The only difference is that on
input there is no check for ^C or linefeed.
5 The CP/M reader and punch devices via CP/M functions 3 and 4. The
same as device 2.
6 On output the CP/M list device via CP/M function 5. On input the
current printer column number is returned.
7 Buffered console input with echo. The buffer may be filled either
from the keyboard or by using output device 7. When input is obtained from
the keyboard (for example READ (7)) CP/M function 10 is used. This means
that the CP/M line editing is in operation and nothing can be returned to
the program until RETURN is typed. Linefeed is sent to the program as well.
You can force input to begin afresh by wiping out the buffer contents before
reading from device 7. When used for output, device 7 writes characters to
the input buffer.
8 Used for output it sets the printer column count. Input returns ^Z
always.
9 Dummy (as device 0).
10 I/O directly to memory.
The device numbers 1-7 use calls to CP/M or MSDOS system calls. No BIOS
calls are made.
2.49 Printer position on streams 3 and 6
When output is sent to either of these printer streams a record of the
carriage position is kept. Tabs are assumed to be at intervals of 8 columns.
If the printer uses a different interval the position returned will be
incorrect if any actual tab characters have been sent.
A count is kept of the column number currently under the print head using
the following logic. All characters sent which have the value 32 (space) or
greater increment the count. All lower value codes are ignored except as
follows:
8 (backspace) decrements the count.
9 (tab) steps on to the next multiple of 8.
12 and 13 (form feed and carriage return) zero the count.
The current count is obtained by CHIN(6). Some printers accept escape
sequences containing printing characters but which do not move the carriage.
To correct the count after such a sequence is sent CHOUT(8,N) may be used.
The column count is set to N.
2.50 Device names in command Lines
When a command line is parsed by the procedure INPUT or OUTPUT, device names
are converted to the device numbers previously listed according to the
following table.
Name Device Operations
NL: 0 Input, output
CON: 4 Input, output
RDR: 5 Input
PUN: 5 Output
LST: 6 Output
TI: 1 Input
VT: 1 Output
TTY: 2 Input, output
LP: 3 Output
TIB: 7 Input
KBD: 3 Input (CP/M version 1 returns a negative number)
A: to P: are discs and require a filename. Only A: to D: are allowed under
CP/M version 1. File names may contain a drive letter, but no path.
Switch options recognized by the parser are:
B Block i/o (random access).
M Modify access (random access write).
2.51 Creating a command line for I/O streams
The Rogalgol system allows the user the ability to select input/output files
or devices from within the program or from the console keyboard. For this
purpose there exists a buffer into which a command line containing I/O
selections is placed. The placement is achieved either through I/O stream
number 7, or through a number of calls to ioc() which create a console
prompt. The basic sequence of events consists of:
1. Place an I/O selection string into the buffer.
2. Call a command string interpreter to read the contents of the buffer and
copy the string into an 'input list' and/or 'output list' as appropriate.
3. A call of predeclared procedures 'input' or 'output' reads the next entry
in the 'input list' or 'output list' and returns to the program the
appropriate stream number, having opened or created any necessary files.
Input from stream 7 is buffered and only made available to the program when
a carriage return character is entered. Incorrect characters can he removed
using the rubout key. There are two pointers associated with stream 7, one
with input and the other output. As characters are entered or read from the
buffer the appropriate pointer is advanced by 1. These pointers may be reset
using the following ioc calls.
ioc(0) Reset the input pointer. The next call of chin(7) will return the
value of the first character in the buffer.
ioc(1) Reset output pointer and write a string terminator into the first
buffer position. The next call of chout(7,char) will place the value of char
into the first position of the buffer and advance the position of the string
terminator. Note that following the use of ioc(2) through ioc(5) described
below, before reading from stream 7 the programmer should issue both an
ioc(0) and an ioc(1) to reset the pointer and wipe out the current buffer
contents.
ioc(2) This produces a prompt on the console of the form:
OUT=IN?
The user than enters a command string of the general form:
outputlist=inputlist <cr>
When the carriage return <cr> character is given to terminate the command
line the command string interpreter is called. Every character up to the
separating equal sign (or carriage return if no equal sign is present) is
copied and stored as the current 'output list' and everything after the
equal sign is copied and stored as the current 'input list'. A pointer is
associated with each of these lists and if a new input or output list entry
is found then the corresponding pointer is reset to the start of that list.
The detailed form of these lists is described later.
ioc(3) This is similar to ioc(2) but the text is taken directly from the
contents of the buffer without any user prompt. A typical calling sequence
to set up an input/output list might be:
ioc(1); text(7,"outputlist=inputlist"); ioc(3);
ioc(4) This produces a prompt on the console of the form:
INPUT=
The user then enters a command string of the general form:
inputlist <cr>
This string then becomes the current 'input list', the output list remaining
unchanged.
ioc(5) This is similar to ioc(4) but the text is taken directly from the
contents of the buffer without any user prompt. A typical calling sequence
might be:
ioc(1); text(7,"inputlist"); ioc(5);
Note: A call of ioc(3) or ioc(5) leaves the contents of the buffer
unaffected. The same string may if desired be parsed twice to set up both
input and output files of the same names. This is done within the compiler
to select its input and output.
The general form of the input and output lists consists of a sequence of one
or more device or file specifications separated by commas e.g.
CON:,A:OUT1,,LST:=DATA.DAT[B],RDR:
In the above example 4 output channels and 2 input channels are specified. A
call of the pre-declared procedures input or output (described later) will
scan the appropriate list from the current position up to the next
occurrence of a comma or end of list indicator. A stream number will be
returned corresponding to the entry found.
A CP/M file specification is of the general form:
<DRIVE>FILENAME.EXT
The characters recognized within file names are letters, digits, "$" and
'?'; the latter should be reserved for specifying ambiguous file names.
Lower case letters are converted to upper case as per the normal CP/M
convention. All characters less than apace (ASCII 0 to 32) are ignored
within I/O lists.
The FILENAME consists of from I to 8 characters. The file extension '.EXT'
if present consists of from 1 to 3 characters. If no extension is given a
default value will be assumed; this is initially set to three spaces. The
method of changing the default file extension is described under library
procedure 'swlist' in the section 'Input/Output directly to or from memory'.
It is possible to force the use of the default file extension regardless of
what is given by the call ioc(20)
In order to return to the default situation where a specified file extension
takes precedence call ioc(21)
<DRIVE> consists of one of A:, B:, C;, D: or may be omitted. When used with
CP/M version 2 disk drive names extending from A: to P: are accepted. If
omitted a default is assumed according to the following rules. At the start
of each line the assumed drive is the 'logged on drive' when the program is
first entered. Any subsequent drive specified within the list then becomes
the default for following entries.
Switch options may be added to any input/output device or file specification
and consists of a series of up to 12 characters enclosed within square
brackets. Lower case letters are converted to upper case. Switch options
must not contain a comma or equal sign. Certain switches are recognized by
the runtime system and acted upon; in the example given above the input file
DATA.DAT[[B] the switch [B] causes the file to be opened for 'random access'
reading. Other switches not used by the system may be used by the program. A
facility exists for the program to read the switch list directly.
The occurrence of two adjacent commas within an I/O list is equivalent to
specifying the 'null' input/output device NL: (stream 0).
2.52 Parsing the I/O command line
The ioc calls described in the previous topic will have set up input/output
lists. These lists may now be used to assign files or devices through the
predeclared procedures INPUT and OUTPUT.
dev:=input;
will read the next entry in the 'input list'. If the entry is found to be a
device then dev will be assigned a value corresponding to that device name.
If a disk file was specified then that file will be opened. A buffer region
will be allocated to contain the file control block and sector buffer (if
serial access). The stream number returned will be from 64 upwards, the
actual value indicating which buffer is allocated to that file.
A negative value for dev indicates an error, e.g. bad syntax, no entry found
in input list or no file found of that name.
dev:=output;
Similar to input but for output files or devices. A number of options exist
regarding what action is to be taken if an output file name specified is
found already to exist. These options are selected by calls to ioc which set
the appropriate flags within the runtime system. The first is the default
case.
ioc(13) No checks are made. A second file of the same name will be created.
A problem may be encountered later on trying to access such files.
ioc(14) The existing file of the name specified will be deleted before the
new file is created.
ioc(15) If a file name is found already to exist, the call of output will
return a stream number of -100. No new file is created.
2.53 Input/output directly to or from memory
As an aid to text processing and related manipulation, e.g. setting up file
extensions or reading the switch list a facility exists to read or write
using the standard input output routines directly to or from anywhere in
memory. Such i/o is associated with stream number 10. A number of string
handling routines relevant to the following are described in the section on
"library procedures'. Before i/o can be performed via memory it is necessary
for the user to set up pointers to where input/output is to occur. As each
character is read/written the corresponding pointer is advanced by one. The
following procedures to manipulate these pointers are in ALIB.ALG.
seti(a)
Set the INPUT pointer to the address a.
seto(a)
Set the OUTPUT pointer to the address a.
In practice a call of location would probably have been used to find the
address. In order to find the current values of the input/output pointers:
i:=ipoint
Returns in i the current address of the input pointer.
i:=opoint
Returns in i the current address of the output pointer.
A typical sequence might be;
BEGIN BYTE ARRAY buf[0:1000];
seto(location(buf[0]);
seti(location(buf[0]);
rwrite(l0,x,0,6);
i:=opoint;
x:-read(10);
It is the user's responsibility to ensure that such I/O stays within the
declared bounds of the array buffer used.
2.54 Switch lists on I/O selections
The memory I/O feature described in the previous topic is used to gain
access to the switch lists associated with I/O streams.
i:=swlist
Returns in i the address of the switch list.
The user can check if any switch options have been specified following a
call of "input" or "output" by reading the contents of this switch list.
These switches (a maximum of 12 characters) can be read using input stream
10. A typical sequence might be:
seti(swlist);
i:=chin(10);
The first switch is now in i. The list is terminated with a zero value. The
switch list always contains information relevant to the most recent call of
the procedures "input" or "output".
The default file extension is stored in the 3 bytes following the switch
list. This can conveniently be set up by writing 3 (and only 3) characters
into the appropriate buffer by means of output to stream 10, e.g.
seto(swlist+13);
text (10, "XYZ");
This sequence will set the default file extension to XYZ. On entry the
default extension is set to null, i.e. 3 spaces.
This technique can also be used as a way of reading small quantities of data
in a manner similar to the DATA statement of BASIC, e.g.
seti(sloc("1.32 99.6 ... ")) ;
FOR i:= 1 STEP I UNTIL 20 DO x[i] := read(10);
The procedure sloc is described in the section on library procedures.
Another example involving text can be found in the program VDU.ALG on the
distribution disk.
2.55 Closing and deleting files
When the use of a file is completed it should be closed by a call of the
predeclared procedure:
close(dev)
This will close the file associated with stream dev by a previous input or
output call. If dev does not correspond to a disk file, nothing happens.
NOTE: If an OUTPUT FILE is not closed its contents will be LOST. Input files
should also be closed, as this call also serves to release the buffer and
file control block associated with that file and makes it available for
further use.
delete(dev)
This will delete the file associated with dev by a previous input or output
call and release the file control block and buffer for reuse.
2.56 Serial input/output procedures
In all of the predeclared i/o procedures the first parameter is the stream
number denoted by dev. The name val indicates a REAL variable and ival an
INTEGER variable. As the formal parameters are called by VALUE the actual
parameters may contain expressions; the system will convert between integer
and real values if necessary.
PROCEDURE skip(dev) ;
Outputs a carriage return/linefeed to dev.
INTEGER PROCEDURE chin(dev);
Read the next character from dev. The result of the procedure is the value
of the character. In the case of disk input the character CONTROL-Z is
returned at the end of file.
REAL PROCEDURE read(dev); or REAL PROCEDURE read(dev,lahel);
Read a floating point number or integer number from dev. The number is in
free format, and is terminated by any character which cannot be part of a
number. Decimal exponentiation is indicated by 'E'. Spaces, tabs and blank
lines preceding the number are ignored but other characters will give an
error. A space will terminate the number except between the 'E' and the
exponent field. Integers may be read without rounding errors provided they
appear as valid integers in the input, i.e., without decimal point or
exponent parts. To allow the possibility of reading a file of unknown
length, the second form given above may be used. In the event of passing the
end of file, control is passed to the label. The name is not preceded by the
LABEL indication as the compiler knows that the second parameter must be a
label or a designational expression. End of file is a legal terminator; the
jump will not happen unless another read is done. If the optional label is
not given a runtime error occurs if end of file is passed. Examples of valid
number formats are:
0.123 +1.23E -3 -123
The read routine will also accept the following, although the output
routines never generate such formats:
E-3 .123 -123.
It may be desirable to read a data source containing text comments. The read
routine can be instructed to ignore any character preceding the number which
cannot be part of the number by the call:
ioc(18);
A consequence of the use of this mode of reading data is that numbers of the
form:
E-6 or .45
are no longer valid. The leading 'E' or '.' is regarded as comment; the
actual numbers read in this case would be -6 and 45. To return to the
default mode where comments are not permitted call:
ioc(19);
PROCEDURE text(dev,"string");
Output a string to dev. See the section on strings regarding interpretation
of format and control characters. The string may also be a string parameter
of the procedure in which text is called, in which case the actual parameter
is the string identifier, e.g.
PROCEDURE message(s); STRING a;
BEGIN text(1,s); ...
PROCEDURE chout(dev,ival);
Outputs a single byte to dev. If a character is to be output, its ASCII
value must be used. This can be found by using the character literal
facility. For example:
chout(1,&X);
will print X on the terminal.
PROCEDURE write(dev,ivai); or PROCEDURE write(dev,ival,radix);
Prints ival as an integer on dev. The default radix is decimal.
Non-significant characters are not printed. If formatted print is required
use rwrite. Output in octal or hexadecimal is possible by including the
optional third parameter.
radix=0 for decimal
radix=1 for octal
radix=2 for hexadecimal
Any other value for radix will lead to a runtime error.
END OF FILE REPORTING
The CHIN function returns -1 at end of file, allowing embedded CTRL/Z to be
processed. Text files normally have a CTRL/Z at the end, unless the last
record is exactly filled.
2.57 Formatted number output
PROCEDURE rwrite(dev,val,a,b); or PROCEDURE rwrite(dev,val)
Floating point output to dev; val is the value to output; a and b define the
format such that:
a = total number of characters including sign and decimal point.
b = number of digits after the decimal point.
If b is zero then we have formatted integer output. If the value of a is
inconsistent with that of b some large value will be substituted,
If a = 0 then exponent format is used with b decimal digits. If both a and b
are zero or if they are omitted altogether as in the above example then the
program defaults to exponent format with 6 decimal digits.
Various aspects of the output formatting can be controlled by calls to the
predeclared procedure ioc. These calls have the effect of setting flags
within the runtime system which remain in effect until some further call is
made to change then. These calls to ioc can be considered in 3 groups. The
first of each group is the default state in effect when the program starts.
The various calls within each group are mutually exclusive.
The first group is concerned with what action is to be taken if the value to
be output is too large to he accommodated by the specified format.
ioc (6) The routine first attempts to accommodate the number by moving along
the decimal point while maintaining the total field width constant. If this
fails the routine will use exponent format provided the field width can he
maintained else a row of asterisks '****' is printed indicating an out of
range number.
ioc(7) No format changes whatsoever are allowed. If the number cannot be
accommodated then a row of asterisks is printed.
ioc(8) No error print allowed. When this ioc call is in effect the error
print indicated by a row of asterisks is never used. Format changes are
allowed; if necessary exponent print will be used regardless of the field
width specified.
The second group is concerted with the representation of space within the
output format.
ioc(9) Set the 'default space character' to space (ASCII 32). Leading zeros
are printed as spaces.
ioc(10) Set the 'default space character' to null (ASCII 0). Leading zeros
will be suppressed. The number is left-justified. (The null character is
trapped by the routine and not actually sent to the output stream).
The third group is concerned with the representation of positive numbers.
ioc(11) Use the current default space character (see group 2 above) where a
positive sign is expected. Initially the default space character is apace.
If ioc(11) is called after ioc(10) the result is to suppress the character
slot reserved to indicate e positive result.
ioc(12) Print '+' to indicate a positive number.
NOTE: Calls to rwrite and write are terminated by printing the 'default
space character' (see group 2 above). This is initially set to space which
serves as a terminator to separate output such that it can be reread by the
read routine.
More flexible number formatting is available using ioc(49), which allows
independent control of the following three character positions, which are
all affected by ioc calls 9-12. They are:
1. Leading spaces (rwrite only).
2. The positive sign (rwrite only; '-' is always printed).
3. Trailing spaces (rwrite and write).
IOC(49) must be called from within a procedure whose first parameter is an
integer by value. The value is 256 * x + y, where y is the character or null
which is to be printed and x is 0, 1 or 2 corresponding to leading spaces, a
positive sign and the trailing space. If x falls outside this range the
ioc(49) call has no effect; y can be any character at all.
By default all characters will be spaces. The following sequence will
suppress the '+' sign and trailing spaces, but will right justify the
number.
PROCEDURE format(n); VALUE n; INTEGER n; ioc(49);
format(&*S); {Leading space}
format (256); {Suppress the '+' sign}
format(512); {Suppress the trailing character}
2.58 Random access files
A file may be opened to be read by random access rather than serial access.
Such files are opened as 'input' files with a switch [B] set to signify
block I/O. If the file is to be updated, i.e. written to, then an additional
switch is needed [BM] where the 'M' indicates 'modify'. These rules imply
that only pre-existing files may be opened for random access. As example of
an 'input' specification.
DATA1.DAT[B],DATA2.DAT[BM]
The first file is opened for random access reading and the second for
reading/writing.
i:=rblock(dev,a.h,n)
will read n blocks from the disk file associated with stream dev, starting
at block number b, writing the contents In memory starting at address a. The
length of the transfer is 128*n bytes. The first block of the file is block
number 0. The address in general will correspond to part of an array set up
by means of procedure location (see section on library procedures) e.g.
i:=rblock(dev,location(buf[0]),b,10) ;
On exit i will have the following meaning.
i=0 successful read.
i=1 read past end of file.
i=2 reading unwritten data.
i=3 hard error.
The user should ensure that the declared array is large enough to accept the
transfer. Any part of a selected transfer extending beyond the end of file
will be set to zero.
i:=wblock(dev,a,b,n);
Will write n blocks to disk; the parameters are the same as for rblock. On
completion i can take the following values.
i=a successful write.
i=1 error in extending file.
i=2 end of disk file.
i=3 hard error.
i=255 no more directory space available.
2.59 Input/output support routines
The following additional procedures are recognized by the runtime system and
are made known to the compiler by including the text of ALIB.ALG with the
program source.
rewind(dev);
The serial input or output file associated with dev is (first closed in the
case of output files and) rewound for reading from the beginning.
dev := findinput ("string");
This call will open the file or device defined in "string" for input on
stream dev. If the first character of "string" is found to be a question
mark "?" then the effect is as follows. The remainder of the string is
printed on the console as a prompt to the operator who enters the required
input file or device name, e.g.
dev:=findinput("?Source file=");
will prompt the operator:
Source file=
who then enters the required name:
dev:=findinput("DATA.DAT");
opens the file DATA.DAT on the logged on drive.
The input specification may in fact consist of an 'input list' the first
entry of which will be used and assigned to dev. Note that the use of this
procedure will wipe out any previous input specifications waiting in the
input list.
dev:=findoutput("string")
This is analogous to findinput but for output. The output specifications may
if desired be generalised to be a complete input/ontput list as described
under ioc(2) and ioc(3) above.
i:=rename;
This procedure renames a file. The old filename and drive information are
taken as the next entry in the 'inputlist'. The new filenane is taken from
the next entry in the 'outputlist', e.g.
ioc(1);
text(7,"FRED.ABC=B:JOE.XYZ");
ioc(3);
i:=rename;
will rename file JOE.XYZ on drive B: as FRED.ABC. Note that the CP/M rename
utility will rename all files that satisfy the input specification. On exit:
i=-1 implies a failure, e.g. file not found or illegal syntax.
i=255 CP/M reply from rename regardless of success or failure.
The default file extension will be used if none is specified, or if ioc(20)
is in effect, will be used regardless. If a file of the same name as the new
name given is found already to exist, then the result will be the same as
described under procedure 'output' with regard to calls of ioc(13) to
ioc(15), namely:
ioc(13) No checks are made.
ioc(14) Erase any pre-existing files of the same name.
ioc(15) Return the value -100 in i.
i:=newext(j, "XYZ");
The file associated with stream j by a previous call of input or output is
closed and its file extension changed to the 3 character string given as the
second parameter. This string becomes the default file extension, e.g.
j:=findinput("FRED.ABC");
i:=newext(J ,"XYZ");
will rename the file FRED.ABC as FRED.XYZ. No checks are made as to the
pre-existence of files of the same name. A negative result in i implies a
failure; the expected reply is 255.
i:=fcblock(dev);
This returns in i the address of the file control block associated with file
stream dev. This can be useful only to users who wish to manipulate CP/M
facilities directly.
i:=exflt(a,t);
Extend the file control block list. The Rogalgol system is initially set up
to allow 4 serial files end 2 random access files open at any time. Should
users require more than this number of files then this procedure may be used
to extend the list of file control blocks available. Each call extends the
length of the list by one. On exit a negative value in I indicates an
attempt has been made to extend beyond its maximum length of 16 entries. The
parameters to exflt are;
a = address of buffer to use
t = file type
If t=0 then serial file else random access
The buffers used are user declared array, the address of which is found
using procedure location, e.g.
BEGIN BYTE ARRAY buf[0;160];
I:=exflt(location(buf[0]),0);
The buffer sizes required are for serial files 164 bytes (36 for the file
control block + 128 for the sector buffer) and for random access files 36
bytes. It is the user's responsibility to ensure that the array is large
enough to accommodate the buffer and that such buffers do not overlap or
become overwritten.
2.60 Direct BDOS and BIOS CP/M calls
Direct call to CP/M BDOS is made by doing ioc(48) within a procedure whose
first two parameters are the call number and the address to be placed In DE.
These must both be VALUE parameters. The second parameter receives the
returned HL value after the call ioc(48). Note that the new value is only
accessible from within the procedure as the DE parameter is by value. You
can either assign this value to the function or to an external variable. In
either case you need to add a statement to the library procedure CP/M after
the ioc(48).
a:=cpm(c,de)
This procedure performs a direct call to CP/M where
c = contents of C register on entry (0 to 27).
de = contents of BE register pair on entry.
a = result in A register on exit.
Refer to the CP/M Interface Guide for details.
a:=bios(n,bc) ;
This procedure performs a direct call through the BIOS jump vector where:
n = entry in the jump table (0 to 14)
bc= contents of BC register pair on entry.
a = contents in A register on exit.
Refer to the CP/M System Alteration Guide for details. The body of the
procedure contains the call ioc(47).
2.61 Library procedures
2.62 Library procedures
The following procedures are built into the runtime system and can be made
known to the compiler by including the source of file "ALIB.ALG" with the
program. Some of the following are machine dependent. See the text of
'ALIB.ALG' for the formal definitions of the procedure parameters.
MEMORY MANIPULATION
i:=location(x);
This returns with i set to the address of variable x; x may be REAL,
INTEGER, or an array element of type REAL, INTEGER, or BYTE. In the case of
REAL or INTEGER arguments the address returned is that of the slot assigned
to that variable (see description of the workings of the runtime system).
Each slot occupies 4 bytes and in the case of INTEGERs only the upper half
is used so that in this case 2 should be added to get the actual address
containing the integer. Array elements as arguments always return the
correct address. The procedure works by recalling the moat recent variable
address computed; as the argument is called by value the compiler will in
fact accept any expression as the actual parameter, although the result will
correspond to the final variable specified. Users who wish to find the
address of Boolean variables may construct a similar procedure with the same
body as location but with a formal parameter of type Boolean by value,
i:=fspace;
This returns the number of bytes free (allowing for a safety margin for
stack operations). Note that on large systems the result may exceed 32K and
thus appear to have a negative value in two's complement representation.
blmove(s,f,len);
Block move of len bytes starting at address 5 to the block starting at
address f. In general the use of procedure location (see above) would be
used to set up the addresses, e.g.
blmove(location(a[0]),location(b[0]),100);
It is the user's responsibility to ensure that such block moves stay within
the limits of the declared arrays. This procedure will work correctly if the
two blocks overlap.
i:=peek(a)
Returns the byte value contained within the address given by a.
poke(a,i)
Sets the contents of address given by a to the value of (the 8 least
significant bits of) i.
clarr (a,len)
Clear array area of length len bytes starting at address a.
SHIFTS AND ROTATES
In the following procedures v is the value (type INTEGER) and n is the
number of places to shift or rotate. Note that only the 4 least significant
bits of n are used so its value should be in the range 0 to 15.
i:=shl(v,m) Shift LEFT.
i:=lsr(v,n) Logical shift RIGHT.
i:=asr(v,n) Arithmetic shift RIGHT.
i:=rotl(v,n) Rotate LEFT.
i:=rotr(v,n) Rotate RIGHT.
Arithmetic shift right extends the sign bit whereas logical shift right
always places zeros into vacated positions.
INPUT/OUTPUT
dpb(u,t,a,a)
Set up the disk parameters, u=unit number (0 to 3), t=track, s=sector, a=DMA
address.
i:=rdisk
Read the disk directly using information set up in a previous call to dpb.
The result from the CP/M call will be in i.
i:=wdisk
Write to disk directly using information set up by a previous call to dpb.
The result from the CP/M call will be in i.
STRING MANIPULATION
i:=sloc("string")
Returns in i the address of the start of the string. The actual parameter
may also be a string parameter of a procedure, e.g.
PROCEDURE x(s); STRING s;
BEGIN INTEGER i;
i:=sloc(s);
i:=sloc("XYZ");
Strings consist of a series of characters stored in sequential bytes
terminated by a zero.
atext(dev,s);
This is similar to the pre-declared procedure "text' but the second
parameter is the address of the string. e.g.
text(dev,"XYZ"); is equivalent to atext(dev,sloc("XYZ"));
i:=tlen(s)
Returns the length of the string whose address is at s, e.g.
i:=tlen(sloc("XYZ"));
returns the value 3.
i:=smatch(long,short)
This procedure compares two strings looking for the first match within the
long string corresponding to the contents of the short string. The
parameters are the addresses of the strings. If a match is found the value
of i is set to the address within the long string corresponding to the start
of the match. If no match is found i will be set to zero. Additional matches
may be found by giving as the starting address of long the value one greater
than the result of the previous match.
MISCELLANEOUS
b:=parity(i)
This Boolean procedure returns TRUE if the character value of i (8 least
significant bits) has EVEN parity else FALSE.
x:=random
Returns a pseudorandom number in the range 0 to 1.
2.63 Library inserts
A facility exists which allows the contents of 'library' source files to be
included with the body of the program at compile time, e.g.
LIBRARY "B:ALIB.ALG"
or, using the upper case convention,
'LIBRARY' "B:ALIB"
The effect at compile time is that on encountering the language key word
LIBRARY the compiler looks for an input file specification enclosed within
string quotes. This file is opened and its contents included with the
program source at the points the call is found. In the above case the file
ALIB.ALG on drive 3: is read, the default extension being '.ALG'. The
default drive is the logged on drive. This capability allows the user to
construct libraries of frequently used procedures, thus avoiding duplication
of text and excessive editing.
BEGIN INTEGER i,j,k;
LIBRARY "ALIB"
LIBRARY "lOLIB"
LIBRARY "STATLIB"
PROCEDURE abc; ...
This example would include the contents of three library files in turn when
compiling. These files may if desired themselves contain LIBRARY directives.
The limit on the depth of such calls is set by the maximum number of input
and output files that may be open at any one time. In the compiler as
distributed this limit is set to five.
2.64 Example programs
The following examples illustrate various aspects of the language. The first
four are fairly straightforward; the final two examples assume a fairly
advanced knowledge of mathematics. Further examples can be found on the
distribution disk.
The first example lists a table of integers up to 20, together with their
square roots, on the console.
BEGIN INTEGER i;
FOR i:=0 STEP 1 UNTIL 20 DO
BEGIN rwrite(1,i,5,0);
rwrite(1,sqrt(i),0,6);
skip(1)
END
END FINISH
The second example lists a file on the console. On detecting the end of file
it loops back for further files to list.
BEGIN INTEGER i, d;
{ Get input file }
loop: ioc(4); d:=input;
{ Check if valid file }
IF d<64 THEN text(1,"*NTry again")
ELSE
BEGIN {list file on console }
FOR i:= chin(d) WHILE i#^Z DO chout(1,i);
close(d); {realease fcb}
END;
GOTO loop;
END FINISH
The next example is a procedure to illustrate string handling. The routine
makes use of several procedures from ALIB.ALG. It scans a piece of text
starting at address "old" and substitutes every occurrence of a given string
"olds" by that given in "news". The source is itself in the form of a
string, i.e. terminated with a zero value. The resultant string will start
at address "new". The calling sequence
la:=location(a[0]);
lb:=location(b[0]);
substitute(lb, la, "Jack","%1");
substitute(la, lb, "Jill","%1");
will replace every occurrence of "%1" by "Jack" and "%2" by "Jill". Both the
initial string text and the resultant string start at location a[0]. The
array b is used as working space.
PROCEDURE substitute (new, old, news, olds) ;
VALUE new, old, INTEGER new, old ;
STRING news, olds ;
BEGIN INTEGER i, j, ns, os, nl ol, oldfin ;
ns := sloc(news) ;
os := sloc(olds) ;
nl := tlen(ns) ;
ol := tlen(os) ; { lengths of strings }
{ address of closing zero of input string }
{ look for matches }
FOR i := smatch(old, os,) WHILE i # 0 DO
BEGIN
j := i - old ; { length of text to copy }
blmove(old, new, j) ; { move over portion of text }
new := new + j ; { update pointers }
old := old + j + ol ; { skip old string }
blmove(ns, new, nl) ; { copy in new string }
new := new + nl ; { update pointer }
END ;
blmove(old, new, oldfin - old) ; { copy remainder }
END substitute ;
The fourth example, quicksort, is a sorting algorithm originally developed
by C.A.R Hoare. An array of values is sorted into ascending order. The
method involves reordering terms such that it can be partitioned in the
form:
a[low],a[low+1], ... a[i-1] < a[i] <= a[i+1],a[i+2], ... a[high]
The pivot value in this case is arbitrarily chosen as the value of the
final element on entry. The procedure then calls itself recursively for
each side of the above expression until each partition contains only one
term. The following coding exploits a feature of this compiler that the
value of the loop variable on exit from a loop will be that which led to
the loop's termination. This may not be the case on other Algol compilers.
PROCEDURE quicksort(a,low,high);
VALUE low,high; INTEGER low,bigh;
INTEGER ARRAY a;
IF low<high THEN
BEGIN INTEGER i,j,pivot,x,y;
i:=low-1; j:=high; pivot:=a[j];
loop: i:=i+1;
FOR x:=a[i] WHILE x<pivot DO i:=i+1;
j:=j-1;
FOR y:=a[j] WHILE j>i AND y>=pivot DO j:=j-1;
IF i<j THEN
BEGIN a[i]:=y; a[j]:=x; GOTO loop;
END ;
{ move pivot to centre }
y:=a[high]; a[high]:=x; a[i]:=y;
{ always deal with the smaller partition
first to minimise depth of recursion}
IF i-Iow<high-i+2 THEN
BEGIN quicksort(a,low,i-1);
quicksort(a,i+l,high)
END ELSE
BEGIN quicksort(a,i+1,high);
quicksort(a,low,i-1)
END
END quicksort;
The next example is a statistical test. Observations are made in pairs, the
float of each pair belonging to one group and the second to another. To find
out if there is any difference between the two groups, we first find the
total difference between then. We calculate the probability of getting this
difference by chance, if the pairs of observations had been assigned
randomly to each group, rather than always the first of the pair to the
first group. If there are j pairs, there are 2^j ways of assigning the pairs
into the groups. Each combination must be tested by finding the total
difference between the groups and counting the number of occasions on which
this difference is greater than or equal to that actually observed. This
count divided by 2^j is the probability of observing the difference by
chance. In the program, the differences between the observations in each
pair are held in the array d. The procedure br adds to the sum the
difference indicated by the parameter n with sign indicated by s. Unless n
indicates the last difference, it generates two more sums, one with a
positive difference and one with a negative difference. Each time n reaches
1 the total sun is checked to see if it is greater than or equal to the
observed sum of differences.
To solve this problem without recursion involves a number of nested FOR
loops equal to the number of data pairs. Thus, a separate program would have
to he kept for each number of data pairs.
BEGIN INTEGER i,j,count;
REAL a,b,obs;
ARRAY d[[1:1OO];
PROCEDURE br(n,s,sum);
VALUE n,s,sun;
INTEGER n,s; REAL sum;
BEGIN sum:=sum+d[n]*s;
IF n=i AND abs(sum)>= abs(obs) THEN
count:=count+i
ELSE
IF n # 1 THEN
BEGIN br(n-1,l,sun); br(n-1,-1,sun)
END
END br;
text(1,"*Nnumher of pairs?"); j:=read(7);
obd := D;
FOR i:= i STEP 1 UNTIL j DO
BEGIN a:= read(7); b:= read(7);
obs := obs + a - h;
d[i] := ahs(a - h);
END ;
text(1,"*Nsum of differences");
rwrite(1,obs,8,2); count:=0;
br(j,-1,0); br(j,1,0);
text (1,
"*NProbahility of same or greater with random signs");
rwrite(1,count/2^j ,7,3);
END
FINISH
The final example is a procedure for solving simultaneous equations. The
left hand side matrix is set up in a two dimensional array a[row,colunm] and
the right hand side in a vector b[row]. The array names (a and b are only
examples) are passed to the procedure to correspond to the names lhs and
rhs, together with an integer giving the number of equations and a label to
exit to if there is no solution. The answers are left in the right hand side
vector. The method uses a Gaussian elimination with partial pivoting.
PROCEDURE solve(order,Iha,rhs,fail);
VALUE order;
INTEGER order; ARRAY Ihs,rhs;
LABEL fail;
BEGIN INTEGER row,col,rawl,orderl,i,j;
REAL max;
FOR order1:=order STEP -1 UNTIL 2 DO
BEGIN max:=0;
FOR J:=1 STEP 1 UNTIL order1 DO
IF abs(lhs[j,order1]) > max THEN
BEGIN max:=abs(lhs[j,orderi]); row:=j;
END
IF row#order1 THEN
BEGIN max:=rhs[order1] ;
rhs[order1]:=rhs[row];
rhs[row]:=max;
FOR col:=I STEP 1 UNTIL order I DO
BEGIN max:=lhs[order1,col] ;
lhs[order1, col]:=lhs[row. col] ;
lhs[row,col]:=max
END
END swop equations;
IF lhs[order1,order1]=0 THEN
nosol: BEGIN text(1,"*Nno solution");
GOTO fail
END
FOR j:=STEP 1 UNTIL order1-1 DO
BEGIN rmax:=Ihs[[i,order1]/lhs[orderl,order1];
rhs[J]:=rhs[j]-rhs[order1]*max;
FOR col:=i STEP I UNTIL order1 DO
BEGIN lhs[j,col]:=
Ihs[j,col]-lhs[order1,coI]*max
END zero one element;
END zero one column;
END triangularise the left hand side;
IF lhs[1,1]=0 THEN GOTO nosol;
FOR row:=1 STEP 1 UNTIL order DO
BEGIN rhs[row]:=rhs[row) / lho[row,row] ;
FOR row1:=row+1 STEP I UNTIL order DO
rho[row1]:=rhs[row1]-lhs[row1,row]*rhs[row];
END
END solve simultaneous equations;
2.66 Compiling and running programs
Running Rogalgol is a two stage process:
1. Compiling. The program source is read by the compiler to produce an
output file in a form to be read by the runtime system.
2. Running. This stage loads the file output from the compiler and runs it.
The simplest sequence of commands given a program source in a single file
'PROG.ALG' would consist of:
ALGOL PROG
ARUN PROG
The default disk drive for input and output files is the logged on drive.
The default file extensions are:
Source files .ALG
Compiler output .OBJ
Monitor file .MON
2.67 Compiling
In the simplest case given above the compiler reeds the program source from
the file specified; if no file extension is given then the default will be
used. The output file created is given the same name as the source file but
with the extension '.OBJ'. Any pre-existing file of the same name as the
output file will be deleted before the new output file is created. If the
compiler detects any errors in the program source the output file is deleted
but compilation continues until the end of the source, checking for further
errors. Error messages are sent to the console. At the of compilation the
size of the resulting program is printed and control is returned to CP/M.
A more general form of calling the compiler is:
ALGOL outlist=inlist
For example:
ALGOL OUT=IOL1B,B:MATHS,PROG
Using this method, the input source is read from a series of files in turn;
typically these files would consist of groups of commonly required
procedures, ending with the file containing the program. It should be
remembered that the overall source should correspond to the required Algol
block structure, from the first BEGIN to the final corresponding END and
FINISH. Files may be taken from several drives; if the drive is not
specifically included then the current default will be used. This is
discussed in the section on i/o selection. In the example IOLIB.ALG is taken
from the logged on drive and MATHS.ALG and FROC.ALG from drive B:. The
output OUT.OBJ goes to the logged on disk. An alternative (and perhaps
better) way of combining source files is by the use of the LIBRARY facility
previously discussed. It must he remembered however that the use of such
library calls is restricted to the final file specified in the input list;
the remaining input file specifiers will otherwise be overwritten. This is
discussed in the details of library procedure 'findinput'.
If a second output stream is specified, then a listing of the compiler
identifier tables will be generated. Compiler error messages will also be
seat to this stream along with an indication of the maximum table size the
system can support.
ALGOL OUT1,OUT2:=PROG
will send the compiler output to OUT1.OBJ, and all compiler error messages
and identifier tables go to OUT2.MON.
ALGOL OUT1,CON:=PROG
will send errors and identifier tables to the console.
If no input/output is specified in the call, or if an error exists, e.g. bad
syntax or a non-existent source file is given, then the compiler will prompt
for i/o. For example, a call of the form:
ALGOL
will result in a prompt of the form:
OUT=IN?
The user may now specify a list of input and output files as for the above
case.
The output from the compiler is about the same length as the corresponding
source text.
If the i/o files were specified in the initial calling lime, i.e. 'ALGOL
PROG', then upon completion the compiler will return to CP/M. If the i/o
files were given as the result of a prompt from the compiler, then upon
completion the compiler will be restarted, to allow further programs to he
compiled. A reply of CONTROL-C in this case will return control to CP/M.
2.68 Compiler directives
A 'DIRECTIVE' <string>; may be included anywhere that 'COMMENT' is allowed.
They are now allowed before the first 'BEGIN' of the program (not standard
ALGOL-6O). The string may contain any basic symbols except ';' and does not
have to be delimited by quotes. If the upper/lower case convention is being
used, then the directive letters must be in lower case, not as below. Only
the following symbols are significant:
+ Any significant letters following will turn the facility ON. This is
the default condition.
- Any significant letters following will turn the facility OFF. This
remains in force until a '+' is
B Controls whether Bitstream output is produced. This should only be
used before the first begin. The default is bitstream; character output
requires a special loader version of ALINK, ARUN will not accept it.The
character format output can be decoded using the list of operation codes.
L Controls whether the input source with added line numbers is
reproduced on the listing device. Default OFF.
T Controls whether a full identifier table is listed. Default OFF.
S Causes a shortened identifier table containing only labels and
procedures to be sent to the list file. If 'T' is in force as well as 'S' a
full table is produced. Default OFF.
P Causes all procedures and function which are not EXTERNAL to be ENTRY
points. Default OFF. See the Linker document.
The tables and source reproduction are sent to the listing file, the second
output file in the CP/M command line. If none was specified, the console is
used. Giving a listing file in the command line (i.e. ALGOL OUT,LIST=INPUT)
in effect causes an automatic 'DIRECTIVE' T-L;
Directives may be given in the CP/M command line which runs the compiler. A
list of options attached to the output file will be analysed in the same way
as a 'DIRECTIVE' string. For example to force a listing on the terminal:
>ALGOLC PROGRAM[L]
Specific ENTRY and EXTERNAL declarations
The symbols ENTRY and EXTERNAL may be placed after the name in a procedure
declaration to allow linking of separate program modules as described in the
linker document.
Directive 'P' may be used to make all procedures which are not EXTERNAL into
ENTRY points.
2.69 Character and bitstream compiler output files
Under default conditions a bitstream is output instead of ASCII characters.
You can force ASCII output by using directive -B. This can help in
debugging, but the loader and standard linker accept only bitsream format.
The special linker ALINKS does accept character format compiler output.
Data is in chunks of 2, 8 or 16 bits. Each chunk is the right way round but
chunks start at the least significant end of each byte. Floating point
literals and entry point/external names are in ASCII starting on a byte
boundary. Floating point numbers are terminated by 13 and names by the
character '. Label numbers are short or long depending on whether the number
will fit into 8 bits.
2 bit codes and their meanings
0 Byte value follows.
1 Short label number follows. The value of the label expanded to 16 bits to
be loaded or used as definition as required.
2 Short label definition follows. After the 8 bit label number, a further
code indicates the type of definition.
0 Colon definition, value = load address.
1 Equate value. Read from top to find it.
2 Label is equated to global name which follows.
3 Second code follows.
0 Word value follows.
1 Long label number follows, value of label is to be loaded or used as
definition.
2 Long label definition follows, then third code as for code 2 above.
3 Third code follows
0 Floating point literal follows.
1 Global symbol definition. After the name in ASCII read from the top
to get its value.
3 End of load module.
2.70 Pre-compiled libraries and the linker
Commencing with version 5.0, the Rogalgol system allows a program to be
compiled in sections. One section (or module) must contain the complete main
program. The procedures may be split over as many modules as is desired, or
may be included with the text of the main program as in previous versions.
Each program module must be a complete program, but individual procedures
may be defined as being EXTERNAL or ENTRY POINTS. An EXTERNAL procedure is
one which is a reference to a procedure which is declared as an entry point
in another program module. Usually, a library of procedures all defined as
ENTRY points would be incorporated into a program with no main program
statements, only the procedure declarations. A linker takes the output of
any number of separate compiler runs and consolidates them into a single
relocatable core image file.
The form of an external procedure declaration is:
PROCEDURE name EXTERNAL ;
Note that no parameter list is given. The only variation from this form is
that BOOLEAN, REAL or INTEGER may precede PROCEDURE to indicate an external
function. The compiler gives the procedure a label number but no code is
generated.
EXTERNAL procedures do not receive a procedure number as they are unable to
access variables within the procedures of the main program. Therefore, they
do not count towards the limit of 255 procedures. This effectively allows a
program to have access to more than 255 procedures.
There are two ways to tell the compiler that a procedure or function is to
be an ENTRY point. First, if DIRECTIVE P is in force all procedures are
available outside the module, unless they are defined as EXTERNAL. Second,
the symbol ENTRY may be placed after the procedure name and before the
parameter list (which may be empty, of course). It does not matter if ENTRY
is used when directive P is in force. An example might be:
REAL PROCEDURE compadd ENTRY (x1,x2,y1,y2); etc.
The runtime program loader does not reecognise entry points or external
definitions and will give an error during loading. The linker must be used
to create a single core image file for the loader.
Ordering of the linked program modules
The first module of a linked program is the one whose main program is
executed. The main programs of any other modules are not accessible. There
is no check on duplicate entry point names; the first declaration
encountered will be the one used at runtime. This allows the use of modules
which have some procedure names in common. Apart from this the ordering of
modules is not important.
Use of main program variables in linked modules
Since each module is a separate program, the compiler will allocate storage
for main program variables starting at the same address in each. It is
therefore quite possible to use main program variables to communicate
between library procedures and the master program. Great care must be
exercised, since the compiler cannot check the number or type of these
variables. The variables in each module are simply overlaid. Arrays will
function as well as unsubscripted variables because they use only one slot
on the variable stack, which contains a pointer to the array Itself.
It is recommended that library procedures should not access main program
variables, since they will not then be of universal application. If you do
decide to use this form of global storage check that the stack slots
assigned to the variables (obtained from an identifier table at compile
time) correspond in the main program and the libraries it is to use.
Procedure numbers of external procedures
External procedures are not allocated a procedure number and do not count
towards the maximum of 255 allowed within any module. However, all other
procedures are allocated a number starting at 1 within each compiled module.
Thus, there will in general be a number of procedures all with the same
procedure number in the linked program. When a runtime error occurs (not
trapped by the program as on page 38) the procedure number in the message
(PROC) may not be unique. The module in which the procedure occurs may
however be deduced from the LOC column, which is the error address relative
to the start of the compiled program. The linker gives the next load address
after each module is processed and this is also relative to the start of the
compiled program. It is therefore straightforward to determine in which
module the error was detected. The list of procedures for that module is
then consulted to find the name corresponding to PROC.
RUNNING THE LINKER
The linker is named ALINK, with extra characters indicating variants; ALINKL
indicates long integer version and ALINKS accepts ASCII symbolic compiler
output instead of the default bitstream.
It is essential to use the correct version of the linker, ALINKL if the long
integer version ARUNL is to be used, otherwise ALINK.
The linker is driven by command lines. The start of a link is heralded by a
command line containing an '=' sign. The prompt for such a line is
Output=input list :
If just <RETURN> is typed the linker returns to CP/M. Otherwise it tries to
open the first input file. It keeps asking for this command line until a
valid input file is given (the output file may be omitted). You can list as
many input files on the line as you wish, subject to a maximum line length
of 96 characters. Any file which fails to open (including the first) is
treated as the end of the line.
The linker processes the input files, after each one checking whether there
are any outstanding unresolved external procedure names If there are none
the output file is written and the linker requests another initial command
line. If when all files have been read there remain outstanding externals
the names of more input files are requested. Again a list may be typed but
in this case an '=' sign preceding the file names is NOT required. The
reason for this difference is to synchronise the start of linking should
there be any error and the lines are being read from a command file (see
later section). If at this stage an empty line is given the linker writes
the output file anyway, with a warning message on the terminal. As with the
initial command line, any input file which does not open is treated as a
line terminator.
The default extension for input and output files is ASC.
Identifier table output
There are three types of identifier table which may be output on the
terminal under the control of an option letter attached to the first input
file.
(a) The current table, including both unresolved references and entry
points, the latter being marked with '*' and having their addresses relative
to the start of the program. This is output after each input file has been
loaded.
(b) A list of as yet unresolved names, output after each input file.
(c) The entry point table listed at the end of linking.
The default (no option letter) is for list (b) only to be typed. Option 'N'
means no tables at all, 'F' means all three of the tables and 'G' means (b)
after each file plus the global list (c) at the end of linking.
Example command lines:
Output=input list : PROGRAM=MAIN[N],LIB1,LIB2
Input files : LIB3
The first line means link a program to be called PROGRAM using MAIN as the
main program, to use library procedures in files LIB1 and LIB2. No tables
are to be listed on the terminal. The second form of line is used when there
are outstanding externals after all the input files of previous lines have
been processed.
Indirect command files
The input lines may be obtained from a command file, default filename
extension .LNK. If the first character of the first command line of a link
(the one with '=' in it) is '@', then the filename following will be opened
and used instead of the terminal as a source of input. Command lines are
still echoed on the terminal so that you can watch the progress of the
linker.
Command files may be chained by using '@' on the last line of a file. The
current file will be closed and a replacement opened. The files are not
nested.
The first command line may be typed as a parameter to the call of the
linker, and this may nominate an indirect command file. For example:
>ALINK @LINKIT
The linker will attempt to open the file LINKIT.LNK on the current default
drive. The command file should not contain any blank lines except at the end
as these tell the linker return to CP/M.
>ALINK FRED=JIM
The linker takes compiler output file JIM.ASC and creates a relocatable core
image file FRED.ASC, assuming that JIM contains no external references. This
performs the same function as using option 's' on the runtime loader except
that the output file name may be different from the input file name.
2.71 Runtime program
Given a successfully compiled program, the output file so created may mow be
rum by a calling the runtime system as follows:
ARUN filename
The assumed file extension is '.ASC'. The file specified will be loaded and
them executed. If no input is specified or if an error is found, e.g. bad
syntax or non-existent filename them the runtime system will prompt the user
for am input file. For example a call of the form:
ARUN
will prompt for input:
INPUT=
to which the user responds with the required filename.
Upon completion of the program the system prints '^' on the console and
waits for an operator response. Typing CONTROL-P will rerun the program or
CONTROL-C will return control to CP/M.
If a runtime error is detected then suitable diagnostic information is sent
to the console (see section on runtime errors). Unless the user is making
use of the error handling facility (see procedure 'error' in library
section) the system will mow wait for the operator to investigate the cause
of the error. The program may be rerun from the beginning by typing
CONTROL-P or control returned to CP/M by typing CONTROL-C.
The return to CP/M upon completion or upon detecting a runtime error can be
made automatic by a call ioc(22) within the program.
A call of the form ioc(60) causes am immediate restart of the program from
the beginning. Any files open at the time will not be closed although all
file control blocks are released.
2.72 Switches on the loader filename
A previous topic describes how to examine the switch list associated with
the last file opened. Since the loader uses the same routines as the Algol
interpreter, the Algol program can examine the switch list associated with
the program begin run. Obviously, this must be done before either INPUT or
OUTPUT is used. The loader itself only acts on the first character of the
list, and ignores all except B and M (which will cause an error). The
programmer can determine if the debugger is active by looking for D or W at
the start of the list, and thus can arrange for the switch to be propagated
over a chain.
2.73 Long integer (32 bit) Algol
ARUNL is a version of the Rogalgol runtime system in which real variables
are represented not in the normal mantissa/exponent form but rather as 32
bit 2's complement integers. This runtime system is useful for those
applications where greater precision is desirable but without the need to
extend the number range to the extent allowed by the floating point
representation, e.g. business programs. The number range allowed is from
(2^31)-1 to -(2^31), (about +-2.15*10^9). The compiler itself remains
unchanged. Variables declared as integers will still be represented as 16
bit 2's complement numbers. This document outlines the differences from the
Algol system described in the manual.
STANDARD FUNCTIONS
The following functions have been removed:
sin, cos, sqrt, arctan, ln, exp
The function entier exists but is equivalent to a real to integer
assignment. For example, the statements
i := entier(x);
i := x ;
have the same effect.
LIBRARY PROCEDURES
The standard library file 'ALIB.ALG' can be used with ARUNL with the
following exception that random has been removed,
Two additional library procedures can be found in file 'ARUNL.ALG'
pow10(n)
lrem(t,b)
DIVISION
Real division (/) always truncates the result towards zero in the same way
as with integer division (%). A procedure has been added to the library
(lrem) to give the remainder term lost by the division.
z := lrem(t, b) ;
gives the remainder lost by the division:
u := t / b ;
The result of lrem will always have the same sign as the quotient (or zero)
in the same way as the MOD operator does for the integer case, e.g.
t b t/b lrem(t,b)
35 8 4 3
-35 8 -4 -3
35 -8 -4 -3
-35 -8 4 3
INPUT/OUTPUT
The decimal input/output routines (read and rwrite) are unchanged except for
the addition of a scaling factor. A call of the ALIBL.ALG routine:
pow10(n);
where 'n' is a small integer causes all subsequent calls to read to be
scaled by a factor of 10^n. The digit string representing the number
(including fractional and exponent fields) is read and the result scaled by
1O^n. Any fractional part is then disregarded before returning the result.
For example with n=2, on reading the number 123.4567 the result would be
12345. On output the converse scaling is performed. The value to be output
is first converted internally into a digit string; the decimal point is then
effectively shifted left by `n' digits before printing the result in the
required format. The meaning of the fopitat parameters remain unchanged.
This scaling on output applies only to rwrite; the integer print routine
(write) is unchanged.
RUNTIME ERROR MESSAGES
The following changes to the runtime error numbers given in the manual have
been made.
8 Real (long integer) division by zero or lrem(t,0).
9 Overflow in real multiply.
16 Overflow in real addition.
17 Overflow in real subtraction.
18 Illegal standard function called e.g. sin, cos etc.
19 Largest negative number -(2^31) with no corresponding positive
representation. This error can occur from abs, *, /, rem, rwrite etc.
2.74 The chaining mechanism
The call ioc(60) re-runs the program from the start, while ioc(22) will
cause exit to CP/M when execution reaches the end of the program. If ioc(22)
is called before ioc(60) chaining is initiated, in which the leader reads
another compiler output file. The name of the new file is obtained from the
keyboard buffer. Note that the filename must be given in full unless the
default extension has been placed in the three bytes following the switch.
All buffers are reset after loading; therefore all files should be closed
before chaining. The correct sequence is:
ioc(1); text(7,"NEXTPROG.ASC");
{ Put the filename into the keyboard buffer }
ioc(22); ioc(6O);
If the nominated file cannot be opened the prompt
INPUT=
will be given, as when ARUN is called from the console.
2.75 Compiler error messages and diagnostic information
2.76 Compiler error messages
FAIL X ON LINE Y IDENT Z SYMBOL S
The name of the current library file, or 'MAIN SOURCE' is printed along with
the error number.
After the error line, the program is reproduced from either:
(a) 100 characters before;
(b) the start of the program; or
(c) the end of the previous error message, whichever is the shorter.
If the whole text is being reproduced (see below) then the error text is
ended with a line of '=========='.
When the whole text is being sent to the listing file (directive 'L') line
numbers are prefixed. The first number is counting from the start of the
program and the second is a count within each file. These line numbers
correspond exactly to the line numbers in the error messages.
Line numbers in error messages are counted from the start of the current
file, except for undeclared labels (error 2). In this case the numbering Is
from the start regardless of whether the lines are coming from libraries or
not.
A selective symbol table containing only labels and procedures may be
obtained by directive 'S'.
Procedures and labels are highlighted in the symbol table by being indented.
The procedure numbers are further highlighted by enclosure in brackets.
If a symbol table but no listing is specified then the start and end of
library files are marked in the symbol table.
X is the failure number (see below), Y the line on which it occurred, Z the
last identifier read, and S the decimal value of the last symbol (see
section entitled 'compiler representation of basic symbols'). 'LINE UP TO
ERROR' is a copy of the input line up to and including the symbol at which
the error was found. The compiler output is switched off and the file
deleted. The compiler however continues to check the syntax of the remainder
of the program. In all compilers a tradeoff is made between the amount of
error information given and the size and speed of the compiler. In this
implementation the emphasis has been to produce a compiler that can be used
on a very modest sized computer. There is always a danger, particularly with
a one pass compiler, that following the detection of a genuine error, the
system may fail to synchronize fully and thus produce additional spurious
errors.
1 Identifier declared twice in same block.
2 Undeclared identifier.
3 No '[' after array name, except as a procedure parameter, or ordinary
procedure used as a function.
4 No ')' at end of subscript list.
5 More than 255 variables in the main program or a procedure.
6 No FINISH at end of program. (Too many ENDs).
7 No ELSE part of a conditional arithmetic expression.
8 No ELSE part of a conditional Boolean or conditional designational
expression.
9 Relational operator not found where expected. Will occur if the first
arithmetic expression of a Boolean relational expression is totally enclosed
in round brackets.
10 Arithmetic primary does not start with '+', '-', '.', '(', digit or
identifier.
11 '%', MOD, '!', MASK, or DIFFER does not have two integer operands.
12 ')' missing in arithmetic expression.
13 Controlled variable in FOR is undeclared or subscripted.
14 ')' missing in Boolean or designational expression.
15 More identifiers in scope than the tables can accommodate. The
compiler automatically makes the tables as large as possible on a given
system.
16 Statement starts incorrectly. If this occurs at the terminating
FINISH is means there are not enough ENDs.
17 Undeclared or unsuitable identifier on left of ':='
18 Array declaration faulty.
19 Type specification of actual parameter is not LABEL, PROCEDURE, REAL
PROCEDURE, BOOLEAN PROCEDURE or INTEGER PROCEDURE.
20 Wrong number of subscripts. In the case of formal arrays, this error
cannot be detected until runtime.
21 No ')' after actual parameter list.
22 FOR statement element not terminated by ',' or DO.
23 More than 255 non-external procedures OR.
23 Procedure body not delimited by ';'.
24 ':=' not found where expected.
23 No THEN after IF.
26 VALUE specification is not the first specification of procedure
formal parameters.
27 FINISH in middle of program. Possibly an unmatched BEGIN, '"' or '''.
28 No ';' after parameter list.
29 Parameter specified twice, or is not in formal list, or specification
not terminated by ';'.
30 Forward reference list full.
31 UNTIL not found where expected.
32 No '(' after name of standard procedure (except input or output).
33 THEN followed immediately by IF.
34 Procedure actual parameter starts with an undeclared identifier.
35 Function or variable used as procedure.
36 procedure input or output is followed by a '('.
38 Arithmetic expression contains Boolean variable in illegal context.
39 Parameter specified VALUE is not in formal list.
40 Parameter specification not complete.
41 Am array has been called by value.
42 Input/output procedure call error.
43 Left parts of multiple assignment have different types.
44 Integer literal not in range.
45 Switch identifier not followed by ':='.
46 Switch list does not end with ';'.
47 Switch has more than one subscript.
48 Word BYTE not followed by ARRAY.
49 Input files exhausted without end of program recognized.
50 A procedure used before its declaration was assumed to be of a type
different from the actual type. Try reordering procedures to eliminate the
forward reference.
51 Input file specified in a LIBRARY call not found.
52 Subset compiler as for 50 but forward reference clash.
53 Subset compiler as for 50 but at block end resolution.
54 More than 100 procedure parameters.
56 No DO after WHILE.
57 No UNTIL after REPEAT.
58 Case statement syntax error.
59 Boolean expression in context where arithmetic one needed.
60 Arithmetic expression where a Boolean one is needed.
61 Array declaration not terminated by ';'.
2.77 Compiler identifier table and identifier types
The compiler may be instructed to print on the console or to the monitor
file a list of all the identifiers declared, together with information about
their type and the addresses they will occupy in the memory. Variables are
placed on a stack and the variable number is the position on the stack
relative to a pointer. The pointer is held in location PBASE in the runtime
program. The address of the variable is found by multiplying the variable
number by 4 and adding this to the contents of PBASE.
Four numbers are printed after each identifier in the compiler identifier
table.
The first of these is the stack position except for labels and procedures.
For labels and procedures the symbolic label number is printed. This is the
digits part of a symbol such as L123 which is output by the compiler.
The second number is the procedure number of the enclosing procedure in
which the identifier is declared. The main program is 0, and the procedures
are numbered serially as they are encountered, regardless of depth of
declaration. As an exception, the actual number of a procedure is printed,
instead of the number of the enclosing procedure.
The third number is the line number of the source program.
The fourth number is the current size of the compiled code. This information
can be related to the position given when runtime errors are detected.
The type information of the identifier is then listed as follows. The
numbers represent the internal representation of the data types.
0 procedure formal parameter (type not yet known)
1 real
2 integer
3 Boolean
5 real array
6 integer array
6 byte array
7 Boolean array
8 switch
10 procedure
11 real procedure
12 integer procedure
13 Boolean procedure
14 label
The compiled code of a procedure contains a list of the types of the
parameters. The following types may appear, in addition to those above.
4 string
21 real by name
22 integer by name
23 Boolean by name
2.78 Compiler representation of basic symbols
These are the decimal values which are printed in a compiler error message.
Language key words are represented in the Algol source by the word enclosed
in single quotes or in upper case and in the compiler by 40*1st
letter+second letter.
If a compiler error message contains a symbol which is not on the list, an
illegal compound symbol has been detected. The usual cause of this is an
unmatched single quote.
1-26 letters A-Z
27 [
29 ]
30 ^ (exponentiation)
7000 :=
33 !
34 " (string delimiter)
35 # (not equal to)
36 $
37 % (integer divide)
38 >= (greater or equal to)
40 (
41 )
42 * (multiply)
43 +
44 ,
45 -
46 .
47 / (real divide)
48-57 digits 0-9
58 :
59 ;
60 < (less than)
61 =
62 > (greater than)
63 <= (less or equal to)
85 BEGIN
95 BOOLEAN
105 BYTE
118 AND
121 CASE
122 ARRAY
123 CC
135 COMMENT
169 DIFFER
175 DO
212 ELSE
214 END
217 EQUIVALENT
224 EXTRA
241 FALSE
249 FINISH
255 FOR
295 GOTO
366 IF
373 IMPLIES
374 INTEGER
481 LABEL
489 LIBRARY
512 MASK
535 MOD
575 NOT
606 OF
618 OR
658 PROCEDURE
725 REAL
726 REPEAT
780 STRING
780 STEP
783 SWITCH
808 THEN
818 TRUE
854 UNTIL
881 VALUE
928 WHILE
2.79 Run time errors and diagnostic information
2.80 Run time errors
In the event of an error condition being detected during program execution,
a message is sent to output device 1 (console or video screen generally) of
the form:
ERROR n
ADD PBASE PROC LOC
aaaa bbbb p1 d1
aaaa bbbb p2 d2
...
aaaa bbbb 0 d0
where:
n error number (see following list)
p1 procedure number where error was detected
aaaa address of program counter
bbbb value of PBASE at error
(see section on runtime system)
d1 location of program counter relative
to the start of the program
Both aaaa and bbbb are printed in hexadecimal. The procedure number can be
found by counting procedures from the beginning of the program starting from
1. The main program is given the number 0. This information can be found in
the compiler identifier table output. If p1 is non-zero the calling sequence
is then printed on the following lines until the outermost level (p=0) is
reached. This traceback information can be used to investigate the nature of
failure in greater detail if required (see section describing working of the
runtime system). The information given in dl etc can be related to the
corresponding information given in the compiler identifier tables to help
locate the position of errors. The program may be restarted from the
beginning by typing CONTROL-P or control returned to CP/M by typing
CONTROL-C.
2.81 Recovery from run time errors
In normal operation a program is terminated by the detection of a runtime
error. It is possible however to continue following an error, allowing the
program to exit in a controlled manner, e.g. close output files, give more
useful diagnostic information, values of variables and so on. This recovery
is achieved by including a call of procedure 'error' (in ALIR.ALC) in the
program before the failure occurs, e.g.
error(LABEL crash);
On detecting an error, the runtime system will place the error information
given above and then transfer control to the label (or designational
expression) 'crash' in the user's program. It is advisable that the label be
located at an outermost program level as it may only be reached if it is
within scope at be time of the error. The error number responsible for the
failure can be found by means of a call to chin(13), e.g.
crash: i := chin(13);
IF i > 30 THEN GOTO cpmbug ELSE ...
2.82 Runtime error numbers
0 Undefined error. This implies that an error has been detected which
has no corresponding entry in the error list. This hopefully will only occur
where the user has made modifications to the runtime system and failed to
update the error list.
1 Variable space used up (stack overflow). Probably the result of
excessive recursion or array declarations too large for the available
memory. The error traceback may fail under these circumstances (the first
lime should always be correct). Overflow is checked following block or
procedure entry and array declarations.
2 Procedure called with the wrong number of parameters.
3 Procedure called where the actual and the formal parameter types do
not match.
4 Array used with the wrong number of subscripts.
5 Array subscript out of range (below base of array).
6 Array subscript out of range (above top of array).
7 Integer division by zero.
8 Real or integer division by zero.
9 Real overflow. ?? overflow in multiply
10 Real to integer conversion overflow, also long integer
11 Real overflow detected during normalisation after real arithmetic
operation, or by integer overflow during add or subtract.
12 Error in READ. Character read which is not a legitimate part of a
number (ASCII value is less than 48 ie <&0).
13 As for 12 but ASCII value is greater than 57 (i.e. >&9).
14 Error in READ. Number contains two or more decimal points.
15 Error in READ. A character '+', '-', '.' or 'E' found with no
associated digits.
16 Square root of a negative number.
17 Exponential argument too large (>87).
18 Exponential argument marginally too large.
19 Logarithm of a negative number.
20 Logarithm of zero.
21 Table item out of range (below). Found in ioc(n), chin(n), chout(n,c)
etc where n<0.
22 As for 21 but where a is greater than maximum value specified in
list.
23 End of file detected during READ.
LOADER ERRORS
24 Loader syntax error. Output from compiler has been corrupted?.
25 End of input is indicated (CONTROL-Z read) but no program has been
loaded. Selecting input device 0 will produce this effect.
26 On completion of input there remain unresolved forward references.
Input source is corrupt?
27 An input device name has been used as an output device in a command
string, or vice versa.
28 Label tables overlap program. Program is too large for available
memory.
29 Forward reference tables full. This error should be rare but can he
avoided by reordering procedures so that they generate fewer forward
references, i.e., try to arrange that procedures are declared before they
are called.
30 Non-relocatable core image input file is not compatible with this
runtime system.
OPERATING SYSTEM ERRORS
31 Channel number is out of range.
32 No directory space found during output.
33 Attempt to read from channel not open for input.
34 Attempt to read from a non-serial channel.
35 Attempt to reed past end of file.
36 Attempt to write to a channel without write access.
37 Attempt to write to a non-serial file.
38 Error in extending file.
39 Attempt to output to random access file without write access.
40 Attempt at random access to a serial access channel.
41 Channel not open.
42 Attempt to rewind a random access file.
43 Random access with a negative block number.
44 No slot available for input or output.
45 Attempt to create an output file for random access.
46 Random access transfer attempted with a block count less than zero or
greater than 255.
2.83 Runtime stack organisation
The stack extends from the end of the runtime program to the end of
available memory, as found by interrogating the system. The variable stack
grows upwards from the end of the program and a working stack, used in
evaluating expressions, passing procedure parameters, and CALL instructions,
grows downwards from the end of memory. The variable stack consists of a
number of frames, one for the main program and one for each procedure call.
Within each stack frame is an array stack, which contains an array frame for
each depth of array declaration. In the following section "word" refers to a
16 bit (2 byte) quantity.
The following pointers are used. Their addresses can be found from the
listing in the section entitled 'adding code sections'.
PBASE points at the current variable stack frame
MBASE points at the main program stack frame. When the main program is
executing locations PBASE and MBASE contain the same value.
WSBAS points at the base of the working stack in the current level. It is
used to delete floaters from the working stack at Algol labels.
ABAS points at the current array frame
FSPT points at the next free location in the variable stack.
FPARAG in 80x86 versions holds the next free paragraph in extra memory.
The following registers are also of significance.
SP points at the top item of the working stack, It must be saved and
restored if used by any machine code added. It is also used for CALL
instructions.
IX should also be restored if used. It is the Algol interpretive code
program pointer.
IY must be restored if used. It points to a series of flags and working
space.
Each stack frame is divided into two parts, a variable part and an array
part. The variable part is divided into slots which are each two words (four
bytes) long. The actual address of a slot is found by multiplying the slot
number by 4 and adding this to the base address which is held in PBASE or
MBASE. In the main program frame the first declared variable is in slot 2
and the word pointed at by MBASE contains 0, the level number of the main
program. In procedure level frames slot 3 is used for the result of a
function and is unused in procedures which do not deliver a result. The
procedure parameters occupy slots 4 and upwards, followed by variables
declared within the procedure. The first word of each procedure in the
compiled program contains the number of variable slots required by the
procedure. The first word of the main program points at the last word of the
compiled program which contains the number of variables slots required by
the main program.
In procedure frames the first three slots are used for linking information.
Starting at the word pointed at by PBASE (slot 0) the words contain the
following information.
Word 1 The number of the procedure.
Word 2 The return address
Word 3 PHASE of calling level
Word 4 WSBAS of calling level
Word 5 ABAS of calling level
Word 6 FPARAG of calling level in 8086 version
A variable stack slot may contain any of the following types of item:
1. A real number which is held in the standard four byte format.
2. An integer number or Boolean value which is held in the highest addressed
word of a slot. Booleans use only the least significant byte of this word.
3. A label or procedure address, always a procedure parameter. The address
itself is in the highest addressed word and in the word below it is the
value of PHASE at the tine the address was evaluated.
4. The address of an array or a switch either as a declared variable or a
procedure parameter. The address is in the highest addressed word of the
slot, the remaining word being unused.
5. The address of a string or an unsubscripted variable for procedure
parameters of type string and variables called by name.
The address in a switch variable points to the switch vector. The word
pointed at contains the number of elements in the switch and subsequent
words the addresses of the labels in the switch list.
The array part of a stack frame contains a number of array levels, numbered
by depth of declaration within a procedure or the main program. Level 0
always exists and is located immediately above the end of the variables.
ABAS points at the base of the current level, which contains the depth of
that level. The next word (except in level 0) contains a pointer to the
level below. Above the level information are the dope vectors and array
elements.
An array variable points at the start of its dope vector. This contains 2*(N
+ 1) words, where N is the number of subscripts. The first* word of the dope
vector contains the number of bytes occupied by each element (1, 2 or 4),
the second the number of subscripts and the third the lower bound of the
first subscript.
There are two additional words for each additional subscript. The first
contains a multiplier for the previously accumulated element number and the
second the lower bound of the next subscript. The final word of the dope
vector contains the address of the word beyond the end of the array
elements. Array elements themselves are stored immediately after the dope
vector.
2.84 Runtime operation codes
These are the operation codes which are output by the compiler. The list
gives their number in decimal. The compiler can be forced to output in
character format by using the switch [-B] (quoted convention) or [-b]
(upper/lower case convention).
Expressions are evaluated using a working stack. The top element is referred
to as S1, the next one down as S2 and so on. The stack pointer 'SP' is used
for this stack (and also for CALL instructions). It grows down from the top
of available memory.
Some of the interpretive routines take data from the program. N1 refers to
the next byte after the code, N2 to the next, and so on. In the following
section 'word' refers to a 16 bit (2 byte) quantity.
0 No operation.
1 Declare array. N1=depth of declaration. N2=nunber of declarations in
multiple. N3-variable number of first declaration. N4=number of bytes in
each element. N5=number of subscripts S1=upper bound of last subscript.
S2=lower bound of last subscript. S3, S4, etc., bounds of other subscripts.
2 Formatted print. S1=b, S2=a, S3=value, S4=device number.
3 Read to S1 from input device in S1.
4 Store local variable from S1. N1=variable number.
5 Print string. Followed by 7 bit ASCII character, terminated by zero.
Device number is in S1.
6 Integer print S1=radix, S2=value, S3=device number.
7 Read next character to S1 from device number in S1.
8 Print S1 as character, 52-device number.
9 Jump. Location is in next word.
10 Leave procedure.
11 Enter procedure with no parameters. Address is in next word.
12 Get local variable to S1. N1=variable number.
13 Integer add. SI:-82+Sl
14 Get array element. N1=procedure number, N2=variable number, N3=number
of subscripts. The subscripts are on the stack. The main program is
procedure number 0.
15 Store array element. S1=value, other information as code 14 except
subscripts are in S2 etc.
16 Set 16 bit constant in S1 from NI and N2.
17 Integer negate. SI:=-S1
18 Real ^ Integer. S1:=S2^S1
19 Integer multiply. S1:=S2*S1
20 Integer divide. S1:=S2/S1
21 Integer subtract. S1:=S2-S1
22 S1:=S1=0
23 S1:=S1>0
24 S1:=S1<0
25 Get any variable to S1. NI=procedure number, N2=variable number.
26 Store to any variable from S1. N1, N2 as for 25.
27 Standard function. Followed by another code.
2 sqrt 3 sin 4 cos
5 arctan 6 exp 7 ln
8 sign 9 entier 10 abs
28 Jump if S1=FALSE. Address in next word.
29 Set zero ln S1.
30 S1:=NOT S1
31 S1:=S1 AND S2
32 S1:=S1 OR S2
33 S1:=S1 EQUIV S2
34 For statement calculator. S1=address of controlled variable. S2=final
value. S3=lncrement. S4=0 for no increment at the first test, else -1.
N1=type of control variable (0=REAL else INTEGER). The following word
contains the exit address for loop completion.
35 'ioc'. Parameter in S1
36 Enter procedure. N1=number of parameters. S1=type of last parameter.
S2=value of last parameter, and so on, in reverse order. The address of the
procedure is in N2 and N3. The first word of a procedure is the fixed space
on the variable stack required by the procedure. The following bytes contain
the procedure number and the number of parameters expected, followed by the
type specification of the parameters, in reverse order.
37 Store outer block variable from S1. N1=variable number.
38 Fetch outer block variable to S1. N1=variable number.
39 Set in S1 the address of the variable whose procedure number is in
N1, variable number in N2.
40 Skip, device number in S1.
41 Integer S1:=sign(S2-S1)
42 Set 8 bit constant in S1 from N1.
43 Fix S1.
44 Float Sl.
45 Set floating point constant from next 4 program bytes.
46 Floating negate.
47 Set label in S1. Address in next word. Second word of S1 becomes
variable stack base pointer.
48 Evaluate switch address. S1=address of element O, S2=subscript. On
exit S1 contains address.
49 Real ^ real. S1:=S2^S1
50 Floating multiply. S1:=S2*S1
51 Float S2.
52 Floating divide. S1:=S2/S1
53 Floating add. S1:=S2+Sl
54 Floating subtract. S1:=S2-S1
55 Store parameter called by name. S1=value, S2=address.
56 Floating S1:=sign(S2-S1).
57 Jump to address in S1.
58 Enter procedure without parameters whose address is in S1.
59 As 58 but number of parameters in N1. For 59 the rest of stack is set
up as for Code 36.
60 Print string whose address is in S1, S2=device number.
61 Set stack depth. N1=procedure number. N2=array depth required.
62 Fetch parameter called by name. S1=address.
63 Stop, end of program. Prints '^' on console or returns to CP/M.
64 Store an address in the program in a local variable. Followed by the
variable number in N1 and the address ln the next whole word.
65 Jump to the address in local variable number N1.
66 Set in S1 the address of local variable number N1.
67 As for 66 but main program variable.
68 Get local array element.
69 Get main program array element.
70 Store local array element.
71 Store main program array element. Codes 68-71 are followed by the
variable number in N1, not by the level number and then the variable number
as for codes 14 and 15.
72 Read a floating point number, check for end of file. S1=address of
label to go to on end of file. S2=device number.
73 Logical OR. Sl:=S2 OR 51
74 Logical AND. S1:=S2 AND S1
75 Logical EXCLUSIVE OR. Sl:=S2 XOR Si.
76 Integer MOD. S1:=S2 MOD S1
77 Close file, stream number in S1.
78 Delete file, stream number in S1.
79 Open INPUT and assign to stream number S1.
80 Create OUTPUT and assign to stream number S1.
Extended opcodes
Compression of the compiler output is achieved by defining the opcodes
below. They combine one of the opcodes in the table above with its argument.
0xxxxxxx (0-127) Existing opcodes.
10xxxxxx (128-191) Set xxxxxx+1 on the stack (range 1-64).
1100xxxx (192-207) Fetch local variable xxxx (range 0-15).
1101xxxx (208-223) Fetch global variable.
1110xxxx (224-239) Store local variable.
1111xxxx (240-255) Store global variable.
A similar extension has been made to the compiler opcodes:
00xxxxxx (0-63) Existing opcodes.
01xxxxxx (64-127) Set integer contant range 1-64.
1000xxxx (128-143) Fetch local variable 0-15.
1001xxxx (144-159) Store local variable 0-15.
101xxxxx (160-191) Store global variable 0-31.
11xxxxxx (192-255) Fetch global variable 0-63.
2.85 Summary of ioc() procedure calls
Utility calls ioc(n), the range of numbers n is in the left hand column.
0-5 input/output selection
6-12 rwrite format control
13-15 output file options
16-17 interrupt option on disk i/o
18-19 read options
20-21 file extension options
22 reboot CF/M on completion
49 More flexible formatted number output
60 rerun program from start
Others are linked to procedures in ALIB.ALG
2.86 Summary of pre-declared procedures
Utility pre-declared function
ioc
Standard functions
abs, arctan, cos, entier, exp, ln, sign, sin, sqrt
Input/Output
chin, chout, read, rwrite, skip, text, write
2.87 Procedures in the library ALIB.ALG
These must be compiled in with the program, or linked with the Algol object
file. Most of these procedures are implemented by an ioc() call within the
body of the procedure. A number preceding the name is the ioc procedure
parameter. Not all these are implemented in MSDOS Rogalgol.
Input/output procedures
findinput
findoutput
26 rblock
27 wblock
28 rewind
30 seti
31 seto
32 ipoint
33 opoint
34 exflt
38 fcblock
39 swlist
47 bios
48 cpmd
58 rename
59 newext
Other library procedures
23 error
24 location
29 fspace
35 blmove
36 peek
37 poke
51 in
52 out
dpb
rdisk
wdisk
40 parity
41 shl
42 lsr
43 asr
44 rotl
45 rotr
46 random
50 clarr
55 sloc
54 slen
56 smatch
53 atext
25 emt (not implemented, RML hardware only)
49 wait (not implemented, RML hardware only)
62 chpos (not implemented, RML hardware only)
61 point (not implemented, RML hardware only)
63 line (not implemented, RML hardware only)
2.88 Distributed programs and files
ALGOL.EXE and ARUN.EXE
The Algol compiler and runtime system.
ARUNL.EXE
The runtime program which uses 32 bit integers in place of real
numbers.
ALINK.EXE, ALINKL.EXE and ALINKS.EXE
The floating point linker, the long integer linker, and a floating
point linker which accepts character format (directive -b) compiler output
files.
ALIB.ALG
The standard Algol library routines.
UCASE.ALG and UCASE.ASC
This program will convert Algol source files written using convention 1
(all upper case with key words enclosed in quotes) into convention 2
(upper/lower case). The program prompts for input and output file names. The
default file extension is 'ALG'.
UCASE.ALG and UCASE.ASC
This in the complement to LCASE.ALG just described. Files are converted
from convention 2 into convention 1.
MMIND.ALC and MMIND.ASC
Mastermind. Game 1 allows 6 colours and no blanks. Game 2 allows 6
colours and blanks.
VDU.ALC and VDU.ASC
This program is designed as an editing aid for creating Algol source
files. The program prompts for an output file name, the default extension is
'.ALC'. If the upper case convention with key words enclosed within quotes
is required then give a [U] switch option with the file specification. Now
start typing in your program. The program detects language key words. As
soon as sufficient characters have been entered to uniquely define the key
word the program will supply the rest. Corrections can be made to the
current line being entered using the rubout key. Other special keys are.
CONTROL-U Erase the current line.
CONTROL-R Retype the current line after cleanup.
CONTROL-X Switch off the auto keyword facility. A second call
will switch it on again. This allows strings etc. to be entered without
extra characters being added
CONTROL-Z End of program. Close file and return to the start.
CONTROL-C In response to the prompt for a file specification will
return control to CP/M.
Other example programs may also be included.
3 Rogalgol for the 80x86
3.1 Overview of 80x86 Rogalgol
Overview of 80x86 Rogalgol
As far as the Algol programmer is concerned, the CP/M-86 and MSDOS/PCDOS
Algol can be considered as virtually identical to the CP/M-Z80 version.
There is no difference in the language. The operation of the runtime program
has been made as compatible as possible. Programs not making system calls
using the library procedures CPMD and BIOS run without change and without
re-compilation, as will most others.
The only difference likely to affect program running is that the RANDOM
library function always starts with the same value on 80x86 versions. Other
changes can be grouped under the headings extra feature, file handling
issues and the operating system interface.
The compiler is called ALGOL, the floating time runtime and linker programs
are called ARUN and ALINK. The long integer versions are called ARUNL and
ALINKL. The .CMD (or .EXE) files try to claim a data segment of 64 KB. The
total memory used is around 76 KB if sufficient RAM for a 64k data segment
is available.
Rogalgol 8088-8086 automatically makes use of more memory than 64K because
the compiler, linker and runtime programs are divided into a code segment
and a data segment. In ARUN, the data segment holds the compiled Algol code
and the variables. This allows all the library functions which use
addresses, (such as LOCATION, ATEXT etc.) to work exactly as on the Z80,
because only 16 address bits are needed. Since all disk transfers and indeed
BDOS and BIOS calls are data-segment relative, these also function in a way
very similar to CP/M-80.
The pre-declared function OPSYSIDCODE can be used to find out which
operating system is being used. Alternate code can then be executed for
system-dependent parts of the program.
3.2 Extra features available on the 80x86 versions
3.3 Using extra data memory beyond 64K
Using extra data memory beyond 64K
The normal data area is limited to 64K to allow all the library procedures
to work. Memory above this limit can be used to hold arrays by giving them
the EXTRA attribute. Compatibility with Z80 Algol is maintained as the EXTRA
attribute is ignored under CP/M-80. The library procedures which manipulate
addresses do not work on EXTRA arrays. A fast copy mechanism between the 64K
block and extra memory is provided.
If the symbol EXTRA is placed after an array declaration it has the
following effect at runtime: The dope vector for the array stays in its
usual place, but the last word of it, instead of containing the address of
the byte beyond the end of the array, contains the paragraph (segment)
address of the start of the array in extra memory. It follows that extra
arrays always start on a 16 byte boundary and that there is no check for a
subscript being over the top of the declared bounds. Algol statements to
fetch and store the elements of extra arrays work exactly as for normal
arrays. However, LOCATION will return the wrong address and the library
procedures depending on it cannot work. A new call, ioc(66), has been
introduced to allow copying between normal and extra memory, analogous to
BLMOVE.
ioc(66) must be called from within a procedure having four parameters as
follows:
1. An integer by value. This is zero to copy from the 64K area to extra
memory and non-zero to copy the other way.
2. An integer by value specifying the number of bytes to copy.
3. An integer by value which is the LOCATION of the start of the block in
normal memory.
4. A Boolean or real (as appropriate) by value to give the address in extra
memory. The actual value is not used. The runtime program keeps a record of
the last calculated address in extra memory and it is this address which is
important. An example:
INTEGER ARRAY normal[1:10] ;
INTEGER ARRAY EXTRA extra[1:10] ;
PROCEDURE copymem(d,l,n,e);
VALUE d,l,n,e; INTEGER d,l,n; REAL e; ioc(66);
{Copy from normal to extra memory}
copymem(0,20,location(normal[1]),extra[1]);
ioc(67) returns the number of remaining free paragraphs (units of 16 bytes)
and it must be called from within an INTEGER PROCEDURE. The result of the
function is the free paragraph count. You should always make this call
because the amount of free memory varies with the number of programs loaded
and can be zero.
3.4 The Runtime Debugger
The Runtime Debugger
A major new facility available on the 16-bit Algol versions is the
trace/debug module. Space considerations led to the decision not to add it
to the Z80 version. It is activated by appending the [D] switch to the
loader input file. The switch is not preserved across a chain. The filename
supplied to the chained program mechanism must have [D] appended to cause
the chained program to be run with debugging facilities. The switch [W] is
used to activate both debugging and force FCB file system calls.
The format of the compiler listing file has been changed to include a
display of the program pointer at the end of each statement. Line numbers
appear at the left margin, as before, but are terminated by '>' to delimit
them more clearly from the program text (and identifier table if one is
begin produced). The program pointer appears as a decimal number enclosed by
'< >'. This is relative to the start of the program. It corresponds to the
last number of the identifier table. When modules are linked you will have
to add the starting location of each module, as given by the linker. The
addresses in the root module will be correct.
After loading a program with the [D] switch, the debug module announces that
the stack area has been set up, and gives the limits of the loaded program
in HEX relative to the DS register. Next the first 100 bytes of the program
are listed. You can use this to check that the expected code has been
loaded, by comparing the numbers with the compiler output, which is legible
if the -B directive is used.
The debugger works by stopping the program when a chosen address is reached,
allowing the user to examine the memory before execution is resumed. The
breakpoint 2 is set initially. This is the address of the first opcode, so
in effect the debugger is entered before any codes are executed.
When a breakpoint is reached, two lines are output to the terminal. The
first shows the current program pointer, followed by the next few bytes of
the Algol program. The first byte is the opcode which is about to be
executed. The second line shows which procedure number is currently being
executed, and then gives a list of the available command letters. For
example:
Algol PC, opcodes 2, 29 42 35 1 1 1 8 1 Procedure 0 G/T/R/D/V/M/B/S ?
This is the initial breakpoint at location 2. The first opcode is 29, set
zero on the stack. Next comes 42, set 6 bit constant, so 35 will be set on
the stack. Next comes opcode 1, declare an array. The full meaning of the
opcodes and their parameters can be found at the end of the user manual.
Since the program is just starting the procedure number is 0, the main
program level. The possible commands will now be described. The letters may
be typed in upper or lower case.
Command G. This means GO to the next breakpoint. The breakpoint is set by
typing a decimal number immediately after the G. Any non-digit will
terminate the number. If you make a mistake type DELETE (RUBOUT) before any
terminator, and then re-type the whole number. Using the example above, G5
will cause the program to stop before declaring the array. If you type no
number or 0, the effect is that no breakpoint is set, because the first
executable opcode is at location 2. The program will run normally and never
re-enter the debugger.
Command T. This means Trace. One opcode will be executed and the debugger
will be re-entered. Command R. Reset the variable stack pointer to the top
level. See next command D for an explanation.
Command D. Descend one level down the variable stack. When the program is
interrupted, the variables available for examination are those belonging to
the currently executing procedure as indicated on the second line of the
display Command D causes the variables of the calling level to be made
available. The command has no effect if the current level is 0. Successive
commands D will eventually result in level 0 being available. Command R
resets the top level as the available one. It is not possible to go back up
one at a time because the stack does not hold the required information (it
is never needed to run the program). Commands G and T cause an automatic
command R.
Command V. Examine a variable. Follow immediately with the decimal number of
the variable to be examined. The variable numbers may be obtained from the
identifier list output by the compiler. The variable will be printed both as
an integer and as a floating point number. If the variable does not conform
to the standard floating point format a row of asterisks is printed. If the
variable is an array it will point to the dope vector and elements which can
be examined using the M command to be described next.
Command M. Examine memory as a series of words. Follow immediately by a
decimal address. The contents of 16 words will be displayed, starting at the
address given. Each is shown as an integer and a floating point number. Most
useful for looking at the contents of arrays.
Command B. Examine memory as a series of bytes. Follow immediately by a
decimal address. Sixteen bytes will be printed first in decimal and then as
an ASCII string.
Command S. Display the Algol stack. Each stack item is shown as an integer
and a floating point number. The top of the stack that is the last value set
is printed first. Only the items deposited by the current procedure will be
displayed. Using the example above, if the program is stopped at location 5
the values 35 and 0 will be on the stack.
If an error occurs which has not been trapped by the programmer, then DEBUG
is entered after the error message has been output. The Algol program
counter is reset to point at the opcode which caused the error. You can
examine the memory, but do not try to restart the program. The already
partly executed opcode will have altered the contents of memory. You can
determine from the location given in the error message how many bytes of
program after the opcode had been used as parameters.
3.5 File handling under PCDOS and MSDOS
3.6 Overview of MSDOS file handling
Overview of MSDOS file handling
MSDOS and PCDOS have a file interface which has a high degree of
compatibility with CP/M and CP/M-86. Although there are differences in
detail, the Algol programmer has been isolated from them, so that with three
exceptions the system runs exactly as under CP/M. The exceptions will not
affect most programs. The compatibility between CP/M and PCDOS/MSDOS extends
as far as the device names in Algol command lines, (e.g. KBD: and VT:),
because these are interpreted as appropriate system calls. To access the
AXO: and AXI: devices, use the names RDR: and PUN:. Rogalgol makes no BIOS
calls, so results are identical under MSDOS and PCDOS.
The known exceptions to CP/M-MSDOS compatibility are
1. CP/M supports sparsely populated random access files, whereas MSDOS does
not. This will only affect your programs if you write to random access files
with high record numbers, expecting the gaps below not to occupy physical
disc space. Under CP/M no physical space is required; under MSDOS it is.
2. MSDOS will not create duplicate files of the same name on the same
device. If you open a file for output, any previous file of that name is
deleted. CP/M creates another file of the same name. IOC calls 13, 14, and
15 have no effect under PCDOS and MSDOS.
3. The CP/M system pads out the last record of a file with CONTROL-Z. MSDOS
pads with zeros, possibly after a single CONTROL-Z. Sequential files written
by Algol are padded with CONTROL-Z on both systems. Algol returns -1 at
physical end of file on both systems.
3.7 File Control Block usage
File Control Block usage
Under MSDOS-PCDOS, the operating system version number is tested before any
file operations. If the version is 1, then the CP/M type system calls using
File Control Blocks are utilised. Under version 2 or later, the newer
Pathname XENIX compatible calls are used by default. System calls in the
range 0FH to 24H are converted to calls in the range 3AH to 42H and 56H.
This applies only to calls made by the interpreter out of programmer
control. Direct calls using the CPMD function are not intercepted, except
that function 0 (exit program) is converted to 4CH.
This change is transparent to the Algol programmer and has been made because
Microsoft suggests that the old calls should not be used. Compatibility
across all versions is maintained because the Algol still uses an FCB, with
slightly altered contents. For MSDOS-PCDOS versions 2 up, the filename at
the start of the FCB is used to create a pathname in a dedicated area of
memory. The FCB of an open file contains the handle at offset 18H. For
random access operations, the record number at offset 21H is used to
calculate the required byte offset into the file. The price of compatibility
is that you cannot used full pathnames with the INPUT and OUTPUT
pre-declared file opening mechanism. You must use the CPMD function to use
full pathnames.
For testing purposes, it is possible to force the interpreter to use the FCB
calls. The switch [V] is added to the loader input file to achieve this, but
the loader itself will always use the new XENIX calls. Note that the switch
setting is not preserved across a chain, which starts the interpreter right
from the beginning, including all installation. The Algol program can
propagate the switch, if required. The 'V' switch is useful for checking
that file handling problems really were due to using the FCB system calls.
3.8 The operating system interface
3.9 Overview of BIOS and SYSTEM calls
Overview of BIOS and SYSTEM calls
BIOS calls are made using the library function BIOS or lOC(47) within an
Integer Procedure. There is a high degree of compatibility between CP/M and
CP/M-86. The BIOS mechanism is quite different under MSDOS and CP/M; they
are therefore TOTALLY INCOMPATIBLE, BIOS calls copied from CP/M will not
work at all under MSDOS.
BIOS calls under CP/M-86
Functions less than 21 (decimal) are converted to the BDOS function 50 in a
way transparent to the programmer. The results are exactly the same as using
the BIOS library routine under CP/M-80.
Under CP/M-86, BI0S functions higher than 20 are interpreted as a request to
make an interrupt. The mechanism is exactly like the MSDOS interrupt request
calls described in the next topic, except that there is no special action
for interrupts 37 and 38 (25H and 26H). Under CP/M-80 BIOS functions higher
than 20 produce unpredictable results,
BDOS (system) calls under CP/M86
The BDOS functions which return an address in BX (27 1BH) and 31 (1FH)) are
intercepted by the IOC(48) routine called by the library function CPMD.
Special treatment is required because the addresses of ALLOC and DPB have a
different segment base to the Algol variable area. To overcome this problem,
the second parameter of the procedure in which IOC(48) is called is used to
pass an address to which the DPB or ALLOC vector is copied. If the second
parameter is zero the information is not copied and no meaningful results
are returned. It is the programmer's responsibility to ensure that a valid
address (an array or the common area) is used and that the array (or the 256
byte common area) is big enough. The DPB is fixed in size and should be
examined first to calculate the array size required for ALLOC. The problem
does not arise with the DMA transfer address as its segment base is the same
as that of the Algol variables.
Calls to BDOS (system calls in MSDOS terminology) using the CPMD library
routine or IOC(48) are in many case compatible across all the operating
systems. BDOS and BIOS calls under MSDOS are described the next topics.
3.10 BDOS (system) calls under MSDOS/PCDOS
BDOS (system) calls under MSDOS/PCDOS
There are three groups of system calls to be considered:
(a) Those with function number 36 or less are CP/M compatible, although some
(such as 27 and 31) are meaningless under MSDOS/PCDOS. Care is required
because of slight differences from CP/M.
(b) Those with higher numbers are specific to MSDOS and will not occur in
programs written for CP/M. A new calling mechanism using a register array
has been provided. Calls less than 56 (38H) return their result in AL and
this register is returned as the result of CPMD, as for group (a).
(c) System functions 38H (56D) and higher numbers use the carry flag to
indicate success or failure. Instead of returning AL, the CPMD function
returns the status register. The value of AL can still be found in the
register array.
Further details on how to use these three groups follow.
(a) CP/M compatible system calls.
A careful study should be made of the MSDOS Operating System Programmers
Reference Manual, as the slight differences between the systems (mainly with
regard to return codes) may affect the running of the program. Some
differences do not matter, for example the SEARCH FIRST and SEARCH NEXT
functions return 0, 1, 2 or 3 for success under CP/M but always 0 under
MSDOS.
None of the CP/M compatible calls returns information except in the register
AL; this is returned to the Algol program as the value of the function CPMD,
as under CP/M-8O.
(b) MSDOS functions higher than 36 (24H) must use a new calling sequence.
The first parameter of CPMD is the function code as for CP/M compatible
calls. You must provide as the second parameter of the procedure calling
IOC(48) the address of a block of 4 words (a 4 element integer of 8 element
byte array is most convenient). The registers are set up from this block
before the function is called, and their values on return are placed there.
The function code to be in AH is however obtained from the first parameter
and not the register array. The lowest address contains AX, followed by BX,
CX and DX in that order. The least significant byte is at the lower address
if you use a byte array. A few functions (get time, get date, get free space
on disc) do not require any registers to be set up on entry.
(c) MSDOS function calls higher than 55 (37H)
The success/error status of these functions is returned in the carry
register, not AL. To allow them to be used with the CPMD direct system
function call, the return value is the flag register. A register array is
used in the way just described under group (b) and the returned value of AL
can be found there. The carry is the least significant bit of the flag
register, so doing MASK 1 will result in zero for success or 1 for an error.
This mechanism allows nearly all the system functions to be called from
Algol. The exceptions are those which require values to be set in SI or DI
or ES. This group includes 56H, rename. However, the Algol library function
RENAME can be used. By using CPMD for file access instead of the built in
mechanism, full pathnames may be utilised for all function except RENAME.
3.11 BIOS calls under MSDOS/PCDOS
BIOS calls under MSDOS/PCDOS
Under MSDOS/PCDOS the Algol BIOS call generates an interrupt. The two
parameters of the BIOS library routine are (1) the interrupt number and (2)
the address of an array containing the desired contents of the registers on
entry. This latter is set up in the same way as described above under 'MSDOS
functions higher than 36'. However, unlike the BDOS call, register AH is set
up from the register array. On return, the register array is updated to
reflect the new contents of the registers. The result of the function is the
hardware flag register on return from the interrupt, as for system function
calls higher than 37H.
Under MSDOS/PCDOS, but not CP/M-86, the stack is balanced by POPing the
flags after interrupts 37 and 38, absolute sector disc read and write. This
is as described in the MSDOS Programmer's Reference Manual. These interrupts
return the success code in the C flag, which is why the value of BIOS
function is the flag register and not AX.
3.12 Compiling and linking the 80x86 Rogalgol executables
The runtime interpreters and the linker are constructed by including source
modules with the extension .INC. These files are identical for CP/M-86 and
MSDOS, except for two short ones which have different names under the two
operating systems. The subset compiler has been changed so that it can
generate code for both 16 bit systems and CP/M-80. You are asked to type a
number on the terminal indicating the target system.
1. CP/M-86
This system has no object module linker, so the necessary include files all
appear in the same assembly.
1a. Subset and full compiler
Assemble CRUN.A86. Run ICOM to generate ICOM86.A86 from ICOM.IAG. The origin
is 1772. Assemble the resulting file to get ICOM86.M86. Then concatenate
using PIP:
PIP ICOM.M86=CRUN.M86[I],ICOM86.H86[H]
Then generate the command file by
GENCMD lCOM DATA[XFFF]
The procedure for the full compiler is exactly the same, except that you use
ALGOL instead of lCOM and you must leave off the [H] switch. PIP runs out of
memory and reports a non-existent error. The [I] switch on CRUN.H86 is
vital.
CP/M-86 Runtime and linker programs
Assembly is straightforward:
ASM86 ARUN $Ax Hx Px Zx (x is your choice of device)
In generating the runtime program you can choose how much memory is to be
allowed for the compiled algol + data area. The following line is
appropriate if you do not require EXTRA arrays:
GENCMD ARUN DATA[XFFF]
To allow as much memory as possible use XFFFF instead of XFFF. The hex
number following X is the maximum number of paragraphs required.
The procedure for ARUNL is Identical. The two linkers are generated in a
similar way, but there is no point in giving them more than FFF paragraphs:
2. MS-DOS
The assembler cannot take long source files, but a linker is provided. The
system is therefore put together by linking object modules. The source files
for the modules use the same include files as the CP/M version.
2a. The subset compiler
This is CRUN+AIOPAK+ICOM. The first two are obtained by using MASM on the
source files of the same name. ICOM.OBJ is obtained by assembling ICOM.ASM,
which is in turn the output of ICO.EXE compiling ICOM.IAG. The origin to
give for MS-DOS is 1772. Note that you cannot use the same version of
ICOM.ASM as under CP/M86 because the source code conventions are different.
2b. The full compiler.
The procedure should in theory be the same as for the subset compiler.
However, MASM may not assemble ALGOL.ASM because it's too big. If so it must
be pre-processd by the program LABELS. This removes all symbolic labels,
reducing the subset compiler output to a list of pure decimal numbers. Call
it like this:
LABELS ALGOLX.ASM=ALGOL.ASM
It will ask OUT-IN? like other Algol programs, If it can't open the files
(or if you called it without giving any).
Either ALGOL.ASM itself or the output of LABELS can be assembled by MASM.
Then link it with CRUN+AIOPAK.
CRUN+AIOPAK+ALGOL(X)
2c.The MS-DOS runtime programs and linkers.
These are linked using object modules as follows:
All object modules are obtained by assembling the source file of the same
name.
ARUN+AFPP+AIOC+AFINT+AIOPAK for the floating point interpreter.
ARUN+ALIAP+AIOC+AFINT+AIOPAK for the long integer interpreter.
ARUN+AIOC+AFINT+AIOPAK for the floating point linker.
ARUN+ALIAP+AFINT+AIOPAK for the long integer linker.
The MSDOS linker automatically generates a .EXE file which, when loaded,
claims all available memory.