	.TITLE	SPACE INVADERS

; 24-MAR-80	J. C. VENN
; 04-JUL-80	VT52 BY MIKE BROOK
; 18-JAN-81	VT100 BY KEN BELL
; 09-MAR-82	Steve Brecher--
;     |		user-selected level of expertise, with instruction for beginner;
;     |		avoid infinite loop in DBOMB routine by returning immediately
;     |		   if no invaders left;
;     |		new random number generator;
;     |		always randomize bombs if no kills yet;
;     |		keep count of invaders per column, reject empty column
;     |		   immediately in DBOMB routine;
;     V		add fast spaceship for 200 extra points.
; 10-MAR-82	correct "Ctrl-Q" to ""Q"" in intructions;
;     |		don't allow fire until previous missile moves up from base;
;     |		change macro and formal parameter names for compatibility with
;     |		   HT-11; expand "CALL" and "RETURN" in case those not
;     |		   compatible;
;     V		some optimization of UP and its subroutines.

; If not using VT-52-type terminal, uncomment one of following 3 definitions:
;
;;;SCOPE = 1	; CONFIGURE FOR H1510
;;;SCOPE = 2	; CONFIGURE FOR TVI912/920
;;;SCOPE = 4	; CONFIGURE FOR VT100

.IIF NDF SCOPE	SCOPE = 3	; DEFAULT TO VT52

	.MCALL	.REGDEF,.PRINT,.TTINR,.TTYIN,.GTIM,.EXIT
	.MCALL	.LOOKUP,.ENTER,.READW,.WRITW,.CLOSE

	.REGDEF

.MACRO	.CP	.X,.Y
	MOVB	.X,-(SP)
	MOVB	.Y,1(SP)
	JSR	PC,CPOS
.ENDM

.MACRO	.SHOW	.ADDR
	JSR	R5,PRINT
	.WORD	.ADDR
.ENDM

.MACRO	.PUTCH	.CHAR
	MOVB	.CHAR,(R4)+
.ENDM

.MACRO	.PUSH	.ARG
	MOV	.ARG,-(SP)
.ENDM
 
.MACRO	.POP	.ARG
	MOV	(SP)+,.ARG
.ENDM
.PAGE
NBASES=3	; NUMBER OF BASES TO START WITH
CTIME=1		; number of hardware clock ticks per program clock tick
NTIMES=4.	; NUMBER OF CTIMES BETWEEN INVADER MOVES (INITIALLY)
BSLEEP=5.	; NUMBER OF CTIMES BEFORE NEW BASE SUPPLIED
WTIME=120.	; NUMBER OF CTIMES NEW BASE ALLOWED TO WAIT BEFORE MOVING
NSHIP=100.	; NUMBER OF CTIMES BETWEEN SPACE SHIPS
SSLEEP=60.	; NUMBER OF CTIMES BEFORE SPACE SHIP DEBRIS CLEARED AWAY
NROWS=6.	; NUMBER OF ROWS OF INVADERS
NCOLS=8.	; NUMBER OF COLUMNS OF INVADERS
TOP=10.		; INITIAL TOP ROW OF INVADERS
.PAGE
START:	MOV	#2,R2		;init #missiles-1 for beginner
	CLR	R3		;and #bombs-1
	.PRINT	#EXPMSG		;prompt for level of expertise
	.TTYIN
	MOV	R0,R1
	.TTYIN			;soak up CR/LF
	.TTYIN
	CMPB	R1,#'B		;beginner?
	BEQ	HELP		;if so, go print instructions
	DEC	R2		;assume intermediate
	INC	R3
	CMPB	R1,#'I		;intermediate?
	BEQ	STRTED
	DEC	R2		;assume expert
	INC	R3
	CMPB	R1,#'E		;expert?
	BEQ	STRTED
	BR	START		;ask the dummy again
HELP:	.PRINT	#HLPMSG
	.TTYIN
STRTED:	MOV	R2,NMIM1
	MOV	R3,NBOM1
	BIS	#10100,@#44	; 04-JUL-80
	.GTIM	#AREA,#TBLK
	MOV	TBLK+2,SEED
	CLR	PRVMAX
	.LOOKUP	#AREA,#1,#DEVBLK
	BCS	RESTRT
	.READW	#AREA,#1,#PRVMAX,#1,#0
	.CLOSE	#1

RESTRT:	JSR	PC,INIT0	; INITIALIZE FOR START OF NEW GAME
NEXT:	JSR	PC,INIT		; INITIALIZE EVERYTHING ELSE

LOOP:	.TTINR
	BCS	COMMON
	TST	DEADB		; BASE DEAD ?
	BNE	COMMON		; YES
	MOVB	R0,KTABE
	MOV	#KTAB,R1
10$:	CMPB	R0,(R1)+
	BNE	10$
	SUB	#KTAB+1,R1
	ASL	R1
	JMP	@KADDR(R1)

KTAB:
	.BYTE	',		; BASE LEFT
	.BYTE	'.		; BASE RIGHT
	.BYTE	40		; STOP BASE AND FIRE
	.BYTE	'Q		; STOP/START BLEEP
	.BYTE	33		; EXIT
KTABE:
	.BYTE	0

KADDR:
	.WORD	LEFT,RIGHT,SHOOT,QUIET,EXIT,STOP
LEFT:
	MOV	#BASEL,BSUB	;BASE LEFT
	BR	COMMON
RIGHT:
	MOV	#BASER,BSUB	; BASE RIGHT
	BR	COMMON
SHOOT:
	CLR	BSUB		; STOP BASE MOVING
	JSR	PC,FIRE
	BR	COMMON
QUIET:
	SWAB	BELL		; TOGGLE AUDIO EFFECT
	BR	COMMON
STOP:
	CLR	BSUB		; ANY OTHER KEY STOPS BASE

COMMON:
	.GTIM	#AREA,#TBLK
	MOV	TBLK+2,R0
	SUB	CLOCK,R0
	BMI	50$
	MOV	TBLK+2,CLOCK
	ADD	#CTIME,CLOCK
TOCKS==.-4

	COM	TWO		; HALF TIMEBASE COUNTER
	BNE	10$
	COM	FOUR		; QUARTER
10$:	TST	DEADB
	BEQ	20$
	DEC	DEADB
	BNE	40$

	TST	NLIVES		; ALL BASES USED ?
	BEQ	EXIT		; YES
	JSR	PC,CLRBAS	; REMOVE OLD BASE
	MOV	#2,BASEX	; MOVE NEW BASE TO INITIAL POSITION
	JSR	PC,DSPBAS
	MOV	#WTIME,WLIMIT

20$:	TST	BSUB
	BNE	30$
	CMP	BASEX,#8.
	BHIS	40$
	DEC	WLIMIT
	BGT	40$
	MOV	#BASER,BSUB	; FORCE BASE OUT INTO THE OPEN
30$:	JSR	PC,@BSUB	; MOVE BASE
40$:	JSR	PC,UP		; MOVE MISSILES UP
	JSR	PC,DOWN		; MOVE BOMBS DOWN
	JSR	PC,MOVE		; MOVE INVADERS ABOUT
	JSR	PC,SHIP		; MOVE SPACESHIP
	CMP	R4,#BUFF
	BEQ	80$
	BR	60$

50$:	CMP	R4,#BUFF
	BEQ	LOOP
60$:	JSR	PC,DISP		; DISPLAY
70$:	BR	LOOP

80$:	TST	NUMINV		; ANY INVADERS LEFT ?
	BNE	LOOP		; YES
	TST	SHIPX		; ANY SPACESHIP ?
	BNE	70$		; YES
	TST	NLIVES		; STILL ALIVE ?
	BLE	EXIT
	INC	NLIVES
	JMP	NEXT		; YES - GENERATE SOME MORE INVADERS
.PAGE
EXIT:	CMP	POINTS,PRVMAX
	BLOS	20$
	MOV	POINTS,PRVMAX
	.LOOKUP	#AREA,#1,#DEVBLK
	BCC	10$
	.ENTER	#AREA,#1,#DEVBLK,#1
	BCS	20$
10$:	.WRITW	#AREA,#1,#PRVMAX,#1,#0
	.CLOSE	#1

20$:	JSR	PC,CLRBAS	; REMOVE BASE
	.CP	#1,#24.
	.SHOW	FGND
	.SHOW	AMSG
	MOV	#AMSGLN-2,BASEX
	JSR	PC,DISP
30$:	.TTYIN
	CMPB	R0,#33
	BEQ	40$
	CMPB	R0,#15
	BNE	30$
	JMP	RESTRT
40$:	MOV	#BUFF,R4
	.SHOW	CLRSCR
	JSR	PC,DISP		; CLEAR SCREEN
	.EXIT
.PAGE
BASER:	CMP	BASEX,#71.
	BHIS	NOMOVE		; ALREADY MAX RIGHT
	INC	BASEX
	BR	DSPBAS

;-------------------------------

BASEL:	CMP	BASEX,#8.
	BLOS	NOMOVE		; ALREADY MAX LEFT
	DEC	BASEX
DSPBAS:	.CP	BASEX,#24.
	.SHOW	BASE
	RTS	PC

;-------------------------------

CLRBAS:	.CP	BASEX,#24.
	JSR	PC,BLANK
	RTS	PC

;-------------------------------

NOMOVE:	CLR	BSUB
	RTS	PC
.PAGE
UP:	BIT	TWO,FOUR	; missiles move 3 out of four ticks
	BEQ	10$
	RTS	PC
10$:	MOV	NMIM1,R5
20$:	MOVB	UPX(R5),R3
	BEQ	100$		; NO MISSILE
	MOV	R3,OKFIRE	; set flag, ok to fire again
	MOVB	UPY(R5),R0
	.CP	R3,R0
	.PUTCH	#40
	DEC	R0		; MOVE MISSILE UP
	TST	SHIPX
	BGT	30$
	CMPB	R0,#4
	BLOS	90$
30$:	CMPB	R0,#2		; TOP OF SCREEN ?
	BEQ	90$		; YES
	MOVB	R0,UPY(R5)
	CMPB	R0,#22.		; BARRIER LINE ?
	BLO	40$		; NO, ABOVE
	JSR	PC,CBARR	; CHECK FOR HIT ON BARRIER
	BNE	90$		; YES

40$:	MOV	R0,R1
	INC	R1		; OLD Y POSITION
	CMPB	R0,#3		; on spaceship line?
	BNE	50$		; no
	TST	SHIPX		; SPACESHIP MOVING ?
	BLE	50$		; NO
	JSR	PC,CSHIP	; CHECK FOR HIT ON SPACESHIP
	BEQ	90$		; YES
50$:	JSR	PC,CBOMB	; CHECK FOR COLLISION WITH BOMB
	BEQ	90$		; YES
	CLR	R2		; CHECK FOR HIT ON INVADERS, top row to bottom
60$:	CMP	R0,ROWY(R2)	; now on same line as row of invaders?
	BEQ	70$		; yes
	CMP	R1,ROWY(R2)	; was on invader line pre-UP?
	BLO	130$		; no, previously above invaders
	BEQ	70$		; yes, invader moved down on missile?
	TST	(R2)+
	CMP	R2,#<NROWS-1>*2
	BLE	60$		; TRY NEXT ROW
	BR	130$		; BELOW INVADERS

70$:	MOV	ROW(R2),R0	; missile at row, check each column
	MOV	#NCOLS,R1
80$:	MOV	@R0,R3
	BEQ	120$		; ALREADY DEAD
	BLT	110$		; cloud of smoke, missile cleans the air
	ADD	#4,R3
	CMPB	UPX(R5),R3
	BHI	120$		; missile to right of this invader
	CMPB	UPX(R5),@R0
	BLO	130$		; missile to left of this invader
	MOV	(R0),R1		; invader x coord
	BIS	#100000,(R0)	; mark as exploded
	SUB	ROW(R2),R0
	MOV	R0,DEAD
	DEC	COLCNT(R0)
	.CP	R1,ROWY(R2)
	JSR	PC,EXPLOD
	ADD	PNTS(R2),POINTS
	JSR	PC,SCORE	; UPDATE SCORE
	DEC	NUMINV		; ONE LESS INVADER
	BGT	90$		; but some left
	TST	SHIPX		; SPACESHIP MOVING ?
	BGT	90$		; YES - DON'T DISABLE BASE YET
	CLR	BSUB		; STOP BASE MOVING
	MOV	#BSLEEP,DEADB
90$:	CLRB	UPX(R5)		; DESTROY MISSILE
100$:	BR	140$

110$:	.CP	@R0,ROWY(R2)
	JSR	PC,BLANK
	CLR	@R0
	MOVB	UPX(R5),R3
	INCB	R3
	.CP	R3,UPY(R5)	; REPOSITION CURSOR

120$:	TST	(R0)+		; TRY NEXT ON THIS ROW
	DEC	R1
	BNE	80$

130$:	.SHOW	CSUP
	.SHOW	FGND
	.PUTCH	MISSLE
	.SHOW	BGND
140$:	DEC	R5
	BLT	150$
	JMP	20$
150$:	RTS	PC
.PAGE
CSHIP:	MOV	R3,-(SP)
	SUB	SHIPX,(SP)
	CMP	(SP)+,#4
	BHI	10$		; MISSED
	.CP	SHIPX,R0
	JSR	PC,EXPLOD
	JSR	PC,RAND
	CLR	R3		; use 2-high order bits of R0 for word offset
	ASL	R0
	ROL	R3
	ASL	R0
	ROL	R3
	ASL	R3
	MOV	SPNTS(R3),R0
	ASR	SHIPM		; fast ship?
	BCS	5$
	ADD	#200.,R0	; 200. extra!
5$:	ADD	R0,POINTS
	.CP	SHIPX,#4
	.SHOW	FGND
	.PUTCH	#'(
	MOV	#3,R3
	JSR	PC,BNW2A$
	.PUTCH	#')
	.SHOW	BGND
	JSR	PC,SCORE	; UPDATE SCORE
	MOVB	#-1,SHIPX+1
	SEZ
10$:	RTS	PC
.PAGE
CBOMB:	MOV	NBOM1,R2	; CHECK FOR COLLISION WITH BOMB
10$:	CMPB	R3,BOMBX(R2)
	BNE	30$
	TSTB	BOMBS(R2)
	BMI	30$
	CMPB	R1,BOMBY(R2)	; did bomb move to stilled missile last tick?
	BEQ	20$		; yes (CRT spot has been wiped by UP routine)
	CMPB	R0,BOMBY(R2)	; missile moving to bomb this tick?
	BNE	30$
	.SHOW	CSUP
	.PUTCH	#40		; blank the bomb
20$:	CLRB	BOMBX(R2)	; DESTROY BOMB
	BR	40$		; DESTROY MISSILE (RETURN Z=1)

30$:	DEC	R2
	BGE	10$
40$:	RTS	PC
.PAGE
CBARR:	MOV	R3,R1
	DEC	R1
	ASL	R1
	ADD	R0,R1		;+ line#, 22 or 23
	TSTB	BARR-22.(R1)	;line 23 barrier data in odd bytes, hence -22.
	BEQ	10$
	DECB	BARR-22.(R1)
	MOVB	BARR-22.(R1),R1
	.CP	R3,R0
	.PUTCH	BCHAR(R1)
10$:	RTS	PC
.PAGE
FIRE:	TST	OKFIRE
	BEQ	30$
	MOV	NMIM1,R5
10$:	TSTB	UPX(R5)		; LOOK FOR SPARE MISSILE SLOT
	BNE	20$
	MOV	BASEX,R0
	CMP	R0,#8.
	BLO	30$
	ADD	#2,R0
	MOVB	R0,UPX(R5)
	MOVB	#24.,UPY(R5)
	CLR	OKFIRE		; Can't fire again 'til this missile moved up
	BR	30$
20$:	DEC	R5
	BGE	10$
30$:	RTS	PC
.PAGE
DOWN:	MOV	NBOM1,R2	; MOVE BOMBS DOWN
10$:	MOVB	BOMBX(R2),R3
	BEQ	80$		; NO BOMB
	BIT	TWO,FOUR	; all move on 1 out of 4 ticks, missiles still
	BNE	20$
	TSTB	BOMBS(R2)	; FAST BOMB ?
	BLE	80$		; NO
	CMP	TWO,FOUR	; fast bombs move 3 out of 4 ticks
	BEQ	80$
20$:	MOVB	BOMBY(R2),R0
	TSTB	BOMBS(R2)	; FIRST MOVEMENT ?
	BMI	30$		; YES
	.CP	R3,R0
	.PUTCH	#40
	BR	40$
30$:	INCB	R3
	.CP	R3,R0
	DECB	R3
	BICB	#200,BOMBS(R2)
40$:	INCB	BOMBY(R2)
	INC	R0
	CMP	R0,#25.		; BOTTOM OF SCREEN ?
	BEQ	60$		; YES
	CMP	R0,#24.		; BASE LINE ?
	BEQ	50$		; YES
	CMP	R0,#22.		; BARRIER LINE ?
	BLO	70$		; NO
	JSR	PC,CBARR	; CHECK FOR HIT ON BARRIER
	BEQ	70$		; NO
	BR	60$		; YES

50$:	TST	DEADB		; BASE ALREADY DEAD ?
	BNE	70$		; YES
	CMP	R3,BASEX
	BLO	70$
	SUB	#4,R3
	CMP	R3,BASEX
	BHI	70$

	.CP	BASEX,#24.
	JSR	PC,EXPLOD
	CLR	BSUB		; STOP BASE MOVEMENT
	MOV	#BSLEEP,DEADB	; DEAD BASE
	DEC	NLIVES
	.SHOW	FGND
	JSR	PC,DLIVES	; UPDATE NUMBER OF BASES LEFT

60$:	CLRB	BOMBX(R2)	; CLEAR BOMB
	BR	80$

70$:	.SHOW	CSDN
	.SHOW	FGND
	.PUTCH	BOMB
	.SHOW	BGND
80$:	DEC	R2
	BGE	10$
90$:	RTS	PC
.PAGE
MOVE:	DEC	TCOUNT
	BEQ	10$
	JMP	DBOMB
10$:	MOV	TIME,TCOUNT
	CLR	R5		; MOVED SOMETHING FLAG
20$:	MOV	TROW,R2
	MOV	ROWY(R2),R0
	BEQ	140$		; NO INVADERS IN THIS ROW
	TST	VMOVE
	BEQ	50$

	MOV	ROW(R2),R1
	MOV	#8.,R3
30$:	TST	@R1
	BEQ	40$
	.CP	@R1,R0
	JSR	PC,BLANK
	TST	@R1
	BPL	40$
	CLR	@R1		; DESTROYED
40$:	TST	(R1)+
	DEC	R3
	BNE	30$

	INC	ROWY(R2)	; DOWN ONE ROW
	INC	R0

50$:	MOV	ROW(R2),R1
	MOV	#8.,R3
60$:	TST	@R1
	BLE	120$		; INVADER DEAD
	ADD	HMOVE,@R1	; HORIZONTAL MOVE
	CMP	@R1,#8.		; LEFT LIMIT FOR INVADERS
	BLOS	70$
	CMP	@R1,#72.	; RIGHT LIMIT
	BLO	80$
70$:	MOV	SP,EFLAG	; HIT EDGE
80$:	CMP	R0,#23.		; LOWEST LINE FOR INVADERS
	BNE	90$
	MOV	SP,BFLAG
90$:	CMP	R0,#22.
	BLO	100$
	JSR	PC,WBARR	; WIPE OUT ANY BARRIERS
100$:	INC	R5		; MOVED SOMETHING
	.CP	@R1,R0
110$:	JSR	PC,SETIN
	.SHOW	INVADR
	BR	130$

120$:	TST	@R1
	BGE	130$
	TST	VMOVE
	BNE	130$
	.CP	@R1,R0
	JSR	PC,BLANK
	CLR	@R1		; DESTROYED
130$:	TST	(R1)+		; NEXT INVADER
	DEC	R3
	BNE	60$
	TST	R5
	BNE	140$
	CLR	ROWY(R2)	; NO INVADERS IN THIS COLUMN
140$:	SUB	#2,TROW
	BLT	160$
	TST	R5
	BNE	150$
	JMP	20$
150$:	MOV	R5,TIME		; NEW TIME DELAY
	BR	DBOMB
160$:	MOV	#10.,TROW	; MOVED ALL ROWS
	COM	INSW
	TST	VMOVE
	BEQ	170$
	CLR	VMOVE
	BR	180$
170$:	TST	EFLAG		; HIT EDGE ?
	BEQ	180$
	NEG	HMOVE		; CHANGE DIRECTION
	TST	BFLAG
	BNE	180$		; HIT BOTTOM
	MOV	#1,VMOVE	; AND MOVE DOWN ONE LINE
180$:	CLR	EFLAG
	CLR	BFLAG

DBOMB:	TST	NUMINV		; any invaders to drop bombs?
	BEQ	20$
	BIT	TWO,FOUR
	BEQ	20$
	MOV	NBOM1,R2
10$:	TSTB	BOMBX(R2)
	BEQ	30$		; FOUND UNUSED BOMB SLOT
	DEC	R2
	BGE	10$
20$:	RTS	PC

30$:	JSR	PC,RAND
	BIT	BMASK,R0
	BNE	80$
	JSR	PC,RAND
	MOV	DEAD,R1		; column of last kill
	BMI	35$		; br if no kills yet
	TST	COLCNT(R1)
	BLE	35$		; br if last kill was of last invader in column
	ASL	R0
	BCS	40$		; 50% chance of bomb from col of last kill
35$:	CLR	R1		; colmn = remaining hi order 3 bits of R0
	ASL	R0
	ROL	R1
	ASL	R0
	ROL	R1
	ASL	R0
	ROL	R1
	ASL	R1		; word index
	TST	COLCNT(R1)	; any invaders in this column?
	BLE	30$		; br if not
40$:	MOV	#ROW+12.,R0
50$:	MOV	-(R0),R3	; look for bottom invader in column
	ADD	R1,R3
	TST	@R3
	BLE 	50$

60$:	MOVB	@R3,R3		; DROP BOMB
	ADD	#2,R3
	MOVB	R3,BOMBX(R2)
	MOVB	ROWY-ROW(R0),BOMBY(R2)
	MOVB	#200,BOMBS(R2)
	CMP	R0,#ROW
	BEQ	70$		; TOP ROW ALWAYS DROPS FAST BOMBS
	JSR	PC,RAND
	BIT	FMASK,R0
	BNE	80$
70$:	INCB	BOMBS(R2)	; FAST BOMB !
80$:	RTS	PC
.PAGE
WBARR:	.PUSH	R3
	MOV	@R1,R3
	SUB	#1,R3
	TST	HMOVE
	BMI	10$
	DEC	R3
10$:	ASL	R3
	ADD	R0,R3
	SUB	#22.,R3
	.PUSH	#7
20$:	CLRB	BARR(R3)
	ADD	#2,R3
	DEC	@SP
	BNE	20$
	TST	(SP)+
	.POP	R3
	RTS	PC
.PAGE
SETIN:	BIT	#1,R3
	BNE	20$
	TST	INSW
	BNE	30$
10$:	MOVB	#INV1,IN1
	MOVB	#INV5,IN5
	RTS	PC
20$:	TST	INSW
	BNE	10$
30$:	MOVB	#INV5,IN1
	MOVB	#INV1,IN5
	RTS	PC
.PAGE
SHIP:	MOV	SHIPX,R2	; SHIP MOVING ?
	BGT	10$		; YES
	DEC	SCOUNT
	BNE	50$
	MOV	#NSHIP,SCOUNT
	MOV	#1,R2		; START SPACESHIP
	MOV	R2,SHIPM
	MOV	#SLOSHP,SHIPIC
	CLR	R1
	JSR	PC,RAND
	ASL	R0
	BCC	5$
	ASL	R0
	BCC	5$
	INC	SHIPM		; 25% chance of fast ship
	MOV	#FASSHP,SHIPIC
	MOV	#FASSIZ,R1
5$:	TST	R0
	BPL	10$
	NEG	SHIPM		; 50% chance right-to-left
	ADD	R1,SHIPIC
	MOV	#74.,R2

10$:	TST	TWO
	BNE	40$
	TST	FOUR
	BNE	40$		; move once per 4 ticks - when missiles still
	TST	SHIPM
	BMI	20$
	CMPB	R2,#73.		; EDGE OF SCREEN ?
	BEQ	60$
	BR	30$

20$:	CMPB	R2,#2
	BEQ	60$
30$:	ADD	SHIPM,R2
	.CP	R2,#3
	JSR	PC,DSHIP
40$:	MOV	R2,SHIPX
	RTS	PC

50$:	TST	R2
	BEQ	70$		; SHIP NOT THERE AT ALL
	CMP	SCOUNT,#NSHIP-SSLEEP
	BGT	70$

60$:	.CP	R2,#3		; REMOVE SHIP
	JSR	PC,BLANK
	.CP	R2,#4		; AND ANY SCORE
	JSR	PC,BLANK
	CLR	SHIPX
	TST	NUMINV
	BGT	70$
	CLR	BSUB		; STOP BASE MOVING
	MOV	#BSLEEP,DEADB
70$:	RTS	PC
DSHIP:
	.SHOW	FGND
	JSR	R5,PRINT
SHIPIC:	.BLKW	1
	.SHOW	BGND
	RTS	PC

;-------------------------------

BLANK:
	.SHOW	BLNK
	RTS	PC

;-------------------------------

EXPLOD:
	.PUTCH	BELL
	.SHOW	FGND
	.SHOW	EXPLD
	.SHOW	BGND
	RTS	PC
.PAGE
; Return random R0 -- linear congruential sequence with maximum period,
;		      R0 := 2053.*seed + 13849., modulo 2**16
;		      seed := R0
; Note: high-order bits of result are "more random" than low-order.
;
RAND:	MOV	SEED,R0
	SWAB	R0
	CLRB	R0
	ASL	R0		;*512.
	ADD	SEED,R0		;*513.
	ASL	R0
	ASL	R0		;*2052.
	ADD	SEED,R0		;*2053.
	ADD	#13849.,R0
	MOV	R0,SEED
	RTS	PC

SEED:	.BLKW	1
.PAGE
;
; SUBROUTINE CPOS
; X = 2(SP)	COLUMN NUMBER
; Y = 3(SP)	LINE NUMBER
;
CPOS:
	.SHOW	CSPSN

.IF EQ	SCOPE - 1		; H1510
	.PUSH	R0
	MOVB	4(SP),R0	; FETCH COLUMN (X)
	DEC	R0
	CMPB	R0,#32.
	BHIS	10$
	ADD	#96.,R0
10$:	.PUTCH	R0
	MOVB	5(SP),R0	; FETCH LINE (Y)
	ADD	#95.,R0
	.PUTCH	R0
	.POP	R0
.ENDC
.IF EQ	SCOPE-2			; TVI912/920
	.PUSH	R0
	MOVB	5(SP),R0	; FETCH LINE (Y)
	ADD	#31.,R0
	.PUTCH	R0
	MOVB	4(SP),R0	; FETCH COLUMN (X)
	ADD	#31.,R0
	.PUTCH	R0
	.POP	R0
.ENDC
.IF EQ	SCOPE-3			; VT52
	.PUSH	R0
	MOVB	5(SP),R0	; FETCH LINE (Y)
	ADD	#31.,R0
	.PUTCH	R0
	MOVB	4(SP),R0	; FETCH COLUMN (X)
	ADD	#31.,R0
	.PUTCH	R0
	.POP	R0
.ENDC
.IF EQ	SCOPE-4			; VT100
	.PUSH	R0
	.PUSH	R3
	MOVB	7(SP),R0	; FETCH LINE (Y)
	MOV	#-2,R3
	JSR	PC,BNW2A$
	.PUTCH	#73
	MOVB	6(SP),R0	; FETCH COLUMN (X)
	MOV	#-2,R3
	JSR	PC,BNW2A$
	.PUTCH	#110
	.POP	R3
	.POP	R0
.ENDC

	MOV	(SP)+,(SP)
	RTS	PC
.PAGE
PRINT:	.PUSH	R0
	MOV	(R5)+,R0
10$:	MOVB	(R0)+,(R4)+
	BNE	10$
	DEC	R4
	.POP	R0
	RTS	R5

;----------------------------------------------

DISP:	MOV	BASEX,R0
	ADD	#2,R0
	.CP	R0,#24.		; POSITION CURSOR ON BASE
	MOVB	#200,(R4)
	.PRINT	#BUFF
	MOV	#BUFF,R4
	RTS	PC

;-------------------------------

SCORE:	MOV	POINTS,R0
	MOV	#5,R3
	.CP	#43.,#2
	.SHOW	FGND
	JSR	PC,BNW2A$
DLIVES:	MOV	NLIVES,R0
	MOV	#3,R3
	.CP	#31.,#2
	JSR	PC,BNW2A$
	.SHOW	BGND
	RTS	PC

POINTS:	.WORD	0
PRVMAX:	.WORD	0
.PAGE
; BINARY TO ASCII
; R0=VALUE
; R3=FIELD WIDTH ( - = LEADING ZEROES)
; R4=OUTPUT STRING ADDRESS

BNW2A$:	.PUSH	R1
	.PUSH	R2
	.PUSH	R5
	CLR	-(SP)		; OUTPUT STARTED FLAG.
	TST	R3
	BMI	10$
	MOV	#40,LEAD
	BR	20$
10$:	NEG	R3
	MOV	#60,LEAD
20$:	MOV	R3,R5
	BEQ	90$
	MOV	#VALEND,R2
	ASL	R5
	SUB	R5,R2
30$:	CLR	R5		; DIGIT COUNTER.
	MOV	(R2)+,R1	; NEXT DECIMAL STAGE VALUE.
	BEQ	90$		; FINISHED.
40$:	SUB	R1,R0		; EXTRACT DIGIT VALUE.
	BLO	50$
	INC	R5		; UPDATE DIGIT.
	BR	40$
50$:	ADD	R1,R0		; CORRECT OVERSHOOT.
	TST	(SP)		; HAS OUTPUT STARTED ?
	BNE	70$		; NO.
	TST	R5		; IS THIS A ZERO DIGIT?
	BEQ	60$
	INC	(SP)		; MARK OUTPUT STARTED.
	BR	70$
60$:	CMP	R3,#1		; IS IT THE LAST DIGIT?
	BEQ	70$
	TST	@R2		; IS IT THE LAST DIGIT?
	BEQ	70$
	MOVB	LEAD,R5
	BR	80$

70$:	ADD	#60,R5
80$:	.PUTCH	R5		; OUTPUT DIGIT.
	DEC	R3
	BNE	30$
90$:	TST	(SP)+		; CLEAR OFF OUTPUT FLAG.
	.POP	R5
	.POP	R2
	.POP	R1
	RTS	PC

LEAD:	.WORD	40
	.WORD	10000.,1000.,100.,10.,1.
VALEND:	.WORD	0
.PAGE
INIT0:	MOV	#NBASES,NLIVES
	CLR	POINTS
	MOV	#174000,FMASK
	MOV	#160000,BMASK

	MOV	#BAR1,R1
	MOV	#6,R0
10$:	MOV	#10.,R2
20$:	MOVB	#STRNG,(R1)+
	DEC	R2
	BNE	20$
	ADD	#10.,R1
	DEC	R0
	BNE	10$

	.GTIM	#AREA,#TBLK
	MOV	TBLK+2,CLOCK

	RTS	PC
.PAGE
INIT:	MOV	#ROWY,R1
	MOV	#NROWS,R0
	MOV	#5,R2		; Y POSITION
10$:	MOV	R2,(R1)+
	ADD	#2,R2
	DEC	R0
	BNE	10$

	CLR	R1
	MOV	#NCOLS,R0
	MOV	#11.,R2

20$:	MOV	R2,ROW5(R1)
	MOV	R2,ROW7(R1)
	MOV	R2,ROW9(R1)
	MOV	R2,ROW11(R1)
	MOV	R2,ROW13(R1)
	MOV	R2,ROW15(R1)
	MOV	#NROWS,COLCNT(R1)
	TST	(R1)+
	ADD	#8.,R2
	DEC	R0
	BNE	20$

	MOV	#ZB,R1
	MOV	#<ZE-ZB>/2,R0
30$:	CLR	(R1)+
	DEC	R0
	BNE	30$

	MOV	#-1,DEAD
	MOV	#<NROWS*NCOLS>,NUMINV	; NUMBER OF INVADERS
	MOV	#2,BASEX
	MOV	#WTIME,WLIMIT
	MOV	#TOP,TROW
	MOV	#NTIMES,TCOUNT
	MOV	#NTIMES,TIME
	MOV	#NSHIP,SCOUNT
	MOV	#1,HMOVE
	ASL	FMASK		; MAKE MORE FAST BOMBS !
	ASL	BMASK		; MAKE MORE BOMBS !
	BNE	40$
	ASR	BMASK		; NEVER GETS TO ZERO

40$:	MOV	#BUFF,R4	; TEXT BUFFER
	.SHOW	CLRSCR
	.SHOW	FGND
	.CP	#2,#24.
	.SHOW	BASE
	.CP	#2,#2
	.SHOW	TITLE1
	.SHOW	BGND
	.SHOW	TITLE2

	DEC	R4
	JSR	PC,DISP
	.CP	#1,#22.		; DISPLAY BARRIERS
	MOV	#BARR,R0
	JSR	PC,70$
	.CP	#1,#23.
	MOV	#BARR+1,R0
	JSR	PC,70$

	JSR	PC,DISP
	JSR	PC,SCORE	; DISPLAY SCORE
	MOV	PRVMAX,R0
	MOV	#5,R3
	.CP	#62.,#2
	JSR	PC,BNW2A$
	JSR	PC,DISP

	MOV	#ROWY,R0
	MOV	#NROWS,R2	; NOW DISPLAY INVADERS
	MOV	ROW,R1

50$:	MOV	#NCOLS,R3
	MOV	R3,OKFIRE	; set flag (any nonzero value)

60$:	.CP	(R1)+,@R0
	INC	R1
	JSR	PC,SETIN
	.SHOW	INVADR
	DEC	R3
	BNE	60$

	.PUSH	R0
	JSR	PC,DISP
	.POP	R0
	TST	(R0)+
	DEC	R2
	BNE	50$

	COM	INSW
	RTS	PC

70$:	MOV	#70.,R1
80$:	MOVB	(R0)+,R2
	INC	R0
	.PUTCH	BCHAR(R2)
	DEC	R1
	BNE	80$
	RTS	PC
.PAGE
INSW:	.WORD	0

ROW:	.WORD	ROW5,ROW7,ROW9,ROW11,ROW13,ROW15
ROWY:	.BLKW	NROWS
COLCNT:	.BLKW	NCOLS

PNTS:	.WORD	30.,25.,20.,15.,10.,05.

ROW5:	.BLKW	NCOLS
ROW7:	.BLKW	NCOLS
ROW9:	.BLKW	NCOLS
ROW11:	.BLKW	NCOLS
ROW13:	.BLKW	NCOLS
ROW15:	.BLKW	NCOLS

BARR:	.BLKB	2*80.
BAR1=BARR+<2*11.>

BCHAR:	.BYTE	40,'-,'+,'*,'#,0
STRNG	= .-BCHAR-2

ZB=.
BOMBX:	.BLKB	3		; BOMB POSITIONS
BOMBY:	.BLKB	3
BOMBS:	.BLKB	3		; BOMB SPEEDS
	.EVEN

UPX:	.BLKB	3		; MISSILE POSITIONS
UPY:	.BLKB	3

TWO:	.WORD	0		; HALF TIMEBASE COUNTER
FOUR:	.WORD	0		; QUARTER TIMEBASE COUNTER
BSUB:	.WORD	0		; BASE MOVEMENT SUBROUTINE ADDRESS
DEADB:	.WORD	0
VMOVE:	.WORD	0		; VERT. MOVE
EFLAG:	.WORD	0		; EDGE CONDITION FLAG
BFLAG:	.WORD	0		; BOTTOM FLAG
SHIPX:	.WORD	0
ZE=.

;Level of expertise parameters, initially set for beginner--
NMIM1:	.WORD	2		; NUMBER OF SIMULTANEOUS MISSILES IN FLIGHT - 1
NBOM1:	.WORD	0		; NUMBER OF SIMULTANEOUS BOMBS - 1

DEAD:	.WORD	-1		; if >0, column word index of last invader kill
OKFIRE:	.WORD	1		; flag: previous missile has moved above base
FMASK:	.WORD	0
BMASK:	.WORD	0
SHIPM:	.WORD	0

NUMINV:	.WORD	0
BASEX:	.WORD	0		; INITIAL X POSITION OF BASE
WLIMIT:	.WORD	0
NLIVES:	.WORD	0		; NUMBER OF BASES TO START WITH

TROW:	.WORD	0
TCOUNT:	.WORD	0
SCOUNT:	.WORD	0

HMOVE:	.WORD	0		; HORIZ. MOVE
SSFLAG:	.WORD	0

BELL:	.BYTE	7,4		; BELL,QUIET BELL

TIME:	.WORD	NTIMES

SPNTS:	.WORD	50.,100.,150.,200.

	.ENABL	LC
TITLE1:	.ASCIZ	'SPACE INVADERS!       '
TITLE2:	.ASCIZ	'Bases:      Score:          Highest: '
AMSG:	.ASCIZ	'Press RETURN to play again, ESC to quit:  '
AMSGLN=.-AMSG-1
EXPMSG:	.ASCII	<15><12>'SPACE INVADERS!  Beginner, Intermediate, or Expert?'
	.ASCII	' (B/I/E): '<200>
HLPMSG:	.BYTE	15,12,12
	.ASCII	'The "," (lowercase "<") key moves left;'<15><12><12>
	.ASCII	'The "." (lowercase ">") key moves right;'<15><12><12>
	.ASCII	'Press the spacebar to stop and fire;'<15><12><12>
	.ASCII	'"Q" toggles sound effects off/on (for those late night '
	.ASCII	'sessions);'<15><12><12>
	.ASCII	'Press any other key to stop without firing;'<15><12><12>
	.ASCII	'Insert quarter in nearest programmer and press RETURN '
	.ASCII	'to start game: '<200>

.IF EQ	SCOPE-1		;HAZELTINE 1510
CLRSCR:	.BYTE	176,34,0	; CLEAR SCREEN
CSUP:	.BYTE	176,14,10,0	; CURSOR UP, BACKSPACE
CSDN:	.BYTE	12,10,0		; CURSOR DOWN (LF), BACKSPACE
CSPSN:	.BYTE	176,21,0	; CURSOR POSITION
FGND:	.BYTE	176,37,0	; FOREGROUND MODE (HIGH INTENSITY)
BGND:	.BYTE	176,31,0	; BACKGROUND MODE (LOW INTENSITY)
.ENDC

.IF EQ	SCOPE-2		;TELEVIDEO 912/920
CLRSCR:	.BYTE	33,132,0
CSUP:	.BYTE	13,10,0
CSDN:	.BYTE	12,10,0
CSPSN:	.BYTE	33,75,0
FGND:
BGND:	.BYTE	0
.ENDC

.IF EQ	SCOPE-3		;VT52
CLRSCR:	.BYTE	33,110,33,112,0
CSUP:	.BYTE	33,101,10,0
CSDN:	.BYTE	12,10,0
CSPSN:	.BYTE	33,131,0
FGND:
BGND:	.BYTE	0
.ENDC

.IF EQ	SCOPE-4		;VT100
CLRSCR:	.BYTE	33,133,62,112,0
CSUP:	.BYTE	33,133,101,10,0
CSDN:	.BYTE	12,10,0
CSPSN:	.BYTE	33,133,0
FGND:
BGND:	.BYTE	0
.ENDC

BASE:	.ASCIZ	<10>' -- -- '
INVADR:	.ASCII	<10>' '
 IN1:	.ASCII	'/-O-'
 IN5:	.ASCIZ	'\ '
 INV1='/
 INV5='\
SLOSHP:	.ASCIZ	<10>' -=O=- '
FASSHP:	.ASCIZ	<10><10>'  =*>>>'
FASSIZ=.-FASSHP
	.ASCIZ	'<<<*=  '

EXPLD:	.ASCIZ	'*****'
BLNK:	.ASCIZ	'     '
MISSLE:	.ASCII	'!'
BOMB:	.IF EQ SCOPE-1
	.ASCII	'`'		;solid block on Hazeltine
	.IFF
	.ASCII	'O'
	.ENDC
	.EVEN

;-------------------------------

DEVBLK:	.RAD50	'DK SPCINVDAT'
AREA:	.BLKW	5
TBLK:	.WORD	0,0
CLOCK:	.WORD	0

	.WORD	0
BUFF:	.BLKB	300.

	.END	START
