The von Neumann Architecture ---------------------------- All commercially available machines today are von-Neumann machines, i.e. their architecture resembles closely that described in von Neumann's original paper. Each processor executes instructions stored in the computer's memory one at a time. The instruction excuted is always the instruction whose address is contained in the program counter after the last instuction has completed execution. The salient features of a von Neumann machine are: 1) Instructions are normally executed sequentially. 2) Control is passed to another section of code by means of a goto statement (it may depend on a condition, or may cause the old program counter to be pushed on to the stack, but it is still basically a goto). A goto statement is essentially an assignment to the program counter. The one exception to this is the interrupt. 3) Though programs normally occupy the same memory as data, data are passive and not normally executed. Data are modified by means of the store or assignment statement. 4) Data are weakly typed - it is possible to add a real to a bit array, and store the result in a character string. It is remarkable that forty years have passed and there have been no major deviations from this design among commercially available machines. History of Programming Languages -------------------------------- FORTRAN Initially, computers were programmed in machine code or assembly language. In the early fifties, FORTRAN was developed. Though the first version of FORTRAN was primitive, it meant that for the first time that the programmer did not need to know details of the machine's architecture or instruction set. However, FORTRAN instructions themselves closely resemble those of a typical von Neumann machine. It still has GOTO statements, assignment statements, subroutine call statements. The improvements were the addition of high level I/O statements, arithmetic expressions, and the passing of parameters to subroutines. Also, FORTRAN was not much more modular - subroutines exist, but they do in machine language too. It might seem that computer hardware and existing high level languages are the only available models on which to develop a new language, but this is (and was) not the case, as we shall see later. ALGOL After the development of FORTRAN, a primitive version of another well-known high level language was developed in the late fifties, ALGOL. This was an improvement on FORTRAN, and was the precursor of many of today's high level languages, including Ada and Pascal. It was more modular than FORTRAN - each procedure (the ALGOL equivalent of subroutine) could have its own local procedures and data, inaccessible from outside it. This is called LEXICAL SCOPING (From the Greek, lexikon, meaning dictionary, and skopein, meaning to see. Lexical scoping is then, literally, words seeing.), and refers to the visibility of particular variables or procedures from a particular place in the program. Lexical scoping is determined at compilation time. There is another type of scoping, DYNAMIC SCOPING (from the Greek, dunamos, meaning power). This refers to the visibility of variables or procedures at a particular point in a program's execution, and is determined at run time. ALGOL also allows recursion. The other improvements were structured program statements instead of the explicit GOTO's of FORTRAN. One computer manufacturer, Burroughs, decided to develop a series of machines (numbered B5000 and upwards) whose hardware was designed to execute ALGOL very efficiently. This is a reversal of the previous idea of basing a programming language on an existing machine architecture. Because of ALGOL's recursion and scoping rules, the machines designed and built were zero address or stack machines. However, ALGOL itself, though an improvement on FORTRAN, still has GOTOs (though no longer explicit), and still has the sequential flow of program control which FORTRAN and machine language has. The B5000 series were, then, still von Neumann machines, but Burroughs had taken the important step of basing the hardware design on software considerations, rather than the other way around as was done in the past. LISP and the AI Community In 1959, the first programming language was developed which was NOT based on the von Neumann architecture, or on previous programming languages [1]. This language is called LISP and is becoming increasingly important today. It was an attempt to implement a branch of mathematics called LAMBDA CALCULUS. LISP is different from previously developed programming languages in several important respects: 1) The basic data structure is the binary tree, from which LISTS are easily built. Ability to process symbols of arbitrary structure is given more priority than 'number crunching'. 2) Programs are data structures (in fact, they are just lists). This meant that programs can be treated as data by other programs. Also, properly constructed data structures can be executed. 3) There are no GOTO statements (not even implicit ones as in ALGOL) or assignment statements in the pure version of the language - '(pure LISP programmers do it without side effects). 4) There are explicit parallel processing instructions (which on a von Neumann machine must be processed sequentially), and implicit parallel processing (arguments to a function are evaluated concurrently, at least in pure LISP). 5) LISP programs are more modular than even ALGOL. A LISP program is just a collection of functions. Any function can be called from anywhere. LISP uses dynamic scoping [2]. 6) LISP functions can make calls on the LISP interpreter. The early versions of LISP executed very slowly (mainly because of badly implemented compilers), and LISP had the reputation of being difficult to learn (mainly because of the lack of suitable textbooks). LISP was not popular among commercial users, but found an important niche as the main programming language of the fledgling Artificial Intelligence community in the US. The AI community was more demanding on computer resources and more innovative than most other computer users, and their efforts were instrumental in the introduction and development of two important computing concepts - TIMESHARING and PROGRAMMING ENVIRONMENTS. Timesharing allowed several users to use the machine at the same time, and required an alternative to the batch operating system. Most of the large operating systems in use today are timesharing systems. Programming environments are used for the fast development of programs. They speed up programmers' productivity considerably. Programming environments contain an editor, compiler/interpreter, symbolic debugging and help facilities. One programming environment, INTERLISP, contains a facility called DWIM (Do What I Mean) which automatically corrects spelling mistakes made by the programmer. A more recent programming environment (called the Programmer's Apprentice) does a certain amount of the programming itself, and even comments its own code. COBOL Back to 1960, and the first COBOL compiler was released. COBOL stands for COmmon Business Oriented Language. It discards many of the improvements made in ALGOL (structured program statements, recursion, scoping), but retains data declarations. A COBOL program has the following structure: IDENTIFICATION DIVISION. ENVIRONMENT DIVISION. DATA DIVISION. PROCEDURE DIVISION. Commercial programs are more concerned with the processing of textual data (held in files), and simple decimal arithmetic operations (such as summation) on tables of numbers (held in files). COBOL is good for both of these tasks, and file handling (both random access and sequential). Often, most of the work in COBOL programming goes into the data division. The data structures defined in the data division are hierarchical. The procedure division is used to define the operations performed on the data in the data division. One of the ideas behind the design of COBOL was that you don't need to have a mathematical background to be a good COBOL programmer. So, out with A = B * C + D / E and in with MULTIPLY B BY C GIVING TEMP-1. DIVIDE E INTO D GIVING TEMP-2. ADD TEMP-1, TEMP-2 GIVING A. This is much easier to understand, if you happen to be a wally. In later versions of COBOL, the above three statements were replaced by COMPUTE A = B * C + D / E. In fact, multiplication and division are not all that common in COBOL programs. Anyway, the idea behind the pseudo-English statements was that COBOL programs could be self documenting, and that COBOL would be easy to learn. In these respects it was partially successful. COBOL is the most frequently used commercial data processing language today, and is likely to be in use for some years to come. For those of you who wish to learn COBOL, I recommend Quick COBOL by Coddington (Macdonald and Jane's). Hardware Requirements of Programming Languages From 1960 onwards, programming languages proliferated. Hardware requirements for the more common programming languages are: FORTRAN: general-purpose register set (for optimised code generation), floating point instructions, hardware multiply and divide. Index registers (for arrays). ALGOL: stack, registers for accessing words from inside the stack (for recursion and scoping). COBOL: fast character string manipulation instructions, decimal arithmetic. There were three approaches taken by the hardware designers. 1) Build processors dedicated to the particular language. 2) Build processors which could be (switch) microcoded to the requirements of any given language. 3) Build general-purpose machines and rely on the compiler writer to generate good code for the particular language. The instruction set needs to be highly symmetrical, with many general-purpose registers. There is also a need for a stack (for subroutine calls), floating point arithmetic (for FORTRAN), and decimal arithmetic (for COBOL). Approach (1) was not often taken, owing to the large number of different languages around. After the advent of COBOL, the instruction set of the Burroughs ALGOL machines had to be augmented to include operations specific to COBOL, such as the EDIT instructions (these are used for inserting characters into decimal numbers, thus 183932 --> $1839.32C Approach (2) was frequently taken, but only to implement a general-purpose instruction set. It is easier to design a microcoded porocessor than a hardwired one. A variation on (2) is the B1000 series of Burroughs. These are 'interpreter machines'. They are designed in such a way that it is easy to implement an interpreter for a particular language, giving a 'virtual architecture'. B1000 series machines are bit- addressible, allowing arbitrary word sizes to be implemented. Approach (3) was taken in machines such as the PDP-11, IBM 370 and attempted with less success in some 8-bit microprocessors such as the Z80 (the instructions are not sufficiently symmetric with respect to the registers for easy code generation).