;	THE GAME OF DEFLECTION     BY ANDREW A. RECUPERO
;	FROM KILOBAUD FEB '78 #14
;
;	TO START TYPE 0,1,2,3
;		0=1 TARGET, CLEAR FIELD
;		1=1 TARGET, SHIFT DEFLECTORS
;		2=3 TARGETS, CLEAR FIELD
;		3=3 TARGETS, SHIFT DEFLECTORS
;
;	THE OBJECT OF THE GAME IS TO HIT TARGET(S) BY
;	DEFLECTING THE RUNNER INTO THE TARGET.
;
;	DEFLECT TARGET BY TYPING A "/" OR A "\".
;
;	ALL TARGETS MUST BE HIT WITHIN A FIXED TIME.
;
;SYSTEM EQUATES:
;
	ORG	100H
;STATUS 	EQU	00	;KEYBOARD STATUS PORT
;STROBE	EQU	80H	;KEYBOARD STROBE BIT
;DATA	EQU	1	;KEYBOARD DATA PORT
STATUS 	EQU	0FAH	;KEYBOARD STATUS PORT -- SOL
STROBE	EQU	01H	;KEYBOARD STROBE BIT  -- SOL
DATA	EQU	0FCH	;KEYBOARD DATA PORT   -- SOL
VDMRAM	EQU	0CC00H	;VDM STARTING ADDR.
VDMPAGE	EQU	0CCH	;VDM RAM PAGE
;VDMPORT	EQU	0C8H	;VDM PORT
VDMPORT	EQU	0FEH	;VDM PORT
TARGET	EQU	05	;TARGET SYMBOL
RUNNER	EQU	07	;RUNNER SYMBOL
USLSH	EQU	2FH	;UP SLASH SYMBOL "/"
DSLSH	EQU	5CH	;DOWN SLASH SYMBOL "\"
LEFT	EQU	80H	;RUNNER
DOWN	EQU	40H	;	DIRECTION
UP	EQU	20H	;		CONTROL
RIGHT	EQU	10H	;			EQUATES
;
;
;	PROGRAM START
;
GSTART	LXI	SP,STACK+8	;INITIALIZE STACK
	CALL	CLSCRN		;CLEAR SCREEN
REIDLE	CALL	DRUNER		;DISPLAY RUNNER
IDLE	CALL	ACTION		;MOVE RUNNER
	CALL 	DELAY		;TIME DELAY AND CHECK INPUT
	MOV	A,C		;KEYBOARD INPUT TO ACC.
	ORA	A		;TEST FOR NON-ZERO
	JZ	IDLE
	MVI	M,20H		;REMOVE RUNNER
	RRC			;SHIFT DEFLECTORS?
	PUSH	PSW		;SAVE GAME OPTION IN 'A' REG
	JC	SHIFT		;JUMP TO SHIFT IF YES
	CALL	CLSCRN		;DO NOT SHIFT, CLEAR SCREEN
	JMP	INIT
SHIFT	LXI	B,VDMRAM	;SHIFT
	MOV	D,B		;   SCREEN
	MOV	E,C		;      ONE
	INX	D		;        POSITION
SMOR	LDAX	D		;              TO
	STAX	B		;               THE
	INX	B		;		  LEFT
	INX	D	
	MOV	A,D
	CPI	VDMPAGE+4
	JNZ	SMOR		;GO SHIFT MORE IF NOT DONE
INIT	POP	PSW		;RESTORE GAME OPTION TO 'A' REG
	MVI	C,1		;SET FOR ONE TARGET
	RRC			;GAME OPTION BIT 0 TO CARRY FLG
	MVI	A,TARGET	;TARGET SYMBOL TO 'A' REG
	JNC	GAM1		;JUMP FOR 1 TARGET OPTION
	MVI	C,3		;INITIALIZE FOR 3 TARGETS
	STA	VDMRAM+34CH	;PUT TARGET LEFT OF CENTER
	STA	VDMRAM+2F4H	;TARGET TO THE RIGHT
GAM1	STA	VDMRAM+320H	;TARGET IN THE MIDDLE
	MOV	A,C
	STA	TARNUM		;STORE # OF TARGETS
	LXI	H,1C0H		;INITIALIZE GAME TIMER
	SHLD	TIMER		;SAVE TIMER VALUE
	CALL	DRUNER		;START GAME, DISPLAY RUNNER
;
;
;	THIS IS THE MAIN LOOP FOR THE GAME
;
MAINLP	CALL	DELAY		;WAIT & CHECK KEYBOARD
	CALL	ACTION	
	PUSH	H		;SAVE HL
	LHLD	TIMER		;GET GAME TIMER
	DCX	H		;DECREMENT TIMER
	MOV	A,H		;CHECK IF GAME
	ORA	L		;	TIMER EXPIRED
	SHLD	TIMER		;STORE UPDATED TIMER
	POP	H		;RESTORE HL
	JNZ	MAINLP		;JMP IF TIMER DIDN'T EXPIRE
	LXI	B,LOSE		;DISPLAY NO
	CALL	SHOW		;	TIME MESSAGE
	CALL	WAER		;WAIT & ERASE MESSAGE
	JMP	GSTART		;TIMER EXPIRED, RESTART GAME
;
;
;	INITIALIZE AND DISPLAY RUNNER
;		'B' HOLDS MOTION DIRECTION AS:
;		REG BIT 7 6 5 4
;			| | | |
;			| | | RIGHT
;			| | UP
;			| DOWN
;			LEFT
;
DRUNER	LXI	H,VDMRAM+40H	;RUNNER STARTING POS.
	LXI	D,100H		;X,Y RUNNER POS. D=VERT E=HORZ
	MVI	M,RUNNER	;DISPLAY RUNNER
	MVI	B,RIGHT		;INITIALIZE RIGHT MOTION
	RET
;
;
;	ALL RUNNER MOVEMENT DONE HERE
;
ACTION	MOV	A,M		;CHECK IF
	CPI	RUNNER		;	RUNNER
	JNZ	DF010		;		DISPLAYED
	MVI	M,20H		;REMOVE RUNNER FROM FIELD
;
;	CALCULATE NEW RUNNER POSITION
;
DF010	MOV	A,B
	CPI	RIGHT		;IS MOTION TO RIGHT?
	JNZ	DF012		;JUMP IF NO
	INR 	E		;MOVE 1 POS TO THE RIGHT
DF012	CPI	UP		;IS MOTION UP?
	JNZ	DF014		;JUMP IF NO
	DCR	D		;MOVE 1 POS UP
DF014	CPI	DOWN		;IS MOTION DOWN?
	JNZ	DF016		;JUMP IF NO
	INR 	D		;MOVE 1 POS DOWN
DF016	CPI	LEFT		;IS MOTION TO THE LEFT?
	JNZ	DF018		;JUMP IF NO
	DCR	E		;MOVE 1 POS TO LEFT
;
;	CALCULATE FIELD POSITION FROM HORZ AND VERT VALUES
;	IN DE WITH RESULT IN HL
;
DF018	MOV	A,D
	RRC
	RRC
	MOV	L,A
	ANI	03
	ORI	VDMPAGE
	MOV	H,A		;SAVE HIGH PART OF ADDR.
	MOV	A,L
	ANI	0C0H
	ADD	E
	MOV	L,A		;SAVE LOW PART OF ADDR.
	MOV	A,M		;CHECK NEXT FIELD POSITION
	CPI	TARGET		;IS IT A TARGET?
	JNZ	DF019		;JUMP IF DIDN'T HIT TARGET
;
;	HIT A TARGET
;
	MVI	M,20H		;REMOVE TARGET
	LDA	TARNUM		;GET # OF TARGETS LEFT
	DCR	A		;REDUCE BY ONE
	STA	TARNUM		;STORE UPDATED TARGET NUMBER
	RNZ			;RETURN IF MORE TARGETS
	POP	D		;ALL TARGETS GONE, FIX STACK
	LXI	B,WIN		;DISPLAY
	CALL	SHOW		;	WIN MESSAGE
	CALL	WAER		;WAIT & ERASE MESSAGE
	JMP	REIDLE		;GO TO IDLE LOOP
;
DF019	MOV	A,C		;PUT KEYBOARD DATA IN 'A' REG
	CPI	USLSH		;IS IT AN UPSLASH "/" ?
	JZ	DF020		;JUMP IF YES
	CPI	DSLSH		;IS IT A DOWNSLASH "\" ?
	JZ	DF020		;JUMP TO ANALYZE DEFLECTOR
;
;	CHECK FOR RUNNER HITTING A DEFLECTOR
;
DELCK	MOV	A,M
	CPI	20H		;IS NEXT POSITION A SPACE?
	JNZ	CHGMOT		;DEFLECTOR IF NO, CHNGE DIR
	CALL	EDGCK		;REVERSE MOTION IF AT EDGE
	MVI	M,RUNNER	;DISPLAY RUNNER AT NEW LOCATION
	RET
DF020	MOV	M,A		;INPUT WAS VALID, DISPLAY IT
;
;	CHANGE DIRECTION OF RUNNER MOTION
;
CHGMOT	CPI	DSLSH		;IS IT A DOWNSLASH?
	MOV	A,B		;DIRECTION FLAG TO 'A' REG
	JNZ	UPSLH		;JUMP IF NOT DOWN SLASH
	ANI	0C0H		;IS MOTION LEFT OR DOWN?
	MOV	A,B		;RESTORE DIRECTION FLAG TO 'A'
	JZ	DF030		;JUMP IF NOT LEFT OR DOWN
	RRC			;CHANGE DIRECTION TO
	RRC			;	RIGHT OR UP
	JMP	DF050
DF030	RLC			;CHANGE DIRECTION TO
	RLC			;	LEFT OR DOWN
	JMP	DF050
UPSLH	ANI	0A0H		;IS MOTION TO LEFT OR UP?
	MOV	A,B		;DIRECTION FLAG TO 'A' REG
	JZ	DF040		;JUMP IF NOT LEFT OR UP
	RRC			;CHANGE DIR TO RIGHT OR DOWN
	JMP	DF050
DF040	RLC			;CHANGE MOTION TO RIGHT OR DOWN
DF050	MOV	B,A		;SAVE NEW DIRECTION
	CALL	EDGCK		;IS RUNNER AT EDGE OF FIELD?
	MOV	A,M		;GET DEFLECTOR IN CASE AT EDGE
	JZ	CHGMOT		;IF AT EDGE, CHANGE DIRECTION
	RET
;
;	THIS ROUTINE CHECKS FOR EDGE OF FIELD, REVERSES MOTION,
;	SETS ZERO FLAG IF AT EDGE
;
EDGCK	MOV	A,B		;LOAD MOTION DETECTOR
	CPI	RIGHT		;TO RIGHT?
	JNZ	DF060		;JUMP IF NO
	MVI	A,3FH		;3FH=RIGHT EDGE
	CMP	E
	RNZ			;RETURN IF NOT AT RIGHT EDGE
	MVI	B,LEFT		;REVERSE DIRECTION TO LEFT
	RET
DF060	CPI	UP		;CHECK FOR UP MOTION
	JNZ	DF070		;JUMP IF MOTION NOT UP
	XRA	A
	ORA	D		;CHECK FOR TOP VERT=0
	RNZ			;RETURN IF NOT AT TOP
	MVI	B,DOWN		;REVERSE DIRECTION TO DOWN
	RET
DF070	CPI	DOWN		;CHECK FOR DOWN MOTION
	JNZ	DF080		;JUMP IF MOTION NOT DOWN
	MVI	A,0FH		;CHECK FOR BOTTOM VERT=0FH
	CMP	D
	RNZ			;RETURN IF NOT AT BOTTOM
	MVI	B,UP		;REVERSE DIRECTION TO UP
	RET
DF080	XRA	A		;MUST BE MOTION TO LEFT
	ORA	E		;CHECK FOR LEFT EDGE HORZ=0
	RNZ			;RETURN IF NOT AT LEFT EDGE
	MVI	B,RIGHT		;REVERSE DIRECTION TO RIGHT
	RET
;
;	THIS ROUTINE PERFORMES TIME DELAY WHILE CONTINUOUSLY
;	CHECKING FOR KEYBOARD INPUT. INPUT RETURN IN 'C'
;
DELAY	MVI	C,0		;INITIALIZE INPUT SAVE REG
	PUSH	D		;FREE UP DE REG
	LXI	D,700H		;LOAD DELAY VALUE
DL01	DCX	D		;DECREMENT DELAY COUNTER
	IN	STATUS		;GET KEYBOARD STATUS
	ANI	STROBE		;CHECK STROBE
	JNZ	DL02		;JUMP IF NO INPUT
	IN	DATA		;GET KEYBOARD DATA
	CPI	03		;CTRL-C
	JZ	0000H		;BACK TO CP/M
	ANI	7FH		;REMOVE BIT 7
	MOV	C,A		;SAVE INPUT IN 'C' REG
DL02	MOV	A,D		;CHECK FOR
	ORA	E		;	TIMER=0
	JNZ	DL01
	POP	D		;DELAY OVER, RESTORE DE REG
	RET
;
;	THIS ROUTINE INITIALIZES THE VDM PORT & CLEARS SCREEN
;
CLSCRN	XRA	A		;CLEAR VDM PORT
	OUT	VDMPORT		;OUTPUT IT
	LXI	H,VDMRAM	;POINT TO START OF VDM RAM
	MVI	A,VDMPAGE+4	;'A' REG=END OF VDM RAM
CL01	MVI	M,20H		;BLANK LOC GIVEN BY HL
	INX	H
	CMP	H		;DONE?
	JNZ	CL01		;JUMP IF NOT
	RET
;
;	THIS ROUTINE DISPLAYS MESSAGE POINTED TO BY BC REG
;
SHOW	LXI	D,VDMRAM+31DH	;POINT TO DISPLAY LOCATION
	MVI	H,7		;MESSAGE LENGTH=7
SH01	LDAX	B		;GET BYTE OF MESSAGE
	STAX	D		;DISPLAY IT
	INX	D		;BUMP TO
	INX	B		;	NEXT BYTE
	DCR	H		;DECREMENT COUNT
	JNZ	SH01		;DONE?
	RET
;
;	THIS ROUTINE WAITS AWHILE AND THEN REMOVES MESSAGE
;
WAER	MVI	D,32		;LOAD UNITS OF DELAY
WDLY	CALL	DELAY		;WAIT ONE UNIT OF DELAY
	DCR	D		;CHECK IF
	JNZ	WDLY		;	DONE WAITING
	LXI	B,BLNK		;POINT TO ALL BLANKS
	JMP	SHOW		;DISPLAY BLANKS TO ERASE & RET
;
;	MESSAGES
;
WIN	DB	'WINNER '
LOSE	DB	'NO TIME'
BLNK	DB	'       '
;
;	SAVE AREAS AND STACK
;
TIMER	DS	2		;TIMER SAVE AREA
TARNUM	DS	1		;NUMBER OF TARGETS
STACK	DS	8		;STACK AREA (ONLY 4 DEEP)

