Part 9: Like a WW II Pilot’s Tally
The 4.8 Scorer Patch and Other Spacewar! 4 Oddities
(Introduction · The 4.8 Scorer Patch · Spacewar! 4 Genealogy · The Peculiar Case of the 4.2 Score Display · The Mystery of the Twin Stars (Revisited) · The Spacewar! 4.0 Game Saver Patch · The Other Space Race)
The story of Spacewar! continues: In this episode we're leaving Steve Russell & Company and are going to investigate some of the hacks and patches for Spacewar! 4. Even after the original authors had left Spacewar! as of version 3.1 as the apparent final version, others came up to step in their place, polishing the code and adding features to the game (most prominently Diamantis Drakos “Monty” Preonas, adding the advanced, high-precision gravity computations of version 4 and adapting the game for the automatic hardware multiply/divide option; see Part 6). Albert W. Kuhfeld even recalls "a couple of [MIT-]students trying to introduce a computer-piloted flying saucer to occasionally zoom through the game as a kind of “wild variable.” To the best of my knowledge, they never got it working." (Kuhfeld, Albert W., "Spacewar"; in Analog, July 1971 issue; p. 79) While this Asteroids-like feature was never to be seen seen in the wild, persumingly failing on predicting the trajectories under the influence of gravity and due to the limits of available real time resources, there's still a number of features to explore that were actually accomplished — and at least some of them are remarkable hacks deserving our most ungrudging attention.
Probably the best known of these hacks is an on-screen scoring device that even got recognition from Spacewar's original authors:
A Mystery, Just For Good Measure
Slug [Steve Russell; N.L.] tells me that there is a Lost Version of Spacewar! There would be, of course. He says the game is pretty much like the original, but the scoring is much more impressive. After each game of a match, cumulative scores are displayed as rows of ships, like a World War II fighter pilot's tally. Slug says he saw this version for a short time on the PDP-1, but never found out who produced it or what became of it.
(J.M. Graetz, "The Origin of Spacewar"; in Creative Computing, August 1981 issue; pp. 66/67)
By now, we're happy to say that the mystery is solved and the version in question is neither lost, being the scorer patch for Spacewar! 4.8. And it even found some further approval by Spacewar's original authors by it being added by Peter Samson in 2005 to the code of Spacewar! 4.1f, the version apparently running on the Computer History Museum's restored PDP-1.
BTW: ▶ You may play Spacewar! here, too (original code running in an online emulation, v. 4.1f preselected).
The 4.8 Scorer Patch
Scoring was not new to Spacewar!: As soon as Spacewar! 2b was polished for its public presentation at the MIT Open Science House in May 1962 a scoring and match playing device was patched to the game. Probably, match play, fit for regulating the screen attendance to equal shares of play time, was the vital part of this as the group was clearly expecting a success with the audience similar to that of William Higinbotham's Tennis for Two four years before. (When this first analog video gaming device was presented at the Visitor's Day of 1958, the Brookhaven National Laboratory saw hundreds of visitor's lined up outside the building, patiently awaiting their moment of utter amusement and wonder.)
The game was essentially complete by the end of April, 1962. The only further immediate work was to make Spacewar! presentable for MIT's annual Science Open House in May. A scoring facility was added so that finite matches could be played, making it easier to limit the time any one person spent at the controls. To provide for the crowds that we (accurately) anticipated, a large screen laboratory CRT was attached to the computer to function as a slave display. Perched on top of a high cabinet, it allowed a roomful of people to watch in relative comfort.
(J.M. Graetz, "The Origin of Spacewar"; in Creative Computing, August 1981 issue; p. 66)
Persumingly, this (now lost) scorer patch would have patch the auto-restart patch (see the last episode) — just like the latter did to the hyperspace patch —, since the auto-restart patch would already detect the end of a game, by this providing an ideal hook for the scorer. We may expect this first scorer to have been similar, both in code and experience, to the scoring device that we've already explored in Part 3: In Spacewar! 3.1 (and ever thereafter), on the end of a match of a preselected number of games or on the test word switch for bit 12 being deployed at the end of any individual game, the scores of the two players are loaded into the accumulator (
AC) and the In-Out register (
IO) respectively and the machine would grind to halt (
hlt), thus displaying the scores in binary at the console lights.
a4, lac \1sc // load score in AC (spaceship 1) lio \2sc // and into IO (spaceship 2) hlt // halt
(Code as in version 3.1. For the basics of PDP-1 instructions and Macro assembler code, please refer to Part 1 or bring up the instruction list available at the tab at the bottom of the page. As usual, comments starting with double slashes are mine and not part of the original code.)
And this is, what it looks like on the PDP-1's control console (player one: 18, player two: 3):
The 4.8 scorer patch essentially exchanges the last halt-instruction in order to divert the flow of control to a secondary, special purpose loop to draw a fancy on-screen display. Here, the players are represented by their respective ships (big) along with their tally depicted as tiny images of the opponent's ship, with a double-sized ship representing 10s. (Thus, even a very high score won't exceed a single row.)
Here is a screenshot of the emulation, again representing a score of 18 and 3:
And here is closeup of the score display (emulated display resolution: 1048 × 1048). — Mind the dotted appearance of the bigger spaceships.
Since the scorer patch for Spacewar! 4.8 is unsigned, we do not know any about it's author or when it came into existence. (Spacewar! 4.8 is signed "
7/24/63 dfw", but, as we'll see, the patch isn't especially related to version 4.8 and would have even worked with Spacewar! 3.1.)
Time to dive into the code and see how it was done — but not whithout an inclusion of the standard disclaimer, here with a notable addition:
Spacewar! was conceived in 1961 by Martin Graetz, Stephen Russell, and Wayne Wiitanen. It was first realized on the PDP-1 in 1962 by Stephen Russell, Peter Samson, Dan Edwards, and Martin Graetz, together with Alan Kotok, Steve Piner, and Robert A Saunders. Spacewar! is in the public domain, but this credit paragraph must accompany all distributed versions of the program.
Spacewar! 4 and it's dedicated patches include code by Monty Preonas ("ddp"), "dfw" (real name unknown), and, maybe, other anonymous authors.
Update: Please mind that while the patch is described as being by an "anonymous author" in the following section, there is now a confirmed author, Peter Samson, compare the note below.
The source code of the scorer patch is to be found at archive.org and at bitsavers.org and seems to be rather a typoscript than an actual listing, since it includes a few typos. Here is a corrected, runable version.
(As we may observe, the only version-specific part is the start address of the patch, as in "
/scorer display 4.8 /starts immediately after compiled outlines 4544/ fi1, law . / last loc. of compiled outline sub (21 dac t1 fi2, law . sub (21 dac t2 lac (jmp scc 1 dac i t1 dac i t2 / set return to scorekeeper dzm ssn dzm ssm jsp i cwg dio t2 s1, clf 6 / do large outline of ss1 law not dap scc lio (240000 law 1 add 1sc scb, cma dac t1 /number of ships on this line dio sy1 / y pos lac (540000 dac sx1 / x pos rar 3s dac stx / dx to next ship law 3000 /size, large dac scm jmp scg sch, lac (30000 dac stx rar 1s szf 6 lac (17000 add sy1 dac sy1 law 400 / size, small sce, sad scm / already set up ? jmp scf / yes, display ship dac scm law not 1 szf 6 / which outline ? law not dap scc lac scm scg, dac ssc / set constants for display dac ssd dac scn dac csm cma dac csn scf, lac sx1 lio sy1 dpy 700-4000 scc, jmp i . lac sx1 add stx dac sx1 /x pos. of next ship isp t1 /line done ? jmp scd jsp i cwg /yes lai sas t2 / return ? jmp a4 3 szf 6 / no, do another line jmp s1 s2, stf 6 law not 1 dap scc lio (730000 law 1 add 2sc jmp scb scd, add (11 / are there 10 or more ships left sma /on this line jmp sch dac t1 lac (30000 szf 6 lac (36000 add sy1 dac sy1 lac (40000 dac stx law 1000 /size for 10 wins jmp sce jpt, dac not 1 / patch to get and loc. of outlines dap fi1 jda oc ot2 dap fi2 jmp a3 3 constants a3/ jmp jpt /to setup a4 2/ jmp fi1 / game or series over, display score start 4 star title s.a.=~6400 4544/ start 4
(Please mind the instruction "
lai" — load accumulator from
IO —, 7 lines below label "
scc", which is specific to an upgraded PDP-1 or a PDP-1D and serves the only clue to the date of origin of this patch.)
Apparently missing is a definition of the symbols which are inherited by the main program, like
ssm, etc. Please mind that some labels are rather defined as variables in the main program, like
\ssm, and so on. The Macro assmbler requires a signifying markup for a variable only once in the entire source, therefor, when included in the same run, both notations are synonymous. In order to enhance readability, we will further ignore these minor differences in symbol notation.
The last few lines are rather a hint to where to include the data of the background star field, modifying the normal start address of
So, where to begin, if not with the patched addresses?
The first one, "
a3/ jmp jpt", reroutes the flow of control to label
a3 happens to be amid the code calling the outline compiler at the very start of each game. Here is the respective part of the unpatched program:
law nnn / start of outline program // load nnn (nnn: first address after objects table) dac not // store as start of outline code for spaceship 1 ... jda oc / compile outline ot1 // address of encoded outline 1 (argument to outline compiler) a3, dac not 1 // AC: address after compiled code, store as start of outline 2 // patched to "jmp jpt" by scorer patch jda oc // call outline compiler again ot2 // address of encoded outline 2 (argument to outline compiler) xct tno // get number of torpedoes ... // setup initial values ...
In Spacewar! 4.8, the last of all known versions of original MIT-Spacewar, the code is still the same as in Spacewar! 3.1, the code we've already explored in Part 4:
nnn contains the very first address after the end of the objects' table and the start of free space in memory, which is loaded into the accumulator (
AC). This will be used as the start address of the compiled code by the outline compiler and is also stored by the instruction "
dac not" as a property of the first spaceship for later use. "
jda oc" actually calls the outline compiler, which fetchs the location of the encoded outline instructions from the next memory location (
ot1). Returning at the next location, the accumulator contains the first free memory address after the compiled code. This is stored as the code address for the second spaceship at label
a3 and is used for the next call of the outline compiler, now followed by the location of the encoded outline at
ot2. (The ellipsis contains three instructions checking the setup flag
ddd: If zero or positive, we'll jump directly to label
a3 and skip the first call of the outline compiler, thus using the same outline, the Wedge, for both of the ships to save memory space for the inclusion of the debugger
By patching the instruction at label
a3, we'll jump to the following code:
jpt, dac not 1 // store start addr for ship 2 (same as patched instruction) dap fi1 // new: also store it as end of outline code 1 in fi1 jda oc // call outline compiler ot2 // address of encoded outline 2 dap fi2 // new: store as the end of outline code 2 in fi2 jmp a3 3 // resume at 3rd instruction after a3
This patch is actually adding only two new instructions to the code: Returning from the outline compiler, the value in the accumulator is now stored in the address part of the locations labeled
fi2 respectively. Since the outline compiler returns with the address of the location immediately following to the generated code, this marks the end of the respective outline codes. — We'll see, what this would be good for …
Anyway, the last instruction, "
jmp a3 3", jumps to the third instruction after label
xct tno"), resuming the normal flow by setting up the initial supply of torpedoes for each of the ships.
The other patch modifies the code displaying the scores on the console lights, the one we've already seen before:
a4 2/ jmp fi1 / game or series over, display score
a4 2/" sets the program counter to the 2nd location after label
a4, overwriting the instruction
hlt (halt), thus rerouting the flow to label
a4, lac \1sc lio \2sc hlt // patched: jump fi1
So, let's see, what's happening at this ominous label
Scores in Space — The Display Loop
fi1 is actually the entry point of the score display and starts with its basic setup:
fi1, law . /last loc. of compiled outline sub (21 // subtract 21 dac t1 // store in t1, t1 = end loc. ss1 - 21 fi2, law . // last loc. of compiled outline of spaceship 2 sub (21 dac t2 // t2 = end loc. ss2 - 21 lac (jmp scc 1 // load "jmp scc 1" in AC dac i t1 // exchange instruction dac i t2 / set return to scorekeeper
As we already know, the address parts of the locations at label
fi2 contain the location at the end of the compiled outline code of spaceship 1 and spaceship 2 respectively. From this we subtract
21 (octal, decimal: 17) and store the result in locations
t2 respectively. (Labels
t2 are defined in the main program and are here repurposed.)
At the end of this snippet, the value corresponding to the instruction "
jmp scc 1" (jump to the location
scc + 1) is loaded as a constant into the accumulator and then stored in the two locations, we've just computed before.
What on earth — no, when arriving at this piece of code, we're actually in space or even burning in the sun; so: Heavens! — what's ging on? Since the two locations in
t2 are deep inside the compiled code, we are obviously patching the code generated by the outline compiler by a jump instruction!
Let's recall what we know about the outline compiler:
The outline compiler, probably one of the first JIT-compilers ever, generates a giant unroled loop by transforming individual outline codes to movements and display instructions by the means of a dispatch table. The display codes (
0...7) are any of five move-and-display commands (down, left, right, down & left, down & right), a command toggling between storing the current location or setting it to the stored value (as required for drawing fins), and, finally, code
7, which, at first encounter, resets the outline to its very start position to start over with a (horizontally) mirrored outline, or, on second encounter, finishes and causes the code to jump back to the main program. Since we're close to the end of the generated code, this might well be related to code
This is, what the end of the generated outline code actually looks like:
-21 szf 5 // program flag 5 set? -20 jmp . 4 // yes (first visit), jump to 4 locations below -19 dac \sx1 // store current x-position in variable sx1 -18 dio \sy1 // store current y-position in variable sy1 -17 jmp sq6 // jump back to main -16 clf 5 // (first visit continued) clear flag 5 -15 lac \scm // invert unit vectors for mirrored movement -14 cma -13 dac \scm -12 lac \ssm -11 cma -10 dac \ssm -9 lac \csm -8 lio \ssd -7 dac \ssd -6 dio \csm -5 lac \ssc -4 lio \csn -3 dac \csn -2 dio \ssc -1 jmp <start+1> // start over 0
Counting up 17 instructions (octal: 21) from the instruction following the last one, we actually end up at the jump to the main program, as in "
jmp sq6", which will be executed during the second run through the generated code. (Program flag 5 is set in the very first instruction of the compiled outline code, therefor, at the end of this snippet, the code starts over at the second instruction.) So, this is the instruction that will be patched by "
jmp scc 1" and label
scc happens to be within the scorer patch itself, thus rerouting the outline compiler to the display loop.
What's striking here, is that the same purpose could have been easily served by just patching the instruction at label
sq6 in the main program and restoring the original contents on the leave of the score display loop. Since the display loop isn't time critical at any rate, this is quite like flexing one's muscles: Is the outline compiler an ingenious piece of code by itself and also the most arcane piece of code in the whole game, this applies even more to the code generated by it on the fly, a piece of code of only a virtual presence. Choosing to hack this virtual code is doing because we can do; it's like choosing to do these things, to quote the President, "not because they are easy, but because they are hard":
"We choose to go to the moon in this decade and do the other things, not because they are easy, but because they are hard, because that goal will serve to organize and measure the best of our energies and skills, because that challenge is one that we are willing to accept, one we are unwilling to postpone, and one which we intend to win, and the others, too." (John F. Kennedy, Address at Rice University on the Nation's Space Effort; September 12, 1962)
What a hack, and what a monument to the Space Program! Well done, anonymous hacker!
And, to return once more to the notion of the source code as a text, we may add, this is not so much making a statement on the expressive level, but rather by how it does its thing. This is a statement in the sense of the true hacker spirit at its very best!
So, unwilling to postpone, let's see how the challenge is accepted and how the patch continues to organize the best of our energies and skills in order to setup the score display and do the other things, too:
dzm ssn // set scaled sine to zero dzm ssm // set to zero (ssm same as ssn) jsp i cwg // get control input dio t2 // store in t2
The two variables
ssm are containing both a scaled sine for the use of the outline compiler in order to advance the outline. Both values are now set to zero. The instruction "
jsp i cwg" and "
dio t2" do the other things, namely fetching the current state of the control input and storing it in
t2, which happens to be repurposed from the main program again.
By this, we're ready for the main task and actually may venture into the loop:
s1, clf 6 / do large outlineof ss1 // clear flag 6 law not // load start of first outline dap scc // deposit it in address part of scc (spaceship to draw) lio (240000 // load constant 240000 into IO law 1 // load 1 into AC add 1sc // ac = 1 + 1sc (score + 1) scb, cma // complement it (for count-up) dac t1 /number of ships on this line dio sy1 / y pos. // y = 240000 for ss1 lac (540000 dac sx1 / x pos. // x = 540000 (-237777) rar 3s // divide by 8 dac stx / dx to next ship (54000) law 3000 /size, large dac scm // store as scaled sine (oc parameter) jmp scg // set up other parameters for oc
This snippet initializes the score display of spaceship 1: First, we clear program flag 6 ("
clf 6") and load the start of the generated outline routine into
AC, just to store it in the address part of the instruction labeled
scc. Then we load the constant
240000 (octal) into
IO and setup the value of the score of spaceship 1 plus 1 (
1sc + 1) in
scb we complement the value in
AC, obviously for later use to control a loop by a count-up, and store this in location
t1. Then, we store the contents of
IO (the constant
sy1, load the constant
AC and store it in the location
sy1 are defined in the main program and happen to contain the display location for use by the outline code. Thus, we've set up a display location at (540000, 240000), or rather, since
540000 is in one's compliment
-237777, at (-237777, 240000). Since only the 10 highest bits are of any significance for a display instruction, this will actually cause a spaceship to be drawn at the screen coordinate (-477, 500), or decimal (-319, 320) on a screen with a coordinates system streching horizontally from -512 to +512 and vertically from +512 to -512 from its origin in the center. Therefor the spaceship will be drawn with its top center at the upper left third of the upper left quadrant.
By a shift by 3 bits to the right, we divide
54000) and store it as the horizontal off set in
stx (in screen locations
130 or decimal 88). Then, we load the value
3000 for the spaceship's size in
scm (scaled sine for movements by the outline code; in screen locations:
6) and jump for further preparations to label
Since this is an unconditional jump, let's skip a few lines and see how it would continue:
scg, dac ssc / set constants for display //ssc = scm dac ssd // ssd = scm dac scn // scn = scm dac csm // csm = scm cma dac csn // csn = -scm scf, lac sx1 // display a dot at x, y (sx1, sy1) lio sy1 dpy 700-4000 // intensity 7 (dimmest), go for a completion pulse
Here, the value in
3000) is stored in
csm and parameter
csn is finally set to the complemented value (inverted offset). With the movement parameters for the outline code all set up, we're ready to prepare the display itself: We may recall that the outline code is waiting for the display's completion pulse, so we'll have to provide one in advance or the code will wait for ever. For this we're setting up a display location at the very top of the spaceship's outline by loading
IO respectively and issue a display command. The instruction
dpy-4000 does not only request a completion pulse from the display, but also instructs the the display to operate at brightness
7 by adding
700 to its instruction code. Since the intensity values are in fact a 4-bit one's complement, this is the dimmest possible intensity of
-3, invisible to the human eye — perfectly suiting our purpose.
Thus prepared, we're ready to actually call the outline code at label
scc (immediately following in the source):
scc, jmp i . // call outline code lac sx1 // load sx1 (left in AC from oc) add stx // add offset dac sx1 /x pos. of next ship
We've seen label
scc before, when we stored the address of the outline code in question (here: spaceship 1) in it's address part. This will be now executed and the patched jump (
scc 1) in the outline code will bring us back at the next instruction. — Please mind that, since the unit vectors / scaling factors are set to 6, the outline will happen to advance by 6 display locations at each step and the outline for the very first ship will be drawn 6 times as big as usual, also causing the dotted appearance of the player's spaceship that may be perceived in the closeup. — The value in
sx1 is set by the outline code and is the last used display location (x-position). To this we add our previously calculated offset in
stx and store it back again. Thus, we're ready for any next ship that we might have to display. But, are there any?
isp t1 /line done ? jmp scd // no, continue with small ships jsp i cwg / yes // get control input lai // load AC from IO sas t2 / return ? // not the same as before? jmp a4 3 // yes, return to main szf 6 / no, do another line? jmp s1 // restart with spaceship 1
The instruction "
isp t1" increments the loop counter in
t1 and skips the next instruction, if it would be zero or positive. (Since we're drawing the player's ship by the same device, we were adding
1 to the score in the initialization before.) If the score is zero, or if we did draw some before and have arrived at the last one, we'll skip the next instruction that would bring us to the code for drawing the actual score at label
scd. If already done, we'll continue at instruction "
jsp i cwg" and fetch a new control input into
The instruction "
lai" is peculiar to a PDP-1 with upgraded hardware, like the one at MIT in 1963 and later, or a PDP-1D. While not a standard one, it is quite a simple one and copies the contents of the IO register into the accumulator. (Without the upgrade, we could have achieved the same with our well known macro "
swap".) With the current control word in
AC, we may now compare it to the one we stored on entering the scorer patch, as in instruction "
sas t2" (skip next instruction, if
AC does not equal the contents of the address). Should we detect a change, we'll leave the scorer patch and jump back to the main loop. The target of the jump, address
a4 + 3, is the very next instruction after our patched jump instruction in the main program, the one that brought us initially to the scorer.
If still in business, we check program flag 6 ("
szf 6"). If zero, we skip the next instruction and continue with spaceship 2, else we start over with a jump to label
s1, the setup for spaceship 1 that we've already seen above.
Let's continue with the part at label
scd, actually responsible for drawing the score:
scd, add (11 / are there 10 or more ships left sma / on this line jmp sch // no, go for small outlines dac t1 // yes, update t1 (now t1 - 10) lac (30000 // 30000 szf 6 // skip if flag zero (drawing scores for spaceship 1) lac (36000 // load 36000 add sy1 // add sy1 dac sy1 // sy1 = sy1 + 30000 (or sy1 + 36000) lac (40000 dac stx // stx = 40000 (offset) law 1000 / size for 10 wins jmp sce // now, draw it ...
In case we would end here, there's still the updated value of the loop counter (score + 1, complemented) in
AC, left there by the
isp instruction. To this we now add (octal)
11 (decimal 9) to check, if there would be at least (decimal) 10 left. If the result is still negative, we'll skip the next instruction. If positive, we'll jump to label
sch to draw the appropriate number of small ships for the individual scoring points.
If we're still in business, there are at least 10 ships and we're going to draw a double sized one to indicate a decade. To do so, we've to reposition our drawing position on the screen. Since the outline code draws from the top-center down and leaves the last coordinates (bottom-center) in
sy1, and further, because we would want to have all the ships neatly aligned to the baseline, we have to add the height of the ship to the baseline (in
Since the ships are of different height, we'll have to check program flag 6 for the type of ship to draw and load the according amount (
30000 for a wedge or
36000 for a needle). To this we now add the contents of
sy1 and store the result back again. To provide a suitable horizontal gapping we load the constant
40000 and store it in
stx. Finally, we load the constant
1000 (for a distance of 2 sceen locations) into
AC and go for the actual drawing code.
Otherwise, if there are less than 10 ships left, we'll end up at label
sch, lac (30000 // setup small gap dac stx // horizontal offset stx = 30000 rar 1s // div by 2 (14000) szf 6 // skip on flag 6 zero lac (17000 // load 17000 add sy1 // add sy1 dac sy1 // store as new pos. y law 400 / size, small
The code is quite analogous to the one we've just seen. To make a difference, we first set up the horizontal gap (30000) and then decide on the vertical offset from the baseline depending on the kind of ship to be drawn (
14000 for a wedge,
17000 for a needle). As above, we add to this
sy1 and store the result back again. Finally,
law 400 sets the scaling factor, this time
400 for an advance by a single screen location (normal size).
At the next instruction at label
sce the two strains meet again, since this is exactly where the strain for the double sized ships would have brought us:
sce, sad scm / already set up ? jmp scf / yes, display ship dac scm law not 1 szf 6 / which outline ? law not dap scc // configure outline lac scm
The instruction "
sad scm" compares the value in
400) to the contents of
scm, the previous scaling factor, and skips, if the two values would be different. If they are the same, we must be drawing the same outline at the same size again and may skip any further configurations. Therefore, the next instructions jumps directly to label
scf, to display the outline without further ado. (We've seen label
scf before, it's the piece of code preparing the outline display by an initial display command.)
If we have a different outline or size to draw, we first store the new scaling factor in
scm. Then, we decide on the outline to draw, once more by referring to program flag 6, and store it in the address part of the instruction at label
scc (the call of the compiled outline code). Finally, we restore
AC to contain the scaling factor, since the next piece of code is the part at label
scg, which we have seen before, copying the scaling factor from the accumulator to the various parameters used by the compiled outline code. (This is immediately followed by label
scf, where to we made the shortcut in the other branch.)
The only code left to explore is the part where we would fall through to start over with the score for spaceship 2, immediately after the "
jmp s1". Let's swap the ships:
s2, stf 6 // set flag 6, set up for spaceship 2 law not 1 // outline of spaceship 2 dap scc // store it in scc lio (730000 // y = 730000 (-47777) law 1 add 2sc // AC = 1 + 2sc jmp scb // go for it
First, we set program flag 6, in order to indicate that we're drawing the score of player two. Therefor, we load the start address for the outline of the wedge (in property "
not 1") into
AC and store it in the address part of the call at label
scc. Then we load the new vertical display location
730000 as a constant into
730000 is a one's complement representing a value of
-47777, a display location of octal
-117 or decimal 79.) Next, we prepare the value for the loop counter (score
AC and are off for further setup procedures at label
scb, the one we've seen bevore (right after the setup for spaceship 1 at label
Did we miss anything? An attentive reader may have observed that the jump instruction so heroicly patched in the compiled outline code is never restored. Wouldn't this break anything? — Not at all, because the JIT comiler is run at the start of each game and the generated outline code is simply written anew.
That's all folks, at least on the 4.8 scorer patch! Time for a break.
The anonymous author of the 4.8 scorer patch isn't anonymous any more, it is no one less than Peter Samson, whom we met already in Part 1 where we were exploring the Expensive Planetarium (background display). Also, Peter Samson may be recognized as the personification of the MIT-hacker tradition and as the author of the original TMRC Dictionary (first edition 1959) that was to become the well known Jargon File.
(As a sidenote, it's may be interesting that both Steve Russell and Martin Graetz didn't know about this authorship, especially, since Martin Graetz claimed to have checked the data for his seminal article on Spacewar! with all the persons involved. "I was able to reach all of the original Spacewar! perpetrators, hackers and Hingham Institute Fellows alike. Not to mention Professors Dennis and Minsky, and John McKenzie. In addition, I am grateful to Marcia Baker, Professor F.J. Corbato, and Professor R. M. Fano, all of MIT, for help with dates and places, and other facts. The help was theirs; any mistakes are mine." (J.M. Graetz, "The Origin of Spacewar"; in Creative Computing, August 1981 issue; p. 67) This may be owed to the very order in which interviews and checks were conducted. We may note that the reference to the score display occurs just at the end of the article, quite like an afterthought, as would be the case when incorparating any last minute informations, in this case provided by Steve Russell.)
Now, that we've explored the whole of the 4.8 scorer patch, we've to admit that Spacewar! 4.8 was by no means the first version to feature a score display. Actually, there was a quite similar one in some earlier versions of Spacewar! 4 (4.2
ddp – 4.4
ddp). In order to understand this a bit better, we first should have a closer look at the family tree of Spacewar 4 …
Spacewar! 4 Genealogy
There are essentially two different strains of Spacewar! 4: The
ddp-strain by Monty Preonas (Diamantis Drakos Preonas) and later with contributions by Joe Morris, and the
dfw-strain by an author hithero unknown — the identity of "
dfw" remains the biggest Spacewar!-mystery yet. Here is a chronology of the known versions of Spacewar! 4:
|ddp-strain (Monty Preonas)||dwf-strain (unknown author)|
|Spacewar 4.0 ddp (February 2, 1963)
Adapted for automatic hardware multiply/divide, new high-precision gravity computations, minor code optimizations.
|Spacewar 4.1 dfw (February 20, 1963)
Sun drawn as dotted line (uses half the instructions), major code reorganizations, rewrite of instruction count / frame stabilization code, instruction count increased from 4000 to 5000 per frame in order to adjust the frame rate/speed for harware arithmetics.
|Spacewar 4.0 TS ddp (May 4, 1963, part 2 dated May 16)
Like Spacewar 4.0 ddp, simplified starfield with stars of 4th magnitude stripped. Minor differences in hyperspace code (angular momentum is set to random value additionally). "TS" and the date suggest a relation to time sharing, even, if there is not hint in the code.
|Spacewar 4.2 ddp (May 11, 1963, part 2 dated May 16)
Background display (Expensive Planetarium) as in Spacewar! 2b, score display, instruction count 5000 (like 4.1 dfw), dotted heavy star (like 4.1 dfw), complex input parsing using the "
|Spacewar 4.2a dfw (22. Feb 193 ?) (binary object tape only)
No visible differences to Spacewar! 4.1 dfw.
(The date is provided in part 2 of Spacewar! 4.1.)
|Spacewar 4.3 ddp (May 17, 1963)
Same as 4.2 ddp, Twin-Star mode (accessible via sense switch 2).
|Spacewar 4.4 ddp (May 17, 1963 / May 21, 1963), probably by Joe Morris, date and signature unmodified as in 4.3 ddp.
Iteration of 4.3 ddp, attempts to produce an dual screen ego shooter (a few issues here), some optimizations for upgraded PDP-1 (new instructions like
|Spacewar 4.8 dfw (July 26, 1963)
Major code reorganization.
|Spacewar 4.1f dfw/prs (June 1, 2005 — Agust 22, 2008)
Spacewar 4.1 dfw as of July 26, 1963 updated by Peter Samson (prs) for the use of the CHM to include the scorer patch, as well as other minor adjustments: "spacewar 4.1 mod for CHM, 2005-06-01 - 2005-11-28, changed delay in score display, 2008-08-22 -- prs"
Intensities of background display were apparently adjusted for use with the restored Type 30 CRT.
|Game saver patch (unsigned, undated)||4.8 scorer patch (unsigned, undated), by Peter Samson|
The listings of the "
ddp-strain", but the one for Spacewar 4.0 TS (archive.org: Spacewar4.0_TS_May63_text.pdf), are all to be found in a PDF containing the assorted listings (CHM: DEC.pdp_1.102664173.pdf, archive.org: spacewar_Ver4.X_text.pdf). The assorted listings are probably those that were once publicized by Joe Morris along with the following remarks:
Late-breaking news: I found the listing binder in my office. The versions I've got are: 4.0, dated 2/2/63 4.2, dated 5/11/63 (with a torn scrap of greenbar where I scribbled notes about which buttons set which bits on the taper pin block (for the IOT instruction) from the drone controller someone bought from Eli's to run Spacewar on the PDP-1 on the first floor of building 20) 4.3, dated 5/17/63 4.4, dated 5/17/63 which still shows Monte's[sic] initials (ddp) but which vague memory says wasn't his update. It may have been my hack, but I hadn't yet learned the tao of marking changed lines in programs 8-(
(Joe Morris, alt.sys.pdp10, Jannuary 6, 2005)
The sources of the "
dfw-strain" may be found here (textfiles.com/bitsavers):
- Spacewar 4.1 dfw
- Spacewar 4.2a dfw (?): sw4.2.bin, see also spacewar4.2a_sa4.bin (binary tapes image only).
- Spacewar 4.8 dfw, part 1, Spacewar 4.8 dfw, part 2
- Spacewar 4.8 scorer
- Spacewar 4.1f dfw/prs
On of the least obvious changes in the code of Spacewar 4 is related to the inputed devices. Earlier, the reading of the control input had been as simple as fetching the state of the control boxes by a single instruction. In the course of 1963 things became a bit more complicated, with a pletora of input devices (including a repurposed drone joystick from USAF surplus supplies) and complex inputs behind a mixer attached to the PDP-1's paper-pin block, while the external control devices soon transcended the once dedicated use. (But still they came in pairs for a reason.)
UPDATE: According to newer informations provided by Dan Edwards these "surplus controllers" originated rather from a Bomarc missile control console: "[O]ther consoles being […] the right arm panel of a military surplus Bomarc missile control console which featured [a] professing joy stick. That console looked fancy, had nice directional micro switches plus a button on the top of the joystick which was used [to] fire the 'photon torpedoes' in the game. In the end, this console wasn't used much because it was too big and bulky."
Joe Morris recalled:
The surplus controllers had a problem: there were so many buttons and such (including a "trigger" thumbswitch on the joystick and a foot pedal) that some of the buttons were dedicated to telling Spacewar which other buttons did what. I doubt seriously that any of my hacks to Spacewar to support these controllers made it into the "standard" version, but it was fun writing them.
(Joe Morris, alt.folklore.computers, April 3, 2001)
As a result, the code for parsing the control input had exceeded the length of an entire page in Spacewar 4.2/3 ddp. Moreover, some of the lost versions had apparently made used of the advanced controls, as we find the following comment on the input encodings as a left-over in Spacewar 4.8 dfw:
/ high order 6 bits, high acceleration, normal acceleration, / rotate cw, rotate ccw, fire torpedo, and hyperspace.
While Spacewar 4.8 actually supports only a single rate of acceleration, there had been, apparently, at least one version with high and normal acceleration.
Here is an image of an advanced control box (probably 1965) which clearly transcends a dedicated use with Spacewar!:
And here is another aspect of the situation, showing the PDP-1 and what looks like a rack of DECtapes in the background (DECtapes were introduced in spring 1963). A contact print proves both pictures to be shot on the same film.
Note: There's an aditional CRT of non-DEC make in the left corner near the PDP-1's console. This could well be the scope that was hooked up as a secondary, public display for the MIT Science Open House in May 1962. — "To provide for the crowds that we (accurately) anticipated, a large screen laboratory CRT was attached to the computer to function as a slave display. Perched on top of a high cabinet, it allowed a roomful of people to watch in relative comfort." (J.M. Graetz, "The Origin of Spacewar"; in Creative Computing, August 1981 issue; p. 66). — Thanks to this image we may comprehend the scene quite well.
Another peculiarity of the
ddp-strain is the quest for the right background display: While version 4.0 is using the background-code of Spacewar! 3.1, version 4.0 TS introduces a significantly simplified background by displaying all the stars at once at each second frame and all at the default intensity, but stripping the stars of the 4th magnitude — being the majority of stars. However, version 4.2 ddp (released in the same month) and up to version 4.4 ddp are falling back to the starfield code of Spacewar! 2b. Apparently, the quest had been for some run time, first by stripping the majority of stars, then by avoiding to have to display them all at once. (We may recall from Part 1 that the original Expensive Planetarium of Spacewar! 2b was modulating the brightness of the stars not by the use of the CRT intensities, but rather by refresh rate.)
The Peculiar Case of the 4.2–4.4 Score Display
As we've seen the wonders of the 4.8 scorer patch, we had no idea that this would not have been the only score display. But there is a similar one, and since it is included in versions 4.2 – 4.4 of the
ddp-strain, it is apparently even older.
As we may observe, there are some visual similarities between the display of the 4.8 scorer patch (compare this screenshot) and the score display of Spacewar 4.2–4.4 (ddp): Both of them are displaying a big player's spaceship iconicly at the left with their respective tally to the right, and both of them use a bigger spaceship to indicate a decade. But there are differences, too, in position, alignment, and spacing, and, most notably, there is a pulsing separator line, drawn by a tiny segment that is moving unremittingly from left to right like a 1980s-TV-series scanning sensor.
And this the score display in version 4.2 ddp:
a3, dac not 1 dap fi1 jda oc ot2 dap fi2 ... a4, jmp fi1 ... /display score routine fss, 0 /set size of spaceship dap fs1 lac fss dac \scm dac \ssc dac \ssd dac \scn dac \csm cma dac \csn dzm \ssn dzm \ssm fs1, jmp . fi1, law . /set return of compiled outline sub c21 dac t6 fi2, law . sub c21 dac t7 fis, lac c23 dac t4 szf 3 lio 2sc /get score szf i 3 lio 1sc scl 1s cla div c12 hlt sza jmp fx1 fkr, dio t3 law 400 jda fss law fys dap frt flt, idx t3 cma dac t3 law fus dap i t6 dap i t7 fus, lac c20 szf 3 cma dac sy1 lac t4 add c30 dac t4 dac sx1 fds, szf 3 /display spaceship law not szf i 3 law not 1 dap fug idx t5 ral 9s cli dpy-4000+700 isp t3 fug, jmp i . frt, jmp . fys, law 4000 jda fss law fub dap i t6 dap i t7 lac c26 dac sx1 lac c20 szf i 3 cma add c30 dac sy1 law i 2 dac t3 jmp fds fub, szf 3 jmp . 3 stf 3 jmp . 2 clf 3 cli iot 11 dio \t1 iot 111 dio \t2 law i and \t1 and \t2 sza jmp fik law 2 and \t1 and \t2 sza i jmp fis law a4+1 dap fiu jmp fwt fik, law 4 dap fiu fwt, add . dac \t1 isp \t1 jmp .-1 fiu, jmp . fx1, dio \t1 dac t3 law fx2 dap frt law 1100 jda fss jmp flt fx2, lio \t1 jmp fkr c12, 12 c20, 200000 c21, 21 c23, -200000-30000 c26, -260000 c30, 30000 t3, 0 t4, 0 t5, 0 t6, 0 t7, 0
Let's have a look at the code — in order to save some space, we'll include any annotations inlined.
First, we find the same hooks as in the 4.8 scorer patch, but this time integrated into the code right from the beginning:
a3, dac not 1 dap fi1 // store end of outline code of spaceship 1 jda oc ot2 dap fi2 // store end of outline code of spaceship 2
a4, jmp fi1 // enter the display loop
We may observe that the same symbols are used here, which is also true for some of the main part of the score display.
The code in question is placed near the end of the program, after the inclusion of
variables, and after the space reserved for any patches (octal
100 locations), but before the final label for the start of the objects table. We'll follow this top down, just as we find it in the source.
/display score routine fss, 0 /set size of spaceship // called by jda dap fs1 // deposit return address (in address part of fs1) lac fss // load scaling factor into AC dac \scm // and setup oc parameters (like 4.8 scorer patch) dac \ssc dac \ssd dac \scn dac \csm cma dac \csn dzm \ssn dzm \ssm fs1, jmp . // return
This is straight forward the same as the code of the 4.8 scorer patch, but this time as a subroutine to be called by
jda. (The instruction "
jda fss" puts the value formerly in
fss, then jumps to location
fss+1 as a subroutine with the return address in
AC.) Follows the main entry point to the display loop:
fi1, law . /set return of compiled outline // start here sub c21 // get patch address in oc for spaceship 1 (c21: 21) dac t6 // store it in t6 fi2, law . // get patch address in oc for spaceship 2 sub c21 dac t7 // store it in t7
Still the same as in the 4.8 scorer patch, but different symbols are used for temporal storage.
fis, lac c23 // c23: -230000 dac t4 // t4 = -230000 (start pos x for outline) szf 3 // skip on flag 3 zero (which score?) lio 2sc /get score // of spaceship 2 into IO szf i 3 // skip on flag 3 not zero lio 1sc // load score for spaceship 1 into IO scl 1s // shift AC & IO 1 bit left (prepare for integer division) cla // clear AC div c12 // divide by 12 (c12: 12, decimal 10) hlt // (overflow condition for div, skipped) sza // result in AC zero? jmp fx1 // no, 10 or more ships left, jump to fx1
Please mind that this is using the hardware divide. (On the preperations for an integer division see the discussion in Part 6.) Follows the branch for single scores (small ships):
fkr, dio t3 // deposit remainder (IO) in t3 law 400 // load 400 into AC (normal scaling, small ship) jda fss // setup scaling parameters for oc law fys // load address of fys as number dap frt // store it in address part of frt (next task after drawing) flt, idx t3 // increment t3 (result left in AC) cma // complement AC dac t3 // store it back again (to be used as loop counter) law fus // load address of label fus as number dap i t6 // deposit it in t6 (patch jmp address in outline 1) dap i t7 // and in t7 (patch jmp address in outline 2)
So we patched the same instruction in the outline codes, but this time by just setting the address part of the respective jump instructions. The next instruction (
fus) is also where we'll return from the outline code:
fus, lac c20 // c20: 200000 szf 3 // skip on flag 3 zero cma // complement AC dac sy1 // pos y (sy1) is either 200000 or -200000 lac t4 // load t4 add c30 // add 30000 (offset) dac t4 // store it back again dac sx1 // and use it for pos x (sx1)
Thus, we've just set up our drawing position …
fds, szf 3 /display spaceship // skip on flag 3 zero (select outline) law not // load code address of outline 1 szf i 3 // skip, if flag 3 not zero law not 1 // load code address of outline 2 dap fug // store it in address part of fug idx t5 // advance x: increment t5 (contents left in AC) ral 9s // rotate AC left by 9 bits (transform to screen location ×2) cli // clear IO (y = 0) dpy-4000+700 // display a dot at intensity 7, get completion pulse isp t3 // increment t3, skip on positive result (count-up loop) fug, jmp i . // still a ship to draw: call outline code (returns to fds) frt, jmp . // next task
We may observe that the advance over the value in
t5 is what is eventually effecting the pulsing line at the vertical center of the screen. Having displayed the ship, we'll jump to our next task. If we came here to draw a single score, as we've following it, this is also the next instruction:
fys, law 4000 // scaling factor vor players ship (large) jda fss // setup oc parameters law fub // load address of fub, for use as a new return address dap i t6 // patch return address of compiled outline 1 dap i t7 // patch return address of compiled outline 2 lac c26 // c26: -260000 dac sx1 // pos x = -260000 lac c20 // c20: 200000 (quarter of a screen width) szf i 3 // skip if flag3 not zero (which score?) cma // complement it (now -200000) add c30 // add 30000 dac sy1 // store it as pos y law i 2 // load number -2 dac t3 // store it in t3 (complement of loop count + 1 => draw one ship) jmp fds // jump to drawing routine (above)
So, we have just drawn a big ship, either at
(260000, 230000) or at
(260000, -15000) (for screen locations divide by 8). Moreover, our code will return at
fub, which is also the very next instruction.
fub, szf 3 // flip flag 3 (spceship selctor) jmp . 3 stf 3 jmp . 2 clf 3 cli // clear IO, read and process current state of control input iot 11 // get control word dio \t1 // deposit it in \t1 iot 111 // do it in a different way (iot 111 is still an enigma) dio \t2 // and deposit it in \t2 law i // load -0 into AC and \t1 // add \t1 and \t2 // add \t2 sza // skip on zero jmp fik // continue at fik (restart) law 2 // load number 2 into AC and \t1 // add \t1 and \t2 // add \t2 sza i // skip on not zero jmp fis // continue at fis (next spaceship) law a4+1 // load address a4+1 (return to main program) dap fiu // store as return address in addr. part of fiu jmp fwt // skip next two instructions fik, law 4 // load number 4 (start address of Spacewar) dap fiu // store it as return address
The first few instructions are easy, since this is simply flipping the state of flag 3 and therby the spaceship that is to be drawn. The next instructions may need some explanation. The instruction "
iot 111" ist still an enigma. It somehow seems to preserve the state of the last control input (as read by "
iot 11"), but seems to complement it or add some other transformation. However, we may detect any changes by adding the readings of "
iot 11" and "
iot 111". By adding an initial bit-offset, we're able to isolate distinctive bits. There's a normal exit condition and even a restart condition. If we detect either, we setup the according return address in the address part of location
fiu. Otherwise, we continue with the next spaceship/player at label
We may observe that this will only work with control boxes, since only the gates of the paper-pin block are queried by the instructions "
iot 11" and "
iot 11s". No way of getting out of the loop by just the test word controls …
fwt, add . // add current location to AC dac \t1 // store in \t1 isp \t1 // increment \t1, skip if result positive jmp .-1 // jump to the instruction above (while \t1 negative) fiu, jmp . // return (leave score display and restart)
This is a tiny loop for a delay depending on the value of the return address in
AC. When done, we finally make the jump to the return address in
fiu (back to main loop or restart).
fx1 is finally where we branche to, if we happen to have a medium ship to draw, indicating tens of scoring points:
fx1, dio \t1 // store IO in \t1 dac t3 // deposit AC in t3 (loop counter for drawing routine) law fx2 // load address of fx2 into AC dap frt // deposit it in address part of frt (next task) law 1100 // load scaling factor 1100 (medium) jda fss // setup parameters for oc jmp flt // go for it fx2, lio \t1 // here from drawing routine jmp fkr // continue at with small ships (single scores)
Follow the constants and storing locations used by the display routine. (We have to add them explicitly here, since we already included the variables and constants.)
c12, 12 c20, 200000 c21, 21 c23, -200000-30000 c26, -260000 c30, 30000 t3, 0 t4, 0 t5, 0 t6, 0 t7, 0
And this was the 4.2 score display.
Which Was First?
(Section uptaded) In the original text version of this episode, we were trying to determine, which one of the visual score displays was first, especially, since the 4.8 scorer patch isn't specific to Spacewar! 4.8 in any way. But now we know from first hand that Peter Samson already knew an existing scoring feature — the score display in Spacewar! 4.2 —that he didn't quite like, consequently adding the more eligant score display as a patch to Spacewar! 4.8:
I remember writing it originally. I believe there was an earlier scoring feature that I didn't like and I thought I could do better; hence the current scoring patch, which shamelessly clobbers code in the spaceship display routines to use them for its own purposes. (Peter Samson, 2015)
But there is another question left: Is there a chance that this could have been the on-screen score display that Steve Russell had seen and communicated as the Lost Version to Martin Graetz? — Hardly. Simply, because only 6 days after its release Spacewar 4.2 ddp was superceded by Spacewar 4.3. And versions 4.3 had another feature (see below) that Steve Russell would have been shown for sure, if it had been this kind of score display. But, when asked about it, he hadn't seen it at all and would think of it rather as a bug …
So, what other hidden feature are we talking about, to be found in versions 4.3 (5/17/63 ddp)?
The Mystery of the Twin Stars (Revisited)
First of all, "Twin-Star Mode" is not an official name by any means, it's just my own way of referring to it by its most prominent feature, but we could call it "Needle Mode" with some justification as well. I haven't found a reference to it of any kind, but, if you assemble the sources of Spacewar 4.3 (ddp) and happen to deploy sense switch #2 (normally activating low gravity), there is undoubtedly this peculiar phenomenon to perceived on the screen.
▶ Play Spacewar! 4.3 in Twin-Star Mode (online emulation, v. 4.3 ddp & sense switch 2 on preselected).
On first encounter it's hard to comprehend what's going on: The Needle (spaceship 1) is pinned to the center, facing downwards, the Wedge (spaceship 2) is placed at the lower left corner, slowly falling into the center, where there is not a central star, but two of them to both sides of the Needle, and the background stars — are moving!
On closer inspections, we'll find that the Needle is always fixed to center: It will, turn, but it will not move. What moves, instead, is the universe! If the Needle thrusts, it pushes the whole universe rather than moving itself on the screen. Thus the movement of the background stars, initially accelerating as the Needle is pulled by the gravitational field. The entire scene, but the central star(s), is plotted relatively to the Needle, like an ego view on a radar sceen in the Needle's cockpit. (Since the Type 30 CRT actually utilizes a tube which was originally developed for radar scopes, it won't become any more realistic than that.) If the Needle swerves, it will stay fixed, instead the universe will swerve in the opposite direction.
But this translation of the screen coordinates is nowhere where it stops: If we happen to fire the rocket engines, the exhaust trails won't show up behind the spaceship. Again, it's not an easy one figuring out what's happening, but the exhausts are apparently revealing the true position of a ship (on further inspection they prove to be translated by some amount to the left and to the top). Same applies to any torpedoes on the screen. However, regardless to where a torpedo may be drawn, any explosions are displayed at the relative location of the respective ship. — Amidst all this cofusion, the hidden location of the gravitational center is soon to be lost, becoming an invisible, enagmatically moving pool of distortion in the spacetime continuum.
And, most amazingly, when peeking inside the code, we'll find that it's just the usual game, with some minor transpositions added to the display layer.
Note: Maybe this would all make more sense, if this were a true ego view from the position of the Needle and the different mode of displacement to be observed for the exhausts and the torpedoes would happen to be a bug or an unfinished feature. We may find out on closer inspection of the source.
We had already a first look at how the twin stars were drawn in Part 2, but we really need to have a look at all the modifications (as compared to Spacewar! 4.2 ddp) in order to properly understand and to document this feature.
This time we're dealing with what is merely a collection of membra disiecta, spread all over the source in tiny bits. Anyway, this is what might be found in Spacewar! 4.3 5/17/63 ddp as related to this feature, based on a diff to version 4.2 5/11/63 ddp. As mentioned previously, the game's logic, the internal coordinates, hit-detection, etc, remain the same as usual, only the display layer is concerned. It's just a view, and we may actually switch between views by operating sense switch 2.
The very first difference, we encounter, when inspecting the source of "
spacewar 4.3 5/17/63 ddp" is in the macro definitions at the beginning:
define dispt A,Y,B repeat 6, B=B+B lio Y szs 20 // new in sw 4.3 jda kcb // new in sw 4.3 dpy-A+B term
dispt is a macro used to display a single dot on the screen, where parameter
A is an argument to the display command (like "
i" for not to wait for a completion pulse) and parameter
B is an intensity (0..7). The y-coordinate for the display intruction
dpi is provided by the parameter
Y, while the x-coordinate is expected in
AC as we enter the macro.
What's new here are the two lines starting with "
szs 20": If sense switch 2 is zero (unset), we'll skip the next instruction, which does the whole job by calling the subroutine
kcb. Let's have a look at this subroutine:
kcb, 0 /relocate for center display dap kc1 swap // y-coor in AC sub ny1 // y-pos spaceship 1 swap lac kcb // relaod x-coor into AC sub nx1 // x-pos spaceship 1 kc1, jmp .
Since this is called by a
jda instruction, the contents of
AC will be placed at label
kcb and the execution will start with the next instruction, now with the return address in
AC. As usual, we store the return address first, here in the address part of location
kc1. The inclusion of the macro
swap exchanges the contents of
AC (by two "
rcl 9s" instructions). What's now in
AC is the y-coordinate for the display instruction. From this we subtract the contents of location
ny1, the y-position of spaceship 1 (compare Part 3). Another swap puts the result back into
IO again and "
lac kcb" brings the x-coordinate into
AC. From this we now subtract the x-position of the spaceship as stored in
nx1 and the final jump to the return address brings us back to where we left before, but now with translated coordinates in
The effect is as simple as impressive: Would we want to display a blip at the position of spaceship 1, the dot would be displayed at the center as both resulting coordinates must be zero. Any other location will be displayed by it's relative offset to spaceship 1.
The same trick is performed when drawing any of the stars of the Expensive Planetarium. This is right in the middle of the macor
dislis for drawing a magnitude of stars (compare Part 1):
fyn, lio /lio Y szs 20 // new in sw 4.3 jda kcb // new in sw 4.3 dpy-i
fyn w load the y-coordinate into
IO and are ready to display it by the
dpy instruction at the end of the snippet. If sense switch 2 would be deployed, the call of
kcb translates the star relative to the position of spaceship 1.
Let's investigate the changes that are recentering the ships in this display mode. We may recall that sense switch 2 is normally used to select low gravity (see Part 6), so we must free the switch for its new task:
// old scr 9s scr 6s szs i 20 /switch 2 for light star scr 2s // new scr 9s scr 8s
It's as simple as that. (The gravity factor currently in
AC is used as a devisor. Thus, low gravity is achieved by skipping the last shift to the right. Since we're using full gravity all the time, we are combining the last two shift instructions into a single "
And here we perform the trick for a spaceship:
scale \sn, 5s, \ssn scale \cs, 5s, \scn lac i mx1 // x pos szs 20 // new in sw 4.3 sub nx1 // new in sw 4.3, translate sub \ssn dac \sx1 // ship stern x sub \ssn dac \stx // torpedo firing pos x lac i my1 // y pos szs 20 // new in sw 4.3 sub ny1 // new in sw 4.3, translate add \scn dac \sy1 // ship stern y add \scn dac \sty // torpedo firing pos y
The first two inclusions of the macro
scale are storing the scaled unit vectors (sine, cosine) for use with the compiled outline code. (In Spacewar! 3.1 theses instructions were labeled
sp2, compare Part 5.) "
lac i mx1" loads the x position of the current object into
AC for further processing, namely subtracting the scaled sine (
\ssn) from it and storing it in \sx1, a position ahead of the center of the ship, where the outline will start to draw, and after subtracting
\ssn a second time, we store the result in
\stx, a position just in front of the spaceship, where any newly fired torpedoes will appear. The second part after the break is doing just the same for the y-properties.
The first of the two newly added line is skipping the next one, if sense switch 2 would be zero. If set, we subtract the position of spaceship 1 from the coordinate in question. — As for the ships, the new code isn't altering any positional values, but just affects the coordinates stored in (
sy1) where the vessels will be drawn, when the outline code is called.
A bit below, we find the instructions actually calling the outline code of ship:
cla cli-opr // clear AC and IO (x: 0, y: 0) szs 20 // new: twin-star mode? jda kcb // new: yes, translate position dpy-4000 // dispaly a dot, request completion puls mot, sp5, jmp i . // call the outline code sq6, ioh // wait for last completion pulse
The jump instruction at label
sp5) does the thing, calling the outline routine of the current object (stored as a pointer in the address part of the instruction). Since the outline code is waiting for a completion pules, we have to provide one beforehand, normally in the center of the screen by clearing
IO and issuing a "
dpy-4000" display instruction. For the same reason we fetch the last completion pulse by the final
In Spacewar! 4.3, we transpose this dot, once again, relatively to spaceship 1 by calling subroutine
kcb, if sense switch 2 would be deployed. (This is actually producing a visible artifact, as we'll see later.)
dispt is doing the same for all dots individually drawn, as for displaying a torpedo, showing the heralding spot at the breakout of hyperspace, or the drawing dots which make the exhaust blast. By this, every display instruction in the game becomes a subject of our displacement treatment.
As for the central star, we've seen already in Part 2 how this was split into two halves. The star is drawn from the center (pos. 0/0) in incremental steps outwards for a fixed number of steps. Having reached its most distinct extent, the code inverts the drawing position, now at the opposite side of the origin, and starts over for a second run, now incrementing towards the center. A clever compromise between speed (unrolled drawing loop) and space (reuse of half the code). The appearance of the twin stars is achieved by simply adding a horizontal offset at the start of the first run:
And here is the code that does it:
cla cli clf 6-opr-opr // clear AC and IO (pos 0/0) and flag 6 at once szf i 20 // new in sw 4.3: sense switch 2 set? jmp bjm-1 // no, jump to first display instruction sub ny1 // twin stars: subtract y-pos of spaceship 1 swap // swap it into IO (y-coor) sub nx1 // subtract x-pos of spaceship 1 dpy-4000 bjm, jmp .
The first instruction clears
IO, and program flag 6 (used to control the two passes). Since all these instructions are micro-programmed and in the same operation group, we may combine them in a single one. The next instruction is the first of the newly added ones: If sense switch 2 is set (not zero), we'll skip the next instruction, which sends the flow to a preliminary display instruction at "
bjm-1". Here we request, once again, a completion pulse required by the actual drawing code, which is finally entered by the jump at label
If in Twin-Star mode, we subtract the contents of
nx1 as usual.
ny1 is zero at this time, since there is no vertical offset to be perceived, and
nx1 must produce a constant offset over all the frames, because the position of the stars isn't moving at all. How comes?
ny1 are not some pointers related to the current object, but two symbols for the absolute address of the positional properties of the first of the moving objects (spaceship 1). If the position is not lost between frames, these locations really should contain the according values. — Awkward, really embarrassing, in deed.
Let's inspect what's actually in memory here:
00534 420060 sub ny1 swap 00535 663777 rcl 9s 00535 663777 rcl 9s 00537 420030 sub nx1
That's awkward, indeed, since we find
nx1 to be translated to location
30 (octal) and
ny1 to transcribe to
60 (octal), while the two symbols are producing the addresses
3516 respectively anywhere else in the code! — So, this is actually a bug!
The problem here is that the two symbols are used before they are defined in the source, which wouldn't cause a trifle normally. The thing is that these symbols are not normal labels, but are resulting from a cascade of pseudo instructions for the Macro assembler:
nob=30 /total number of colliding objects nx1=mtb nob // mtb + 30 ... ny1=nx1 nob // nx1 + 30 ... // last lines of source: mtb, / table of objects and their properties /start 4
So, on first pass, when the assembler encounters the pseudo instruction defining
mtb is still unknown and per definition minus zero, resulting in a value of
ny1 is derived by
nx1, it will inherit the error. Only as we encouter this definition at the second pass, the true value will be known, resulting in the correct address being used for the two symbols ever thereafter. Since the code for drawing the gravitational star happens to be located before these definitions, both symbols will be still as of after pass 1, causing the bug.
Therefor, instead of the properties of the first spaceship, the contents of the addresses
60 will be subtracted from the origin. Location
60 happens to be placed in a range of memory reserved for future patches and contains zero. Address
30 contains the value
40000, which, transformed to a screen location, makes for an offset of octal
100 or decimal 64 — just what we observe on the screen. And it produces a constant offset, since it happens to be a constant definition for real:
00030 040000 hur, 30, 40000 / hyperspatial uncertancy
But we may be just pleased with this bug, since the Star splits like magic to make room for the Needle. And when we would patch the code to fix the two instructions, the Star would still be split, but this time the two parts would be moving in relation with the spaceship's logical position around the origin:
So, what happened to the exhausts and the torpedoes? They are drawn by the macro
dispt and are therfore receiving the same transposing treatment like the rest of the display commands. Turns out, they receive the treatment twice.
As for the exhaust blasts — as we may recall from previous episodes (and have even seen near the top of this one) — the outline code sets the variables
\sy1 to the very last screen location at the bottom center of the outline, the very spot, where the exhaust is drawn using the said variables. Since the outline is already drawn at a translated pair of coordinates, the second translation in
dispt causes a duplication of the offset. The effect for the torpedoes is quite similar, while even more severe: At the begining of this section we've already seen how the coordinates for any torpedo would have been transposed together with the display coordinate of the spaceship. But the pair (
\stx) isn't used for display purpose only, it's also the actual location of the torpedo assigned in memory. Therefore, the location of the torpedo already transposed will be translated once again by
dispt, effecting in a doubled offset. But, even more, the torpedoe will be logically mapped by its initial offset to spaceship 1! Therefor spaceship 1 will be always firing from the center and spaceship 2 will be firing with an offset that is equal to the offset of spaceship 1 to the origin.
The last issue with the code is a tiny screen artifact left by the modification of the code just before the call of the outline code, the transposition of the spot, where the first, preliminary blip is displayed in order to optain a completion pulse from the display. Normally, displayed at the origin, it mends with the central star. But here, obviously some kind of translation would be of help. By translating it by the usual offset, it is displayed where the first part of the heavy star would be drawn, if there wouldn't be the bug. So, as it is, it happens to be drawn at the same offset that both the echaust and torpedoes of spaceship 1 will have assigned to, by this disclosing the spaceships firing position on the screen.
Bug or Feature?
Judging from our inspection of the source code, a Needle's 2D ego mode was clearly what was intended first. (All modifications are apt to transposing each of the display commands there are in the program to a co-ordinates system relative to the Needle.) And this is an important fact, because this would be a major step towards the pseudo 3D ego shooter, something that we wouldn't see until a decade later.
But, and this is the important question, was it left so intentionally? We may guess that the result was even more interesting than the intended ego-view: A vexing special mode, reserved for the very elite of Spacewar-gamers, just like a blind game of chess. We may assume with some degree of certainty and fairness that Monty Preonas would have been adept enough to fix any issues of the program and make it do what ever he wanted it to. So, either he would have been pleased with the result (qualifying this mode as both a bug and a feature), or maybe he was just running out of time and had to leave things as they were. — On the other hand, especially the issue with the assembler variable cousing the split gravitaional star is a bug that could drive you crazy. It's just a the usual use of the variable that works like a charm anywhere else in the code. However, we see the project continued in Spacewar! 4.4, but then in a fashion much more radical. (Compare Part 10.)
Fixing Needle's View
So, what would be necessary to fix this to produce a true Needle's ego view?
First of all, we might introduce two extra symbols at the end of the source, after the definition of
mtb in order to have them ready, when we draw the heavy star. Then, we would want to have the macro
dispt in two flavors, once in its usual form and once in a modified version applying the translations. Then, we would have to fix
\sty to map to the untransposed position of the ship. We would use our normal version of "
dispt" for the exhausts and the modified version for the hyperspace-breakout-blip and the torpedoes:
define dispt A,Y,B // as in other versions repeat 6, B=B+B lio Y dpy-A+B term define ddispt A,Y,B // using offset for ego view repeat 6, B=B+B lio Y szs 20 jda kcb dpy-A+B term ... // star code - merge to single star at offset -nx1/-ny1 ... szf i 20 jmp bjm-1 sub oy1 // use oy1 instead of ny1 swap sub ox1 // use ox1 instead of nx1 dpy-4000 bjm, jmp . bds, repeat 10, starp szf 6 bpx, jmp . stf 6 szf 20 // translate? jmp bpy // yes cma swap cma swap jmp bjm bpy, add ox1 // translate back to center cma sub ox1 // subtract x-offset again swap add oy1 // translate back to center cma sub oy1 // and subtract y-offset again swap jmp bjm ... // modified positions lac i mx1 szs 20 sub nx1 sub \ssn dac \sx1 szs 20 // fix for torpedo pos add nx1 // no offset sub \ssn dac \stx lac i my1 szs 20 sub ny1 add \scn dac \sy1 szs 20 // fix for torpedo pos add ny1 // no offset add \scn dac \sty ... // further // - use ddispt for torpedo display near tc1 // - use ddispt for hyperspace breakout near hp6 ... mtb, / table of objects and their properties ox1=mtb+nob // same as nx1, available after pass 1 oy1=ox1+nob // same as ny1, available after pass 1 /start 4
With these modifications applied, we get a working Needle's Ego View:
▶ Play Spacewar! 4.3f (fixed) Needle Mode (online emulation, v. 4.3f & sense switch 2 on preselected).
The source code of this mod is available at https://www.masswerk.at/spacewar/sources/spacewar4_3f.txt. (The source contains the changes mentioned above. I've also switched the implementation of the Expensive Planetarium to the one of Spacewar! 3.1, just for personal liking.) It is left to the judgment of the observer, which version is to be prefered. We may conclude that the original Twin-Star Mode is certainly as entertaining as it is enigmatic — a perfect novelty.
Edit: Please mind the continuation of this quest for a subject view, as it is found in Spacewar! 4.4, discussed in Part 10.
The Spacewar! 4.0 Game Saver Patch
There is a last source we should have an eye at, because it well might be the very first record-and-replay device for a game: The game saver patch for Spacewar! 4.0, probably, like the game itself, by Monty Preonas (ddp). Once again we'll include any comments inline, to save some space (bits are numbered — as usual — according to DEC documentation from left to right, MSB first):
spacewar game saver patch mg1=1510 ran=31 /+ tw -> punch /- tw -> read 40/ // patches "cwr, jmp mg1", new input routine cwr, dap cwx // called as subroutine, store return address lat // load test word switches into AC cw2, cks // check status (in IO bits 0..6, bit 1: tape reader buffer) ril 1s // rotate IO left (tape buffer bit into sign position) spi+spa i-skp // punch mode? jmp cw1 // yes, jump to cw1 rrb // transfer tape read buffer into IO rpa-i // read next character into the buffer (async) cw3, rir 4s // rotate IO right 4 bits (correct bit format) cwx, jmp . // return cw1, ril 3s // rotate IO left 3 bits (punch ready bit into sign position) spi+sma i-skp // punch mode and ready? jmp cw2 // no, redo at cw2 jsp mg1 // read control input (into IO) ril 4s // rotate IO left 4 bits ppa-i // punch to paper tape (char mode, IO bits 17..10, async) jmp cw3 // finish at cw3 6000/ /new starting address go, lat // load test word into AC sma // punch mode? jmp pu // yes, jump to pu rpb // read next 3 valid lines from paper tape into IO dio ran // store it in ran (random number) rpa-i // read next character from tape into tape buffer jmp 4 // start the game pu, law i 200 // AC = -200 dac \pc // store it in var \pc (chars to punch) cli // clear IO, now punch a prespan of dec. 128 empty chars ppa // punch as char to paper tape isp \pc // index \pc and skip, if positve jmp .-2 // punch next char lio ran // load contents of random number ppb // punch to paper tape (binary, highest 6 bits) ril 6s // rotate left 6 bits ppb // punch to paper tape (binary, next 6 bits) ril 6s // rotate left 6 bits ppb-i // punch to paper tape (binary, next 6 bits, async) jmp 4 // start the game variables start go
What would be needed to record a game? The random seed and the stream of control inputs per user and frame. And what would we need to replay a game? The random seed, once again, and a stream of control inputs to be used instead of the readings of the actual input devices (control boxes or test word switches). That's exactly what is recorded and read by this patch, to and from the paper tape.
Using this patch the game starts now at label
go: If we're in read mode, we'll read the first 3 valid lines from the paper tape (6 bits per line) as a binary number (18 bits) and initialize the random number by this. The instruction "
rpa-i" requests a read of the next character (alphabetical) from the paper tape (see below). If in punch mode, we first write a prespan of (octal) 200 empty lines. Next, we write the current state of the random number onto the tape. Read or write mode, we then jump to the normal start address at
While the game is running, any attempt to read a control input is caught by the trap installed at address
cwg). Since this will be called as a subroutine, we'll first store the return address. Then we check the status word by reading it into
cks") and check the tape reader buffer status (in bit 1). The shift brings the paper tape status bit into sign position. If the sign bits in
AC are both unset, we're in punch mode and jump to
cw1. Otherwise, we're in read mode: Since the last read instruction was an asynchronous one, we'll have the to fetch the input from the tape reader buffer into
rrb). Another "
rpa-1" issues the next asynchronous read. Meanwhile, we transform the character format to the bit-patterns used in the game by a rotation by 4 bits to the right and return, thus supplied with the suitable control input, to the main program.
If in punch mode, we first check for the paper tape punch being ready for the next command. If not, we redo at label
cw2 with a new status check. If ready, we fetch the control input from the usual input routine at
mg1. A rotation by 4 bits to the left converts the reading to a valid character, which is then punched onto the tape. Finally, the jump to
cw3 restores the reading in
IO and returns to the main program.
As the games is recorder on paper tape, the length of the tape available may well put a practical limit to the extent of a recorded session …
Promised, this was the last time we actually investigated some of Spacewar's source code in detail. But still, we're not near the end, but for this episode. And as for this episode, it's time for a little extra from the realms of fun-facts:
The Other Space Race — The Quest for Orbital Flight in Manned and Virtual Space Programs in the US
The following is a comparative timeline describing the parallel quest for orbit, both in manned space flight as conducted by NASA and on the PDP-1's scope. While NASA had certainly a head start, we dare say both programs achieved their primary goal quite on par (in February 1962), both programs reassuring the success in May and ending their respective missions a year later on the arrival of summer 1963.
|Date||Project Mercury (NASA)||The Hingham Institute Study Group On Space Warfare (MIT)|
|May 1961||May 5: FREEDOM 7 (Mercury-Redstone 3), Alan B. Shepard, Jr., first suborbital flight (15 min 28 sec).||Steve Russell et al. imagine Skylark movies at the Hingham Institute.
Probably first notion of a DEC PDP-1 to be donated to MIT.
|Jul. 1961||July 21: LIBERTY BELL 7 (Mercury-Redstone 4), Virgil I. Grissom, suborbital flight (15 min 37 sec).||Summer 1961: The Hingham Institute Study Group On Space Warfare is formed, ideas on Spacewar! beginning to take shape.|
|Sep. 1961||September 13: MA-4 (18) / Mercury-Atlas 4, unmanned test flight, testing environmental controls in orbit (92 min 28 sec).||Fall 1961: The TX-0 development tools are ported to the PDP-1 in a weekend's rush, gaining the group access permission for Spacewar-programming in afterhours. Wayne Wiitanen called to active duty during the Berlin Wall crisis in October. Steve Russell, elected programmer in chief, does honor to his nickname (Slug) and procrastinates …|
|Nov. 1961||November 1: MR-1 (9) / Mercury-Scout 1, unmanned test flight, test of Mercury Scout launch configuration (43 sec).||November 6, 10:00 a.m.: Official presentation of the PDP-1 at MIT, Department of Electrical Engineering (Room 26-260 – Campton Laboratories; coffee served afterwards in the Conference Room of the Research Laboratory of Engineering).
Roughly the date of the Sine-Cosine-Incident ("All right, Russell, here's a sine-cosine routine; now what's your excuse?")    — soon thereafter start of development.
|November 29: MA-5 (22) / Mercury-Atlas 5, Enos (chimpanzee), primate test of environmental control system in orbit (88 min 22 sec).||About this time there would be first moving blips to be seen on the screen.|
|—||First playable version with moving objects drawn by an outline-interpreter. Dan Edwards adds gravity, the game is pushed out of limits of flicker-free display.|
|Jan. 1962||—||Dan Edwards adds the ingenious outline-compiler. Debugging and experiments …|
|Feb. 1962||February 20: FRIENDSHIP 7 (Mercury-Atlas 6), John H. Glenn, Jr., first orbital flight (4 h 55 min 23 sec).||Game basically operational. Outlines final. Control boxes are added.|
|Mar. 1962||—||Peter Samson adds the Expensive Planetarium for Spacewar 2b (March 13, 1962) replacing the original random starfield.
Earliest known source of Spacewar 2b (March 25, 1962).
|Apr. 1962||—||Spacewar 2b final (polarity of sense switches reversed, April 2, 1962), additional patches: auto-restart patch, hyperspace patch.
Spacewar! is announced in the first issue of Decuscope.
|May 1962||May 24: AURORA 7 (Mercury-Atlas 7), Scott M. Carpenter, orbital flight, duplication of Mercury-Atlas 6 (4 h 56 min 5 sec).||Final version of the hyperspace patch and the Minskytron signature (Hyperspace VIci, May 2, 1962), scorer patch added in rush towards the MIT Open Science House Day. Public demonstration of Spacewar!
Martin Graetz presents a paper on Spacewar! at the first DECUS meeting in Bedford, MA, “SPACEWAR! Real-Time Capability of the PDP-1”.
(Steve Russell leaves [persumingly for a six months turn with the US Army], not to return to MIT thereafter, except see below.)
|Sept. 1962||September 12: John F. Kennedy gives his Moon Speech at the Rice Stadium.||Steve Russell returns to MIT to arrange Spacewar 3, incorporating the various patches into the game, hyperspace and explosions gain their final form, a parameter table is added for tweaking adjustments and allowing some variants (e.g., Winds of Space) by hacking these constants. Final version of original Spacewar!, Spacewar 3.1 (September 24, 1962).
The original team drifts away.
|Oct. 1962||October 3: SIGMA 7 (Mercury-Atlas 8), Walter M. Schirra, six-orbit engineering test flight (9 h 13 min 11 sec).||—|
|Feb. 1963||—||Spacewar 4.0 by Diamantis Drakos “Monty” Preonas (February 2, 1963), update for automatic multiply-divide option, new gravity computations for best use of 18-bit precision.
Spacewar 4.1 by “dfw” (February 20, 1963)
|May 1963||May 15-16: FAITH 7 (Mercury-Atlas 9), Gordon Cooper, Jr., last Mercury mission, 22 orbits (34 h 19 min 49 sec).||Spacewar 4.2 by Monty Preonas (ddp), May 11, 1963 introduces a first score display.
Spacewar 4.3 by Monty Preonas (ddp), May 17, 1963 including a score display and Twin-Star Mode.
Spacewar 4.4 for two displays by Monty Preonas (ddp), May 21, 1963, edited by Joe Morris (date unknown).
Various models of control boxes and joysticks in use, parsing of control inputs gains in complexity.
|Jul. 1963||—||Spacewar 4.8 by “dfw”, last known/preserved version of classic MIT-Spacewar! (July 24, 1963).
Visual scorer patch (unsigned) by Peter Samson (prs).
Russell, never one to "do something" when there was an alternative, begged off for one reason or another. One of the excuses for not doing it, Slug remembers, was "Oh, we don't have a sine-cosine routine and gee, I don't know how to write a sine-cosine routine..." Then Alan Kotok came back from a trip all the way to Maynard (DEC headquarters) with paper tapes saying "All right, Russell, here's a sine-cosine routine; now what's your excuse?" "Well," says Slug, "I looked around and I didn't find an excuse, so I had to settle down and do some figuring."
(J.M. Graetz, "The Origin of Spacewar"; in Creative Computing, August 1981 issue; p. 62)
I don't remember the exact order of things, but I'm pretty sure I started talking up a better demonstration program than the Minskytron, and eventually, Alan Kotok went up to Maynard and collected the sine and cosine routines from DECUS, presented them to me, and said, "Okay, here are the sine and cosine routines; now what's your excuse?" And I discovered I had run out of excuses; I had to actually think. And so I started work and figured out the basic trick of Spacewar! display which is that you only need to calculate a unit vector pointing in the direction of the spaceship.
(Steve Russell in Oral History of Steve Russell, Computer History Museum 2008, p. 12)
I kept talking this up and eventually Alan [Kotok] went out on an expedition to Maynard and came back with the sine and cosine routines and sort of plopped them down in front of me and said: "All right, Russell, here are the sine and cosine routines; now, what's your excuse?" So I actually had to sit down and do some thinking. And I wrote up a program that [had] two spaceships, but it didn't have any gravity and it didn't have any stars. And we played that for a while; it was kind of interesting.
(Steve Russell, transcript of The Mouse that Roared: PDP 1 Celebration Event, Computer History Museum 2006)
Please mind that this timeline is meant to be taken with some grano salis and that the dates of some of the MIT-progress (where not exactly given) and the respective chronological order are merely rough estimates. The full dates of any Spacewar!-programs as provided in this table are referring either to dates provided at the head of the respective source code or, in one case (Spacewar 2b April 2, 1962), to a date found at the label of a binary paper tape. Please mind that there were also some experimental features, like fuzzy torpedoes, that were not included in the final program and which have not found a place in this timeline. The total development time of the original Spacewar! is said to have amounted to a mythical man-month (200 h) distributed over a few months (please mind "Hyperspace VIci" in this context; not to speak of “testing” …).
Vienna, February 2015
In case you would have found any errors or inconsistencies, or would have additional information,
please contact me.
◀ Previous: Part 8: Hyperspace!
▶ Next: Intermission: Yet Another Patch — Spacewar! 2b SA 5 and the Secrets of PDP-1 Loaders
▲ Back to the index.