TOPS-10: Essex BCPL
BCPL - Basic Combined Programming Language - is probably best known as the ancestor of the C programming language made famous by Unix. But BCPL was an important and widely used language in its own right. First implemented by Martin Richards for CTSS, it spread to many different architectures during the 1970s, especially in the UK where it was used for teaching computer science, AI and in industry.
(Incidentally my first ever job, as an intern in 1990 working for the former British semiconductor company Inmos, was writing CAD tools using BCPL on a micro-VAX).
Essex BCPL for TOPS-10
The version we are looking at today for TOPS-10 is from the University of Essex and dates from the mid 1970s. The original compiler by Richards generated machine code for a generic stack machine, called OCODE, and the compiler itself was available in OCODE, so it was easy to port to new machines. Essex produced a version for the ICL 1900 to start with, and moved this across to TOPS-10 on their new PDP-10 in 1970. They then rewrote the compiler to remove the OCODE layer and improve performance.
This second implementation of BCPL is included in the TOPS-10 6.03
disk images we are using so there are no special set up instructions.
The compilation system recognises files with a .BCP extension by
default. Here's what a hello world program looks like and how to run it:
.type hello.bcp GET "BCL:BCPLIB" LET START() BE $( WRITES(TTY,"Hello, world*C*L") $) .execute hello.bcp BCPL: HELLO 400031/2 32% LINK: Loading [LNKXCT BCPL Execution] Hello, world EXIT
TPK in BCPL
Let's use the compiler to run the TPK algorithm. The source code can be found here. This can be loaded onto the disk using the techniques described in this article.
The TPK formula (√|x | + 5x³) is implemented as a function with a single expression:
LET TPK(X) = SQRT(#ABS X) #+ 5.0 #* X ** 3
As BCPL is typeless, different operators are needed for floating point
and integer arithmetic such as #+ for floating point add. Note also
that #ABS is a unary operator.
However, we have a problem in that SQRT is not part of the language
library at this point in history. We can implement it using the
Babylonian approximation:
LET SQRT(X) = VALOF
$(
LET X1, X2 = X, X #/ 2.0
WHILE #ABS (X1 #- X2) #> 0.0001 DO
$(
LET OLD = X2
X2 := 0.5 #* (X1 #+ (X2 #/ X1))
X1 := OLD
$)
RESULTIS X2
$)
This uses a VALOF expression which yields its results using
RESULTIS. As there is more than one statement we use a block, marked
by $( ... $). Semicolon can be used to separate statements, but is
not needed if there is a single statement per line.
Inside the block we define a variable, LET OLD = X2, but note that
assignment uses := ad plain = is used for comparison.
With this, we can supply the driving logic in START, BCPL's
equivalent of C's main.
// Define constants
MANIFEST $( N = 11; IOVS = 300 $)
// Main program
LET START() BE
$(
LET A = VEC N
LET IOVECTOR = VEC IOVS
INITIALISEIO(IOVECTOR, IOVS)
WRITE(TTY, "Please enter :N numbers*C*L", N)
FOR J = 0 TO N-1 DO A!J := RDF(TTY)
WRITE(TTY, "Results are*C*L")
FOR J = N-1 TO 0 BY -1 DO
$(
LET R = TPK(A!J)
TEST R #> 400.0 THEN
WRITE(TTY, "Too large*C*L")
OR
WRITE(TTY, ":F*C*L", R)
$)
$)
BCPL allows both single line comments with // and block comments
with /* ... */. C initially only took block comments, with single
line comments first re-implemented by C++ and coming back to C in the
1999 standard.
MANIFEST sets up compile time constants, which we can use when
declaring the stack vector LET A = VEC N. Elements of this vector
can be accessed using infix !, eg A!2. Two FOR loops are used,
one counting forwards to read in numbers and the second counting
backwards to calculate and print results. BCPL has IF, but it only
supports a single clause for the true case; TEST ... THEN ... OR
supports both true and false cases.
I/O is done using RDF to read in a single floating point value and
WRITE for output. I/O is stream based, and we use the predefined
value TTY to communicate with the user's console. INITIALISEIO is
needed before we use I/O to allocate buffers. WRITE supports output
of different types using : as positional markers, eg :F for a
floating point number. *C*L in the string means carriage return /
line feed.
A full transcript of the program execution can be found here.
Further information
The Github PDP-10 organisation has the essex-bcpl repo which contains the source tape and manual for the version of the language. There is also MUD1 which is an early multi-user dungeon written in BCPL; this looks like it needs TOPS-10 7.03 and a KI CPU to run, however.
A completely separate implementation of BCPL on TENEX from BBN can also be found at tenex-bcpl.
The Computer History Museum Software Preservation Group has a detailed history of BCPL, including links to documents, papers and other implementations.
The classic reference to BCPL (and what I originally leaned the language from) is "BCPL - the language and its compiler" by Martin Richards and Colin Whitby-Strevens; there's a copy at the Internet Archive.
Martin Richards continues to work on BCPL and his home page has links to implementations for modern computers.
Questions, corrections, comments
I welcome any questions or comments, and also especially any corrections if I have got something wrong. Please email me at rupert@timereshared.com and I will add it here and update the main text.