A little over a year ago, while researching the Bendix G-15 computer system, I came across a blog, "The Way It Was: Tales from a life in computing," by Jim Horning. The earlier posts in that blog discuss his early programming experiences, starting in 1959, with a G-15 in the Data Processing Lab at Pacific Union College in Angwin, California. Jim went on to spend significant portions of his career at Xerox PARC and the DEC Systems Research Center.
I enjoyed Jim's stories of his adventures with the G-15, but one post in particular caught my attention, "My shortest Program," from 31 August 2006. In it he describes a program for the G-15, only four words long, that would completely clear the machine's drum-based main memory, its software-accessible registers, the arithmetic overflow flag, and a sign flip-flop used in double-precision arithmetic. That's a lot to accomplish in just four 29-bit words, especially since one of those words was executed only once, at the beginning of the program.
Jim did not provide the code for the program, and in the original post described it only in very general terms. His memory was stimulated by a reader's comment to that post, resulting in Jim appending a rather long comment of his own. In that comment he described in some detail the types of commands the program used, the general scheme by which they worked, and gave many clues for how the program used quirks in the G-15 architecture to achieve its ends. I suspect that in 2006 he was working entirely from an almost 50-year old memory and perhaps was unable to recreate the actual code of the program.
Having recently written a software emulator for the G-15 and taken it to the point where it is working fairly well, I thought it would be interesting to try to reconstruct that four-word program and get it to work. It would be a nice test of the emulator, and something of a programming challenge for me. So I started out by reading his post and long comment multiple times, making notes, and trying to write down what the commands must have looked like.
Overview
You can read Jim's post and long comment at the link above for details, but the general scheme of the program, once initialized, was a loop consisting of five commands:
- CLEAR—clear one "line" (track) on the drum using the value (initially) in AR, the accumulator register, which was cleared to zero during initialization by the command that got executed only once.
- SWAP—exchange that CLEAR command in memory with the zero in AR.
- NOOP—execute the zero word just swapped, which was effectively a no-op (short for "no operation"), i.e., it made no change to the internal state of the computer except to specify the location of the next command to be executed.
- INCREMENT—increase the value of a field in the original CLEAR command (now swapped into AR) such that it addressed a different line on the drum, and when executed would clear that line.
- SWAP—again exchange AR with the NOOP command in memory, leaving the zero value in AR and the incremented CLEAR command in memory ready to be executed again. Then specify the CLEAR command as the next one to be executed and repeat the process from step 1.
That's a five-word command sequence that fits in three words of memory! How is that possible? As Jim pointed out, one of the commands gets held in the AR register, alternating between CLEAR and NOOP on each cycle through the loop, so that's actually four words of storage. The fifth command is SWAP. It gets executed twice, but in both cases it's the same word in the same location of memory.
How that loop works requires some explanation. Understanding that explanation in turn requires some background on the G-15 and how its commands worked, so I'll start there. One of Jim's clues was that the program modified itself during initialization, so the form in which the program was loaded into the system was different than the form in which it did most of its work.
I'll describe that second form first, since it is easier to understand. After that, I'll describe the initial form the program had when it was loaded, then how it started executing and transformed itself into the second form that actually cleared system.
The Bendix G-15
The G-15 was a mid-1950s, binary, vacuum tube, drum memory computer. As Jim hinted in his blog, there were two models, the original G-15A and the 1957 G-15D. There were some minor differences between the two that need not concern us here. My emulator is for the D model.
Drum Memory
The drum memory was organized as 29-bit words arranged in "lines", or tracks. There were 20 108-word ("long") lines numbered 0-19, four 4-word ("short" or "fast") lines numbered 20-23, three 2-word lines that functioned as double-precision registers numbered 24, 25, 26, and known as MQ (Multiplier-Quotient), ID (Multiplicand-Denominator), and PN (Product-Numerator), respectively. There was a single 1-word line numbered 28, AR, the single-precision accumulator. Line numbers 27 and 29-31 did not directly reference storage lines on the drum, but instead specified special functions the processor could perform. Note that the arithmetic registers were storage lines on the drum, not sets of flip-flops inside the processor.
All of the storage lines were implemented as recirculating delay lines, meaning that all of them were read and completely rewritten during each cycle through the line. The long lines cycled every 108 words (about once every 29 milliseconds). The fast lines cycled every four words (about once every millisecond), and AR cycled every word (once every 0.269 milliseconds). The amount of time it took for one word to pass its read or write head on the drum was termed a "word-time," also 0.269 milliseconds.
The basic operation the G-15 performed when executing commands was termed "transfer." Usually this involved copying one or more words from a location on one of the drum lines (termed the "source") to the corresponding location on another line (termed the "destination"). In some cases it was meaningful to specify the same line as both source and destination. When transferring between lines of different sizes, the locations were interpreted modulo the line size, e.g., location 2 on a 4-word line was also location 6, 10, 14, 18,... 102, and 106.
By default, the transfer simply copied the bits from source to destination without change. A command could specify that certain transformations on a word were to take place during transfer, however. We will see one example of that in the memory-clear program. When a line was not being written by a software command, its bits were simply copied from its read head to its write head, hence recirculating them. The "delay" in the line was the time it took a bit on the surface of the drum to rotate from its write head back around to its read head.
Command Words
A command word in the G-15 executed a specific operation and consisted of eight fields having the following arrangement:
G-15 Command Word Layout |
Briefly, the fields in this word, from right to left, had these purposes:
S/D (Single/Double) Bit
This is the sign bit in the word. In a command word it usually specified whether the processor operated in single (0) or double (1) precision mode. It was always zero in the memory-clear program, so need not concern us further here.
D (Destination) Field
The destination line for the transfer operation. This five-bit field could have values from 0 to 31, corresponding to the line numbers described above for the drum. Destination 31 specified that a special function was to be performed. The S field in this case, sometimes in conjunction with the CH field, specified the function. You will see below that the INCREMENT command of the memory-clear program was a Destination 31 command.
S (Source) Field
The source line for the transfer operation. Like D, this five-bit field could have values from 0 to 31. Sources 27 and 29-31 were not storage lines. Instead they supplied a value generated from Boolean operations on other storage lines.
CH (Characteristic) Field
This 2-bit field usually specified a transformation that was applied to a word as it transferred from source to destination. In commands that did not involve line-to-line data transfer, this field sometimes specified optional or variant behavior of the command. In the memory-clear program, this field was always zero—meaning no transformation, the bits were just copied—except for the SWAP command, where it was 2. The meaning of that code will be explained in the next section.
N (Next Command Word-Time) Field
As with many drum or delay-line memory systems of its era, each G-15 command specified the location of the next command to be executed. In most systems with other types of memories, commands were taken from sequential locations unless some command specifically altered the sequence. In the G-15, commands were executed from a single drum line at a time, each command's N field specifying the location of the next command on that line to be executed. Execution continued from that line until a command switched it to a different line. The memory-clear program was loaded into and ran entirely from the 4-word line 23.
BP (Breakpoint) Bit
This bit, if 1, caused the processor to halt if the COMPUTE switch was in the BP position. It was also always zero in the memory-clear program, and thus can be ignored in this discussion.
T (Timing) Field
This field controlled the timing of the transfer phase of the command. The way that field was interpreted depended on the state of the I/D bit described next.
I/D (Immediate/Deferred) Bit
This bit specified whether the command was to be run in Immediate (0) or Deferred (1) mode. All commands in the memory-clear program were Immediate, so we need not be concerned with Deferred mode. In Immediate mode, transfer began with the word-time (location) immediately following that of the command itself, although often on different source and destination lines. Transfer continued up to, but not including, the word-time specified in the T field. You will see below that the use of the T field in the INCREMENT command of the memory-clear program was an exception to this.
For more detailed information on G-15 commands, you may wish to look at a prior post in this blog, which gives fuller descriptions. For the whole story, the best reference is the G-15 Programmers Reference Manual. If you are feeling particularly brave, circuit-level details of command execution can be found in the G-15 Theory of Operation Manual.
Precession
A very important concept in the G-15 is precession, which is a form of data shifting. The processor did precession in units of bits and whole words; the input/output subsystem did precession in units of one bit, three bits, and four bits. All of these were accomplished by interrupting the normal transfer path from the read head for the source drum line and routing that stream of bits through a shift register of length n bits before routing the bits to the write head for the destination line. The effect of this was to delay writing to the destination line by n bits. The end result was:
- The original n bits of the register became the low-order n bits of the first word written to the destination line.
- The original contents of the destination line were shifted to the left (towards higher addresses) by n bits.
- The original high-order n bits in the last word from the source line were left in the register.
This was a powerful, if somewhat unusual, feature of the system and was used extensively in many programs. The SWAP command in the memory-clear program made use of whole-word precession, primarily to exchange the CLEAR and NOOP commands through the AR register, but also as part of the transformation the program underwent from its loaded to its executing form.
The Second Program Form
As previously mentioned, the memory-clear program changed during its initialization from the form in which it was initially loaded to the form in which it cleared the system. This section describes that second form the program took and how it worked. The following table shows the program's command words immediately following their initialization:
L | Hex | T | N | CH | S | D | Description |
---|---|---|---|---|---|---|---|
0 | 060235z | 6 | 2 | 0 | 26 | 31 | INCREMENT AR by 3 |
1 | 02693vw | 2 | 105 | 0 | 29 | 28 | Set AR to 0 (not used here) |
2 | 0403uz7 | 4 | 3 | 2 | 23 | 23 | SWAP location 3 with AR |
3 | 0402380* | 4 | 2 | 0 | 28* | 0* | CLEAR a line to zero |
0000000 | 0 | 0 | 0 | 0 | 0 | NOOP (copy line 0 to itself) |
L is the location of the command in line 23 on the drum. The Hex column shows the hexadecimal value of the command word with the low-order sign bit stripped off, since it is always zero, leaving the 7 hex digits of the remaining 28 bits. Note the use of the G-15 convention for hexadecimal digits: u=10(a), v=11(b), w=12(c), x=13(d), y=14(e), and z=15(f).
The initialization phase of the program loads zero into the AR register, transforms the code into this second form, and then sets location 3 to be the next command executed. The table entry for location 3 shows two commands, CLEAR and NOOP, because these commands are swapped with the AR register on each cycle through the program's loop. When the CLEAR command is in location 3, the NOOP command (a word of zeroes) is in AR, and vice versa.
Execution of this phase begins at location 3 with the CLEAR command, shown here in its initial state. That command copies zeroes from AR (line 28) to long line 0. This command is modified by the INCREMENT command, however, changing the value of the CLEAR command's D field to address a sequence of lines on the drum. Hence the asterisks and underscores in those table cells, indicating that these values change as the program runs.
The command in location 1 is not executed during this phase of the program and was used only once to initialize AR to zero. Note that this command has a strange N value of 105. How can you have a next-command location of 105 for a 4-word line? It turns out you can, and that value is critically important to the initialization process, as will be explained in the next section.
We are now ready to trace through the execution of this phase of the program, starting with the CLEAR command in word 3. The AR register is zero.
CLEAR—This command transfers the value of AR to a line on the drum, initially line 0. Because it executes in Immediate mode, transfer begins with the word-time (location) after that of the command itself. Since the command is at word-time 3, transfer begins at 4. The transfer phase for an Immediate command continues up to but not including the value in the T field, 4, so transfer ends at word-time 3. Thus this one command will copy the zero in AR to locations 4, 5, 6,... 106, 107, 0, 1, 2, and 3, clearing the entire 108-word line.
The N field of the command specifies that the next command will be taken from word-time 2, the SWAP command.
SWAP—This command has source 23 and destination 23, which is the line from which the program's commands are being executed. This seems like it will not accomplish anything useful other than to specify the next command location, but this command has a CH (characteristic) field of 2.
CH=2 specifies a type of transformation termed Transfer Via AR (TVA). This is a form of whole-word precession. During each word-time of the transfer phase, the value of the word from the source line is copied to AR, while simultaneously copying the value originally in AR to the corresponding location on the destination line.
Since this is an Immediate command at word-time 2 with a T field of 4, the transfer phase will start with word-time 3 and end with word-time 3. That means that a single word will be precessed from the source through AR to the destination. Also, in this case the source and destination lines are the same, so the effect is simply to swap the contents of AR with the word at location 3, leaving the CLEAR command in AR and the word of zeroes (the NOOP command) in location 3.
The N field of the command specifies that the next command will be taken from word-time 3, now the NOOP command.
NOOP—This command, being a word of zero, simply copies data from line 0 to line 0. Since the location of the command is 3 and the T field is 0, transfer state will extend from word-times 4 through 107, but that is not important, because nothing gets changed. The only effect of this command comes from its N field, which specifies the next command will be taken from word-time 0, the INCREMENT command. Another (passive) purpose of this command is to preserve the value of zero while the CLEAR command is in the AR register being incremented.
INCREMENT—As Jim pointed out in his long comment, this is actually a G-15 shift command. It shifts the MQ register (line 24) left while simultaneously shifting the ID register (line 25) right. Since these are 2-word registers, each shift requires two word-times. This command executes in Immediate mode, but the T field in this case is interpreted as an absolute count, not the ending word-time. Since each shift requires two word-times, the T value must be twice the number of shifts desired.
The shifting behavior of the command is not relevant to its purpose in this program, however, and those registers are eventually going to be cleared anyway. Instead, the command is used for a side effect—if the CH field of the command is 0, the AR register is incremented by one for each shift performed.
At this point, the AR register holds the CLEAR command. The T field has the value 6, which means that three shifts will be performed. Thus, the real purpose of this command is to increment the D field of the CLEAR command by 3. Jim explains the rationale for incrementing by 3 in his long comment, but suffice it to say that this results in a sequence of values for the D field of 0, 3, 9,... 24, 27, 30, 1, 4, 7,... 25, 28, 31, 2, 5, 8,... 23, 26, 29, 0. Overflows from field D into field S of the CLEAR command occur when incrementing from 30 to 1 (yielding S=29) and from 31 to 2 (yielding S=30). The reason why the CLEAR command continued to work properly after its S field no longer referenced AR is explained below.
The N field of the INCREMENT command specifies that the next command will be taken from word-time 2, which is again the SWAP command.
SWAP—This is exactly the same SWAP command as the one above and works in exactly the same way, swapping AR with the word in location 3. The difference this time, however, is that the incremented CLEAR command is in AR and the zero for the NOOP command is in location 3. These are exchanged so that once again the zero is in AR and the CLEAR command is in location 3.
The N field of this command is still 3, so the next command to be executed will be the now-incremented CLEAR command. This completes the program's cycle for one destination line. That cycle now starts over again at the CLEAR command.
There are a several additional details necessary to complete the picture of how this phase of the program worked, most of which Jim mentioned in his long comment.
- How does the program terminate? It doesn't. Once line 23 gets cleared, the program no longer exists, or rather, its words have all been overwritten with zeroes, which are NOOP commands. No matter in which word-time the CLEAR for line 23 ends, the NOOP will copy line 0 to line 0 and take the next command from location 0, which is also a NOOP. Thus, the program finishes by endlessly executing NOOP commands, copying line 0 to line 0 until the operator halts the system.
- The CLEAR command always executes with AR being zero, so once the CLEAR for line 23 finishes, AR remains zero, as do all of the other registers.
- Note from the discussion of the INCREMENT command above that the sequence of incremented D field values for the CLEAR command does not end with 23—destinations 26 and 29 come after that. Thus, once line 23 is cleared and the system starts continuously executing NOOPs, those last two destinations do not get cleared. That does not matter, though, because:
- As Jim pointed out, line 26 is the PN register, which the system automatically clears when a value is copied to line 25, the ID register. This happens before line 23 is cleared.
- Destination 29 is not actually a storage line on the drum. Instead it is a destination used to add a value to AR. Thus, not copying (nor adding) anything to it is not a problem.
- When a value from an even word-time is copied into line 25 (ID), the sign of that value is copied automatically into the IP flip-flop, which holds the sign for multiplication and division commands. Thus, when line 25 is cleared to zero, IP also gets reset.
- Destination 27 is also not a storage line on the drum. It is the TEST destination. If during the transfer phase it receives any 1-bits, then the next command is taken from the location one after that specified in the N field of the test command. The CLEAR command only transfers zero bits, however, so this "skip on non-zero" action never takes place.
- Similarly, destination 31 is not a storage line on the drum. It indicates that the operation specified by the S field in the command will be performed. Destination 31 will be addressed during the time that the S field in the CLEAR command has the value 29. The combination D=31, S=29 tests and resets the arithmetic overflow flip-flop. As with the TEST destination above, if the overflow flip-flop was set, the next command is taken from the location one after that specified in the N field of the command. The CLEAR command that triggers this test has an N field of 2, so if the overflow flip-flop was not set, execution continues to that location in the normal manner. If overflow was set, however, the next command will be taken from location 3, which is once again the CLEAR command for destination 27. This time, though, the overflow flip-flop is guaranteed to be reset, since the test done by the prior CLEAR would have reset it, and execution will still continue to location 2 in the normal manner.
How was I able to reconstruct this form of the program? Jim's description in his long comment of the individual commands and the sequence in which they operated gave me a general idea of what the commands needed to be. What wasn't so clear were the locations in which they needed to be stored and values of the T and N fields in each command. I was able to piece the rest of it together by reading between the lines a bit and taking into consideration some constraints imposed by the G-15 architecture.
- As Jim pointed out, the INCREMENT command had to be in location 0, because the NOOP command specified it as the next command, and the N field of the NOOP command was 0.
- The command that cleared AR to zero was executed only once during initialization, so its location was unimportant during this phase of the program, and it could be placed anywhere.
- The most tantalizing clue was this statement of Jim's:
"The final bit of cleverness was the third use of SWAP. When used in the loop, it executed for a single word time, thereby exchanging AR and a single word of line 23. But it was a block command, and its first execution was at a different word time than all the others. It precessed line 23 to place INCREMENT in word 0."That suggested to me that the initial precession rotated all four words of line 23 by one word. The command that cleared AR probably had to be in location 0 initially, so that it would be first to be executed after the program loaded. Precession moves words to higher locations; thus after precession that command would be in location 1.
- That left only two undetermined locations, 2 and 3, which had to be used for the SWAP and CLEAR/NOOP commands. Since SWAP was an Immediate command, the CLEAR/NOOP command it operated on during its transfer phase had to start at the word-time immediately after its own. Therefore SWAP had to be at location 2 and CLEAR/NOOP at location 3.
Once those locations were determined, the values for the N fields were obvious (well, except for that weird N=105 in the command that cleared AR—that came later), and the values for the T fields were straightforward to calculate from the command locations.
Sad to say, this phase of the program was the easy part to figure out. Understanding how the program had to be structured for loading and how it got transformed into this second form was much more difficult and took a lot longer. That is the goal of the next section.
The Initial Program Form
Programs were typically loaded into the G-15 from paper tape, something that could be initiated from the typewriter by engaging the ENABLE switch on the base of the typewriter and pressing the "p" key. Jim pointed out that you could also enter programs directly from the typewriter keyboard using a different sequence of control commands that are discussed in the final section of this post. Typewriter entry was practical only for very small programs like this one, though. There was no way to backspace or make corrections—one mistake and you had to start all over.
An input operation read data from paper tape or the typewriter and precessed it into line 19 in blocks of up to 108 words. Line 19, however, with a cycle time of 29 milliseconds, was too slow to precess digits directly from the paper tape reader, which at 250 characters/second read tape frames every 4 milliseconds. To get around this, 4-word line 23, which cycled about once per millisecond, was used as an intermediate high-speed buffer. Once line 23 accumulated four words, an operation termed "reload" would cause the hardware to copy the contents of line 23 to another 4-word line, MZ, which was not visible to software. From there MZ had plenty of time to precess its data into the beginning of line 19 while line 23 continued to fill up with digits from the reader. Reload was triggered by a control frame on the tape or the "/" key on the typewriter, although a later enhancement to the G-15 to handle alphanumeric data allowed reload to happen automatically.
Input from the typewriter was slow enough that direct entry into line 19 would have been feasible, but typewriter input still went through the same Tinker-to-Evers-to-Chance process that input from paper tape did. Since the memory-clear program was only four words long, there was no need for a reload to move it into line 19, as those four words could be executed directly from line 23.
With that preface on how a program gets loaded into the G-15, my problem at this point was to figure out how to arrange the words of code for the memory-clear program so that it would start executing properly.
Since programs started executing at word 0 after loading and the command that cleared AR to zero was executed only once, it made sense to assume that one-time command should be loaded into word 0. That command transferred source 29 (the Input Register, IR) to destination 28 (the AR register). The IR was a means to read a word of bits from external equipment, but unless it was enabled and the external equipment was actually present, it supplied zeroes, thus clearing AR.
The next issue was to understand how the initial execution of the SWAP command could precess the words in line 23 so that the INCREMENT command would end up in word 0 on the line, where from the discussion in the prior section we know it had to be. The only thing that made sense was to have the SWAP command rotate the entire line by one word. This is tricky, because in doing that rotate, all of the words would change location, including that for the SWAP command itself. It also meant that the next command originally addressed by the N field of the SWAP wouldn't be there any more after the swap—a different command would be in that location.
How a SWAP command executed from one location could rotate all four words of the line and the exact same command executed from a different location after the rotation would exchange only one word on the line with AR was the most difficult part of the initialization phase to figure out. The fact that the SWAP command moved, though, was also a clue. Executing the command from a different location would change the effect of the T field in that command, namely, the number of word-times the command's transfer phase would last.
Recall that a command executing in Immediate mode starts its transfer phase in the word-time immediately after that command's word-time on the drum. Transfer continues up to, but not including the location specified by the T field. Thus far, I have been using the terms "word-time" and "location" as if they are interchangeable, but they are not. It is tempting to think of the values in the T and N fields of a command as locations (addresses) of words on a drum line, but they are not that either. Those values actually represent counts.
The G-15 did not keep track of addresses on the drum. Instead it loaded the T and N values into a register, which then counted its way to the correct position on the drum. In reality, it loaded the complement of the difference between the T or N value and the drum's current position into the register and counted up until the 7-bit register overflowed. The counts were held and incremented as fields in a 1-word line on the drum, CM, the Command register, that was not visible to software. Exactly how this worked is complicated and is described in the G-15 Theory of Operation manual, section C-17 (starting on page 40, or PDF page 46).
After struggling over a number of days with how the initial SWAP command could be made to do different things from different locations, I finally realized that the answer had to be in the potential difference between a command's drum location and its word-time. For the long 108-word lines there normally is no difference, but for a 4-word line, word-times are still accounted for on a 108-word basis, even though a word's physical location is its word-time modulo 4. What I needed to do was arrange for the SWAP command to start at a word-time that was congruent modulo 4 with its drum location but that allowed the command's transfer phase to run longer.
For that, I first had to determine how many word-times of the SWAP command's TVA transformation were necessary to rotate all four words of line 23 by one word. TVA uses AR for its precession, and AR at that point has the zero value that will be needed by the CLEAR commands later, so this execution of SWAP had to work in a way that also preserved the zero for later use.
The answer turned out to be six word-times. To see this, first look at the sequence of commands as I now knew they had to be loaded into line 23. Un-rotating the commands as presented in the prior section, that sequence is:
0: SET-ZERO (set AR to zero)
1: SWAP
2: CLEAR
3: INCREMENT
Precessing these words through six TVA transformations yields the following sequence of exchanges:
Step | Drum Word |
Before | After | ||
---|---|---|---|---|---|
AR | Line 23 | AR | Line 23 | ||
1 | 2 | 0 | CLEAR | CLEAR | 0 |
2 | 3 | CLEAR | INCREMENT | INCREMENT | CLEAR |
3 | 0 | INCREMENT | SET-ZERO | SET-ZERO | INCREMENT |
4 | 1 | SET-ZERO | SWAP | SWAP | SET-ZERO |
5 | 2 | SWAP | 0 | 0 | SWAP |
6 | 3 | 0 | CLEAR | CLEAR | 0 |
The SWAP is executed from location 1 (but not word-time 1—that important detail will be discussed next), so it starts operating on the word immediately after that at location 2. After the command finishes, line 23 has been rotated by one word and now has the following arrangement:
0: INCREMENT
1: SET-ZERO
2: SWAP
3: NOOP (the 0 originally in AR)
The only remaining piece of the puzzle was to arrange for the initial execution of the SWAP command to run for six word-times instead of one. The solution to that involves the difference between drum locations and word-times, and is the reason for that weird N value of 105 in the one-time command that sets the AR register to zero.
N=105 modulo 4 is 1, so that will specify the SWAP command, initially at location 1, as the next command. The SWAP command will then start its transfer phase in the word-time after that, 106, and will continue up to but not including the word-time specified by its T field, 4. Thus, the transfer phase for the initial SWAP command executes during word-times 106, 107, 0, 1, 2, and 3—for a total of 6 word times.
After this point, the SWAP is in location 2 and is specified as the next command by the INCREMENT and CLEAR commands, which both have N=2, so in those instances there is no difference between location and word-time, and thus the SWAP executes only during word-time 3.
The way in which this precession worked is pretty slick, but it left me with two curiosities. First the CLEAR command was not at word 3 in line 23 anymore where I thought it needed to be—it was now in AR and word 3 now had a 0, the NOOP command. Second, and even more curious, the N field of the SWAP command has a value of 3, and therefore:
- The next command to be executed after the initial SWAP will be the NOOP.
- The only effect of the NOOP will be to move on to the command specified by its N field, which is 0, specifying the INCREMENT command.
- The INCREMENT command will add 3 to the contents of AR (the CLEAR command), then move to its next command, the SWAP, now at location 2.
- The SWAP, now in its new location, will execute for a single word time and exchange the NOOP and CLEAR commands, leaving the value 0 in AR and the CLEAR command in location 3.
- SWAP still designates its next command to be location 3, now the CLEAR, which starts the second phase of the program as described in the prior section.
Thus, this sequence of commands has rearranged the words of line 23 into precisely the form needed to clear the system, just as described in the prior section.
The only problem now is that the CLEAR command has already been incremented, so if it had originally been loaded ready to clear line 0, it now has a D field of 3, meaning line 3 would be the first to be cleared and the program would never get to line 0. This was disappointing—the program had transformed itself exactly as needed except for that initial INCREMENT messing up the CLEAR command's starting line number.
I couldn't see any way to bypass the INCREMENT step, and worried over the problem for more than an hour before finally realizing (and smacking myself upside the head for being so dense) that the obvious solution was to load the CLEAR command so that incrementing it would properly address line 0 at the start of the clearing phase. Instead of loading the command with S=28 and D=0, it needed to be loaded with S=27 and D=29. Adding 3 to the word would increase D to 32, which in its five-bit field would be zero, and the resulting overflow into S would increase that field to 28. VoilĂ !
Therefore, the initial form of the program as loaded is this:
L | Hex | T | N | CH | S | D | Description |
---|---|---|---|---|---|---|---|
0 | 02693vw | 2 | 105 | 0 | 29 | 28 | Set AR to 0 |
1 | 0403uz7 | 4 | 3 | 2 | 23 | 23 | SWAP (rotate line 23 by 1 word) |
2 | 040237x* | 4 | 2 | 0 | 27* | 29* | CLEAR (before initial INCREMENT) |
3 | 060235z | 6 | 2 | 0 | 26 | 31 | INCREMENT AR by 3 |
The asterisks indicate fields that are modified by the initial INCREMENT command during the initialization process.
In Summary
The form of the program that Jim would have typed on the G-15 typewriter at Pacific Union likely would have been this:
060235z02011vy8100yvxw04x2778s
The "s" at the end is a Stop control code that terminated typewriter input. The hexadecimal string above is in a compact form where the 116 bits of the four 29-bit words are packed into 4-bit hex digits without regard to word boundaries. The underscores indicate digits that contain bits from adjacent words.
There is a less-compact and more-easily understood form that could also be used:
060235zT 040237xT 0403uz7T 02693vwT s
Jim would have have preceded either of these forms by engaging the ENABLE switch on the base of the typewriter and typing "sc7fq". The ENABLE switch caused the G-15 hardware to recognize certain typewriter keys as control commands. These keys caused the following to occur:
- "s" canceled any input/output operation that was currently in progress. This was not needed if no I/O was active.
- "c7" set the system's Command Line register to 7, which would cause the processor to execute commands from line 23.
- "f" set the location (word-time) of the next command to be executed as 0. When the system was started, it would thus take its first command from word 0 of line 23.
- "q" enabled the typewriter for data input.
See the G-15 Operating Manual for more information on the ENABLE switch, its keyboard commands, and data entry from the typewriter.
I can't be certain that the code I have presented here is exactly what Jim would have remembered using. It's possible there may be variations on what I have managed to reconstruct that would also work, although my code does match his description, and more importantly, it works just as Jim described. In my retro-g15 emulator, which attempts to run at the same speed as a real G-15, the program takes about six seconds to complete.
You can download a paper-tape image of the program code, in a form that will work in my emulator, from the project's GitHub software repository. There is also a text file containing a trace of the program's execution as it initializes itself and proceeds to clear the system.
This program is a good example—perhaps even an extreme one—of the cleverness and trickery in which programmers were willing to engage during the 1950s and early 1960s in order to obtain maximum performance in minimum storage space from early computer systems. It illustrates a couple of important G-15 programming techniques, including precession and the gaming of word-times to control what a command did.
I was quite pleased with myself for being able to reconstruct this program from Jim's description and my still-growing knowledge of G-15 architecture and programming techniques. Multiple head-pounding episodes of frustration aside, it was a lot of fun, and very gratifying to accomplish.
I had hoped to contact Jim after getting the program to work and telling him about my efforts and my interest in the G-15. Alas, I learned from a mutual friend that Jim had passed away in 2011, and I regret that I was never able to know him. His blog is still available, though, and I recommend it to anyone who is interested in knowing what it was like ca. 1960 to use a slow and difficult system from computing's early days.