Go to the Index

Inside Spacewar!
Part 9: Like a WW II Pilot’s Tally
The 4.8 Scorer Patch and Other Spacewar! 4 Oddities

Prev. Next

(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.

Spacewar Scoring: Like a WW II fighter pilots's tally (illustration)

“Like a World War II fighter pilot's tally”
Artist's impression of the scoring experience in Spacewar! 4.8 patched.
Illustration: Norbert Landsteiner, 2015

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):

Spacewar! scores displayed at the control console of the DEC PDP-1 (18:3)

Scores displayed at the console lights of the DEC PDP-1.
Player one (accumulator, 4th row from top): 18, player two (IO register, below): 3.
(Image credits: Marcin Wichary, 2008, CreativeCommons, edited N.L. 2015)

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:

Scoring in Spacewar! 4.8 (Emulation)

Score display of the Spacewar! 4.8 scorer patch (Emulation).
Player one (Needle): 18, player two (Wedge): 3; a big ship accounts for ten scores.

And here is closeup of the score display (emulated display resolution: 1048 × 1048). — Mind the dotted appearance of the bigger spaceships.

Closeup of the Spacewar! 4.8 scorer patch (Emulation)

Closeup of the score display. (Emulation, high resolution, 1048 × 1048)

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.

The Code

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 "4544/".)

/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 sx1, sx2, ssn, ssm, etc. Please mind that some labels are rather defined as variables in the main program, like \sx1, \sx2, \ssn, \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 6077.

So, where to begin, if not with the patched addresses?

The Patches

The first one, "a3/ jmp jpt", reroutes the flow of control to label jpt. 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:

Symbol 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 ddt.)

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 fi1 and 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 a3 ("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 fi1:

a4,	lac \1sc
	lio \2sc
	hlt  // patched: jump fi1

So, let's see, what's happening at this ominous label fi1

Scores in Space — The Display Loop

Label 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 fi1 and 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 t1 and t2 respectively. (Labels t1 and 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 t1 and 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 7.

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 ssn and 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 AC.

At label 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 240000) in sy1, load the constant 540000 into AC and store it in the location sx1. Both sx1 and 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 540000 by 8 (now 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 scg.

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 AC (still 3000) is stored in scm, ssd, scn, 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 stx and sty into AC and 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 IO.

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 sx1 and 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 sy1).

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:

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 AC (either 1000 or 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 IO. (Again, 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 2sc plus 1) in 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 s1).

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.

UPDATE

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 "iot 111" instructions.
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 swp), some input parsing code stripped.
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.
Patches Available
Game saver patch (unsigned, undated) 4.8 scorer patch (unsigned, undated), by Peter Samson

Sources

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):

Remarks

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!:

Control box for the PDP-1 at MIT

MIT, advanced control box attached to the PDP-1 (but there are still two of them).
Image courtesy of the Computer History Museum (CHM), Catalog No 102631249.

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.

PDP-1 and control boxes at MIT

MIT, PDP-1 with advanced control boxes.
Image courtesy of the Computer History Museum (CHM), Catalog No 102631246.

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.

Scoring in Spacewar! 4.3 (Emulation)

Score display of Spacewar! 4.3 ddp (Emulation).
Player one (Needle): 18, player two (Wedge): 3; a big ship accounts for ten scores.

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.

The Code

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

and:

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 constants and 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 AC into 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 fis.

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.

Spacewar! 4.3: Twin-Star mode (Emulation)

Twin-Star mode in Spacewar! 4.3 ddp, sense switch 2 deployed (Emulation):
The ships and background are plotted relative to the Needle (center), the central star is split to
both sides of the Needle, torpedoes and exhaust blasts are drawn in tranposed screen locations.

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.

The Code

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

Now, 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 IO and 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 AC and IO.

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

At label 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 "scr 8s".)

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 sp1 and 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 (\sx1, 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 mot (or 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 AC and IO and issuing a "dpy-4000" display instruction. For the same reason we fetch the last completion pulse by the final ioh instruction.

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.)

The macro 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:

Spacewar: Drawing the central star

Drawing algorithm for the heavy star, normal and Twin-Star mode.

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 AC, 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 bjm.
If in Twin-Star mode, we subtract the contents of ny1 and nx1 as usual.

Spacewar! Twin-Star Mode, Closeup (Emulation)

Closeup of the twin stars in Spacewar! 4.3 (Emulation).

Obviously 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?

First, nx1 and 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 3466 and 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 nx1, symbol mtb is still unknown and per definition minus zero, resulting in a value of 30 for nx1. Since 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 30 and 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:

Spacewar! 4.3 Twin-Star Mode fixed (Emulation)

Twin-Stars fixed. (Emulation) — Mind the movement trails left by the two parts of the heavy star.

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 \sx1 and \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, \sty) 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 \stx and \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:

Spacewar! 4.3f Needle View (Emulation)

The joys of Ptolemaic space travel: Needle View in fixed Spacewar! 4.3f.
Screenshot of emulation, N.L. 2015.

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 4.

While the game is running, any attempt to read a control input is caught by the trap installed at address 40 (label 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 AC ("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 IO and 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 IO (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 …

*****

Placing up the feet in front of a PDP'1 Type 30 CRT

Laid-back, feet up in front of a DEC Type 30 CRT.
Image courtesy of the Computer History Museum (CHM), Catalog No 102631261.

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 Engi­neering (Room 26-260 – Campton Laboratories; coffee served afterwards in the Conference Room of the Research Laboratory of Engi­neering).
November 29: MA-5 (22) / Mercury-Atlas 5, Enos (chimpanzee), primate test of environmental control system in orbit (88 min 22 sec). Roughly the date of the Sine-Cosine-Incident ("All right, Russell, here's a sine-cosine routine; now what's your excuse?") [1] [2] [3].
Dec. 1961 DECEMBER 29, 1961: The Type 30 Visual CRT display is installed. Spacewar! programming can actually commence.
Jan. 1962 Spacewar! is taking shape. Steve Russell ingeniously manages the economics of rotation by rotating the reference grid.
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. Dan Edwards adds the ingenious outline-compiler and gravity. A random starfield is added to serve as a positional reference. Debugging and experiments …
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).
The Sine-Cosine Incident is everyone's favorite story from the origin of Spacewar! — here in its "canonical" presentation:

  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)
And here is another version of the Sine-Cosine Incident, from Steve Russell's oral history interview:

  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)
And yet another version of the Sine-Cosine Incident, from Steve Russell's account at The Mouse that Roared: PDP 1 Celebration Event:

  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” …).

 

Norbert Landsteiner
Vienna, February 2015
www.masswerk.at

 

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.