I left school after my 'A' levels in the summer of 1964 and started on a five-year thin- sandwich course in Electrical Engineering, which resulted in a Bachelors' Degree in 1969. The course consisted of alternate six month periods in college (at Beaconside in Stafford) and in industry (at what was then English Electric Computers in Kidsgrove). The college periods typically ran from September to February, and the industrial periods ran from March to August.
I think I was very fortunate to attend the college in Stafford at that time. The building was a new site, and the number of students was relatively small; the college had just taken ownership of a DEUCE computer (which had been previously located at Liverpool University); and at that time there were no specific courses for Computer Science, Programming or any of the related computer subjects. Within the couple of weeks of being at the college, all that year's intake were put through a 2-day course on programming the computer in a language called Alphacode. This course was intended to allow students to make use of the computer as part of their normal course work, and also included about a day and a half of actually writing programs and putting them through the machine. We got physical hands-on experience.
Six or seven years later, this had all changed. There were many more students; there were formal courses in computer related topics; and the running of programs was performed by a professional team who ran a bureau service for the students.
Those first programs were quite strange compared with modern computing. Alphacode handled only two main types of variable - floating point numbers, and integers. All floating point variables used names of the form x1, x2, etc, while integers use variable names of the form n1, n2, etc. There was a technique for indirect addressing so that a program could access successive variables in a loop, but how that was achieved escapes me after 40 years. The language supported only a single function in each statement, so the instructions were of the form
x1 = x2 + x3
This one means the variable x1 is assigned the value that is the sum of the contents of variables x2 and x3. The language had no need to handle operator precedence, so expressions of the form
x1 = x2 + x3 * x4
had to broken down broken down into separate statements as:
x1 = x3 * x4
x1 = x2 + x1
Programs were typed up on to 5-hole paper tape on what were essentially Telex machines, and these tapes were then taken through to the computer room so the fun could begin. If the program was to read in various data (rather than having the data encoded within the program), then a separate tape had to be prepared with the data on it.
The first step in running the program was to load a pack of cards into the DEUCE card reader and press the Initial Input key. This pack was called the Alphacode Compiler. Once the pack had been read in, the paper tape reader would spring into life, and a program tape would be read in. If all was well, a few seconds later the card punch would burst into life and a small pack of cards would be produced. At this point, further program tapes could be read in and compiled. All that was necessary to prime the machine to read the next tape was to take the last card from the compiler pack, load it into the card reader and press the Initial Input key.
Once all the program tapes had been read and the corresponding small card packs had been produced, another pack of cards - the Alphacode Interpreter - was loaded into the card reader, and the Initial Input key pressed. After the interpreter had been loaded, a user's program card pack could be loaded into the reader and the Initial Input key pressed to load and run the program. Results from the program were output on a paper tape punch, and these tapes were taken back to the Telex machines to print out a human readable hard copy.
The first few days of running programs through the computer all followed a similar process. I for one, and I suspect many of my fellow students, did not really understand what the Alphacode Compiler and the Alphacode Interpreter were actually doing. I can remember thinking at the time that the computer must have thousands of different circuits that were configured according to the punched holes on the various cards in each of the packs so that the machine would carry out its intended role. It was some weeks later that I learned just what was really happening, at which point the real adventure of working with computers began.
One lunch time I wanted to try out something new (to me) in an Alphacode program, and I prepared my tape and wandered into the computer room. As often happened at lunch times in the latter part of 1964, the room was empty. When I reached the card reader, I noticed the card reader light, which shows when the computer is trying to read cards, was flashing on and off about once a second. A few lights were flickering on the console, so it was obvious that something was running. But what? At that moment, George Williamson, the resident engineer, came into the room and explained that he had written a little program in the machines own language that was flashing the light. The program had just eight instructions, but it was the break through for me in terms of understanding what had been happening when we had been running programs for the previous few weeks. George showed me the relevant pages in the DEUCE programming manual and explained just what his program was doing. The manual also told me about the whole range of functions and operations that the machine was capable of doing directly at the hardware level.
The penny dropped. The Alphacode Compiler and Interpreter were merely programs written in the computer's own native language, and our Alphacode programs were merely data to be processed by these native language programs.
After that, my use of the DEUCE split into two separate paths. I used Alphacode programs for processing course work, but in my spare time I started developing native code programs to learn more about the fundamentals of computers and computer programming. For example, in Alphacode if a statement says:
it would cause the contents of the variable x1 to be punched on to tape as a floating point number. At the native level, the computer had to convert the contents of variable x1 from its internal floating point binary form into the equivalent decimal form; then start the punch; convert each decimal digit, decimal point, etc to the appropriate paper tape punch code; and punch that code onto the tape. Finally, the native level code had to stop the paper tape punch when all the characters had been output.
To run programs in DEUCE's own language, they either had to be keyed into the machine manually from the console, or they had to be punched on to card. It soon became apparent that manually keying programs directly in to the machine was not suitable for sequences of more than about 20 to 30 instructions, so I learned how to use the manual card punch. Punch cards had 12 rows and 80 columns, and were really intended to carry 80 characters of data per card, with one character per column. Each character normally consisted of one or two holes per column. A space was represented by a column without any punching, and decimal numbers were represented by a single punching in a column. In fact, using only 0, 1 or 2 holes in a column can provide a character set of 79 different codes. The manual card punch was optimised for preparing such 80 column cards.
DEUCE actually used the card differently from the 80 column mode described above. Columns 1 to 16 were not used by the computer, although the card punch could apply a sequential number to each card in these columns to allow a pack to be sorted manually into the correct sequence. Instead, DEUCE read the cards a row at a time, with columns 17 to 48 (the alpha field) representing one 32-bit word, and if required, columns 49 to 80 (the beta field) representing a second word. Therefore, a card could carry up to twenty four 32-bit instructions. Punching DEUCE programs directly onto a card by hand was time consuming as each word had to be punched separately. An accidental extra hole meant that the card had to be started again.
One of my first machine code programs was a convertor program, which allowed such programs to be prepared on paper tape, and then converted directly into a card pack using the computer to do the work. This greatly speeded up the process of producing card packs for the programs I wrote subsequently.
The actual boot process was elegant in its simplicity. DEUCE had twelve 32-word mercury delay lines (with each word being 32 bits long). Eight of these delay lines (DL1 to DL8) could hold instructions for normal execution. DL9 to DL12 also held 32 words but could not hold instructions for direct execution. There were also three 2-word delay lines (DS19 to DS21), two 4-word delay lines (QS17 and QS18), and five 1-word delay lines. Four of the 1-word lines (TS13 to TS16) were available as fast access storage, while the fifth one (TS-COUNT) was the current instruction decoder. Behind the delay line memory was the magnetic drum, which held 256 tracks of information, where each track was equivalent to a 32-word delay line. Data was transferred to and from the drum by writing or reading the entire contents of delay line 11 to or from the drum. Every drum transfer consisted of 32 words of 32-bits each.
When the Initial Input key was pressed, this cleared all the contents of all the delay lines (but not the magnetic drum), activated the card reader read circuits within the computer, and started the process of reading cards through the reader. As each row of a card passed over the read head, the card reader gave the computer a single shot to execute the instruction it currently had loaded.
Now for the elegant bit - the all-zeroes instruction that resulted from clearing the TS-COUNT delay line was an instruction to wait for a single shot before proceeding, and then to read source 0 (the card reader) to destination 0 (the TS-COUNT delay line). By default, this would read the instruction in the alpha field of the card and treat it as the next instruction to be executed. Typically, the first couple of instructions on the first card would be something like:
The first of these instructions is to load copies of the next input on the card into delay line 8. The 1,31,30 signifies that the transfer should be repeated for a total of 32 words - the whole of delay line 8; and the X signifies that this instruction should wait until the next row is read from the card. Effectively this loads 32 copies of the second instruction into the 32 words of delay line 8.
Each copy of the second instruction tells the computer to load a row from the card into the word where that specific instruction is located (overwriting the instruction in delay line 8), and then go to the next instruction in the delay line. A successive row is loaded into consecutive words of delay line 8 as the corresponding single shot is received from the reader. After 32 such rows have been read, the computer comes back to the first word in delay line 8, which is the first of the 32 words that have been loaded. As each instruction had two timing fields in it - one to define the timing when the operation of the instruction was to start, and one to define which word out of 32 contained the next instruction, it was possible to create a single card that would load and run a small program.
Program card packs typically fell into three different types. The simplest was one that loaded and ran a small program (less than 256 instructions) in DEUCE's own language. This type of pack would load instructions directly into one or more delay lines (DL1 to DL8). Each delay line would be loaded with a simple boot mechanism as outlined in the previous section.
The second type of pack was for larger programs in the machine's own language. These programs had to reside on the drum as they were too large to reside in the delay lines alone. The packs for these programs normally consisted of the following:
A synchronisation card
After the Initial Input key was operated, the first instruction to be executed could be aligned to any of the 32 words in the long delay lines. For many programs this did not matter, however, if a program was to make use of double length arithmetic in DS21, or was to use the automatic multiplier or divider, it was essential that the instruction performed its task correctly in an even or odd word. For instance, if a double length addition was to be performed, the least significant (even) word had to be added first so that the carry would work correctly into the most significant (odd) word. The synchronisation card worked along the following steps:
Enable double length arithmetic.
Load both words of DS21 with all ones.
Add 1 to one word.
Check if the other word was now zero.
If the second word was now zero, then the alignment was correct. However, if the second word was still all ones, then the carry had not worked and the alignment was one word out. An extra delay of one word in executing the next instruction would correct the error. When the main program was loaded, it would be correctly aligned for double length arithmetic and multiplication and division.
Clear drum card The second card of this type of pack was normally a small program to clear the previous contents of the drum. This had to write all zeroes to all 256 tracks and typically took around 10 seconds to complete.
Drum loader card
The third card of the pack was a drum loader that would take groups of three cards and treat them as the contents of two drum tracks. Three cards contained 36 rows, and each row could hold two 32 bit words (columns 17-48 and 49-80). The first row of the 36 had an 8 bit track address in the least significant 8 bits in each field. The next three rows were dummy rows, and were not used except to provide single shots to keep track of how far the card had passed through the reader. The final 32 rows were the 32 words to be written onto each of two tracks of the drum. The loader had to write the two tracks to the drum between the last row of one 3-card group and the first row of the next group. A special marker bit towards the most significant end of the first row of a group informed the drum loader program that this was the last pair of tracks to be loaded. Once these had been written to the drum, the machine would take the next card as a new program.
Drum data in 3-card groups
See previous sub-section.
Sum check card
After the triads of cards had been read and loaded to the drum, a single card sum check program was loaded to check that the load had been successful. By modern standards, this was not at all rigorous, but as most errors were gross misreads of cards, this was normally sufficient. If the sum check was okay, the computer would start the reader again. If the sum check failed, the reader would not be started, and an error code was displayed on the lights on the console.
Program initiation card
The program initiation card fetched a specific track from the drum (this could vary from one program pack to another), and then search the track for a special marker bit that pointed to the first instruction. The code on the loaded track fetched further tracks as required to run the program.
While a program was loaded on the drum, it could be restarted simply by reloading the Program Initiation Card and operating the Initial Input key. This was used when several Alphacode programs were to be compiled in succession.
The final type of program pack was the type output by programs such as the Alphacode Compiler. These card packs were mainly programs to be run by the Alphacode Interpreter. However, the first card of each such pack was a Program Initiation Card. This card located the start of the Interpreter in the drum and started it running. The Interpreter read in the remainder of the card pack and processed the data on those cards as an Alphacode program.
My Main Contributions
During my time at the college, I wrote a large number of programs directly related to my college work. I also produced a number of programs in DEUCE machine code for my own learning and a few for use by the college:
Machine Code Convertor
As I have mentioned earlier, one of my first such programs was a machine code convertor that allowed me to prepare programs on paper tape rather than punching them directly on to card. This greatly improved the preparation time of subsequent programs.
Bulk Card Copy Program
When I first started using DEUCE, there was a program available that would read a card pack in through the card reader, and produce a copy on the card punch. This particular program worked by reading one card, stopping the reader, starting the punch, punching the one card, stopping the punch and then restarting the reader. Although this worked fine for a few cards, it was particularly cruel to the various clutches in the reader and the punch as these were repeatedly engaged and disengaged. The clutches did not respond well to being released and then re-engaged very shortly afterwards, and sometimes a mechanical failure would result.
My copy program read in a complete pack of up to 256 cards in one continuous operation. These cards were placed on sequential tracks of the drum, with one card (24 words) on each track. Once the entire pack had been read, a key on the console was set (the TIL switch for those who want more detail) and the machine was given a single shot. At this point, the program turned off the reader, and started the punch. It then proceeded to punch out the entire pack in one continuous sequence. As the program had counted the cards during the input phase, it 'knew' how many cards to punch during the output phase.
Drum Dump Program
The original Alphacode Interpreter card pack as used at the college was really quite inefficient in the way it was constructed. The pack contained around 160 cards, and loaded around 86 tracks on the drum. The first 130 or so cards were a normal drum load pack as described previously, and ending with the sum check card. The remaining cards were a series of patches and a final sum check that verified that the patches had been applied. Each patch card read one track from the drum, replaced one word and then wrote the track back to the drum. This made the pack around 30 cards larger than it needed to be, and this and the second sum check card slowed down the process of loading the interpreter.
My Drum Dump Program scanned the drum and then output any non-zero tracks with two tracks (of 32 words each) on each group of three cards. The output format was compatible with the input format for the drum data loader described previously.
I simply loaded the 160 card Interpreter onto the drum, and then loaded my dump program to produce a pack of around 126 cards. The first three cards from the original pack (the synchroniser, clear drum and drum loader) were added on the front, and the final sum check card was added to the back to produce a more compact and efficient version of the interpreter card pack. As the actual drum image was not altered, this did not affect the performance of the Interpreter, only the efficiency of its load process.
Machine Code Compiler
This was my first large scale program project. It was not totally successful; however, it did work after a fashion. The compiler was what might be called an assembler today, but assemblers were relatively unknown in the first half of the 1960s.
The compiler handled all the headaches of allocating locations for each instruction in the 8 program delay lines (DL1 to DL8), and automatically worked out the correct timing parameters for each instruction to link it to the next one. It also supported instruction labelling and jumps to a label rather than requiring the next instruction to be identified by delay line number and minor cycle.
Compiled programs were limited to a maximum of 240 instructions and completed programs tended to run at less than 10% of the speed of fully optimised code.
My second large scale project was a programming language that handled multiple operators (unlike Alphacode). Unlike Alphacode, Simplecode worked with a single card pack as the compilation and interpretation code were loaded together and the transition from one to the other occurred automatically without the production of any intermediate card pack.
At the time I developed Simplecode, I had not heard of operator precedence, and so the language would not automatically perform multiplication and division operations before additions and subtractions. It did support the use of parentheses to define operations that needed to be performed before other operations completed within an arithmetic statement.
Although Simplecode was used by only a few people, it was used. After its completion I used it for all my college course work.
Alphacode Combined Pack
After completing my Simplecode program, and experiencing the advantages of loading a single card pack that could remain loaded for most of the day, started looking at the possibility of Alphacode working the same way. An examination of the structures of the Alphacode Compiler and Interpreter suggested that there was likely to be sufficient drum space to keep both sets of code resident at the same time. There were a couple of problems to be solved.
The first problem was imposed by the interpreter, which would attempt to use the whole of the drum. The first quarter of the drum was reserved for the user's program to be loaded into. The next part of the drum (just over a quarter) was reserved for the actual interpreter code. The rest of the drum was reserved for the storage of floating point numbers. I believe the original design of Alphacode allowed for 2047 such locations to be defined. Potentially this left no space for the compiler. Reducing the number of floating point storage locations created space at the top of the drum to hold the compiler.
The second problem was that the compiler and interpreter were both designed to be executed from the same part of the drum (starting one quarter of the way up the drum's space). Either one of the programs would have to be modified so that all references to the drum for the purposes of fetching new code would access that code from a new location, or the two programs would have to be swapped over between compilation and interpretation. I chose the latter approach and developed some code that would swap the locations of the compiler and interpreter when required.
The last card of the Combined Pack was changed so that is called my new code at a point that tested to see if the compiler was ready, and if not would trigger a swap over. Similarly, the first card of the intermediate card pack produced by the compiler would call my new code but at a different entry point and check for the interpreter being ready, and arrange a swap over if required. The actual swap over took around 15 seconds, which was considerably faster than reloading a full card pack. It also meant that the wear and tear on the card packs was greatly reduced.
The Combined Pack was used by the college as part of the Computer Science course. The separate packs were effectively relegated for use only by those students that had programs using a large number of floating point variables.
Alphacode 'Run & Go' Pack
My final major programming project on DEUCE was a further enhancement to Alphacode in the preparation of what I called the 'Run & Go' pack. When the compiler was run to produce an intermediate card pack, it also placed a copy of the program in the first quarter of the drum - in exactly the same place that the interpreter used to execute the program.
This final stage involved suppressing the punching of the intermediate card pack; triggering the automatic swap over to the interpreter once the compilation was complete; and the elimination of the read in of the intermediate card pack that was no longer being produced. The result was to produce a system that would read in a user's new program tape, and execute the program without any further intervention, and without producing the intermediate pack of cards.
Loading the last card of the Run & Go pack into the card reader, and pressing the Initial Input key was all that was required to activate the compiler and run through the entire compile & interpret sequence.
There were a couple of minor disadvantages incurred by the Run & Go version when compared with the Combined Pack. If there were several programs to be compiled and run, it was quicker to use the Combined Pack to compile all the programs first and then run the interpreter phase afterwards on all the programs. This eliminated the repeated swapping of the compiler and interpreter between each program. The second disadvantage arose if a user wanted to run a program more than once. With the Run & Go pack, the program had to be re-compiled each time it was run, whereas the Combined Pack would simply require the intermediate card pack to be reloaded.
Nevertheless, the Run & Go pack was used by Computer Science students particularly in their first few months of learning to write programs.
My experience and time with DEUCE provided me with a wealth of knowledge and experience in code writing and optimisation as well as program development and debugging. It also taught me much about how digital systems worked. All of this proved invaluable to me over the next 40 years of my working career.
Richard Taylor BSc (Hons)