Retrochallenge 2016/10:
Ironic Computer Space Simulator (ICSS)

Episode 5: Yet Another Progress Update

We had high hopes of putting some more on the screen today (think saucers), but we're still tinkering with the rocket ship.

PDP-1 babble

Unidentified PDP-1-esque babble.
(Image: Still from Mars Attacks!, Warner Bros. 1996)

Last episode, we proudly presented a hack to combine a constant velocity for the spaceship with a cheap way to manage viscous acceleration. Maybe, a bit too much of a hack. Also, on the Type 30 CRT Display and its iconic trails glowing in the long sustain, the very path of any maneuvering object poses not only an algorithmic challenge, but an esthetical one, as well. And it's with the latter, we weren't exactly happy with. Moreover, we're looking for a nicely conigurable solution that would be still viable when we will fine-tune the timing and pace of the game. So we return to the problem of maneuvering a spaceship with grace.

But not all was terrible with our hack. Especially the idea of using averages for damping was quite nice. We'll keep this, while we get rid of the hack regarding the acceleration. As one of the problem of simulating the behavior of Computer Space on the PDP-1 originates in the much faster response of the DEC machine, we decide to opt for a configureable sensitivity, in stead.

This is our plan:

As a side effect, we change the name of a few symbols and see some grow of the parameters table at the top of the code. Other, we decide to implement a general frame counter to manage some common skipping and alternation conditions, like for the background display or the exhaust flame animation of the rocket ship.

Screenshot

In a still v.0.05 still looks much like v.0.04.
(Section of screenshot, www.masswerk.at/icss/)

Experience the code live:   www.masswerk.at/icss/.

PDP-1 Instruction of the Day

In our ongoing endeavor of familiarizing our readers with the PDP-1 and empowering them to benefit from the source code provided below, we present another nifty instruction of the PDP-1, namely "xct", as in execute.

Instruction "xct" performs the remote execution of a single instruction located in the memory register specified in its address part. In other words, "xct" remotes the control flow for a single step. That is, as long as no skip occurs: In this special case the skip will occur locally and the control flow will be changed permanently.

This nifty indirect execution comes just at the price of a single additional instruction cycle and is especially handy for configuring parameters.

Macro Assembler Trick of the Day

Speaking of configurable parameters, we find a quite interesting construct in the parameters table of Spacewar!, our gold standard in PDP-1 code, which also illustrates nicely the use of remote instructions to be called by "xct":

(Spacewar! 3.1)

/ interesting and often changed constants

/symb loc   usual value (all instructions are executed,
/ and may be replaced by jda or jsp)

tno,  6,	law i 41 	/ number of torps + 1
tvl,  7,	sar 4s		/ torpedo velocity
rlt, 10,	law i 20	/ torpedo reload time
tlf, 11,	law i 140	/ torpedo life
foo, 12,	-20000		/ fuel supply
....

Now, what are these numbers following the labels and sperated by commata from the instructions? Incidentially, the code shown here starts at address 6 — are these numbers somehow magically asserting the memory locations (while nothing alike is specified anywhere in the syntax of the Macro assembler)?

Short answer: No. These are actually labels, which may, by the way, consist of numbers only. Since these "interesting and often changed constants" are meant to be changed on the fly in memory, the source code also serves as a reference to these parameters. It's actually a nice idea to mark the memory location in a clear and easily discernable way by putting them in front of the respective values or instructions by the means of an otherwise unused numeric label.

An observant reader, risking a glimpse at the source code below, may catch us copying this neat trick in our own program.

And here's the code, so far:

ironic computer space  0.05  nl 2016-10-18
                                          
	mul=mus
	div=dis

define initialize A,B
	law B
	dap A
	term

define index A,B,C
	idx A
	sas B
	jmp C
	term

define swap
	rcl 9s
	rcl 9s
	term

define load A,B
	lio (B
	dio A
	term

define setup A,B
	law i B
	dac A
	term

define count A,B
	isp A
	jmp B
	term

/macros specific to the program

define scale A,B,C
	lac A
	sar B
	dac C
	term

define random
	lac ran
	rar 1s
	xor (355671
	add (355671
	dac ran
	term


/ Computer Space
/ Original arcade game by Nolan Bushnell and Ted Dabney,
/ Syzygy Engineering / Nutting Associates, 1971.
/"A simulated space battle that pits
/ computer-guided saucers against
/ a rocket ship that you control."


3/	jmp sbf			/ ignore seq. break
	jmp a0			/ start addr, use control boxes
	jmp a1			/ alt. start addr, use testword controls


/game parameters

raa,  6,	2400		/ rocket angular acceleration
rvl,  7,	sar 7s		/ scaling rocket velocity
ras, 10,	law i 4		/ rocket acceleration sensitivity (skip)
rad, 11,	law i 3		/ rocket acceleration damping (averaging)
ran, 12,	0		/ random number


/routine to flush sequence breaks, if they occur.

sbf,	tyi
	lio 2
	lac 0
	lsm
	jmp i 1


/sine-cosine subroutine - Adams associates
/calling sequence= number in AC, jda sin or jdacos.
/argument is between +/- +2 pi, with binary point to right of bit 3.
/answer has binary point to right of bit 0.  Time = 2.35-? ms.
/changed for auto-multiply , ddp 1/19/63

cos,	0
	dap csx
	lac (62210
	add cos
	dac sin
	jmp .+4

sin,	0
	dap csx
	lac sin
	spa
si1,	add (311040
	sub (62210
	sma
	jmp si2
	add (62210

si3,	ral 2s
	mul (242763
	dac sin
	mul sin
	dac cos
	mul (756103
	add (121312
	mul cos
	add (532511
	mul cos
	add (144417
	mul sin
	scl 3s
	dac cos
	xor sin
	sma
	jmp csx-1
	lac (377777
	lio sin
	spi
	cma
	jmp csx

	lac cos
csx,	jmp .

si2,	cma
	add (62210
	sma
	jmp si3
	add (62210
	spa
	jmp .+3
	sub (62210
	jmp si3

	sub (62210
	jmp si1


/subroutines for background display

nos=77 	/number of stars

/table of stars coors (nos words, 9 bits x and 9 bits y)
bst,	. nos/

/setup (nos random coors starting at bst)

bsi,	dap bsx				/ deposit return address
	init bsc, bst			/ deposit first addr of bst in bsc
bsl,	random				/ get a new random number
bsc,	dac .				/ store it in current loc of st
	index bsc, (dac bst+nos, bsl	/ increment bsl, repeat at bgl for nos times
bsx,	jmp .				/ return


/display background stars (displays every 2nd frame only)

bg,	dap bgx				/ deposit return address
	lac \frc				/ check frame conter
	and (1
	sza i					/skip every second frame
bgx,	jmp .				/ return
	init bgl, bst			/ init bgl to first addr of stars table
bgl,	lac .				/ get coors of next star (x/y)
	cli				/ clear io
	scr 9s				/ shift low 9 bits in high 9 bits of io (x)
	sal 9s				/ move remaining 9 bits in ac in high part (y)
	add bgv				/ add vertical offset
	swap				/ swap contents of ac and io
	add bgh				/ add horizontal offset
	dpy-i				/ display a dot at coors in ac (x) and io (y)
	index bgl, (lac bst+nos, bgl	/ repeat the loop at bgl nos times
	jmp bgx				/ return

bgh,	0
bgv,	0


/here from start

a0,	law rcb			/configure to read control boxes (sa 4)
	dap rcw
	jmp a2

a1,	law rtw			/configure to read testword (sa 5)
	dap rcw

/start a new run

a2,	jsp bsi			/ initial setup of bg-stars
	dzm bgh			/ reset offsets of stars to zero
	dzm bgv
	
/rocket reset

ar,	dzm \rth		/ rotational angle
	lac (-70000
	dac \rpx		/ pos x
	cla
	dac \rpy		/ pos y
	dzm \rdx
	dzm \rdy
	dzm \rac		/acceleration skip counter

/main loop

fr0,	load \ict, -4500	/ initial instruction budget (delay)

	idx \frc		/ increment frame counter
	jsp bg			/ display background
	jsp rkt			/ rocket routine

	count \ict, .		/ use up rest of time of main loop
	jmp fr0			/ next frame


/player rocket routine

rkt,	dap rkx
rcw,	jsp .			/read control word (ccw, cw, trust, fire)
	cla clf 5 -opr		
	dio \cw			/merge (or) spacewar player inputs
	rcr 4s
	ior \cw
	dac \cw

	lio \cw			/parse and process rotation
	lac \rth		/load angle
	spi			/sign cleared in io?
	add raa			/no, add angular acceleration
	ril 1s			/next bit
	spi
	sub raa
	sma			/normalize 0 >= angle >= 2Pi (311040)
	sub (311040
	spa
	add (311040
	dac \rth		/update angle

	ril 1s			/parse thrust input
	spi
	stf 5			/set flag 5 for thrust
	jda sin			/get sin, store it in \sn
	dac \sn
	lac \rth
	jda cos			/get cos, store it in \cs
	dac \cs

	szf i 5			/flag 5 set? update dx / dy
	jmp rp0
	lac \frc		/load frame counter
	isp \rac		/sensitivity, frame skip
	jmp rp1
	xct ras			/reset counter
	dac \rac
	lac \sn			/dx
	cma
	xct rvl			/apply scaling for acceleration
	swap			/swap result into io
	xct rad			/damping, get loop count
	dac \rdc
rx0,	swap			/get intermediate value from io
	add \rdx		/average with old dx
	sar 1s
	swap			/swap into io
	isp \rdc		/increment loop
	jmp rx0
	dio \rdx		/store updated dx
	lac \cs			/same for dy
	xct rvl
	swap
	xct rad
	dac \rdc
ry0,	swap
	add \rdy
	sar 1s
	swap
	isp \rdc
	jmp ry0
	dio \rdy
	jmp rp1

rp0,	dzm \rac

rp1,	scale \sn, 4s, \sn1	/get position of rocket tip
	sar 4s			/offset x = (sin >> 4) - (sin >> 8)
	cma
	add \sn1
	dac \sn1
	scale \cs, 4s, \cn1	/offset y = (cos >> 4) - (cos >> 8)
	sar 4s
	cma
	add \cn1
	dac \cn1
	lac \rpx		/update pos x (add dx)
	add \rdx
	dac \rpx
	sub \sn1		/subtract offset for tip, store it in \px
	dac \px
	lac \rpy		/same for y
	add \rdy
	dac \rpy
	add \cn1
	dac \py

	scale \sn, 7s, \sn4	/scaled sine 4 steps
	sar 1s
	add \sn4
	dac \sn4		/sn4 = (sin >> 7) + (sin >> 8)
	dac \sm4		/sm4 for lateral offsets (will be complemented)
	sar 1s
	dac \sn2		/scaled sine 2 steps
	dac \sm2
	sar 1s			/scaled sine single step
	dac \sn1
	dac \sm1
	scale \cs, 7s, \cn4	/same for cosine
	sar 1s
	add \cn4
	dac \cn4
	dac \cm4
	sar 1s
	dac \cn2
	dac \cm2
	sar 1s
	dac \cn1
	dac \cm1
	
	jsp rod			/display it
	
	lac \ict		/update instruction count
	add (1000
	dac \ict

rkx,	jmp .


/control word get routines

/read control boxes
rcb,	dap rcx
	cli
	iot 11
rcx,	jmp .

/read testword
rtw,	dap rtx
	lat
	swap
rtx,	jmp .


/rocket display
/ step rearwards -  x add \sn1, y sub \cn1
/ step outwards  -  x add \cm1, y add \sm1
/ step inwards   -  x sub \cm1, y sub \sm1

define disp
	dpy-i 100		/display a dot at brightness level +1
	term

rod, 	dap rox
	stf 6			/set flag 6
	lac \px
	lio \py
	disp

rop,	swap			/y +3, x +3
	sub \cn2
	sub \cn1
	add \sm2
	add \sm1
	swap
	add \sn2
	add \sn1
	add \cm2
	add \cm1
	disp

	swap			/y +4, x +2
	sub \cn4
	add \sm2
	swap
	add \sn4
	add \cm2
	disp

	swap			/y +4, x +1
	sub \cn4
	add \sm1
	swap
	add \sn4
	add \cm1
	disp

	swap			/y +4, x -1
	sub \cn4
	sub \sm1
	swap
	add \sn4
	sub \cm1
	disp

	swap			/y +5, x +4
	sub \cn4
	sub \cn1
	add \sm4
	swap
	add \sn4
	add \sn1
	add \cm4
	disp

	swap			/y +1, x -6
	sub \cn1
	sub \sm4
	sub \sm2
	swap
	add \sn1
	sub \cm4
	sub \cm2
	disp

	swap			/y +4, x +3
	sub \cn4
	add \sm2
	add \sm1
	swap
	add \sn4
	add \cm2
	add \cm1
	disp
	
	szf i 6			/flag 6 set? (skip on not zero)
	jmp rot
	clf 6			/clear flag 6
	lac \cm1		/invert unit vector components
	cma
	dac \cm1
	lac \sm1
	cma
	dac \sm1
	lac \cm2
	cma
	dac \cm2
	lac \sm2
	cma
	dac \sm2
	lac \cm4
	cma
	dac \cm4
	lac \sm4
	cma
	dac \sm4
	lac \px			/load first pos
	lio \py
	jmp rop			/second pass for other side
	
rot,	szf i 5			/no, flag 5 set?
rox,	jmp .			/no, return
	swap			/draw exhaust flame
	sub \sm4		/x -6
	sub \sm2
	swap
	sub \cm4
	sub \cm2
	dac \px			/store position
	dio \py
	lac \frc		/load frame counter
	and (2
	sza			/is it zero? (state switch)
	jmp ro2
ro1,	lac \py			/state 1, display at y-1
	add \cn1
	swap
	lac \px
	sub \sn1
	disp
	jmp rox
ro2,	lac \py			/state 2, display at y+4
	sub \cn4
	swap
	lac \px
	add \sn4
	disp
	jmp rox			/jump to return

	constants
	variables

	start 4

 

That's all for this post.

— stay tuned! —

 

Next:   Episode 6: Saucers!

Previous:   Episode 4: “A Rocket Ship That You Control”

Back to the index.

— This series is part of Retrochallenge 2016/10. —