# Episode 7: Skeet in Space!

Meanwhile, we've scored a few milestones at once, like collision detection and torpedos, even some visual effects. In fact, we've everything in place required for a little game of skeet shooting in outer space. Best of all, removing most of the artificial throttling, we're now running at twice the frame rate, nurturing the best hopes regarding the outcome of this venture.
So it's high time for another writeup to catch up with the progress of our little project.

### Collisions

So far, we had a maneuverable ship and randomly moving saucers on the screen. The saucers were quite a bit of work, most of it dedicated to the few animated dots moving in the central "rotating" section of the saucers, and, quite perversely, to the idle phase of the saucers. (Just as a reminder, there are in total 4 phases out of two different idle behaviors, and the central animation is to be kept for any consecutive leg of movement.)

Now, we may actually do some with these objcts, maybe have them collide with each other?
I may be wrong, but I believe the original game to do some kind of pixel-wise hit detection, similar to what we see with sprites/MOBs in early game consoles and home computers. *) The next best option, in case we're lacking the hardware for this, is to detect collisions by comparing radii. But this involves time consuming operations like multiplications, even, if we opted for comparing distances to squared radii and thus saving the runtime required for costly square roots.

*) Update: I stand corrected. The original game features a rather generous hit detection and ships collide even at still a distance.

Moreover, having a closer look at our saucers, they are, while ideally disk-shaped in our imagination, far from disk-shaped on the screen. Actually, some kind of vertically compressed hit box would do the job. Maybe something that would be more like an octagon instead of a circular area?

Enter Spacewar! and the solution Steve Russell came up with:

• Compare the absolute delta x (dx) of the respective centers to epsilon 1.
• If it fits, compare the absolute delta y (dy) of objects to epsilon 1.
• If it still fits, clip the corners of the hitbox for extreme values of both dx and dy by adding dx to the result of the comparing operation dy – ε1 and compare this to another treshold, epsilon 2, of just half the size of epsilon 1.
In other words, we're checking, if (ε1 + ε2) < abs(dx) + abs(dy).

This is next to ideal for our purpose. Instead of using the same epsilon for both dx and dy, we're going to use distinctive thresholds, thus achieving our vertical compression. We move the code into a subroutine and set up the values of the various epsilons with regard to the objects we're gong to compare. As we're checking for any overlap, the sums of the vertical and horizontal raddi (half the side-lengths), respectively, will do. And of course, we've to set up the respective center cooordinates of the objects, we're going to compare, as well.

Here, we've to remind ourselves that the rotational center of the rocket ship is not its geometrical center. In fact, it's somewhere below, in the "umbilical" region of the ship. Therefor, we've to subtract an offset based on the current sine and cosine of the ships rotational angle. As this happens to be just half the offset we already used to get the tip of the ship for the drawing routine, we're just storing this when we're inside the rockte ship's routine to be reused here.

Here's a quick look at the actual code for comparing the player's ship with the first of the two saucers:

lac \rpx		/get rotational center of rocket
sub \rxc
dac \px
lac \rpy
dac \py

law \px			/set up rocket for comparison
dap mx1
law \py
dap my1

lac (21000		/set up collision radii (x 34, y 24) in screen locs
dac \dxe
sar 1s
dac \de2
lac (14000
dac \dye

law \upx		/first saucer
dap mx2
law \upy
dap my2
jsp dmf			/compare them
sza			/hit?
jmp dxr			/yes

(...)

/subroutine to compare object positions (from spacewar, mod n.l.)
dmf,	dap dmx
mx1,	lac .			/calc if collision
mx2,	sub .			/delta x
spa			/take abs val
cma
dac \t
sub \dxe		/ < epsilon x ?
sma
jmp dm0			/no
my1,	lac .
my2,	sub .
spa
cma
sub \dye		/ < epsilon y ?
sma
jmp dm0			/no
sub \de2		/ < epsilon 2 ?
sma
jmp dm0
law 1			/return 1 (in ac) for hit
jmp dmx
dm0,	cla			/return 0 for miss
dmx,	jmp .

But, aren't there two of the saucers on the screen? — Yes and no: It's more like a single saucer appearing twice at a constant vertical offset. Therefor, all we have to do is to set up a temporary value with the offset added for a new vertical position (my2) and go for another round, using otherwise the parameters, we've set up earlier.

By this, we should be able to detect any ship-to-saucer collisions. So, what's going to happen in case, we were actually detecting one?

### Symbolic Death (© Stewart Brand)

As indicated by the subtitle, the jest of death is in the notice, even more so for a video game. Therefor, it's of uttermost importance that the event should be symbolized to the player in a proper and orderly manner.

Computer Space doesn't disappoint here, informing us of the tragic event by a spinning animation of the ship. (The saucers are respawned quickly after a short flashing of the screen, making this a proper twitch game.) And, as this is just a symbolic death, live goes on and the player's rocket is repositioned on the screen, just as it was, in order to commence with the short-lived but epic quest of shooting saucers.

This is rather easy to implement: We just trick the rocket routine into performing a clockwise rotation, but at twice the speed. At second thought, we make this a progressively accelerating spinning motion, starting rather slowly, at twice the speed of a normal turn, becoming quicker and quicker, until it just stencils a nice dotted pattern into the long sustain of the Type 30 CRT. For this, we've to add some counters, but are paid off by a rather nice, while ephimeral effect.

### Space Pigeons, or, Gosh, What I'm Supposed to Do with a Torpedo?

In the Apollo era, NASA dubbed any unidentified lunar emanations Moon pigeons, but, in spite of this code name, astronauts restrained their lunar sports activities to golf only, that is, as far as we know it. :-)
But we're here for some serious sport and are facing the by far fiercer space pigeon — and there's no room for error. So we'd better do something about it…

With hit detection already in place, it would be nice to implement a torpedo (that is, a photon torpedo, of course), so that we may have something to shoot at, while testing the program. This is rather straight forward, again, as there is just a single torpedo at a time and it's about just a single dot on the screen, advancing by the constant delta of its velocity. That is, if it wouldn't be for a peculiar property of Computer Space, namely guided missiles.

In Computer Space, if you've fired a torpedo (or missile) and happen to turn your ship, the torpedo is altering its direction, too. Steerable torpedos! Just think of it and the possibilities in gameplay! But, judging from footage of the game, the steering is rather quirky and not too easy to master — otherwise, the game might be to easy.

We've to come up with a similar solution. Additionally, the trail of the torpedo will be pretty visible in the afterglow of the Type 30 CRT, so it should be an aesthetical pleasing solution, as well. We implement this, quite like we did it for the acceleration of the ship, by a combination of frame skips (delayed response) and repetitive averaging with the old movement delta (for damping), where we derive the new factor from the scaled unit vector of the ship's current rotational angle.

In fact, we even implement two solutions to the problem: A default one, where torpedo steering only applies when the ship is actually rotated (similar to what is seen in footage of the game), and another one, to be activated by sense switch 3, where the torpedo eventually follows the rotation of the ship, but in a delayed manner at a greater turning radius. (In fact, this is just skipping the test for the turn controls currently being employed, but effects in an aesthetical pleasing curve and better control.)

Another peculiarity of Computer Space to be mentioned here, torpdoes are actually fired from the belly of the ship, its rotational center, and not from its bow — this is not a mistake.

### Computer Aesthetics

Speaking of computer aesthetics with respect to the platform, the screen isn't the only thing to be considered. We are using some of the program flags, either to control the various passes and states of the drawing routines, or to trace the state of the player controls, and, as we've to repeatedly check for the player's rocket active state, we use another flag for this. Now, the program flags are implemented in hardware on the PDP-1, and there are console lights for them on the operator's console. And not only is the state of the flag register clearly visible to everyone near the machine, so is our use of it, as well.

Therefor, in order to deliver a proper program, we should pay attention to this part of the runtime experience, too. In our program, the indicators for flags 5 and 6 (at the right) will flash rapidly as we make heavy use of them in the drawing routines. With flag 4 being practically taboo most of the time, we've to consider a pleasing use of flags 1 to 3. We'll use flag 1 (at the left) for the active state of the player's rocket (it will flash each frame of an ongoing game, being on most of the time), flag 2 for thrust, and, finally, flag 3 for the torpedo trigger. Thus, the lights will give a somewhat coordinated feedback of the game's state and the player action as read by the machine.

Speaking of the experience and platform specific considerations, another thing to consider is the tactical behavior of the game. The saucers are meant to act randomly, but, as it is, with our random number generator implemented in software and nothing but our own program on the machine to be used as an additional source of randomness, the game is rather showing a deterministic, patterned behavior. So we really should look for some randomness and variety that even a single additional run of the random generator could provide. Looking around, the only additional source there is, is the player and the interaction via the control input. Since this input is that fine in granularity that we've, in fact, to skip most of it in order to model the behavior of Computer Space, there's some randomness in it: It's near to impossible that a player should be able to press any of the buttons exactly as long in two separate runs of the game. So, for the sake of randomness, we run the random number generator each time, we register the signal for the rocket to thrust. Therefor, even when starting fresh from a previously cleared memory (core memory is nonvolatile!), there should always be different games.

### Pyrotechnics

Returning to the torpedo and its course across the screen, we'll probably more often miss than hit — otherwise it may be quite a boring game. But, in case we score a hit, we should be notified properly by an attractive experience, since shooting at and eventually hitting saucers is the core purpose of this game, isn't it? The visual feedback of Computer Space is rather harsh, just a short flashing of the screen in reverse video. But there's sound — and did we mention already? —, it's a sound of rather peculiar nature.

However, we can't do any of this on the PDP-1. Reverse video is impossible with a X/Y random access display and — while Peter Samson famously did impressive playback of Bach on the PDP-1 by hoooking up special circuitry to the flag register (see this video of Lyle Bickley explaining the details) —, there's no sound at all on a default PDP-1. Thus, we've to come up with a solution of our own. Maybe a visual animation of an explosion?

Visual explosions by rendering some particles at a random offset should be rather simple, even more on a painted display. But, alas, what's supposed to be simple on paper rarely is so in real-life implementation. So it is in the case of our particle explosion. Just using some scaled random numbers as an offset to the central screen location of our effect won't do, since it shows pretty much the confining box of the scaled random number.

Obviously, we are in nedd of some additional random scaling for any of the offsets of our distinctive particles. Playing around with it, I'm always close to the solution found in Spacewar! (maybe, I've just seen too much of it). But, this isn't only the first particle explosion in history for a computer game, it's one of the most beautiful, ever — and it is already custom tailored to the specific display and hardware. A bespoke animation, to say the least. Therefor, we may have a closer look at this one, in order to recklessly steal it for our own purpose.

As I understand it, the explosion routine was originally written by J. M. Graetz and was later refined by Steve Russell in version 3 and never to be changed thereafter. First, the explosion graphics suffered from the very disease already mentioned above, a more than obvious confining box of the effect,

"(…) appropriately-named Crock Explosion. Something dramatic obviously had to happen when a ship was destroyed, but we were dealing with a plain dot-matrix screen. The original control program produced a random-dot burst confined within a small square whose outlines were all too discernible."
(J. M. Graetz, The Origin of Spacewar, Creative Computing, August issue 1981.)

The cure applied by Steve Russell was essentially an additional random shift, assembled on the fly for each of the particles. Further, the final explosion came at two sizes, expanding at a wide scale at the beginning and ending in smaller spread, with a gradually diminishing number of particles.

For our purpose, we modify the explosion routine a bit (at least, to show that we have a decent understanding of the code, we steal). We're going with three phases, starting small, expanding wider in the middle, and ending small again. And we're going to tune the scaling parameters to fit our purpose. Moreover, our explosion will be rather short in duration, thus we really go for it and draw a lush octal 30 (decimal 24) particles each of the frames.

Here's the code:

/saucer explosion (similar to spacewar)
/ jump here from inside ufo

uxe,	law uxs			/set up instr at uxs to be xtc-ed in ux1
dap ux1
lac \ufs
add (30			/done with explosion, 030 frames setup delay
sma
jmp ux3
sal 1s
dac \uxc		/repeat for 030 particles
add (50			/first and last frames small explosion
spa
jmp ux0
sub (30
sma
idx ux1			/increment addr in ux1 for bigger explosion
ux0,	random			/start of loop, get random number
and (6s			/constrain to lowest 6 bits (as no of shifts)
ior (scl		/assemble a shift instruction for these
dac ux2			/insert it at ux2
random			/new random number
cli
scr 9s			/split it into lower parts of ac and io
sir 9s
ux1,	xct .			/apply scaling (size)
ux2,	opr			/apply shift for random spread
swap
dpy -i 100		/display a  dot
isp \uxc		/redo \uxc times
jmp ux0
ux3,	isp \ufs		/increment status counter and skip next on positive
jsp urs			/last iteration, respawn saucers
jmp ufi			/exit

uxs,	scl 1s			/scaling for small explosion
scl 2s			/ and bigger one, to be xct-ed at ux1

And this is what we get:

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

*****

Update/Note: While we were completele happy with this effect — and it was a beautiful one —, we eventually replaced it by something other that was not as much a Spacewar! trademark visual and more something of our own.

*****

By this we've already arrived at a somewhat playable game. Still missing are the shots by the saucers and scoring, — and probably some fine-tuning, too.

However, we're that proud of our progress that we leave the shabby outskirts of pre-prerelease revisions behind and enter, by skipping version 0.09, the gleaming prerelease territory of 0.x-code. (At least, if Microsoft is allowed to skip Windows 9, why shouldn't we dare to skip version 0.09?)

And, last but not least, we remove most of the throttling applied to the code, thus running nearly at full speed, resulting in more than twice the frame rate we had before. (Thus, we're not too displeased with having to readjust most of the frame counts, skips, and scalings.) In fact, the game might be even a bit too fast — and we've still some buffer left. Therefor, we shouldn't face any severe timing issues, even, when we eventually are going to implement the score display.

And here's our growing code, so far:

ironic computer space  0.1  nl 2016-10-23

mul=mus			/instruction code for multiplication
div=dis			/instruction code for division
ioh=iot i		/wait for completion pulse (CRT display)
szm=sza sma-szf		/skip on ac zero or minus
spq=szm i		/skip on ac plus and not zero

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

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
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,	1200		/ rocket angular acceleration
rvl,  7,	sar 8s		/ scaling rocket velocity
ras, 10,	law i 6		/ rocket acceleration sensitivity (skip)
rad, 11,	law i 4		/ rocket acceleration damping (averaging)
trv, 12,	sar 7s		/ scaling torpedo velocity
tlf, 13,	law i 250		/ torpedo life
rsd, 14,	40		/ rocket reset delay (frames)
ran, 15,	0		/ random number

/routine to flush sequence breaks, if they occur.

sbf,	tyi
lio 2
lac 0
lsm
jmp i 1

/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
dac sin
jmp .+4

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

si3,	ral 2s
mul (242763
dac sin
mul sin
dac cos
mul (756103
mul cos
mul cos
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
sma
jmp si3
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				/ skip every second frame
szs i 10			/ sense switch 1 for parallax effect
jmp bgd
lac \rdx
szs i 20			/ sense switch 2 for stronger effect
jmp . 3
sar 1s
sar 3s
cma
dac bgh
lac \rdy
szs i 20
jmp . 3
sar 1s
sar 3s
cma
dac bgv
jmp bgx				/ return
bgd,	lac \frc			/ advance x offset slowly
and (177
sza
jmp bgx
law i 400
dac bgh
jmp bgx				/ return
bgi,	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)
swap				/ swap contents of ac and io
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
bgx,	jmp .				/ 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 setup

ar,	lac (240000
dac \rth		/ rotational angle
lac (-200000
dac \rpx		/ pos x
cma
dac \rpy		/ pos y
law 300
dac \rdx
law i 500
dac \rdy
dzm \rac		/ acceleration skip counter
dzm \rxc		/ reset center offset
dzm \ryc
law 1
dac \rks		/status
dzm \trs		/torpedo status counter

/saucer setup

au,	lac (200000
dac \upy		/ pos x
lac (140000
dac \upx		/ pos y
dzm \udc		/ animation skip counter
law 1
dac \udd		/ animation direction
dzm \umo		/ direction code
law 600
dac \udy		/ delta y
dzm \udx		/ delta x
law i 100
dac \ufc		/ duration of movement
law 1
dac \uft		/ speed of center animation (1,3)
law 1
dac \ufs		/ state counter

/main loop

fr0,	load \ict, -2100	/ initial instruction budget (delay)
idx \frc		/ increment frame counter
clf 1				/flag ship active

jsp bg			/ display background
jsp rkt			/ rocket routine
jsp ufo
lac \trs
sza
jsp tr
jsp df			/ check collisions

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

/collision / hit detection

df,	dap dfx
lac \ufs		/check saucer state
spq			/skip on positive
jmp dfx
lac \rks		/check rocket state
spq
jmp dfx

lac \rpx		/get rotational center of rocket
sub \rxc
dac \px
lac \rpy
dac \py

law \px			/set up rocket for comparison
dap mx1
law \py
dap my1

lac (21000		/set up collision radii (x 34, y 24) in screen locs
dac \dxe
sar 1s
dac \de2
lac (14000
dac \dye

law \upx		/first saucer
dap mx2
law \upy
dap my2
jsp dmf			/compare them
sza			/hit?
jmp dxr			/yes

lac \upy		/second saucer
sub (400000		/vertical offset
dac \vpy
law \vpy
dap my2
jsp dmf			/compare them
sza			/hit?
jmp dxr			/yes

dft,	lac \trs		/torpedo active
sma
jmp dfx
lac (12200		/setup hit box (x 21, y 10)
dac \dxe
sar 1s
dac \de2
lac (5000
dac \dfy
law \tpx		/set up torp as first object
dap mx1
law \tpy
dap my1
jsp dmf			/compare
sza
jmp dxu			/hit, respawn

law \upy
dap my2
jsp dmf			/compare them
sza			/hit?
jmp dxu
jmp dfx

dxr,	law i 100		/set rocket state to collision
sub rsd			/additional delay before reset
dac \rks
dzm \rdx
dzm \rdy
dzm \trs
lac \rth
dac \th0
dzm \thc
law i 10
dac \thd
jsp urs			/respawn saucers
jmp dfx

dxu,	dzm \trs		/set up saucer explosion
lac i my2		/set up pos y
dac \upy
law i 30 30
dac \ufs

dfx,	jmp .			/return

/subroutine to compare object positions (from spacewar, mod n.l.)
dmf,	dap dmx
mx1,	lac .			/calc if collision
mx2,	sub .			/delta x
spa			/take abs val
cma
dac \t
sub \dxe		/ < epsilon x ?
sma
jmp dm0			/no
my1,	lac .
my2,	sub .
spa
cma
sub \dye		/ < epsilon y ?
sma
jmp dm0			/no
sub \de2		/ < epsilon 2 ?
sma
jmp dm0
law 1			/return 1 (in ac) for hit
jmp dmx
dm0,	cla			/return 0 for miss
dmx,	jmp .

/saucers

ufo,	dap ufx
lac \ufs
spq
jmp uxe
clf 6

lac \upx		/update position
dac \upx
dac \px
lac \upy
dac \upy
dac \py
jsp sd			/display first saucer
lac \py
sub (400000		/half-screen vertical offset
dac \py
jsp sd			/display second saucer

isp \ufc		/increment leg counter
jmp uf1			/continue
lac \umo		/new direction
spa
jmp uza			/we're in a 3-steps stop (\umo: -3..-1)
sar 3s			/ 010 set for single step stop
sza
jmp uz1
random
sma
jmp uf2			/set up new leg

dzm \udx		/stop
dzm \udy
lac ran			/what kind of stop will it be?
ral 3s
spa
jmp uz0			/three-steps stop
law 10			/single-step stop, keep center animation
dac \umo
jmp uzb			/get delay and continue
uz0,	law i 3			/first period of three-steps stop
dac \umo		/reuse \umo as step counter (negative)
law 3
dac \uft		/set animation to slow (3)
uzb,	jsp utd			/get duration
sar 1s
dac \ufc
jmp ufi			/continue

uza,	cma			/3-steps stop, dispatch on -\umo (-3..-1)
dap . 2
idx \umo
jmp .
jmp uz1
jmp uz2
uz3,	dzm \udd		/3 - stop animation
jmp . 2
uz2,	jsp ucd			/2 - still stopped, new animation direction
jsp utd
sar 1s
dac \ufc
jmp ufi
uz1,	stf 6			/1 - start over, but keep animation
random
jmp . 2

uf2,	clf 6			/set up for a new leg (flag 6: keep anim. dir.)
and (7
dac \umo		/new motion code (0..7)
sal 1s			/read corresponding dx/dy form table umt
add (umt		/ setup reference location for dy in \t1
dac \t1
dac \udy
idx \t1			/increment addr. in \t1
dac \udx
szf 6 i			/skip next on flag 6
jsp ucd			/set new direction for animation
jsp utd			/get delay
dac \ufc

uf1,	lac \frc		/increment center animation
and \uft
sza
jmp ufi
lac \udc		/udc = 0..5
sub \udd
dac \udc
sma
jmp . 4
law 5
dac \udc
jmp ufi
dzm \udc

ufi,	lac \ict		/update instruction count
dac \ict

ufx, jmp .

ucd,	dap ucx			/subroutine to set center animation
lio ran
ril 1s
law 1
spi
cma
dac \udd
lac ran			/animation speed, favor faster
rar 9s
and (3
sza
jmp . 3
law 1
jmp . 2
law 3
dac \uft
ucx,	jmp .

utd,	dap utx			/subroutine to get random delay (leg length)
random
sar 9s
sar 3s
sub (270
utx,	jmp .

urs,	dap urx			/subroutine to respawn the saucers
lac ran			/random offset x to player's rocket
rar 9s
sar 2s
dac \upx
sar 1s
dac \upx
random			/random pos y
dac \upy
jsp ucd			/set up center animation
dac \udd
law i 40		/set up delay before next leg
dac \ufc
law i 1			/stopped (end of 3-steps stop)
dac \umo
law 1			/set state to active
dac \ufs
urx,	jmp .

/saucer movement table (dy, dx)
umt,	 600		   0
-600		   0
0		-600
0		 600
400		 400
-400		-400
-400		 400
400		-400

/saucer explosion (similar to spacewar)
/ jump here from inside ufo

uxe,	law uxs			/set up instr at uxs to be xtc-ed in ux1
dap ux1
lac \ufs
add (30			/done with explosion, 030 frames setup delay
sma
jmp ux3
sal 1s
dac \uxc		/repeat for 030 particles
add (50			/first and last frames small explosion
spa
jmp ux0
sub (30
sma
idx ux1			/increment addr in ux1 for bigger explosion
ux0,	random			/start of loop, get random number
and (6s			/constrain to lowest 6 bits (as no of shifts)
ior (scl		/assemble a shift instruction for these
dac ux2			/insert it at ux2
random			/new random number
cli
scr 9s			/split it into lower parts of ac and io
sir 9s
ux1,	xct .			/apply scaling (size)
ux2,	opr			/apply shift for random spread
swap
dpy -i 100		/display a  dot
isp \uxc		/redo \uxc times
jmp ux0
ux3,	isp \ufs		/increment status counter
jmp ufi
jsp urs			/last iteration, respawn saucers
jmp ufi

uxs,	scl 1s			/scaling for small explosion
scl 2s			/ and bigger one, to be xct-ed at ux1

/player rocket routine

rkt,	dap rkx
clf 2
clf 3
lac \rks		/check for collision state
szm			/ < 0 ?
jmp rka			/no
isp \rks		/manage rocket hit state
jmp rke
idx \rks
lac \th0
dac \rth
jmp rcw
sma
jmp rkx

rt1,	lac \thc		/animate collision, spin clockwise progressively
dac \thn
lio raa			/angular acceleration
rt2,	sil 1s
isp \thn		/shift left \thd times
jmp rt2
isp \thd
jmp rt3
law i 20		/add another shift every 20 frames
dac \thd
lac \thc
sub (1
dac \thc
rt3,	swap
cma cli -opr		/make it a clockwise spin, clear io
dzm \cw			/and continue with cleared control input
jmp rkr

rka,	stf 1			/start active rocket
rcw,	jsp .			/read control word (ccw, cw, trust, fire)
dio \cw			/merge (or) spacewar player inputs
cla
rcr 4s
ior \cw
dac \cw

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

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

szf i 2			/flag 5 set? update dx / dy
jmp rk0
isp \rac		/sensitivity, frame skip
jmp rk1
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
dac \rdc
ry0,	swap
sar 1s
swap
isp \rdc
jmp ry0
dio \rdy
jmp rk1

rk0,	dzm \rac

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

scale \sn1, 1s, \rxc	/store half-offset for hit detection
scale \cn1, 1s, \ryc

scale \sn, 6s, \sn8	/scaled sine, 8 steps
sar 1s
dac \sn4		/4 steps
sar 1s
dac \sn2		/2 steps
sar 1s
dac \sn1		/1 step
dac \sm1
dac \sn3		/3 steps
dac \sm3
dac \sn5		/5 steps
dac \sn6		/6 steps
dac \sm6

scale \cs, 6s, \cn8	/scaled cosine, 8 steps
sar 1s
dac \cn4		/4 steps
sar 1s
dac \cn2		/2 steps
sar 1s
dac \cn1		/1 step
dac \cm1
dac \cn3		/3 steps
dac \cm3
dac \cn5		/5 steps
dac \cn6		/6 steps
dac \cm6

jsp rod			/display it

szf i 3			/fire torpedo?
jmp rkq			/no
szf i 1			/are we active?
jmp rkq			/no
lac \trs
jmp rkq			/yes
xct tlf			/set up torpedo
dac \trs
lac \rpx		/copy position
dac \tpx
lac \rpy
dac \tpy
lac \sn			/dx
cma
xct trv			/apply scaling for velocity
dac \tdx
lac \cs			/dy
xct trv
dac \tdy

rkq,	szf i 2			/advance random on thrust
jmp rki			/ to prevent patterned behavior of saucers
random

rki,	lac \ict		/update instruction count
dac \ict

rkx,	jmp .

/torpedo (rocket)

tr,	dap trx
isp \trs		/count up status counter (zero = inactive)
jmp . 2
trx,	jmp .
szf i 1
jmp tr1
lac \frc		/frame skip
and (3
sza
jmp tr1
szs 30			/sense switch 3 for high agility
jmp tr2
lac \cw			/check control input for rotation (ccw, cw)
and (600000
sza i			/steering?
jmp tr1			/no
tr2,	lac \cs			/update with steering
xct trv			/scale cos of rocket
add \tdy		/average 3 times with current dy
sar 1s
sar 1s
sar 1s
dac \tdy		/store updated dy
dac \tpy
swap
lac \sn			/same for sine, dx and pos x
cma
xct trv
sar 1s
sar 1s
sar 1s
dac \tdx
dac \tpx
jmp trx
tr1,	lac \tpy		/no steering, simple update
dac \tpy
swap
lac \tpx
dac \tpx
dpy -i
jmp trx

/control word get routines

rcb,	dap rcx
cli
iot 11
rcx,	jmp .

rtw,	dap rtx
lat
swap
rtx,	jmp .

/rocket display
/ step rearwards -  x add \sn1, y sub \cn1
/ 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 +4, x +6
sub \cn4
swap
disp

swap			/y +5, x +3
sub \cn5
swap
disp

swap			/y +6, x +1
sub \cn6
swap
disp

swap			/y +6, x -1
sub \cn6
sub \sm1
swap
sub \cm1
disp

swap			/y +8, x +6
sub \cn8
swap
disp

swap			/y 3, x -10
sub \cn3
sub \sm3
sub \sm6
sub \sm1
swap
sub \cm3
sub \cm6
sub \cm1
disp

swap			/y +7, x +6
sub \cn8
swap
sub \sn1
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 \cm3
cma
dac \cm3
lac \sm3
cma
dac \sm3
lac \cm6
cma
dac \cm6
lac \sm6
cma
dac \sm6
lio \py
jmp rop			/second pass for other side

rot,	szf i 2			/no, flag 2 set?
rox,	jmp .			/no, return
swap			/draw exhaust flame
sub \sm6		/x -11
sub \sm6
swap
sub \cm6
sub \cm6
dac \px			/store position
dio \py
and (4
sza			/is it zero? (state switch)
jmp ro2
ro1,	lac \py			/state 1, display at y-1
swap
lac \px
sub \sn1
disp
jmp rox
ro2,	lac \py			/state 2, display at y+5
sub \cn5
swap
lac \px
disp

/saucer display

define sdisp B			/display a dot (opt. brightness parameter)
dpy -4000 B		/ request completion pulse
term

sd,	dap sdx
clf 5
clf 6
law i 4
dac \sdc

sdl,	law 5000		/y +/- 10, x +/- 4
szf 6
cma
swap
law 2000
szf 5
cma
disp

law 3400		/y +/- 7, x +/- 7
szf 6
cma
swap
law 3400
szf 5
cma
disp

law 3400		/y +/- 7, x x+/- 15
szf 6
cma
swap
law 7400
szf 5
cma
disp

lac \sdc		/dispatch on \sdc (-4..-1) for passes (set flags)
cma
dap sdd
idx \sdc		/increment counter and jump
sdd,	jmp .
jmp sd1
jmp sd2
jmp sd3
stf 6			/2nd pass
jmp sdl
sd3,	stf 5			/3rd pass
jmp sdl
sd2,	clf 6			/4th pass
jmp sdl
sd1,	lio \py			/done, display outer dots
lac (12400		/y 0, x + 21 (right side)
sdisp 100
lac (-12400		/y 0, x - 21 (left side)
ioh
sdisp 100

swap

lac \udc		/draw first group of dots at the left
sal 1s			/ dispatch on \udc x 3 (udc = 0..5) for clipping
dap \sd4
swap
lio \py
sd4, 	jmp .
nop
nop
ioh
sdisp
ioh
sdisp
ioh
sdisp
ioh
sdisp

add (3000		/5, display 4 dots
ioh
sdisp
ioh
sdisp
ioh
sdisp
ioh
sdisp

sdisp
ioh
sdisp
ioh
sdisp
ioh
sdisp

add (3000		/draw group of remaining dots at the right
swap
lac \udc		/dispatch on \udc x 3 (udc = 0..5) for clipping
dap sd5
swap
lio \py
sd5,	jmp .
jmp sd0
jmp sd0
jmp sd9
jmp sd8
jmp sd7
jmp sd6
sd6,	ioh			/4 dots
sdisp
sd7,	ioh			/3 dots
sdisp
sd8,	ioh			/2 dots
sdisp
sd9,	ioh			/last dot
sdisp
sd0,	ioh			/fetch and clear last completion pulse

sdx,	jmp .			/return

constants
variables

start 4

(~ 1.24 K 18-bit words, 02354 locations incl. variables.)

That's all for this post.

Next:   Episode 8: The Saucers Strike Back

Previous:   Episode 6: Saucers!

Back to the index.

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