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

Episode 6: Saucers!

It's the 20th — and thus our personal halftime (since we started Oct, 10). To celebrate the event, let's have some saucers!

Ironic Computer Space Simulator: Flying Saucer

Unidentified flying objects detected on the scope of our virtual PDP-1.

Some work has been done since our last episode. Actually, by now the code is now growing to fair complexity and we've to constrain ourselves to just illustrating the outlines and approches we chose. Let's start with …

The Rocket Ship, Again

As we're planning to finally put some saucers on the screen, we notice that we ended up scaling our rocket sprite to a grid based on one-and-a-half locations of the PDP-1's Type 30 scope as the unit. Since the signal to the scope is a digital one, our nifty fixed point coordinates will be truncated to integers, resulting in possible distortions of the outline with regard to the fractual part of the coordinates. While this may be OK for the rocket ship and may even mimic the skewy outlines of the original, this won't work well for the saucers and the rather steady appearance of their outlines.

So back to the drawing table and readjust the outline, once more, to match the native resolution of the Type 30 CRT Display. — Maybe we're just finding out why Spacewar! featured configurable outlines early on in the process by the means of experimental software archeology.

And this is, what our final outline looks like:

Ironic Computer Space Simulator: Final outline of rocket ship

The outline as provided in the Computer Space schematics and our final transposition to Type 30 space.
(Even, if not so obvious in this image, our dots fairly match the average of the outline at the various angles.)

To Parallax Or Not To Parallax

When we were setting up our starfield routine, we were also thinking of some kind of parallax effect relative to the movements of the player's ship. This may actually be the very time to address this. The implementation is easy: Just update the base position of the background (as in "bgh" and "bgv") by a fraction of the rocket ship's delta x and delta y, but in opposite direction. We choose to do this in the background routine, to be exact, in the skip-frames when we're not displaying anything, thus sharing runtime fairly between tasks.

But the result isn't that pleasing, to say the least: Thanks to the afterglow of the Type 30 Display the screen appears like hatched by the strokes drawn by the moving stars. Even some inclinations to dizziness may be involved. So we decide to scale the parallax down somewhat and leave an option for a bit more prominent effect for those who want to try. Still, I'm actually more pleased with the steady display. So, add another option to switch off the effect entirely. In case parallax is off, we'll add a bit of nearly indiscernible movement to the left, just to prevent burn-in.

We control our options by the sense switches on the control panel of the PDP-1 and read the respective state by the istruction "szf n0" (skip on zero sense switch n, n = 1 .. 6). In our emulation, we add two checkboxes for this at the top right of the virtual scope. And this is how the background behavior is controlled:

Having dealt with our Hamletesque ponderings that way, let's leave this Denmark of celestial aesthetics behind and head for outer space, where there are …

Saucers, Flying Saucers!

Let's have, finally, some saucers. Because, strictly speaking, this is why we visted the code at all.

Of course we need a plan for this, and we begin with a sketch of the outline transposed to Type 30 coordinates space:

Ironic Computer Space Simulator: Outline of the saucer

Saucer as in Computer Space (reverse video), as in the schematics, and our transposition to screen coordinates.
Mind the half-pixel artifacts at the left side of a moving saucer, common to most Computer Space machines.

On most machines, there are peculiar half-sized pixels to be seen at the very left of a moving saucer. Presumably, these are artifacts produced by aging components (capacitors?) of analog timing circuits. Most machines exhibit them, some do not, and there's footage of one-and-the-same machine showing these half-pixels in some stages of a restoration process, while not in others. Also, the FPGA implementation doesn't show them, indicating that they are not to be supposed to be there according to the digital layout. Thus, we're not going to implement this effect.

(The basic idea about these half-pixels is that they are produced by timing issues that occur only when the circuits for "rotating" the center part of the saucer are active, causing the gates controlling the pixels that make the outer "rim" of the saucer to be opened early, and that they are not intended to be there. Had these been meant as some kind of thrust animation, we might expect them to show up on the other side, as well.)

Still, there are other thrills to the saucer display, namely an animated, dashed center line:

Ironic Computer Space Simulator: Saucer animation

Saucer, animation steps of center pixels (graphics move towards the right).

The animation consists (in our case) of 6 steps, moves both to the left and right, involves some clipping, and is of either of two speeds. Apparently, direction and speed of the animation is chosen at random for a certain leg, from time to time it stops completely for an idle saucer, and, if a saucers starts to speed in any direction, the animation is — so it seems to me — never changed at all. (Thus, an idle saucer first starts to animate before it zooms off in any direction, a distant pre-echo of the eyes of the "ghosts" in Pac-Man.)

Ironic Computer Space Simulator: Sprites

Hi-res rendering of our sprites.
(Exported screenshot from emulation in hi-res mode, rendering internally at 1024 × 1024, i.e., 1:1.)
BTW: You may export a screenshot by hitting ALT + P, and a transparent one by hitting ALT + SHIFT + P.

Putting the Saucers on the Screen

The basic outline of a saucer is as simple as 3 dots mirrored allong two axis, drawn in any of the 4 quadrants around its center. Then, there are two dots to the left and right to mark the sides of the saucer, and finally, there's the animated center.

We decide to handle the mirroring by flags to control the sign of the center-offsets for each of the quadrants. But, how are we to decide, which flags are to be set on each of the four passes in total? Also, there are lots of clippings and offsets involved with the center animation, how may we draw these?

Assembler Trick of the Day: Dispatch Tables

A dispatch table is the machine code equivalent to a switch statement. It's as simple as fixing up a jump vector pointing to any of the locations of the dispatch table and executing the jump. The dispatch table either consists of a list of yet another jumps, or evenly spaced blocks of instrcutions. We may chose to exit at the end of a given block by a final jump, or we may chose to fall through to the next one.

1) Basic Dispatch

	lac \dpc		/ load a value, here \dpc = (0, 1, 2)
	add (dpv 1		/ add address of label "dpv" + 1
	dap dpv			/ fix up jump target at "dpv"
dpv,	jmp .
	jmp dp0			/ jump to code for 0
	jmp dp1			/ jump to code for 1
	jmp dp2			/ jump to code for 2


2) Dispatching for Offset-Based Execution
Here we (partly) execute an unrolled loop, starting at an offset in "\dpc":

	lac \dpc		/ load an offset (again of 0, 1, 2)
	sal 2s			/ multiply by 4, offset by 4 instr.
	add (dpv 1		/ add address of label + 1
	dap dpv			/ fix up jump target
dpv,	jmp .			/ go there
	idx \tbp		/ case 0, some code, 4 instructions long
	lac i \tbp		/ (copy some using pointers)
	dac i \dxp
	idx \dxp
	idx \tbp		/ case 1, next iteration, offset 4
	...
	...
	...
	idx \tbp		/ case 2, next iteration, offset 8
	...
	...
	...

We make havy use of this trick in order to implement the saucer graphics. For the center animation, we use a state counter for offsets and clippings. With the saucers eventually hovering on our virtual screen, it may be nice to have them move around, just a little bit. There are just 8 directions and idle (i.e., no movement) for the saucers, and we decide to put the values for delta x and delta y into a lookup table, for each of the 8 basic directions respectively. Thus we may load them by a random offset that is conveniently just fitting into a power of 2. For idle state (no movement), we decide to use the sign-bit of the random number used to generate the next direction. (Thus, the saucers are more likely to stop than to go in any other of the 8 directions.)

Since the two saucers are always moving in parallel at a constant vertical offset, we just get along with a single set of variables to control them. Essentially, there's a single saucer displayed twice. — Much too easy! — As a compensation, choosing movement directions is a bit tricky again, especially for the zero states (idle saucers). There are actually 4 possible states involving up to 3 steps, providing us ample opportunity to exercise our nifty dispatch trick. Timing and animation are controlled by yet another frame counter.

(In reallity, it took a fair time to figure this out. As for the pupose of this blog, we'll keep it that simple. Moreover, while inspecting footage of Comuter Space over and over, I started to hate those nasty people who are always shooting at the saucers when the animation starts to reaveal a crucial step. As if this would be the purpose of the game!)

Miraculously, all this still works out without any debugging facilities. — For sure, this is a world full of wonders! :-)

And in the end, this is what we get (TIWWG):

Ironic Computer Space Simulator: Saucers in action

Ironic Computer Space Simulator: Saucers on the virtual scope (www.masswerk.at/icss/, v.00.7).

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

And here's our growing code, so far (we're currently using about 0.9 K of memory, 01605 words including variables):

ironic computer space  0.07  nl 2016-10-20

	mul=mus
	div=dis
	ioh=iot i

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				/ 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 (37
	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 reset

ar,	lac (240130
	dac \rth		/ rotational angle
	lac (-200000
	dac \rpx		/ pos x
	cma
	dac \rpy		/ pos y
	law 600
	dac \rdx
	law i 1200
	dac \rdy
	dzm \rac		/ acceleration skip counter

/saucer reset

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

/main loop

fr0,	load \ict, -4500	/ initial instruction budget (delay)
	idx \frc		/ increment frame counter

	jsp bg			/ display background
	jsp rkt			/ rocket routine
	jsp ufo

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


/saucers

ufo,	dap ufx
	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 uz0			/we're in a 3-steps stop (\umo: -3..-1)
	random
	sma
	jmp uf2			/set up new leg

	dzm \udx		/stop
	dzm \udy
	lio ran			/what kind of stop will it be?
	ril 3s
	spi
	jmp . 4			/three-steps stop
	stf 6			/single-step stop, keep center animation
	lac \umo
	jmp uf2			/set up leg and continue
	law i 3			/first period of three-steps stop
	dac \umo		/reuse \umo as step counter (negative)
	law 3
	dac \ufc		/set animation to slow (3)
	jsp utd			/get duration
	sar 1s
	dac \ufc
	jmp ufi			/continue

uz0,	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 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
	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 ufi
	lac \udc		/udc = 0..5
	sub \udd
	dac \udc
	sma
	jmp . 4
	law 5
	dac \udc
	jmp ufi
	sad (6
	dzm \udc


ufi,	lac \ict		/update instruction count
	add (1000
	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 6s
	sub (200
utx,	jmp .

/saucer movement table (dy, dx)
umt,	1400		0
	-1400		0
	0		-1400
	0		1400
	1000		1000
	-1000		-1000
	-1000		1000
	1000		-1000


/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 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 \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

	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 +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 5			/no, flag 5 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 (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+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

 

That's all for this post.

— stay tuned! —

 

Next:   Episode 7: Skeet in Space!

Previous:   Episode 5: Yet Another Progress Update

Back to the index.

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