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

Episode 8: The Saucers Strike Back

We return to our little project to do the obvious. Because, with the rocketship, torpedoes and saucers in place, there's still one thing missing to make this a proper game. And we'll address this in the most painfull *umm* -staking manner: Not a single frame will be left untouched or without computer generated images (CGI). — Promise!

Ironic Computer Space Simulator: The Saucers Strike Back

ICSS Version 0.3:  The Return of  The Saucers  Awaken  Strike Back.

So We're Going to Add an Enemy Torpedo

The process involves watching various footage of Computer Space to study, what's actually going on. Frame by frame.

And here are some fun facts, we returned with from our video sessions:

Based on this knowledge, the thing should be straight forward to implement. We'll add the few instructions for managing the enemy torpedo (easier than what we did for the rocket ship's one) and start with some random targeting, just to see, if we did this right. — And, lo and behold, there's actually a torpedo. But for some reason unknown, there's no hit detection.

We stare at our code, find a little issue that shouldn't cause that much of an effect and retry. Still, the enemy torpedo passes elegantly through the outline of our rocket ship, and, … nothing, no spinning ship. We do some staring, again — BTW, we're beginning to understand, why "Men Staring at Code" wouldn't have been as much fun of a series title as "Halt and Catch Fire" —, but there's nothing to find. We insert an unconditional jump, just to test, if we're really passing this piece of code, … nothing, again. — ??? — Luckily, it's National Holiday in Austria and there's nothing else to do. Eventually, we realize, we've forgotton to update a jump target and the collision check is only visted, if we are shooting a torpedo ourselves. — Fine.

This is actually the first — and, for the time being, only — time, we're missing a debugger. Otherwise, we have to be grateful for our newfangled coding environment. It's that easy to edit the source on screen, insert or delete some instructions or entire blocks of code, a keypress later it has passed the assembler and we're reloading the virtual tape into our emulator. No re-editing of the source code. No clunky console typewriter. No address fixing with the online debugger after each newly inserted instruction. No going back to the source code to update it after any interim online coding sessions. No repunching. No feeding newly punched paper tape in the assembler to get a new object tape. — We've to stand in awe in front of those, who did a complex game like Spacewwar! in just 200 hours.
(Personally, I've always read this figure with a grain of salt. It's much like being asked about the time actually spent on a pet project and answering, Umm, about a month or so (mumble).)

Enemy A.I.

We've already implemented random-based movement for the saucers — and it doesn't seem to be much more about it in Computer Space. (Actually, it's probably more patterns of movement randomly selected, similar to the "ghosts" in Ms Pac-Man.) We're going to stick with this and see, how far it will get us.

As mentioned previously, the A.I. for the saucers in Computer Space is quite deadly and it's not an easy game. (In fact, the game is supposably — if this is a word — hooking its player by annoying her/him.) As also previously mentioned, I'm supposing, much of this deadly effect is attributed to the confined space, the game is taking place in, and that a rather dumb A.I. may perform even better under these circumstances than a more sophisticated one. — But how dumb is it?

Let's start with some cooling for nice intervals with some random noise added to it. If we've counted up on the cooling, the saucers will shoot. We select the direction nearest to the supposed position of the target (the player's rocket + some look-ahead in the direction of its movement delta + some random noise) and fire.

After some tinkering, this actually works — but it's not good enough. Not that dumb.

(In case you're keeping track of version numbers and are wondering, what may have happened to version 0.2, this was it.)

The next best thing is the saucer looking out for a target to appear in any of its shooting directions. We do this, by setting up a target position in front of the moving rocket, compare this to an epsilon (the size of the ship + some random noise), and, if the rocket happens to appear in this algorithmic crosshair, we go for it.

This works actually nice and is rather close to the original. It is even as mercilessly pounding a sitting ship as long as it is in range as the original game. Maybe we improve this later a bit by adding the noise rather to the target position than to the epsilon, but this is esentially what we're going with.

(Also, this is still something that could be done with a handful of logic chips and a good plan, just like in Computer Space.)

Update: This is how we actually do it:

But there's still more to consider, namely motion and pace.

Watching the game, it's much like as if the saucers passed along a diagonal path faster than they would in horizontal or vertical directions. Even, if not so, the game's principal orientation is essentially horizontal, due to the 4:3 aspect ratio of its TV screen. So, a rather shallow, somewhat compressed movement vector for the diagonal motions may be favorable for our game on the round scope of the Type 30 CRT. We chose to implement this by defining "diagonals" in the ratio of (octal) 600:400, compared to a movement vector of a length of (octal) 600 in any other, principal direction. This looks pretty neat and we've still an option (sense switch 5) for true, geometrically correct diagonal motions for the saucers.

We add some code to compensate for these distored diagonals, which are to be inherited by the torpedoes, as well. But the result is not that great: The speed varies with the direction of the torpedo, which is pretty irritating, to say the least. So, we'll have diagonals proper for the torpedo and go with a ratio close to the square root of 2:

Ironic Computer Space Simulator: Saucer motion and torpedoes

Ironic Computer Space Simulator: Saucer movements and torpedoes.

Moreover, a pleasing velocity for the torpedo doesn't work out just of the motion vectors of the saucers. Either it's too fast or too slow. Thus, torpedoes get a movement table of their own, similar to what we've implemented already for the saucers.

The Fata Morgana Awakens

As mentioned above, in Computer Space, there is really just a single saucer displayed twice. And only one of the images, the "real" ship, will shoot at you. But it isn't very likely that a casual player may actually discover this, as large areas of the game are clipped and the saucers are frequently crossing the screen, disappearing into the hidden nothingness of pure mathematical positions, shooting from this hidden ambush with shots suddenly appearing at the borders of the screen, and so on. It's hard to keep track, which saucer is which.

In our implementation, there are no hidden areas. So, will this still hold up?
As we're not too sure, we implement an option (sense switch 6) to select the main saucer at random for each frame. If deployed, even the mirror image gets its fair chance of going after the rocket ship. In order to not to give away this little secret of Computer Space too easily, we give this option a mystery name, "Saucer Piloting".

That's pretty much it. — And we've a playable game!

Ironic Computer Space Simulator: Screenshot of space combat (v. 0.3)

Ironic Computer Space Simulator: Space combat (as of v. 0.3).

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

Phew!

Things still missing / to come:

And here's our growing code, so far:

ironic computer space  0.3  nl 2016-10-26

	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

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,	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)
etl, 15,	law i 240	/ enemy torpedo life
etd, 16,	law i 30	/ enemy torpedo cooling (frames)
ela, 17, 	sal 5s		/ scaling enemey torp look-ahead
ete, 20,	24000		/ epsilon for enemy aiming
etn, 21,	sar 6s		/ scaling noise for enemy torpedo aim
ran, 22,	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				/ skip every second frame
	jmp bgi				/ jump to star display
	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
	add \rdx
	sar 3s
	cma
	add bgh
	dac bgh
	lac \rdy
	szs i 20
	jmp . 3
	sar 1s
	add \rdy
	sar 3s
	cma
	add bgv
	dac bgv
	jmp bgx				/ return
bgd,	lac \frc			/ advance x offset slowly
	and (177
	sza
	jmp bgx
	law i 400
	add bgh
	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)
	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
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
	dzm \cwo		/old control word

/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
	dzm \ets		/ torpedo state
	jsp etr			/ get torpedo cooling (\etc)

/main loop

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

	jsp bg			/ display background
	jsp rkt			/ rocket routine
	jsp ufo
	lac \trs
	sza
	jsp trp			/ torpedo routine
	lac \ets
	sza
	jsp etm			/ enemy torpedo routine
	jsp df			/ check collisions

	lac \cw
	dac \cwo

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


/collision / hit detection

df,	clf 5
	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
	add \ryc
	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 dfe
	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

dfe,	lac \ets		/enemy torpedo active?
	sza i
	jmp dfx
	lac (11000
	dac \dxe
	dac \dye
	sar 1s
	dac \de2
	law \px
	dap mx1
	law \py
	dap my1
	law \epx
	dap mx2
	law \epy
	dap my2
	jsp dmf
	sza i
	jmp dfx
	jsp etr			/reset enemy torpedo
	stf 5

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
	szf i 5
	jsp urs			/respawn saucers
	jmp dfx

dxu,	dzm \trs		/set up saucer explosion, reset torpedoes
	dzm \ets
	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
	add \t
	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
	add \udx
	dac \upx
	dac \px
	lac \upy
	add \udy
	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 eta			/continue for torpedo setup

uza,	cma			/3-steps stop, dispatch on -\umo (-3..-1)
	add (. 3
	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 eta
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 movement table
	szs i 50		/sense switch 5 to select table (ut1 or ut2)
	jmp . 3
	add (ut1		/setup reference location for dy in \t1
	jmp . 2
	add (ut2		/select other table
	dac \t1
	lac i \t1		/load dy indirectly from addr. in \t1
	dac \udy
	idx \t1			/increment addr. in \t1
	lac i \t1		/load dx indirectly from 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 eta
	lac \udc		/udc = 0..5
	sub \udd
	dac \udc
	sma
	jmp . 4
	law 5
	dac \udc
	jmp eta
	sad (6
	dzm \udc
	jmp eta

ufi,	lac \ict		/update instruction count
	add (1200
	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
	add \upx
	add (400000
	add \rpx
	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
	jsp etr			/reset torpedo, get cooling
	law 1			/set state to active
	dac \ufs
urx,	jmp .

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

ut2,	 600		   0
	-600		   0
	   0		 600
	   0		-600
	 400		 600
	-400		 600
	 400		-600
	-400		-600


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

uxe,	law uxs			/set up instr at uxs to be xct-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
	add \upy		/add center pos x/y
	swap
	add \upx
	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


/saucer torpedo ai (setup)
/ here from inside ufo

eta,	lac \ets		/torpedo already active?
	spa
	jmp ufi
	szf i 1			/rocket active?
	jmp ufi
	lac \ufs		/saucers active?
	spq
	jmp ufi
	isp \etc		/count up cooling
	jmp ufi
	dzm \etc
	
	lac \upx		/position of tubes
	add (10400
	dac \epx
	lac \upy
	sub (3400
	dac \epy

	szs i 60		/sense switch 6 to select saucers at random
	jmp et1			/ original shoots always from same saucer
	random
	sma			/which saucer
	jmp et1
	lac \epy
	add (400000
	dac \epy
et1,
	random
	sar 3s
	xct etn
	add ete
	spa
	cma
	dac \eps

	lac \rdx
	xct ela			/look-ahead
	add \rpx
	sub \epx
	dac \dx			/delta x
	spa
	cma
	dac \adx		/abs delta x
	
	lac \rdy		/same for y
	xct ela
	add \rpy
	sub \epy
	dac \dy
	spa
	cma
	dac \ady
	sub \eps
	spa
	jmp eah
	lac \adx
	sub \eps
	spa
	jmp eav
	
	lac \adx
	sub \ady
	spa
	cma
	sub eps
	sma
	jmp ufi
ead,	
	law 4
	lio \dy
	spi
	law 5
	lio \dx
	spi
	add (2
	jmp et3
eah,
	law 2
	lio \dx
	spi
	law 3
	jmp et3
eav,
	cla
	lio \dy
	spi
	law 1
/	jmp et3
et3,
	sal 1s			/read corresponding dx/dy form movement table
	add (ut3		/setup reference location for dy in \t1
	dac \t1
	lac i \t1		/load dy indirectly from addr. in \t1
	dac \edy
	idx \t1			/increment addr. in \t1
	lac i \t1		/load dx indirectly from addr. in \t1
	dac \edx
	xct etl
	dac \ets

eti,	lac \ict		/update instruction count
	add (40
	dac \ict
	jmp ufi			/return to ufo

/saucer torpedo movement table (dy, dx)
ut3,	 2400		    0
	-2400		    0
	    0		 2400
	    0		-2400
	 1600		 1600
	-1600		 1600
	 1600		-1600
	-1600		-1600


/saucer torpedo movement routine

etm,	dap etx
	isp \ets		/count up life
	jmp . 2
	jmp etr 1		/expired
	lac \epy
	add \edy
	dac \epy
	swap
	lac \epx
	add \edx
	dac \epx
	dpy -i 100
etx,	jmp .			/return
etr,	dap etx			/reset
	dzm \ets
	xct etd
	dac \etc
	jmp etx


/player rocket routine

rkt,	dap rkx
	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
rke,	add rsd
	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
	add \rth		/update rotational angle
	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
	lac \rth		/load angle
	spi			/sign cleared in io?
	add raa			/no, add angular acceleration
	ril 1s			/next bit
	spi
	sub raa
rkr,	sma			/normalize 0 >= angle >= 2Pi (0311040)
	sub (311040
	spa
	add (311040
	dac \rth		/update angle

	ril 1s			/parse thrust input
	spi
	stf 2			/set flag 2 for thrust
	ril 1s
	spi i			/check next bit for fire
	jmp . 7
	szs i 30		/sense switch 3 for single shot
	jmp . 4
	lio \cwo
	ril 3s
	spi i			/block if already set in old control word
	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
	lac \frc		/load frame counter
	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
	xct rad
	dac \rdc
ry0,	swap
	add \rdy
	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
	add \sn1
	dac \sn1
	scale \cs, 4s, \cn1	/offset y = (cos >> 4) - (cos >> 8)
	sar 2s
	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 \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
	add \sn2
	dac \sn3		/3 steps
	dac \sm3
	add \sn2
	dac \sn5		/5 steps
	add \sn1
	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
	add \cn2
	dac \cn3		/3 steps
	dac \cm3
	add \cn2
	dac \cn5		/5 steps
	add \cn1
	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
	sza			/torpedo already active?
	jmp rkq			/yes
	lac \ufs
	spq			/saucers active?
	jmp rkq			/no
	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
	add (600
	dac \ict

rkx,	jmp .


/torpedo (rocket)

trp,	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 40			/sense switch 4 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
	add \tdy
	sar 1s
	add \tdy
	sar 1s
	dac \tdy		/store updated dy
	add \tpy		/update pos y
	dac \tpy
	swap
	lac \sn			/same for sine, dx and pos x
	cma
	xct trv
	add \tdx
	sar 1s
	add \tdx
	sar 1s
	add \tdx
	sar 1s
	dac \tdx
	add \tpx
	dac \tpx
	dpy -i			/display adot and jump to return
	jmp trx
tr1,	lac \tpy		/no steering, simple update
	add \tdy
	dac \tpy
	swap
	lac \tpx
	add \tdx
	dac \tpx
	dpy -i
	jmp trx

/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 +4, x +6
	sub \cn4
	add \sm6
	swap
	add \sn4
	add \cm6
	disp

	swap			/y +5, x +3
	sub \cn5
	add \sm3
	swap
	add \sn5
	add \cm3
	disp

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

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

	swap			/y +8, x +6
	sub \cn8
	add \sm6
	swap
	add \sn8
	add \cm6
	disp

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

	swap			/y +7, x +6
	sub \cn8
	add \cn1
	add \sm6
	swap
	add \sn8
	sub \sn1
	add \cm6
	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
	lac \px			/load first pos
	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
	add \sm1
	swap
	sub \cm6
	sub \cm6
	add \cm1
	dac \px			/store position
	dio \py
	lac \frc		/load frame counter
	and (4
	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+5
	sub \cn5
	swap
	lac \px
	add \sn5
	disp
	jmp rox			/jump to return


/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
	add \py
	swap
	law 2000
	szf 5
	cma
	add \px
	disp

	law 3400		/y +/- 7, x +/- 7
	szf 6
	cma
	add \py
	swap
	law 3400
	szf 5
	cma
	add \px
	disp

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

	lac \sdc		/dispatch on \sdc (-4..-1) for passes (set flags)
	cma
	add (sdd
	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)
	add \px
	sdisp 100
	lac (-12400		/y 0, x - 21 (left side)
	add \px
	ioh
	sdisp 100

	add (1000
	swap

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

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

	add (3000		/display 4 dots
	sdisp
	add (1000
	ioh
	sdisp
	add (1000
	ioh
	sdisp
	add (1000
	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
	add (sd5 1
	dap sd5
	swap
	lio \py
sd5,	jmp .
	jmp sd0
	jmp sd0
	jmp sd9
	jmp sd8
	jmp sd7
	jmp sd6
sd6,	ioh			/4 dots
	sdisp
	add (1000
sd7,	ioh			/3 dots
	sdisp
	add (1000
sd8,	ioh			/2 dots
	sdisp
	add (1000
sd9,	ioh			/last dot
	sdisp
sd0,	ioh			/fetch and clear last completion pulse

sdx,	jmp .			/return


	constants
	variables

	start 4

(~ 1.45 K 18-bit words, 02716 locations incl. variables.)

 

That's all for this post.

 

Next:   Episode 9: Scores, Just in Time

Previous:   Episode 7: Skeet in Space

Back to the index.

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