This shows that the total size of the compiler is 34,576 bytes and that it can be compiled (on a 1.6GHz Pentium machine) in 0.280 seconds. Since this involves executing 28,261,599 Cintcode instructions, the rate is just over 100 million Cint-code instructions per second with the current interpreter.
1.2 A Cintpos Console Session
When the Cintpos system is started (on a machine called meopham) in the di-rectory Cintpos/cintpos, its opening message is as follows:
meopham$ cintpos
Cintpos System (09 Mar 2010) 0.000 1>
There is a directory called com that holds the BCPL source code of several Cintpos commands, such as bcpl.b, bench100.b and fact.b. We can inspect fact.b using the type command as follows.
0.000 1> type com/fact.b
It can be compiled and run as follows.
0.000 1> c bc fact
bcpl com/fact.b to cin/fact hdrs POSHDRS BCPL (20 Oct 2009)
There is a benchmark program called bench100.b which can be compiled and run as follows.
0.000 1> c bc bench100
bcpl com/bench100.b to cin/bench100 hdrs POSHDRS BCPL (20 Oct 2009)
qpkt count = 2326410 holdcount = 930563 these results are correct
end of run 9.170 1>
The latest prompt (9.170 1>) indicates that the benchmark program took 9.17 seconds to run and that we are connected to the root command language inter-preter running as task one.
When Cintpos starts these are six resident tasks which can be seen using the status command as follows.
0.000 1> status
Task 1: Root_Cli running CLI Loaded command: status Task 2: Debug_Task waiting DEBUG
Task 2 is an interactive debugging aid, task 3 handles communication between tasks and the keyboard and display devices, task 4 handles communication be-tween tasks and the filing system, task 5 provides a mailbox facility that allows communication of short text messages between tasks and, finally, task 6 handles TCP/IP communication between tasks and the internet.
Tasks may be dynamically created and destoyed. For instance, the run com-mand will create a new CLI task giving it a comcom-mand to run.
0.010 1> run status
0.000 1> Task 1: Root_Cli waiting CLI No command loaded Task 2: Debug_Task waiting DEBUG
Task 3: Console_Handler waiting COHAND Task 4: File_Handler waiting FH0 Task 5: MBX_Handler waiting MBXHAND Task 6: TCP_Handler waiting TCPHAND
Task 7: Run_Cli running CLI Loaded command: status
1.2. A CINTPOS CONSOLE SESSION 9
Notice that the root CLI (task 1) completes the execution of the run command and issues a prompt (0.000 1>) before the newly created CLI (task 7) has had time to load and run the status command. As soon as task 7 finishes running the status command it commits suicide leaving the original 6 tasks.
The bounce.b program provides a demonstration of communication between Cintpos tasks work.
It can be compiled and run as follows.
0.000 1> c bc bounce
bcpl com/bounce.b to cin/bounce hdrs POSHDRS BCPL (20 Oct 2009)
Code size = 60 bytes 0.010 1> run bounce 0.000 1> status
Task 1: Root_Cli running CLI Loaded command: status Task 2: Debug_Task waiting DEBUG
Task 3: Console_Handler waiting COHAND Task 4: File_Handler waiting FH0 Task 5: MBX_Handler waiting MBXHAND Task 6: TCP_Handler waiting TCPHAND
Task 7: Run_Cli waiting CLI Loaded command: bounce
0.000 1>
The status output shows that the bounce program is running as task 7 and is supended in taskwait waiting for another task to send it a packet. When it receives a packet it immediately return it to the sender and wait for another to arrive. We can send a suitable packet to bounce using the send command whose source code is as follows.
0.000 1> type com/send.b SECTION "send"
GET "libhdr"
GLOBAL { task: 200; count: 201 } LET start() BE
}
task, count := 7, 1_000_000 IF argv!0 DO task := !argv!0 IF argv!1 DO count := !argv!1
pkt!0, pkt!1, pkt!2 := notinuse, task, count
writef("*nSending a packet to task %n, %n times*n", task, count) { LET k = pkt!2
UNLESS k BREAK pkt!2 := k-1 qpkt(pkt)
pkt := taskwait() } REPEAT
writes("Done*n") }
0.010 1>
This program creates a packet consisting of a vector (one dimensional array) of three elements. The first is used by the system for chaining packets together and must be initialised the the special value notinuse. The next element of the packet (pkt!1) holds the destination task number and the final element (pkt!2) holds a value (initially 1000000) which is going to be used as a counter. The REPEAT loop decrements this counter field and send the packet using qpkt to the bounce task suspending itself in taskwait until the packet returns. Control leaves the REPEAT loop when the counter reaches zero, causing send to output the message Done. We can compile and run send as follows.
0.010 1> c bc send
bcpl com/send.b to cin/send hdrs POSHDRS BCPL (20 Oct 2009)
Code size = 252 bytes 0.020 1> send
Sending a packet to task 7, 1000000 times Done
3.940 1>
This demonstration indicates that a packet can be sent from one task to another about 500000 times per second.
Chapter 2
The BCPL Language
The design of BCPL owes much to the work done on CPL (originally Cambridge Programming Language) which was conceived at Cambridge to be the main lan-guage to run on the new and powerful Ferranti Atlas computer to be installed in 1963. At that time there was another Atlas computer in London and it was decided to make the development of CPL a joint project between the two Uni-versities. As a result the name changed to Combined Programming Language. It could reasonably be called Christopher’s Programming Language in recognition of Christpher Strachey whose bubbling enthusiasm and talent steered the course of its development.
CPL was an ambitious language in the ALGOL tradition but with many novel and significant extensions intended to make its area of application more general.
These included a greater richness in control constructs such as the now well known IF, UNLESS, WHILE, UNTIL, REPEATWHILE, SWITCHON statements. It could handle a wide variety of data types including string and bit patterns and was one of the first strictly typed languages to provided a structure mechanism that permitted convenient handling of lists, trees and directed graphs. Work on CPL ran from about 1961 to 1967, but was hampered by a number of factors that eventually killed it. It was, for instance, too large and complicated for the machines available at the time, and the desire for elegance and mathematical cleanliness outweighed the more pragmatic arguments for efficiency and implementability. Much of the implementation was done by research students who came and left during the lifetime of the project. As soon as they knew enough to be useful they had to transfer their attention to writing theses. Another problem (that became of particular interest to me) was that the implementation at Cambridge had to move from EDSAC II to the Atlas computer about halfway through the project. The CPL compiler thus needed to be portable. This was achieved by writing it in a simple subset of CPL which was then hand translated into a sequence of low level macro calls that could be expanded into the assembly language of either machine.
The macrogenerator used was GPM[6] designed by Strachey specifically for this task. A delightfully elegant work of art in its own right it is well worth study. A
11
variant of GPM, called BGPM, is included in the standard BCPL distribution.
BCPL was initially similar to this subset of CPL used in the encoding of the CPL compiler. An outline of BCPL’s main features first appeared in my PhD thesis [4] in 1966 but it was not fully designed and implemented until early the following year when I was working at Project MAC of the Massachussetts Institute of Technology. Its first implementation was written in Ross’s Algol Extended for Design (AED-0)[1] which was the only language then available on CTSS, the time sharing system at Project MAC, other than LISP that allowed recursion.
2.1 Language Overview
A BCPL program is made up of separately compiled sections, each consisting of a list of declarations that define the constants, static data and functions belonging to the section. Within functions it is possible to declare dynamic variables and vectors that exist only as long as they are required. The language is designed so that these dynamic quantities can be allocated space on a runtime stack. The addressing of these quantities is relative to the base of the stack frame belonging to the current function activation. For this to be efficient, dynamic vectors have sizes that are known at compile time. Functions may be called recursively and their arguments are called by value. The effect of call by reference can be achieved by passing pointers. Input and output and other system operations are provided by means of library functions.
The main syntactic components of BCPL are: expressions, commands, and declarations. These are described in the next few sections. In general, the pur-pose of an expression is to compute a value, while the purpur-pose of a command is normally to change the value of one or more variables or to perform input/output.
2.1.1 Comments
There are two form of comments. One starts with the symbol // and extends up to but not including the end-of-line character, and the other starts with the symbol /* and ends at a matching occurrence of */. Comment brackets (/*
and */ may be nested, and within such a comments the lexical analyser is only looking for /* and */ and so care is needed when commenting out fragments of program containing string constants. Comments are equivalent to white space and so may not occur in the middle of multi-character symbols such as identifiers or constants.
2.1. LANGUAGE OVERVIEW 13
2.1.2 The GET Directive
A directives of the form GET "filename" is replaced by the contents of the named file. Early versions of the compiler only inserted the file up to the first occurring dot but now the entire file is inserted. By convention, GET directives normally appear on separate lines. If the filename does not end in .h or .b the extension .h is added.
The name is looked up by first searching the current directory and then the directories specified by the environment variable whose name is held in the rtn hdrsvar of the rootnode, but this can be overridden using the hdrs com-piler option. The default environment variable for BCPL headers is BCPLHDRS under Cintsys and POSHDRS under Cintpos. header files are normally in the g/
directory in the root directory of the current system. To check whether the envi-ronment variables are set correctly, enter cintsys or cintpos with the -f option as suggested in Section 3.5.
2.1.3 Conditional Compilation
A simple mechanism, whose implementation takes fewer than 20 lines of code in the lexical analyser allows conditional skipping of lexical symbols. It uses directives of the following form:
$$tag
$<tag
$>tag
where tag is conditional compilation tag composed of letters, digits, dots and underlines. All tags are initially unset, but may be complemented using the $$tag directive. All the lexical tokens between $<tag and $>tag are skipped (treated as comments) unless the specified tag is set. The following example shows how this conditional compilation feature can be used.
$$Linux // Set the Linux conditional compilation tag
$<Linux // Include if the Linux tag is set
$<WinNT $$WinNT $>WinNT // Unset the WinNT tag if set writef("This was compiled for Linux")
$>Linux
$<WinNT // Include if the WinNT tag is set writef("This was compiled for Windows NT")
$>WinNT
2.1.4 Section Brackets
Historically BCPL used the symbols $( and $) to bracket commands and decla-rations. These symbols are called section brackets and are allowed to be followed
by tags composed of letters, digits, dots and underlines. A tagged closing section bracket is forced to match with its corresponding open section bracket by the automatic insertion of extra closing brackets as needed. Use of this mechanism is no longer recommended since it can lead to obscure programming errors. BCPL has been extended to allow all untagged section brackets to be replaced by { and } as appropriate.