^^
.TITLE BCPL/RSX11D USER MANUAL
.SKIP
.SPACING 1
.FIGURE 10
.CENTER
BCPL/RSX11D USER MANUAL
.CENTER
-----------------------
.SKIP
.CENTER
Jeremy Holden
.CENTER
Department of Computer Science
.CENTER
University of York
.CENTER
June 1976
.BREAK
^^
.BREAK
.LEFT MARGIN 6
.PAGE
.LEFT MARGIN 0
.SPACING 1
.NOFILL
.CENTER
CONTENTS
.CENTER
--------
.SKIP 2
Page
3    Introduction
5    The compiler
11   The translator
13   Operating procedures
16   Store utilisation
18   The object-time environment
24   The runtime system
30   Task building BCPL programs
32   References
33   Appendix A - The character set
34   Appendix B - Files used in the system
35   Appendix C - Changes for RSX11D version 6
36   Appendix D - The BCPL library
39   Index
.FILL
.LEFT MARGIN 6
.PAGE
.PARAGRAPH -6
1#####INTRODUCTION
.BREAK
------------
.SKIP
BCPL is a programming language originally developed
and implemented by Martin Richards at MIT project MAC for systems
and non-numerical programming.  It originated as a subset of CPL,
the never-implemented language designed for TITAN by Barron et.
al. [0], and is notable for its varied and efficient
control constructs and for the fact that it is a typeless language.
The BCPL compiler is written in BCPL and produces object code
(OCODE) for an idealised stack machine.  The compiler is therefore
portable and can be transported by the provision of an OCODE
translator for the target machine.  It was by this method that BCPL
was first implemented in England, at Cambridge, and it is from this
implementation, via ICL 4130 implementations at Warwick and York, that
the present system is derived.  The BCPL language is defined in the
BCPL Reference Manual [1] and an extended version of the language
in use at Essex is defined in [2] .  This document describes an 
implementation of BCPL to run on a PDP-11/40 or 45 under the RSX-11D
V004A operating system [3].
.PARAGRAPH -6
1.1###Contents of the system
.BREAK
----------------------
.BREAK
The BCPL system consists of two programs, the compiler and the
translator, and run-time library modules.
.PARAGRAPH -6
1.1.2#The compiler
.INDEX COMPILER
.SKIP
The compiler compiles BCPL into OCODE, optionally producing a
source listing.  It is a modified and extended version of the 4130
BCPL compiler and is described in detail in Section 2.
.PARAGRAPH
1.1.3#The translator
.INDEX TRANSLATOR
.SKIP
The translator takes as input an OCODE program and produces as 
output a translation of the program into MACRO-11.  The MACRO-11
produced is compatible with the RSX assembler [4], although it is
incompatible with earlier DEC assembler formats such as
DOS and RT-11.  The translator is described in Section 3.
.TEST PAGE 6
.PARAGRAPH -6
1.1.4#Runtime library modules
.INDEX Runtime system
.SKIP
The runtime library consists of object modules which provide various
I/O, store management and other facilities.  The modules are collected
in an object code library and the required modules are linked with a
BCPL program at# task-build# time using the DEC task builder [8]
(see Section 8) to form a runnable program.  The modules are written in
BCPL and MACRO-11 and are described in Section 6 and Section 7.
.PAGE
.PARAGRAPH
2#####THE COMPILER
.INDEX COMPILER
.BREAK
------------
.SKIP
The BCPL compiler is written in BCPL and consists of two parts,
the parser which reads in BCPL source one segment at a time 
producing a tree, and the codegenerator which walks the tree producing
OCODE from it.  The compiler
was developed from that used on the 4130  at York, which is 
described in [5] and [6].  The main changes to the compiler were to
the machine dependant parts at the beginnning of the parser which
deal with I/O and wordlength-dependent routines.  Other changes
to the compiler involve  a number of enhancements and extensions
.PARAGRAPH
2.1###Enhancements to the 4130 compiler
.BREAK
---------------------------------
.PARAGRAPH
2.1.1#Constant expressions
.SKIP
The compiler completely evaluates all compile-time evaluable
expressions (that is, all those expressions consisting of manifest
constants, constant denotations and any BCPL operators other than
%, LV, RV).
.PARAGRAPH
2.1.2#Conditional compilation
.INDEX Conditional compilation
.SKIP
The compiler performs conditional compilation. That is, all conditional
expressions (using ->) and commands (IF, UNLESS, TEST) whose conditions
are evaluable at compile time do not produce any code for the test
and unsatisfied condition.
.PARAGRAPH
2.2###The GET facility
.INDEX GET command
.BREAK
----------------
.BREAK
Textual insertions from another file may be made into the BCPL
source text.  This allows global and manifest declarations to be
filed and inserted where needed.
.SKIP
A GET command is used to specify where the insertion is to occur.
The syntax of a GET  command is:-
.SKIP
######<command>#::=#GET#<string>
.SKIP
e.g. GET "DK1:BCPLIB.GET"
.SKIP
Semantically, the string is treated as an RSX dataset identification
and the whole of the line containing the GET statement is replaced by
the contents of the specified dataset.
.SKIP
Any number of GET commands may occur within a segment, with the
proviso that they cannot be nested.  That is, the replacement text
may not contain another GET command.
.PARAGRAPH
2.3###The character set (Appendix A)
.INDEX Character set
.BREAK
-----------------
.BREAK
BCPL uses the full ASCII character set (even those which have no 
equivalents in the 4130 character code).
.PARAGRAPH
2.3.1#Lower case letters
.INDEX Lower case letters
.SKIP
Identifiers may use lower case letters, as well as upper case,
digits and full stops.  They are regarded as being different from
the corresponding upper case characters.  All BCPL keywords must
be in upper case.
.PARAGRAPH
2.3.2#Braces {, }
.SKIP
These may be used synonymously with the BCPL section brackets, 
$( and $), and may be similarly subscripted with an identifier.
There is a problem however, in that the present version of the
teletype handler for RSX-11D will not print }.
.PARAGRAPH
2.3.3#Tilde ~
.SKIP
The tilde symbol may be used in place of the keyword NOT.
.PARAGRAPH
2.3.4#Vertical bar |
.SKIP
The vertical bar character may be used as the vector
application operator in place of %.
.INDEX Language extensions
.PARAGRAPH
2.4###Language extensions
.BREAK
-------------------
.BREAK
The compiler accepts a number of the more useful language extensions
as defined in [2].  These are:-
.PARAGRAPH
2.4.1#Comments
.INDEX Comments
.SKIP
As well as // which causes the remainder of the line to be treated
as comment, the compiler also accepts /* and */ which may be used
in pairs to delimit comments which may span one or more lines.
.TEST PAGE 5
.PARAGRAPH
2.4.2#Infix function calls
.INDEX Infix functions
.SKIP
The construct A !PLUS B is equivalent to the function call PLUS(A,B).
In general, any identifier preceded by  '!' is treated as a dyadic
operator with the same precedence as vector application, and the
result is the value of the function call on the identifier, using
the two operands as parameters.
.PARAGRAPH
2.4.3#Very binding semicolon
.SKIP
This is used to join two commands together as if they were a section.
There are two representations of it, ;; and <>
.SKIP
e.g.#####IF#I<10###I#:=#I+1<>WRITEN(I)
.SKIP
is equivalent to
.SKIP
#########IF#I<10###$(#I:=I+1;#WRITEN(I)#$)
.PARAGRAPH
2.4.4#LOOP command
.INDEX LOOP command
.SKIP
This  is similar to a BREAK command, in that it can occur inside the
body of any loop (FOR, UNTIL, WHILE, etc) command.  Whereas a 
BREAK transfers control to the command immediately after the loop
command, a LOOP transfers control to the point immediately before
the condition is evaluated.
.NOFILL
 
e.g.     FOR I=1 TO 10 $( IF V%I=0 LOOP; V%I:=3 $)
 
.FILL
is equivalent to
.NOFILL
 
         FOR I=1 TO 10 $( IF V%I=0 GOTO L
                          V%I:=3
                        L: $)
 
.FILL
.PARAGRAPH
2.4.5#WHERE statement
.SKIP
This statement is used to associate a declaration with a command
and is of the form
.SKIP
######<command> WHERE <declaration>
.SKIP
The scope of the declaration is the preceding command. A WHERE can
introduce any declaration which can be used in a LET statement.
.NOFILL
 
e.g.#####x:=f(7) WHERE f(n) = (n=0)->1, n*f(n-1)
 
.FILL
Since a WHERE command can be used to declare routines, the keyword
IS is allowed (universally) as a synonym for BE.
.PARAGRAPH
2.5###CODE statement
.INDEX Code statements
.BREAK
--------------
.BREAK
Code statements containing MACRO-11 bodies may be used  within
BCPL programs.  Code statements may be used syntactically as 
either commands or expressions.  In either case the syntax is
.SKIP
######CODE#<string>
.SKIP
In the case of code statements which are used as expressions, the
value of the code statement is that which is held in the result
register on exit from the code body.  The result register is R0.
.NOFILL
 
e.g.  LET LEVEL() BE
       CODE "    MOV    2(P), R0
                 ASR    R0"
.INDEX Register usage
 
or    X := CODE "    MOV    _#4, R0" * 3 
      /* X should now be 12 */
.SKIP
.FILL
[ See Section 6.3 ]
.PARAGRAPH
2.6###Compiler output
.INDEX Compiler output
.BREAK
---------------
.BREAK
During the first pass over each segment the source is 
listed as it is read in, if a listing dataset is specified. The 
listing is provided with sequential line numbers. At the end of
the first pass  the compiler prints 
.SKIP
#######TREESIZE#=#n
.SKIP
where n is the decimal number of words in the tree. At the end of the
program, provided the compilation was error free, the compiler prints
.SKIP
#######COMPILATION#OK#-#0#ERRORS
.SKIP
.TEST PAGE 3
otherwise it prints
.SKIP
#######COMPILATION#FAILED#-#n#ERRORS
.PARAGRAPH
2.7###Error handling
.INDEX Compiler error handling
.INDEX Error handling
.BREAK
--------------
.BREAK
There are three classes of error which the compiler recognises.
These are:
.PARAGRAPH
2.7.1#Irrecoverable errors
.SKIP
These include syntax errors in the command string (See Section
4.1), or failure to open any of the datasets in the command string.
The compiler prints an appropriate message, immediately terminates
compilation and reissues its prompt.
.PARAGRAPH
2.7.2#Syntax errors
.INDEX Syntax errors
.SKIP
These are errors discovered by the parser. The compiler issues a
message of the form:
.SKIP
.NOFILL
        53 IF I = 10 BREAK
                         _^
       *** SYNTAX ERROR, ILLEGAL USE OF BREAK 
.FILL
.SKIP
If no listing is being produced the offending source line is issued
on the console. The arrow marks the point which the parser had
reached when the error was detected.
.PARAGRAPH
2.7.3#Semantic errors
.INDEX Semantic errors
.SKIP
These are errors  discovered during the codegeneration phase.
As the compiler no longer has the source text of the program available it
prints out the number of commands it had compiled so far in the
current segment. The error message is of the form:
.NOFILL
.SKIP
       *** ERROR, NAME NOT DECLARED
       XYZ
       COMMANDS COMPILED = 17
.FILL
.SKIP
Syntax  and semantic errors are not immediately regarded as
fatal; compilation and output of OCODE continue until a maximum of six 
errors are reported whereupon the compilation is terminated.
.PARAGRAPH
2.8###Limitations
.INDEX Compiler limitations
.BREAK
-----------
.BREAK
A number of items have statically determined maximum values
which will cause errors if they are exceeded. 
Violation of any of these limits is regarded as an irrecoverable
error, and compilation is aborted with a suitable  error message.
They are:
.PARAGRAPH
2.8.1#The tree
.SKIP
At the moment 5500 (decimal) words are allocated to the tree.
This should be enough for at least 200 lines of BCPL. If a segment
causes the tree to be filled then the segment must be split into
smaller units.
.PARAGRAPH
2.8.2#Strings
.SKIP
Strings are permitted a maximum length of 255 characters.
.PARAGRAPH
2.8.3#Globals
.SKIP
The maximum number of global variables allowed in a segment is 100,
although more than this can be used in a program.
.PARAGRAPH
2.8.4#Identifiers
.SKIP
Identifiers may not exceed 100 characters in length.
.PARAGRAPH
2.8.5#Cases
.SKIP
The maximum number of cases in a SWITCHON command is 150.
.PAGE
.PARAGRAPH
3#####THE#TRANSLATOR
.INDEX TRANSLATOR
.BREAK
--------------
.SKIP
The translator, which is also written in BCPL, accepts OCODE
as input and produces MACRO-11 as output.
.PARAGRAPH
3.1###The CODE instruction
.INDEX Code statements
.INDEX Code instruction
.BREAK
--------------------
.BREAK
The repertoire of OCODE instructions accepted by the translator
has been extended to cover a new instruction, the CODE instruction.
This has the format
.SKIP
.NOFILL
        CODE n
        C1 C2 C3 .. Cn
.FILL
.SKIP
where the Ci are the decimal equivalents of the ASCII characters
forming the body of a BCPL CODE statement.
.PARAGRAPH
3.2###Optimisation
.INDEX Translator optimisation
.BREAK
------------
.BREAK
The translator attempts various optimisations on the code it 
produces. The topmost elements of the OCODE stack are kept in
registers whenever possible, to produce faster and more
compact code. Conditional branch instructions are generated
wherever possible, again reducing the code length. Various redundant
operations generated by the compiler are trapped and removed.
.PARAGRAPH
3.3###Elaboration of assignments
.BREAK
--------------------------
.BREAK
BCPL constructs such as
.SKIP
.NOFILL
       LET I = 3
       AND J = I + 1
 
or     A, B := A+1, A-1
.FILL
.SKIP
have no defined order of elaboration and different translators
may produce code which gives different ordering for the elaboration
of multiple assignments. Due to the optimisation algorithms
employed by the translator the code it produces will not necessarily
elaborate the assignments in textual order. On the other hand
when the ordering of statements is explicitly stated e.g.
.SKIP
.NOFILL
       LET I = 3
       LET J = I + 1
 
or     A := A+1; B := A-1
.FILL
.SKIP
the translator produces code to elaborate the assignments in 
textual order.
.PAGE
.PARAGRAPH
4#####OPERATING PROCEDURES
.INDEX OPERATING PROCEDURES
.BREAK
--------------------
.PARAGRAPH
4.1###Running the compiler
.INDEX Compiler commands
.BREAK
--------------------
.BREAK
The compiler is an MCR task and
as such can be run by replying to MCR with:
.SKIP
.NOFILL
       MCR>BCPL command string
or
       MCR>BCPL
       BCPL>command string
 
.FILL
A command string for the compiler may be an indirect command 
(see 4.3) or in the form
.SKIP
.NOFILL
       ocode, listing/switches = bcpl1, bcpl2, ...
.FILL
where
.NOFILL
.BREAK
#######ocode###is the OCODE output dataset (optional)
#######listing#is the listing dataset (optional)
#######bcpl1###is the BCPL source dataset
#######bcpl2..#are BCPL datasets (optional)
.FILL
.SKIP
The usual RSX conventions for default device names and UICs
apply to  all command strings.
.PARAGRAPH
4.1.1#Default file extensions
.INDEX Default extensions
.SKIP
.NOFILL
#######OCODE output####.OCO
#######Listing output##.LST
#######BCPL source#####.BCL
.FILL
.SKIP
The default file name and extension for GET datasets (See 2.3)
is
.SKIP
#######BCPLIB.GET
.PARAGRAPH
4.1.2#Switches
.INDEX Compiler switches
.INDEX TR compiler switch
.INDEX NA compiler switch
.INDEX GE compiler switch
.SKIP
All switches apply to the listing dataset only.
.SKIP
.NOFILL
#######/TREE##print tree for each segment
#######/NAMES#print names used in each segmaent
#######/GET###list replacement text for GET commands
.FILL
.SKIP
.BREAK
Only the first two letters of each switch are mandatory.
If no listing dataset is specified  the first two switches
will still cause the tree or names to be printed (on CO:)
but the GET switch will be ignored.
.TEST PAGE 6
.PARAGRAPH
4.1.3#Examples
.SKIP
.SKIP
.NOFILL
#######BCPL>DK1:TEMP=DK1:TEMP
.FILL
.SKIP
Compiles the program in DK1:TEMP.BCL and produces the output
file DK1:TEMP.OCO. No listing is produced.
.SKIP
.NOFILL
#######BCPL>DK1:TRAN,TT:/GE=DK1:TRAN1,DK1:TRAN2
.FILL
.SKIP
Compiles the source in DK1:TRAN1.BCL and DK1:TRAN2.BCL
into the OCODE file DK1:TRAN.OCO. A listing of the source
including expansion of GET commands is produced on TT:.
.PARAGRAPH
4.2###Running the translator
.INDEX Translator commands
.BREAK
----------------------
.BREAK
The translator is an MCR task  and can be run by
replying to MCR with:
.SKIP
.NOFILL
#######MCR>TRAN command string
.SKIP
or#####MCR>TRAN
#######TRAN>command string
.FILL
.SKIP
A command string for the translator may be an indirect command
(see 4.3) or in the form
.SKIP
.NOFILL
#######macro/switches = ocode1, ocode2, ...
.FILL
.SKIP
where
.BREAK
.NOFILL
#######macro#######is the MACRO-11 output dataset
#######ocode1######is the OCODE source dataset
#######ocode2#...##are more OCODE datasets (optional)
.FILL
.SKIP
Note that an OCODE segment
can not cross dataset boundaries. Also, the translator
produces a complete MACRO program module so that
separately translated OCODE segments must be separately
assembled.
.PARAGRAPH
4.2.1#Default file extensions
.INDEX Default extensions
.SKIP
.NOFILL
#######MACRO-11#output####.MAC
#######OCODE#input########.OCO
.FILL
.TEST PAGE 10
.PARAGRAPH
4.2.2#Switches
.INDEX Translator switches
.INDEX TI translator switch
.INDEX ID translator switch
.INDEX PS Translator switch
.SKIP
Switches may only be supplied to the output dataset.
They are
.SKIP
.NOFILL
#######/TI:title          Program title
#######/ID:ident          Program identification
#######/PS:psect          Psect name for code
.FILL
.SKIP
All values are ASCII strings of upto 6 characters. If not supplied
the title and psect name both default to "BCPL" and the identification
is omitted.
.PARAGRAPH
4.2.3#Example
.SKIP
#######TRAN>DK1:TEMP/TI:XTASK=DK1:TEMP
.SKIP
The OCODE file in DK1:TEMP.OCO is translated into a MACRO-11
module with the title 'XTASK', and stored in DK1:TEMP.MAC.
.PARAGRAPH
4.3###Indirect command files
.INDEX Indirect commands
.BREAK
----------------------
.BREAK
Both the compiler and the translator accept one level of
indirect command files. For example, the two BCPL programs
TSYN and NTRN will be compiled in response to the single command
.SKIP
#######BCPL>@BCPLCOM
.SKIP
where the file BCPLCOM.CMD conntains
.SKIP
.NOFILL
#######DK1:TSYN=DK1:TSYN
#######DK1:NTRN=DK1:NTRN
.FILL
.PAGE
.FILL
.PARAGRAPH
5#####STORE UTILISATION
.INDEX Storage utilisation
.BREAK
-----------------
.SKIP
The address space of a BCPL task is composed of a number of 
discrete areas: a static storage region (for code,
static data items, I/O buffers) and a dynamic storage 
region (for run-time stack and heap). The address space may also contain
core-resident libraries or global common areas.
The storage map for a BCPL task will usually have the following
form:
.SKIP 2
.NOFILL
         |--------------|
         |              |
         |    SYSRES    |  system resident
         |              |  library
160000 - |--------------|
         |    BLANK     |  unused address
         |              |  space
         |--------------| 
         |              | 
         |     CODE     |  static storage
         |              |  region ( code,
         |              |  static data,
         |              |  I/O buffers)
n - - -  |--------------|
         |              |
         |     HEAP     |  heap
         |--------------|
         |      ||      |
         |      _\/      |
         |              |
         |      /_\      |
         |      ||      | 
         |--------------|
         |              |
         |    STACK     |  runtime stack
         |              |
1000 - - |--------------|
         |      SP      |  processor stack
         |--------------|
         |      ||      |
         |      _\/      |
         |              |
0 - - -  |--------------|  virtual zero
.FILL
.SKIP 2
The positioning of the various areas can be altered using
appropriate Task-Builder directives.
.TEST PAGE 5
.PARAGRAPH
5.1###Static data
.INDEX Static storage
.BREAK
-----------
.BREAK
This consists of all BCPL objects whose extent is that 
of the whole program, i.e. static variables, global variables,
tables and string bodies. The global vector is allocated in a
special psect called GLOBAL which has the attributes 
'global' and 'overlayable'. The other static data items are 
allocated storage in a psect called CONST.
.PARAGRAPH
5.2###Dynamic storage
.INDEX Dynamic storage
.BREAK
---------------
.BREAK
Dynamic storage is required for all objects whose storage
requirements are not statically determined. It is allocated from
two areas, the runtime stack and the heap. All  dynamic storage
must be allocated at task-build time by the directive
.SKIP
.NOFILL
        STACK=n
.FILL
.SKIP
This causes the allocation of n (decimal) words of address space
from virtual 0, of which the first 256 are used for the 
processor stack, and the remainder for the runtime stack
and the heap.
.PARAGRAPH
5.2.1#The runtime stack
.INDEX Stack
.INDEX Runtime stack
.SKIP
This is used for the storage of all local variables. It is 
allocated upwards from the base of the processor stack.
.PARAGRAPH
5.2.2#The heap
.INDEX Heap
.SKIP
This is used for the storage of all dynamic objects
whose extent is not related to the block structure of the 
program. In particular it is used by library I/O modules for
the storage of FDBs, record buffers etc. It is allocated downwards
from the top of the area reserved by the STACK directive.
.PAGE
.PARAGRAPH
6#####THE OBJECT-TIME ENVIRONMENT
.INDEX  Data representation
.BREAK
---------------------------
.PARAGRAPH
6.1###Representation of data
.BREAK
----------------------
.BREAK
As defined in the BCPL language all data items are represented
by single words, which in the case of the PDP11 are of 16 bits
each.
.PARAGRAPH
6.1.1#Integers
.SKIP
These are represented by signed twos complement integers.
.PARAGRAPH
6.1.2#Booleans
.SKIP
.BREAK
.NOFILL
       TRUE is represented by -1
       FALSE is represented by 0
.FILL
.SKIP
When testing a Boolean any non-zero value is regarded as TRUE.
.PARAGRAPH
6.1.3#Labels
.SKIP
All global and static labels, including functions
and routines, are represented by the 16 bit virtual address of the
associated code.
.PARAGRAPH
6.1.4#Strings
.SKIP
These are represented by a word containing 
half the virtual address of the body of the string. The body
of the string is stored as a word containing the length of the
string followed by the characters in the string packed one per 
byte.
.PARAGRAPH
6.1.5#Vectors
.SKIP
These are represented by a word containing half the
virtual address of the zeroth word of the vector.
.SKIP
This seemingly peculiar representation of strings and vectors is
due to the fact that BCPL models a word machine in that consecutive
data items, such as the elements of an array, are assumed
to have consecutive integral addresses (L-values).
The L-value of any word is thus represented by a value equal
to half the true virtual address of the word.
.TEST PAGE 10
.PARAGRAPH
6.2###Addressing of variables
.BREAK
------------------------
.PARAGRAPH
6.2.1#Local variables
.INDEX Local variables
.SKIP
All local variables are stored on the runtime stack. The
form of a stack frame is
.TEST PAGE 17
.SKIP 2
.NOFILL
          |            |
.INDEX Stack
          |   local    | 
          | variables  | 
          |            |
          |------------|
          |            |
          | parameters |
        6 |            |
          |------------|
        4 | proc name  |
          |------------|
        2 |   old P    | 
          |------------|
 P  ->  0 |    link    |   
          |------------|
.FILL
.SKIP 2
The current frame is always pointed to by P (Register 5).
All local variables are referenced by an index off this register.
.SKIP
e.g.###MOV####6(P),#R0####;#GET#FIRST#PARAMETER
.SKIP
The first three words of a stack frame are reserved for
the frame head which consists of:
.BREAK
.LEFT MARGIN 13
.INDENT -7
Link#-#the return address to the point of call of the current 
stack frame.
.INDENT -7
Old#P-#The stack pointer P for the previous environment
.INDENT -7
Name#-#A pointer to the name of the current routine.
.LEFT MARGIN 6
.PARAGRAPH
6.2.2#Global variables
.INDEX Global variables
.SKIP
The global vector is allocated in an overlayable psect called
GLOBAL. The module header produced by the translator contains
the following code:
.SKIP
.NOFILL
###############.PSECT##GLOBAL,OVR
#######GV::
.FILL
.SKIP
All references to global variables are by constant offset from the
symbol GV.
.SKIP
.NOFILL
###############MOV####_#14,#GV+124.###;#GLOBAL 62 := 12
.FILL
.PARAGRAPH
6.2.3#Static variables
.INDEX Static variables
.SKIP
There are two types of static variables in BCPL, label and 
numeric valued variables. These correspond to, in the first case,
label function and routine definitions, and in the second case
to STATIC and TABLE declarations. Storage for static variables
is allocated in the CONST psect. The code produced by
.SKIP
.NOFILL
              STATIC $( S = 3 $)
.SKIP
is            .PSECT  CONST
.BREAK
       L7:    .WORD   3
.SKIP
Referencing this variable by 
.SKIP
              S := 17
.SKIP
gives rise to code of the form
.SKIP
              MOV     _#21, L7
.SKIP
The code produced by
.SKIP
       LABEL: FOR I =1 TO ....
.SKIP
is            .PSECT  CONST
       L9:    .WORD  L7
.SKIP
.FILL
where L7 is the address of the code 
associated with the label 'LABEL'. Referencing such a variable
by 
.SKIP
.NOFILL
              GOTO LABEL
.SKIP
causes the code
.SKIP
              JMP    @L9
.SKIP
to be produced.
.FILL
.PARAGRAPH
6.3###Register usage
.INDEX Register usage
.BREAK
--------------
.BREAK
.NOFILL
R7##(PC)##Normal usage (program counter)
R6##(SP)##Normal usage (processor stack)
R5##(P)###Current stack frame pointer
R4...R0###Allocated by translator as work registers
R0########Result register for returning values from
##########function, VALOF and CODE expressions
.FILL
.TEST PAGE 8
.PARAGRAPH
6.4###Code conventions
.BREAK
----------------
.PARAGRAPH
6.4.1#Function and routine calls
.SKIP
These are performed by code of the form
.SKIP
.NOFILL
              JSR     R0, @GV+120.
.BREAK
              .WORD   24
.SKIP
.FILL
The word following the JSR is used to pass the offset
for the new stack frame (actually the byte offset of
the first parameter of the new frame from the base 
of the current frame).
.PARAGRAPH
6.4.2#Function and routine entry
.SKIP
These produce code of the form
.SKIP
.NOFILL
              .PSECT  CONST
       L9999: .WORD   4
              .ASCII  /NAME/
              .PSECT  BCPL
       L3:    JSR     R1, ENTER
              .WORD   L9999
.SKIP
.FILL
where NAME is the function or routine name, and ENTER
is a library module which performs the entry code.
.PARAGRAPH
6.4.3#Function and routine return
.SKIP
This is performed by the code
.SKIP
.NOFILL
              JMP    EXIT
.FILL
.SKIP
where EXIT is another library module. The result from
a function is always passed in R0.
.PARAGRAPH
6.4.4#Program termination
.SKIP
This is performed by the code 
.SKIP
.NOFILL
              JMP    STOP
.FILL
.SKIP
where STOP is a library module.
.PARAGRAPH
6.5###Extended instruction set (EIS)
.INDEX EIS
.BREAK
------------------------------
.BREAK
Full use is made of the EIS option (which must be present
in RSX systems). The translator generates inline code for
multiplication, division and shifts using the EIS.
.BREAK
Shifts, as defined in BCPL, are logical shifts. Due to the 
peculiarities of the EIS, shifts are undefined except in the
range 0 to 15.
.PARAGRAPH
6.6###Inter-module linking
.BREAK
--------------------
.BREAK
Certain conventions are used to allow programs to be linked with
object modules from object code libraries and to allow the 
production of such modules.
.SKIP
The global variables 1-49 are treated in a special way. Definition
of such a variable, by label function or routine declarations, gives
rise to the definition of a global symbol Gn (n = 1-49)
which is equated to the value of the variable.
.SKIP
.NOFILL
e.g.          GLOBAL $( RTNE:5 $)
              LET RTNE() BE ....
.SKIP
would normally produce the code
.SKIP
              .PSECT  GLOBAL
              .=GV+10.
              .WORD   L7
.SKIP
.FILL
As the variable being defined is in the range 1-49, the code
.SKIP
.NOFILL
              G5==L7
.FILL
.SKIP
is also produced. 
.BREAK
Any global variables in this range which are referenced but
not defined in a module, e.g. by
.SKIP
.NOFILL
              IF Y<3 DO RTNE()
.FILL
.SKIP
cause the generation of the additional code
.SKIP
.NOFILL
              .GLOBL  G5
.FILL
.SKIP
The module which contains this reference will
thus contain an unresolved reference to the symbol G5,
and task-builder will try to resolve this reference
at task-build time by searching libraries for this symbol
and including the associated object module into the
task image.
.PARAGRAPH
6.7###Position independance and reentrancy
.BREAK
------------------------------------
.BREAK
Object modules produced from BCPL programs are
neither position independent nor reentrant
and so can not be used in resident libraries.
.PAGE
.PARAGRAPH
7#####THE RUN-TIME SYSTEM
.BREAK
-------------------
.SKIP
The runtime system consists of a number of object modules which
are stored  in an object-code library, called BCPLIB.
The modules required by a program are linked with it at task-build
time (See 6.6 and 8.2).
.PARAGRAPH
7.2###Utility routines
.INDEX Library modules
.BREAK
----------------
.BREAK
These are MACRO-11 routines referenced by the code of
BCPL programs.
.SKIP
.NOFILL
ENTER         routine entry
EXIT          routine exit
STOP          program exit
GO            program entry
.SKIP
.FILL
The module GO contains the transfer address for the task and
enters the user program at global label 1.
.PARAGRAPH
7.3###Character I/O modules
.INDEX I/O library modules
.BREAK
---------------------
.BREAK
These modules provide simple character based I/O facilities.
They are all written in BCPL. Details of the character set 
are given in appendix A. A listing of the source of
these modules is included in APPENDIX D.
.PARAGRAPH
7.3.1#Streams
.INDEX Streams
.SKIP
The streams used resemble fairly closely those used in the Essex
system [2] and the fast streams of OS6 [7].
A stream is a single word item and two special global variables,
INPUT and OUTPUT are provided to hold the values of the current
input and output streams. All stream input routines act on the 
stream INPUT and output routines on the stream OUTPUT.
.PARAGRAPH
7.3.2#Input functions
.SKIP
.LEFT MARGIN 20
.SKIP
.TAB STOPS 20
.INDENT -14
READC()
.BREAK
Returns next character. A newline character
signifies end-of-record; an end-of-file character signifies that
the input stream is exhausted.
.SKIP
.INDENT -14
READN()
.BREAK
Reads signed decimal integer.
Leading non-numeric characters (except '-') are ignored.
.SKIP
.INDENT -14
READO()
.BREAK
Reads unsigned octal integer. Leading
non-numeric characters are ignored.
.LEFT MARGIN 6
.PARAGRAPH -6
7.3.3#Output routines
.SKIP
.LEFT MARGIN 20
.INDENT -14
WRITEC(C)
.BREAK
Writes character C. A newline
character signifies end-of-record.
.SKIP
.INDENT -14
WRITEN(N)
.BREAK
Writes N as a signed decimal integer.
.SKIP
.INDENT -14
WRITEO(N)
.BREAK
Writes N as an unsigned octal integer.
.SKIP
.INDENT -14
WRITES(S)
.BREAK
Writes string S.
.SKIP
.INDENT -14
WRITED(N,D)
.BREAK
Writes N as a signed decimal integer right-justified
 in a field of width D, packed with spaces. If D
is less than the number of characters required for N, N is
printed in full with no leading spaces.
.SKIP
.INDENT -14
WRITEOCT(N,D)
.BREAK
As WRITED, but N is written as an unsigned octal
number and the field is packed with zeroes.
.SKIP
.INDENT -14
WRITEF(F,A,B, .. )
.BREAK
Writes the parameters A, B etc. according 
 to the format string F. The format string is written out a character
at a time until the escape character '%' is found.
If the next character is one of the following then the next parameter
is output using the appropriate routine. If not the character
is output itself.
.NOFILL
.SKIP
%C     WRITEC
%N     WRITEN
%S     WRITES
%O     WRITEO
%Dd    WRITED using field d
.SKIP
e.g.   WRITEF("%S=%%%N", "PC", 7)
.SKIP
would produce the output
.SKIP
       PC=%7
.SKIP
.FILL
.INDENT -14
NEWLINE()
.BREAK
Writes a newline character.
.INDEX Stream functions
.LEFT MARGIN 6
.PARAGRAPH -6
7.3.4#Stream functions
.SKIP
Standard functions are provided for creating and destroying 
streams corresponding to RSX datasets. The datasets must
be record structured with variable length records. 
.SKIP
.LEFT MARGIN 20
.INDENT -14
CREATEOUTPUT(S)
.BREAK
Returns a stream for output.The string S must contain a 
legal RSX dataset specification. e.g.
.SKIP
.NOFILL
OUTPUT := CREATEOUTPUT("DK1:TEMP")
.SKIP
.FILL
.INDENT -14
FINDINPUT(S)
.BREAK
Returns an input stream, parameter as for CREATEOUTPUT.
.SKIP
.INDENT -14
ENDREAD(I)
.BREAK
Terminates the input stream I, causing deallocation
of all storage for the stream.
.SKIP
.INDENT -14
ENDWRITE(O)
.BREAK
Terminates the output stream O, causing deallocation
of all storage for the stream.
.LEFT MARGIN 6
.PARAGRAPH -6
7.4###MACRO-11 library functions
.BREAK
-----------------------
.BREAK
.LEFT MARGIN 20
.SKIP
.INDENT -14
INITIALIZEIO()
.BREAK
This routine must be called before any I/O
routines are used.
.SKIP
.INDENT -14
PACKSTRING(V,S)
.BREAK
This function packs the characters V%1 to V%n,
where V%0 = n, into the string S. It returns S.
.SKIP
.INDENT -14
UNPACKSTRING(S,V)
.BREAK
This function unpacks the string S
into the vector V, with the length of the string in V%0.
It returns V.
.SKIP
.INDENT -14
LEVEL()
.BREAK
This function returns the value of the current environment,
i.e. the current stack pointer.
.SKIP
.INDENT -14
LONGJUMP(P,L)
.BREAK
This routine resets the environment to P
and jumps to the label L. It is used in conjunction with LEVEL
for non-local GOTOs.
.LEFT MARGIN 6
.PARAGRAPH -6
7.5###RSX-11 functions and routines
.INDEX RSX library functions
.BREAK
-----------------------------
.BREAK
These library functions allow the use of some of the more
common RSX subroutines. They are all written in
MACRO-11.
.BREAK
Since a number of these functions can detect error conditions
a standard error action is taken, the runtime error routine (global 2)
is called. A default error routine is always linked with a BCPL task,
but this can be superceded by assigning a new routine to this
global variable.
.LEFT MARGIN 20
.SKIP
.INDENT -14
ERROR()
.BREAK
This is the default runtime error routine. It causes
messages of the form:
.SKIP
.NOFILL
Z -- RUN TIME ERROR IN ROUTINE X
.FILL
.SKIP
where Z is the task name and X the routine where the error
occured. The task is then aborted.
.SKIP
.INDENT -14
READCOMMAND(P,B)
.BREAK
This function uses the GCML routines to read a command
line from the console into the buffer B. The prompt string
P is issued when necessary.
One level of indirect files is allowed. An error is generated
if an illegal indirect file is supplied. The function returns
B, or 0 if command input has been terminated.
.SKIP
.INDENT -14
ANALYZE(S)
.BREAK
This function uses the CSI1 routine to perform syntax
analysis## of an ##RSX command string S. A syntax error will
result in an error call. The function returns a CSI block
allocated from the heap.
.SKIP
.INDENT -14
PARSEINPUT(C,S)
.INDENT -14
PARSEOUTPUT(C,S)
.BREAK
These functions use the CSI2 routine to form a dataset
descriptor  for the next input/output dataset specified
in the command line used to generate the CSI block C.
A switch table (S or 0) may be provided. Error is generated
for illegal switches. The functions return a dataset descriptor.
.SKIP
.INDENT -14
OPENINPUT(D,F,S)
.INDENT -14
.BREAK
OPENOUTPUT(D,F,S)
.BREAK
These functions use the OPEN routine to open
the dataset specified by the dataset descriptor D and the default
file name block F. The dataset is opened for varaiable
length record, move mode record I/O with maximum record size S.
Error is called if the dataset cannot be opened. The functions
return a FDB allocated from the heap.
.SKIP
.INDENT -14
READREC(F,B)
.BREAK
This routine reads a record from the dataset specified by the
FDB F into the buffer B using the GET routine.
Error is generated by a read fail.
.SKIP
.INDENT -14
WRITEREC(F,B)
.BREAK
This routine uses the PUT routine to write a record
from the buffer B to the dataset specified by the FDB F.
Error is generated by a write fail.
.SKIP
.INDENT -14
ENDOFFILE(F)
.BREAK
This function returns TRUE whenever the end-of-file has been
reached on the dataset specified by the FDB F, and
FALSE otherwise.
.SKIP
.INDENT -14
CLOSE(F)
.BREAK
This routine uses the CLOSE routine to close the dataset 
specified by the FDB F, and frees the storage used by the FDB.
.LEFT MARGIN 6
.PARAGRAPH -6
7.6###Store management routines
.BREAK
-------------------------
.BREAK
These routines are used to allocate and free areas of storage
from the heap.
.SKIP
.LEFT MARGIN 20
.INDENT -14
HEAP(N)
.BREAK
This function delivers a N+1 word vector, allocated
from the heap.
.SKIP
.INDENT -14
UNHEAP(V)
.BREAK
This routine returns the vector V to free storage.
.LEFT MARGIN 6
.PARAGRAPH -6
7.7###Streams - implementation
.INDEX Streams
.BREAK
------------------------
.BREAK
A stream is a BCPL vector of at least 5 words in length.
It must contain the following elements:-
.SKIP
.LEFT MARGIN 20
.INDENT -14
FILE	A variable
.INDENT -14
BUFFER	A vector
.INDENT -14
TRANSFER	A routine such that
.BREAK
######TRANSFER(FILE,BUFFER)
.BREAK
causes a string to be transferred into the buffer
in the case of an input stream, or out of the buffer in
the case of an output stream.
.INDENT -14
ENDOF	A Boolean function such that
.BREAK
######ENDOF(FILE)
.BREAK
returns TRUE if and only if no more TRANSFERs
may take place on the FILE.
.INDENT -14
POINTER	An integer marking the current character position
in the buffer.
.SKIP
.LEFT MARGIN 6
When these five elements are provided any of the library
I/O functions detailed in 7.2.3 and 7.2.4 can be used on the
stream.
Conversely, if an I/O routine is written in terms
of these five elements then it can be used on any stream,
and in particular on those generated by FINDINPUT
and CREATEOUTPUT.
.PARAGRAPH
7.8###Logical unit (LUN) assignments
.INDEX Logical units
.BREAK
------------------------------
.BREAK
The allocation of LUNs when using the library I/O routines is invisible
to the user. The routines OPENINPUT and OPENOUTPUT
allocate LUNs starting from 2 and working upwards.
The routine CLOSE frees the associated LUN and subsequent OPENs
may reuse it.
.SKIP
LUN 1 is dynamically assigned to the console input device (CI:)
 on program entry and is used by the error routine and the
GCML routines.
.PAGE
.PARAGRAPH
8#####TASK BUILDING BCPL PROGRAMS
.BREAK
---------------------------
.PARAGRAPH
8.1###Separate assembly
.INDEX Task building
.INDEX Assembly
.BREAK
-----------------
.BREAK
BCPL program segments may be separately compiled,
translated and assembled. The resulting modules may be linked
together at task-build time, since all communication between segments
is via the global vector and this is an overlayed psect.
.PARAGRAPH
8.2###Library modules
.BREAK
---------------
.INDEX Library modules
.BREAK
All BCPL programs must give the filename BCPLIB.OBJ
as the final input dataset to task-builder. The /LB
switch must be used to inform task builder that this
is an object code library. All references to the functions and
routines described in 7.2-7.5 will be resolved by the
inclusion of the appropriate object modules into the task.
.PARAGRAPH
8.3###Stack requirements
.INDEX Stack
.BREAK
------------------
.BREAK
The STACK directive must always be used to allocate enough
space for the runtime stack, the processor stack and the heap.
For a BCPL program using the standard stream I/O functions
the formula for calculating the exact requirement is:
.SKIP
       STACK = 280+S+232*N (words)
.SKIP
where S is the size of the runtime stack, and N the maximum
number of streams open simultaneously.
.PARAGRAPH
8.4###Block buffer requirements
.INDEX I/O buffers
.BREAK
-------------------------
.BREAK
Since all the stream I/O is via the RSX11D filing system,
space must be allocated for the block buffer requirements
of the program. This is done by using the EXTSCT
directive to extend the size of the psect $$FSR1.
Each block buffer and header will require 1020 octal bytes
of store, and buffers are required for the maximum number
of streams which may be open concurrently.
.PARAGRAPH
8.5###Logical unit requirements
.INDEX Logical units
.BREAK
-------------------------
.BREAK
The default task-builder allocation of 6 LUNs to a task is
sufficient to allow a BCPL task to have 5 streams open
concurrently. If more than this is necessary then the
further LUNs must be allocated by using the UNITS directive
to task-builder.
.PARAGRAPH
8.6###Example
.BREAK
-------
.BREAK
To## build #a# BCPL task##"TEST" from the two object modules
TEST1 and TEST2 which has a maximum of two streams open
simultaneously the following task-builder dialogue would
be necessary.
.SKIP
.NOFILL
       TKB>DK1:TEST,TT:/SH=DK1:TEST1,
       TKB>DK1:TEST2,DK1:BCPLIB/LB
       TKB>/
       ENTER OPTIONS:
       TKB>STACK=2000
       TKB>EXTSCT=$$FSR1:2040
       TKB>LIBR=SYSRES:RO
       TKB>TASK=TEST
       TKB>//
.FILL
.SKIP
The STACK directive reserves about 1.3K words for the
runtime stack. The LIBR directive is optional but
if  used forces the sharing of the resident library, thereby
reducing the physical storage required by the task.
.PARAGRAPH
8.7###Alternative library functions
.BREAK
-----------------------------
.BREAK
Different versions  of the standard library functions,
such as special procedure entry and exit routines for tracing,
may be included in a task simply by using the appropriate
global names in the new versions and supplying them as input to
task-builder.
.BREAK
e.g. For procedure tracing: appropraiate routines with the
global names ENTER and EXIT are assembled into an object module
called TRACE.OBJ. To build a task using these
routines in place of the usual ones the following form of
command line would be required:
.SKIP
.NOFILL
       TKB>DK1:TEST=DK1:TEST,DK1:TRACE,
       TKB>DK1:BCPLIB/LB
.FILL
.SKIP
.PAGE
.CENTER
References
.CENTER
----------
.NOFILL
.LEFT MARGIN 0
 
0   D. W Barron et. al.  The Main Features of CPL
    Computer Journal. 6 (1963), pp 134-143
 
1   Martin Richards,  The BCPL Reference Manual
    University of Cambridge Computing laboratory. 1970.
 
2   Pete Gardner,  The BCPL Reference Manual
    University of Essex Computing Centre. 1974.
 
3   Introduction to RSX-11D.  Digital Equipment Corporation
    DEC-11-OXINA-A-D
 
4   RSX-11D MACRO Assembler.  Digital Equipment Corporation
    DEC-11-OXDMA-B-C
 
5   Jeremy Holden,  An OCODE Translator
    University of York. 1974.
 
6   D. L. Atkin,  BCPL for writing KOS subsystems
    University of York. 1975.
 
7   J. E. Stoy and C. Strachey,
    OS6 Input/Output and Filing System
    Computer Journal.  15 (1972), pp 195-203
 
8   RSX-11D Task Builder. Digital Equipment Corporation
    DEC-11-OXDLA-B-D
.FILL
.PAGE
.CENTER
APPENDIX A
.CENTER
----------
.LEFT MARGIN 6
.PARAGRAPH
######The character set
.BREAK
-----------------
.INDEX Character set
.INDEX Escape characters
.BREAK
BCPL uses the standard ASCII character set. The two special
characters newline and end-of-file which are recognised by the
I/O system are represented by the ASCII characters line-feed (LF)
and control-Z (SUB).
.PARAGRAPH
######Representation
.BREAK
--------------
.BREAK
All printing characters may be used within BCPL string 
and character denotations. Some special escape conventions are used
to allow the representation of non-printing characters.
They are:
.TAB STOPS 13 26 42
.NOFILL
.LEFT MARGIN 0
.SKIP
       Representation     ASCII
       --------------     -----
	*S	SP-40	space
	*T	HT-11	tab
	*N	LF-12	newline
	*L	LF-12	newline
	*C	CR-15	carriage return
	*P	FF-14	page
	*E	SUB-32	escape
 
	*_^c	where c is any printing character
.LEFT MARGIN 26
.FILL
is the control  character (ASCII 0-37) corresponding
to the low order 5 bits  of c.
e.g. *_^G is BEL (ASCII 7)
.NOFILL
.LEFT MARGIN 6
Also,
.BREAK
	**	is *
.BREAK
	*"	is "
	*'	is '
.FILL
.SKIP
Note that both ' and " can be used in matched pairs to
delimit strings, except in the case of strings of
length 1 in which case "C" is a string and 'C' is a
character. The ' quote can be freely used within
strings delimited by " and vice versa.
.PAGE
.CENTER
APPENDIX B
.CENTER
----------
.PARAGRAPH
.INDEX Files
######Files used in the system
.BREAK
------------------------
.BREAK
.SKIP
.NOFILL
The compiler
------------
Compiler task         BCPL.TSK
Compiler source       TSYN.BCL, NTRN.BCL
Compiler globals      TSYN.GET, NTRN.GET
Compiler build file   BCPL.CMD
.SKIP
The translator
--------------
Translator task       TRAN.TSK
Translator source     TRAN.BCL
Translator globals    TRAN.GET
Translator build file TRAN.CMD
.SKIP
Runtime system
--------------
Library modules       BCPLIB.OBJ
Standard globals      BCPLIB.GET
Source files:
BCPL functions        BCPLIB.BCL
RSX functions         RSXLIB.MAC
Ancillary functions   MACLIB.MAC
.FILL
.PAGE
.CENTER
APPENDIX C
.CENTER
----------
.SKIP 2
.NOFILL
Changes for RSX11D version 6
----------------------------
.FILL
.SKIP
The only difference between the two systems is that
the device TI: is used instead of CI: and CO: in all
cases.
.PAGE
.CENTER
APPENDIX D
.CENTER
----------
.SKIP 2
.LEFT MARGIN 0
The BCPL libary
.BREAK
---------------
.BREAK
.SPACING 1
.NOFILL
 
GET "DK1:[100,10]BCPLIB.GET"
 
LET makestream(open, t, string) = VALOF
  { LET s = heap(streamsize)
    LET c = analyze(string)
    LET dataset = parseoutput(c, 0)
    s|file := open(dataset, 0, buffersize)
    s|buffer := heap(buffersize)
    s|pointer, s|ended := 0, FALSE
    s|endof, s|transfer := endoffile, t
    unheap(c)
    RESULTIS s }
 
AND findinput(s) = makestream(openinput, readrec, s)
 
AND createoutput(s) = makestream(openoutput, writerec, s)
 
AND readc() = VALOF
  { LET line = input|buffer
    AND next = input|pointer + 1
    IF next = 1
    { LET f = input|file
      IF input|ended RESULTIS eofch
      (input|transfer)(f, line)
      unpackstring(line, line)
      IF (endof|input)(f)
      { input|ended := TRUE ; RESULTIS eofch } }
    input|pointer := next
    IF next > line|0
    { input|pointer := 0 ; RESULTIS '*N' }
    RESULTIS line|next  }
 
AND writec(ch) BE
  { LET line = output|buffer
    AND next = output|pointer + 1
    TEST ch = '*N'
    THEN { line|0 := output|pointer
           packstring(line, line)
           (output|transfer)(output|file, line)
           output|pointer := 0 }
    OR   { IF next > buffersize
           { writec('*N') ; next := 1 }
           output|pointer := next
           line|next := ch } }
 
AND endread(stream) BE
  { close(stream|file)
    unheap(stream|buffer)
    unheap(stream) }
 
AND endwrite(stream) BE
  { UNLESS stream|pointer = 0
    { LET o = output
      output := stream
      writec('*N')
      output := o }
    endread(stream) }
 
AND readn() = VALOF
  { LET s, n, c = 1, 0, 0
    UNTIL '0' <= c <= '9' DO
    { c := readc() ; IF c = '-' s := -s }
    WHILE '0' <= c <= '9' DO
    { n := 10*n+c-'0' ; c := readc() }
    RESULTIS s*n }
 
AND reado() = VALOF
  { LET n, c = 0, 0
    UNTIL '0' <= c <= '7' DO c := readc()
    WHILE '0' <= c <= '7' DO
    { n := 8*n+c-'0' ; c := readc() }
    RESULTIS n }
 
AND writed(n, d) BE
  { STATIC { negative = FALSE }
    LET w(n, d) BE
      TEST n > 9
      THEN { w(n/10, d-1)
             writec(n REM 10 + '0') }
      OR   { FOR i=2 TO d writec('*S')
             IF negative writec('-')
             writec(n + '0') }
    negative := n<0
    TEST negative 
    THEN w(-n, d-1)
    OR   w(n, d) }
 
AND writen(n) BE writed(n, 0)
 
AND writes(s) BE
  { LET v = VEC 256
    unpackstring(s, v)
    FOR i=1 TO v|0 writec(v|i) }
 
AND writeoct(n, d) BE
    TEST 0 <= n <= 7
    THEN { FOR i=2 TO d writec('0')
           writec(n + '0') }
    OR   { writeoct(n>>3, d-1)
           writec((n_&7)+'0') }
 
AND writeo(n) BE writeoct(n, 0)
 
AND newline() BE writec('*N')
 
AND writef(s, a, b, c, d, e, f, g, h, i, j) BE
  { LET v = VEC 256
    LET param, j = @s, 1
    unpackstring(s, v)
    FOR i=1 TO v|0
    { LET c = v|i
      TEST c = '%'
      THEN { LET val = param|j
             i, j := i+1, j+1
             SWITCHON v|i INTO
             { CASE 'C': writec(val) ; ENDCASE
               CASE 'S': writes(val) ; ENDCASE
               CASE 'N': writen(val) ; ENDCASE
               CASE 'O': writeo(val) ; ENDCASE
               CASE 'D': writed(val, param|j)
                         j := j+1 ; ENDCASE
               DEFAULT:  j := j-1 ; writec(v|i) } }
      OR     writec(c) } }
 
.PAGE
.CENTER
INDEX
.CENTER
-----
.SKIP 2
.PRINT INDEX
