4466 lines
195 KiB
Plaintext
4466 lines
195 KiB
Plaintext
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.
|