The B5000 was designed in 1961 by a team at Burroughs under the leadership of Robert (Bob) Barton. It was a unique machine, well ahead of its time, and is still ahead of many of today's information processors. Here is a summary of the unique features of the B5000, which are explained in more detail in the rest of this article:
- Hardware was designed to support software requirements
- Hardware designed to exclusively support high-level languages
- No assembler (all system software written in an extended variety of ALGOL)
- Support for other languages such as COBOL
- Powerful string manipulation
- Stack architecture (to support high-level algorithmic languages)
- No programmer accessible registers
- Support for high-level operating system (MCP, Master Control Program)
- Data-driven tagged and descriptor-based architecture
- Secure architecture prohibiting unauthorized access of data or disruptions to operations
- Early error-detection supporting development and testing of software
- First commercial implementation of virtual memory (10 years before IBM "invented" it)
- Simplified instruction set
- Its successors still exist in the Unisys ClearPath/MCP machines
- Influential on many of today's computing techniques
In the following discussion, the machine assignations, B5000, A Series, and ClearPath/MCP are used interchangeably.
Unique System Design
The B5000 was revolutionary at the time in that the architecture and instruction set was designed with the needs of software taken into consideration. This was a large departure from the computer system design of the time, where a processor and its instruction set would be designed and then handed over to the software people. (In fact, it still is a unique attribute of these machines.)
The B5000 was designed to exclusively support high-level languages. This was at a time when such languages were just coming to prominence with FORTRAN and then COBOL. FORTRAN and COBOL are both deficient languages when it comes to modern software techniques, so a newer, mostly untried language was adopted, ALGOL-60. The ALGOL dialect chosen for the B5000 was Elliot ALGOL, first designed and implemented by C.A.R. Hoare on an Elliot 503. This was a practical extension of ALGOL with IO instructions (which ALGOL had ignored) and powerful string processing instructions. Hoare gave a famous Turing lecture on this subject.
ALGOL-60 was designed by experts who from their experiences with designing FORTRAN and other languages had learnt a lot. The design of ALGOL used the formal syntax description language BNF (Backus-Naur Form) which resulted in a very regular language with a clean syntax. It is the grandfather of most modern languages today, including Pascal, C (and related languages), Simula (and its object-oriented descendants), Ada, Eiffel, and many others. Many of these languages were in fact not an improvement on ALGOL at all, as C.A.R. Hoare observed, "ALGOL was a significant improvement on most of its successors".
Thus the B5000 was based on a very strong language. Most other vendors could only dream of implementing an ALGOL compiler and most in the industry wrote ALGOL off as being unimplementable (the language wars started early). However, a bright young student had previously implemented ALGOL-58 on an earlier Burroughs machine during the three months of his summer break. (The name of that student – Donald Knuth.) Many wrote ALGOL off, mistakenly believing that high-level languages could not have the same power as assembler, and thus not realizing ALGOL's potential as a systems programming language. (C came along later to appease this thinking.)
The Burroughs ALGOL compiler was extremely fast – this impressed E.W. Djkstra when he submitted a program to be compiled at the B5000 Pasadena plant. His deck of cards was compiled almost immediately and he immediately wanted several machines for his university back in Europe.
The B-Series architecture (now A-Series) is a ALGOL stack architecture, unlike linear architectures such as PDP-11 and Motorola or segmented architectures such as INTeL and Texas Instruments.
ALGOL is a systems-programming language, and while B5000 was designed specifically around ALGOL, this was only a starting point. Other business-oriented languages such as COBOL were also well supported, most notably by the powerful string operators which were included for the development of fast compilers.
The ALGOL used on the B5000 is actually an extended ALGOL, which includes powerful string manipulation instructions. It also has an elegant preprocessing DEFINE mechanism which is much neater than the #defines found in C. Another extension of note is the EVENT data type facilitating coordination between processes and integrated with INTERRUPTs which may be attached to events to handle them. Thus an interrupt can be coded as an ALGOL code block in order to handle exceptions such as numeric overflow and other user defined EVENTs.
The user-level of ALGOL does not include many of the more dangerous facilities needed by the operating system. There are two levels of more powerful facilities not required in the normal user level of algorithmic processing. The first level is writing the operating system. Originally, the B5000 MCP operating system was written in an extension of extended ALGOL called ESPOL (Executive Systems Programming Language). This was replaced in the mid-to-late 70s by a language called NEWP (and nobody seems to remember what the name of this language stood for). NEWP too was an ALGOL extension, but it was more secure than ALGOL, in fact most low-level operating system calls were rejected by the NEWP compiler unless a block was specifically marked to allow those instructions. Such marking of blocks provided a multi-level protection mechanism.
So what was to stop malicious programmers from just writing NEWP programs and setting a high-privilege level? NEWP programs are non-executable and thus the NEWP compiler is only useful for writing the operating system.
The second intermediate level of security between operating system code (in NEWP) and user programs (in ALGOL) is for middleware programs, which are written in DCALGOL (data comms ALGOL). This is used for message reception and dispatching which remove messages from input queues and places them on queues for other processes in the system to handle. Middleware such as COMS (introduced around 1984) receive messages from around the network and dispatch these messages to specific handling processes or to an MCS (Message Control System) such as CANDE (the program development environment).
Message Control Systems (MCS)
MCSs are items of software worth noting – they control user sessions and provide keeping track of user state without having to run per-user processes since a single MCS stack can be shared by many users. Load balancing can also be achieved at the MCS level. For example saying that you want to handle 30 users per stack, in which case if you have 31 to 60 users, you have two stacks, 61 to 90 users, three stacks, etc. This gives B5000 machines a great performance advantage in a server since you don't need to start up another user process and thus create a new stack each time a user attaches to the system. Thus you can efficiently service users (whether they require state or not) with MCSs. MCSs also provide the backbone of large-scale transaction processing.
Another variant of ALGOL is also worth noting – DMALGOL (Data Management ALGOL). DMALGOL is a language extended for compiling database systems and for generating code from database descriptions. Thus a database designer and administrator compiles their database description and this generates the DMALGOL code tailored for the tables and indexes required. An administrator never needs to write DMALGOL themselves. Database access is provided in normal user-level programs such as ALGOL, COBOL and others, extended with database instructions and transaction processing directives. The most notable feature of DMALGOL is that on top of ALGOL's elegant define mechanism as already mentioned, DMALGOL has an even more sophisticated preprocessing mechanism to generate tables and indexes.
In many early systems and languages, programmers were often told not to make their routines too small – the reason was that procedure calls and returns were expensive operations. The reason for this was that operators had to be executed in order to maintain the stack. The B5000 was designed as a stack machine – all program data except for arrays (which include strings and objects) was kept on the stack.
Multi tasking is also very efficient on B5000 machines. There is one specific instruction to perform process switches – MVST (move stack). Each stack represents a process (task or thread) and tasks can become blocked waiting on resource requests (which includes waiting for a processor to run on if the task has been interrupted because of preemptive multitasking). User programs cannot issue a MVST, and there is only one line of code in the operating system where this is done.
So a process switch proceeds something like this – a process requests a resource that is not immediately available, maybe a read of a record of a file from a block which is not currently in memory, or the system timer has triggered an interrupt. The operating system code is entered and run on top of the user stack. It turns off user process timers, and perhaps turns on other timers like I/O timers, or waiting timers to measure how much time a process has been inactive, which may later be used to tune the system. As you can see, B5000 machines do not need to trap or context switch into the operating system. The current process is placed in the appropriate queue for the resource being requested, or the ready queue waiting for the processor if this is a preemptive context switch. The operating system determines the first process in the ready queue and invokes the instruction move_stack, which generates the MVST operator which makes the process at the head of the ready queue active.
As a stack-oriented machine, there are no programmer addressable registers (the operating system may be able to change some registers, such as the system clock or other internal registers on any particular machine, but the details of such registers might change between implementations).
Speed and Performance
Some of the detractors of the B5000 architecture believed that stack architecture was implicitly slow compared to register-based architectures. (Notable detractors were Hennessy and Paterson, champions of RISC who noted that the B5000 was a much slower machine than the CDC6000. However, the B5000's successors are now much faster because as the underlying technology became faster, so did the B5000 processors – the B5000 has survived, but where are CDC6000s today?)
The trick to system speed is to keep data as close to the processor as possible. In the B5000 stack, this was done by assigning the top two positions of the stack to two registers A and B. Most operations are performed on those two top of stack positions. On faster machines past the B5000, more of the stack may be kept in registers or cache near the processor (such a cache being implemented as a barrel).
Thus the designers of the current B5000 systems can optimize in whatever is the latest technique, and programmers do not have to adjust their code for it to run faster – they do not even need to recompile, thus protecting software investment, where programs have been known to run for years over many processor upgrades. On other register-based machines a programmer may have to revisit a program in order to optimize it, or at least recompile it targeting one or another processor.
This is not to say that B5000 programmers are not conscious of program performance – they are extremely conscious of optimization, but they can concentrate on the performance at an algorithmic level rather than a machine level and the current operating system provides many tools to do performance analysis of single tasks or system-wide performance.
First Single Chip
Another point for speed as promoted by the RISC designers was that processor speed is considerably faster if everything is on a single chip. It was a valid point in the 1970s when more complex architectures such as the B5000 required too many transistors to fit on a single chip. However, this is not the case today and every B5000 successor machine now fits on a single chip as well as the performance support techniques such as caches and instruction pipelines.
In fact, the A Series line of B5000 successors were the first single chip mainframe, the Micro-A of the late 1980s. This "mainframe" chip sat on an Intel-based plug-in PC board. Not only, was the A Series the first single-chip mainframe, but the first "zero-chip" mainframe, since an emulator was built to run on Intel PCs without any extra hardware. This is continued in the ClearPath systems and you can now run a B5000 on an Intel laptop.
Here is an example of how programs map to the stack architecture
— This is lexical level 2 (level zero is reserved for the operating system and level 1 for code segments).
— At level 2 we place global variables for our program.
integer i, j, k real f, g array a [0:9]
procedure p (real p1, p2) value p1 — p1 passed by value, p2 implicitly passed by reference. begin — This block is at lexical level 3 real r1, r2
r2 := p1 * 5 p2 := r2 — This sets 'g' to the value of r2 p1 := r2 — This set 'p2' to r2, but not 'f' — Since this overwrites the original value of f in p1 it most likely indicates — an error. Few of ALGOL's successors have corrected this situation by — making value parameters read only – most have not.
if r2 > 10 then begin — A variable declared here makes this lexical level 4 integer n
— The declaration of a variable makes this a block, which will invoke some — stack building code. Normally you won't declare variables here, in which — case this would be a compound statement, not a block.
... <== sample stack is executing somewhere here. end end
p (f, g) end
Each stack frame corresponds to a lexical level in the current execution environment. As you can see, lexical level is the static textual nesting of a program, not the dynamic call nesting. The visibility rules of ALGOL (as a language designed for single pass compilers) mean that only variables declared before the current position are visible at that part of the code (apart from forward declarations). All variables thus declared in enclosing blocks are visible. Another case is that variables of the same name may be declared in inner blocks and these effectively hide the outer variables which become inaccessible. Note that most of ALGOL's successors failed to correct these mistakes of ALGOL, in fact built more errors in, resulting in programmers developing bad programming habits.
Since lexical nesting is static, it is extremely rare to find a program nested more than five levels deep, and it could be argued that such programs would be poorly structured. B5000 machines allow nesting of up to 32 levels. Note that user programs generally start at lex level 2. Indeed procedures can be invoked in four ways – normal, call, process, and run. These cover all possibilities.
The normal invocation invokes a procedure in the normal way any language invokes a routine, by suspending the calling routine until the invoked procedure returns.
The call mechanism invokes a procedure as a coroutine. Coroutines have partner tasks, where control is explicitly passed between the tasks by means of a CONTINUE instruction. These are synchronous processes.
The process mechanism invokes a procedure as an asynchronous task and in this case a separate stack is set up starting at the lexical level of the processed procedure (generally three or above). As an asynchronous task, there is no control over exactly when control will be passed between the tasks as with coroutines. Note also that the processed procedure still has access to the enclosing environment and this is a very efficient IPC (Inter Process Communication) mechanism. Since two or more tasks now have access to common variables, the tasks must be synchronized to prevent race conditions and this is the purpose of the fore-mentioned EVENT data type, where processes can WAIT on an event (or multiple events) until they are CAUSEd by another cooperating process. If for any reason the child task dies, the calling task can continue – however, if the parent process dies, then all child processes must also be terminated. It is therefore neat for a parent process to wait for completion of all child tasks (which is provided as a task attribute).
The last invocation type is run. This runs a procedure as an independent task which can continue on after the originating process terminates. For this reason, the child process cannot access variables in the parent's environment, and all parameters passed to the invoked procedure must be call-by-value.
Thus Burroughs Extended ALGOL had all of the multi-processing and synchronization features of later languages like Ada, with the added benefit that support for asynchronous processes was built into the hardware level.
One last possibility is that a procedure may be declared INLINE, that is when the compiler sees a reference to it the code for the procedure is generated inline to save the overhead of a procedure call. This is best done for small pieces of code and is like a define, except you don't get the problems with parameters that you can with defines. This facility is available in NEWP.
In our example program, we are just doing normal calls, so all the information will be on a single stack. For asynchronous calls, the stack would be split into multiple stacks so that the processes could share data but run asynchronously.
A stack hardware optimization is the provision of D registers. These a registers that point to the start of each called stack frame. These registers are completely updated automatically as procedures are entered and exited and are not accessible by any software. There are 32 D registers and this is what limits us to 32 levels of lexical nesting, although in theory higher nesting levels could exist, but code in them would run slower if it accessed non-local variables. However, this possibility is precluded on the B5000 because the maximum number of bits used to code the lex level of an entity is five, hence giving a hard limit of 32. Any nesting above 5 or 6 levels would most likely be extremely poorly structured code.
Consider how we would access a lex level 2 (D) global variable from lex level 5 (D). Suppose the variable is 6 words away from the base of lex level 2. It is thus represented by the address couple (2, 6). If we don't have D registers, we have to look at the control word at the base of the D frame, which points to the frame containing the D environment. We then look at the control word at the base of this environment to find the D environment, and continue in this fashion until we have followed all the links back to the required lex level. Note this is not the same path as the return path back through the procedures which have been called in order to get to this point.
As you can see, this is quite inefficient just to access a variable. With D registers, the D register just points at the base of the lex level 2 environment, and all we need to do to generate the address of the variable is to add its offset from the stack frame base to the frame base address in the D register. (There is an efficient linked list search operator LLLU, which could search the stack in the above fashion, but the D register approach is still going to be faster.) With D registers, access to entities in outer and global environments is just as efficient as local variable access.
D Tag Data — Comments register 0| n | — The integer 'n' address couple (4, 1) |-----------| D ==>3| MSCW | — The Mark Stack Control Word containing the link to D. |===========| 0 | r2 | — The real 'r2' address couple (3, 5) |-----------| 0 | r1 | — The real 'r1' address couple (3, 4) |-----------| 1 | p2 | — An SIRW reference to 'g' at (2,6) |-----------| 0 | p1 | — The parameter 'p1' from value of 'f' address couple (3, 2) |-----------| 3| RCW | — A return control word |-----------| D ==>3| MSCW | — The Mark Stack Control Word containing the link to D. |===========| — The array 'a' address couple (2, 7) 1 | a | ====================>[ ten word memory block] |-----------| 0 | g | — The real 'g' address couple (2, 6) |-----------| 0 | f | — The real 'f' address couple (2, 5) |-----------| 0 | k | — The integer 'k' address couple (2, 4) |-----------| 0 | j | — The integer 'j' address couple (2, 3) |-----------| 0 | i | — The integer 'i' address couple (2, 2) |-----------| 3| RCW | — A return control word |-----------| D ==>3| MSCW | — The Mark Stack Control Word containing the link to the previous stack frame. ============= — Stack bottom
If we had invoked the procedure p as a coroutine, or a process instruction, the D environment would have become a separate D-based stack. Note that this means that asynchronous processes still have access to the D environment as implied in ALGOL program code.
Note that the D and D environments do not occur in the current processes stack. The D environment is the code segment dictionary, which is shared by all processes running the same code. The D environment represents entities exported by the operating system.
One nice thing about the stack structure is that if a program does happen to fail, a stack dump is taken and it is very easy for a programmer to find out exactly what the state of a running program was. Compare that to core dumps and exchange packages of other systems.
Another thing about the stack structure is that programs are implicitly recursive. FORTRAN was not a recursive language and perhaps one stumbling block to people's understanding of how ALGOL was to be implemented was how to implement recursion. On the B5000, this was not a problem – in fact, they had the reverse problem, how to stop programs from being recursive. In the end they didn't bother, even the Burroughs FORTRAN compiler was recursive, since it was unproductive to stop it being so.
Thus Burroughs FORTRAN was better than any other implementation of FORTRAN. In fact, Burroughs became known for its superior compilers and implementation of languages, including the object-oriented Simula (a superset of ALGOL), and Iverson, the designer of APL declared that the Burroughs implementation of APL was the best he'd seen. John McCarthy, the language designer of LISP disagreed, since LISP was based on modifiable code, he did not like the unmodifiable code of the B5000, but most LISP implementations would run in an interpretive environment anyway.
Note also that stacks automatically used as much memory as was needed by a process. There was no having to do SYSGENs on Burroughs systems as with competing systems in order to preconfigure memory partitions in which to run tasks. In fact, Burroughs really championed "plug and play" in that extra peripherals could be plugged into the system without having to recompile the operating system with new peripheral tables. Thus these machines could be seen as the forerunners of today's USB and FireWire devices.
Certainly, the most defining aspect in most people's minds of the B5000 is that it is a stack machine as described above. However, two other very important features of the architecture is that it is a tag-based and descriptor-based architecture.
On the original B5000 a bit in each word was set aside to identify the word as a code or data word. This was a security mechanism in order to stop programs from being able to corrupt code (in the way that hackers do today).
An advantage to unmodifiable code is that B5000 code is fully reentrant – it does not matter how many users are running a program, there will only be one copy of the code in memory, thus saving a great deal of memory (these machines are actually very memory and disk efficient).
Later, when the B5500 was designed it was realized that the 1-bit code/data distinction was a powerful idea and this was extended to three bits outside of the 48 bit word into a tag. The data bits are bits 0-47 and the tag is in bits 48-50. Bit 48 was the read-only bit, thus odd tags indicated control words that could not be written by a user-level program. Code words were given tag 3. Here is a list of the tags and their function:
Tag Word kind Description 0 Data All kinds of user and system data (text data and single precision numbers) 2 Double Double Precision data 4 SIW Step Index word (used in loops) 6 Uninitialised data 1 IRW Indirect Reference Word SIRW Stuffed Indirect Reference Word 3 Code Program code word MSCW Mark Stack Control Word RCW Return Control Word TOSCW Top of Stack Control Word SD Segment Descriptor 5 Descriptor Data block descriptors 7 PCW Program Control Word
Note: Internally, some of the machines had 60 bit words, with the extra bits being used for engineering purposes such as a Hamming code error-correction field, but these were never seen by programmers.
Note: The current incarnation of these machines, the Unisys ClearPath has extended tags further into a four bit tag.
Even-tagged words are user data which can be modified by a user program as user state. Odd-tagged words are created and used directly by the hardware and represent a program's execution state. Since these words are created and consumed by specific instructions or the hardware, the exact format of these words can change between hardware implementation and user programs do not need to be recompiled, since the same code stream will produce the same results, even though system word format may have changed.
Tag 1 words represent on-stack data addresses. The normal IRW simply stores an address couple to data on the current stack. The SIRW references data on any stack by including a stack number in the address.
Tag 5 words are descriptors, which are more fully described in the next section. Tag 5 words represent off-stack data addresses.
Tag 7 is the program control word which describes a procedure entry point. When operators hit a PCW, the procedure is entered. The ENTR operator explicitly enters a procedure (non-value-returning routine). Functions (value-returning routines) are implicitly entered by operators such as value call (VALC). Note that global routines are stored in the D environment as SIRWs that point to a PCW stored in the code segment dictionary in the D environment. The D environment is not stored on the current stack because it can be referenced by all processes sharing this code. Thus code is reentrant and shared.
Tag 3 represents code words themselves, which won't occur on the stack. Tag 3 is also used for the stack control words MSCW, RCW, TOSCW.
The other notable architectural feature of the B5000 is that it is descriptor-based. Descriptors are the means of having data that does not reside on the stack as for arrays and objects. Descriptors are also used for string data as in compilers and commercial applications. Descriptors describe data blocks. Each descriptor contains a 20-bit address field referencing the data block. Each block has a length which is stored in the descriptor, also 20 bits. The size of the data is also given, being 4-, 6-, 8- or 48-bit data in a three bit field.
The various status bits are
Bit 47 — The presence bit (P-Bit)
Bit 46 — The copy bit
Bit 45 — The indexed bit
Bit 44 — The paged bit
Bit 43 — The read only bit
Bit 47 is maybe the most interesting bit in the system – it was the way the B5000 implemented virtual memory. Virtual memory was originally developed for the Atlas project at the University of Manchester in the late 1950s. Keen to see this used in commercial applications, they invited engineers from several computer companies to a seminar, including those from Burroughs and IBM. The Burroughs engineers saw the significance of virtual memory and put it into the B5000. The IBM engineers weren't interested and IBM did not "invent" virtual memory for another ten years.
When a descriptor was referenced, the hardware checked bit 47. If it were 1, the data was present in memory at the location indicated in the address field. If bit 47 were 0, the data block was not present and an interrupt (p-bit interrupt) was raised and MCP code entered to make the block present. In this case, if the address field were 0, the data block had not been allocated (init p-bit) and the MCP would search for a free block the size of which was given in the length field. Note that in ALGOL, the bounds of an array were completely dynamic, could be taken from values computed at run time, which was unlike Pascal where the size of arrays was fixed at compile time. This was the main weakness of Pascal as defined in its standard, but which was removed in many commercial implementations of Pascal, notably the Burroughs implementations (both the University of Tasmania version by Arthur Sale and Roy Freak, and the Burroughs Slice implementation by Matt Miller et al.)
Note that in a program, an array was not allocated when it was declared, but only where it was touched for the first time – thus arrays could be declared and the overhead of allocating them was avoided if they weren't used.
Also note that low-level memory allocation system calls such as the malloc class of calls of C and Unix are not needed – arrays are automatically allocated as used. This saves the programmer the great burden of filling programs with the error-prone activity of memory management.
When porting programs in lower-level languages such as C, the C memory structure is dealt with by doing its own memory allocation within a large allocated B5000 block – thus the security of the rest of the B5000 system cannot be compromised by errant C programs. In fact, many buffer overruns in apparently otherwise running C programs have been caught when ported to the B5000 architecture. C, like Pascal, was also implemented using the Slice compiler system (using a common code generator and optimizer for all languages). The C compiler, run-time system, POSIX interfaces, as well as a port of many Unix tools was done by Steve Bartels. An Eiffel compiler was also developed using Slice.
For object-oriented programs which require more dynamic creation of objects than the B5000 architecture, objects are best allocated within a single B5000 block. Such object allocation is higher level than C's malloc and is best implemented with a modern efficient garbage collector.
The last p-bit scenario is when bit 47 is 0, indicating that the data is not in memory, but the address is non-zero, indicating that the data has been allocated and in this case the address represents a disk address in the virtual memory area on disk. In this case a p-bit interrupt is raised and it is noted as an 'other' p-bit.
Thus the B5000 had a virtual memory system integrated into the hardware – a virtual memory system that has to this day been unsurpassed, since all other systems must build virtual memory on top of lower-level hardware. ALGOL and the B5000 also represented a significant advance on the low-level, error-prone, and programmer intensive 'malloc' mechanisms of later systems.
You may have noted that the address field in the B5000 was only 20 bits which meant that only 1 Meg words (6MB) of memory could be addressed by descriptors. This was a significant restriction on the Bnm00 range of machines. With the advent of the A Series in the early 1980s, the meaning of this field was changed to contain the address of a master descriptor, which meant that 1 Meg data blocks could be allocated, but that the machine memory could be greatly expanded to gigabytes or perhaps terabytes.
Another significant advantage was realized for virtual memory. In the B5000 design, if a data block were rolled out, all descriptors referencing that block needed to be found in order to update the presence bit and address. With the master descriptor, only the presence bit in the master descriptor needs changing. Also the MCP can move blocks around in memory for compaction and only needs to change the address in the master descriptor.
Another difference between the B5000 and most other systems is that other systems mainly used paged virtual memory, that is pages are swapped out is fixed-sized chunks regardless of the structure of the information in them. B5000 virtual memory works with varying-size segments as described by the descriptors.
When the memory is filled to a certain capacity, an OS process called the 'Working Set Sheriff' is invoked to either compact memory or start moving segments out of memory. It chooses code segments first, since these cannot change and can be reloaded from the original in the code file, so do not need writing out, and then data segments which are written out to the virtual memory file.
P-bit interrupts are also useful to measure system performance. For first-time allocations, 'init p-bits' indicate a potential performance problem in a program, for example if a procedure allocating an array is continually called. Reloading blocks from virtual memory on disk can significantly degrade system performance and is not the fault of any specific task. Many of today's computer users are familiar with the fact that adding memory can improve system performance, and this is why. On B5000 machines, 'other p-bits' indicate a system problem, which can be solved either by better balancing the computing load across the day, or by adding more memory.
Thus the high-level architecture of B5000 machines helps optimization of both individual tasks and the system as a whole.
The last and maybe most important point to note about descriptors is how they affect the complementary notions of system security and program correctness. One of the best tools a hacker has against the wide-spread operating systems of today is the buffer overflow. This is particularly easily done in C and with a little more effort in assemblers. C, in particular, uses the most primitive and error-prone way to mark the end of strings, using a null byte as an end-of-string sentinel in the data stream itself.
Pointers are implemented on the B5000 by indexed descriptors. During data scanning and transfer operations, pointers are checked at each increment to make sure that neither the source not the destination blocks are overflowed. Thus a significant source of program errors can be detected early before software goes into production, and a more significant class of attacks on system security is not possible.
As you would expect from the above description of the run-time data structures used in the B5000, B5000 machines also have an interesting instruction set. There are less than 200 operators all of which fit into 8-bit "syllables" and many of these operators are polymorphic depending on the kind of data being acted on as given by the tag. If we ignore the powerful string scanning, transfer, and edit operators, the basic set is only about 120 operators. If we remove the operators reserved for the operating system such as MVST and HALT, the set of operators commonly used by user-level programs is less than 100.
Obviously, since there are no programmer addressable registers the great plethora of register manipulating operations required in other architectures is not there, nor the variants for performing operations between any two registers, since all operations are applied to the top of the stack. This also makes code files very compact, since operators are zero-address and do not need to include the address of registers or memory locations in the code stream.
For example the B5000 has only one ADD operator. Typical architectures require multiple operators for each data type, for example add.i, add.f, add.d, add.l for integer, float, double, and long data types. The B5000 only distinguishes single and double precision numbers – integers are just reals with a zero exponent. When one or both of the operands has a tag of 2, a double precision add is performed, otherwise tag 0 indicates single precision. Thus the tag itself is the equivalent of the operator .i, .f, .d, and .l extension. This also means that the code and data can never be mismatched.
Two operators are important in the handling of on-stack data – VALC and NAMC. These are two-bit operators, 00 being VALC, value call, and 01 being NAMC, name call. The following six bits of the syllable provide the address couple. Thus VALC covers syllable values 00 to 3F and NAMC 40 to 7F.
VALC is another polymorphic operator. If it hits a data word, that is loaded to the top of stack. If it hits and IRW, that is followed, possibly in a chain of IRWs until a data word is found. If a PCW is found, then a function is entered to compute the value and the VALC does not complete until the function returns.
NAMC simply loads the address couple onto the top of the stack as an IRW (with the tag automatically set to 1).
In the following operator explanations remember that A and B are the top two stack registers. Double precision extensions are provided by the X and Y registers; thus the top two double precision operands are given by AX and BY. (Mostly AX and BY is implied by just A and B.)
B5000 Instruction Set
The B5000 line also were pioneers in having multiple processors connected together on a high-speed bus. The B7000 line could have up to 8 processors, as long as at least one was an IO module. Notable operators are:
HEYU — send an interrupt to another processor
RDLK — Low-level semaphore operator.
— Load the A register with the memory location given by the A register.
— and place the value in the B register at that memory location in a single uninterruptable cycle
WHOI — Processor identification
IDLE — Idle until an interrupt is received
Note that RDLK is a very low-level way of synchronizing between processors. The high level used by user programs is the EVENT data type. The EVENT data type did have some system overhead. To avoid this overhead, a special locking technique called Dahm locks (named after a Burroughs software guru, Dave Dahm) can be used.
Influence of the B5000
Undoubtedly, the direct influence of the B5000 is the current Unisys ClearPath range of mainframes which are the direct descendants of the B5000 and still have the MCP operating system after 40 years of consistent development. This architecture is now called emode (for emulation mode) since the B5000 architecture can be implemented on many platforms. There was also going to be an nmode (native mode), but this was dropped, so you may often hear the B5000 successor machines being referred to as "emode machines".
As already noted, B5000 machines are programmed exclusively in high-level languages – there is no assembler. There is a language that looks rather like the machine code of the B5000 – FORTH. There is no evidence that FORTH was directly influenced by the B5000, in fact, probably if it had been, its designers would have understood the relationship between high-level languages and the stack architecture and have not seen the need for the assembler-style FORTH.
Hewlett Packard systems were influenced by the B5000, since some Burroughs engineers found later employment designing machines for HP and these also were stack machines. Bob Barton's work on reverse polish notation found its way into HP calculators firstly the 9100A, and notably the HP-35 and subsequent calculators. HP is a recognized leader in the production of high-quality equipment. Reverse polish notation (RPN) was developed in the late 1950s by Australian computer scientist and philosopher C.L. Hamblin (1922-85). Barton it seems independently also discovered RPN in 1958.
It may also be noted that Steve Jobs and Steve Wozniak were employees at Hewlett Packard when they built the first Apple I and Apple II and subsequently left to form Apple Computer.
Bob Barton was also very influential on Alan Kay. Kay was also impressed by the data-driven tagged architecture of the B5000 and this influenced his thinking in his developments in object-oriented programming and Smalltalk. Alan Kay is also the person that invented the GUI window.
Another facet of the B5000 architecture is that it is a secure architecture that runs directly on hardware. This technique has descendants in the virtual machines of today in their attempts to provide secure environments. One notable such product is the Java JVM which provides a secure sandbox in which applications run.
The Extended ALGOL Primer (Three Volumes) Donald J. Gregory.
Computer System Organization: The B5700/B6700 Series Elliot I Organick Academic Press (1973).
Computer Architecture: A Structured Approach R. Doran Press (1979).
Stack Computers: The New Wave Philip J. Koopman available at:  (http://www.ece.cmu.edu/~koopman/stack_computers/index.html)
Categories; Mainframe computers Early computers