;	***** LIFE *****
;
; A GAME OF LIFE FOR A VDM DISPLAY.
;
;Adapted from the 'Oznaki' life program (Dr Dobbs, No. 24)
;by R. Daniells of the S.A.M.G. Inc.
;
;This version of life uses the screen memory of the Processor
;Technology VDM-1 display to store the current status of the
;cells.  It should work OK with similar memory mapped VDU's
;such as the DG-640 with perhaps a few modifications.
;
;Caution: Program contains some machine dependant I/O due to
;the lack of a non-echoing input routine in CP/M.
;
VDM	EQU	0CC00H	;START OF VDM MEMORY.
VDMU	EQU	VDM SHR 8  ;HIGH BYTE OF VDM MEMORY.
VDMPT	EQU	0C8H	;VDM CONTROL PORT (TO INHIBIT SCROLL).
ALIVEC	EQU	0AAH	;ALIVE CHARACTER.
DEADC	EQU	7FH	;DEAD CHARACTER.
CR	EQU	0DH	;CARRIAGE RETURN CHARACTER.
LF	EQU	0AH	;LINEFEED CHARACTER.
KSTAT	EQU	0	;KEYBOARD STATUS PORT.
KDATA	EQU	2	;KEYBOARD DATA PORT.
RDR	EQU	10H	;KEYBOARD DATA READY MASK.
ENTRY	EQU	5	;ENTRY TO CP/M.
;
	ORG	100H
START:	LXI	H,0  ;SAVE CPM'S STACK.
	DAD	SP
	SHLD	OLDSTK
MLOOP:	LXI	SP,NEWSTK ;SET UP NEW LOCAL STACK.
	MVI	A,0
	OUT	0C8H	;INIT VDM (NO SCROLLING).
CLEAR:	LXI	H,VDM+400H  ;GENERATE DEAD SCREEN.	
	MVI	A,VDMU-1
BLANK:	MVI	M,DEADC
	DCX	H
	CMP	H
	JNZ	BLANK
READY:	PUSH	H
	MVI	A,0	;CLEAR ONE-GENERATION FLAG.
	STA	ONEFLAG
	LXI	B,0FFC0H  ;NORTH ADDRESS INCREMENT.
			  ;B IS ALSO CURSOR CHAR.
	LXI	D,40H	;SOUTH ADDRESS INCREMENT.
READIN:	MOV	C,M	;REAL CHAR IN C.
	MOV	M,B
INPUT:	CALL	GETCH	;WAIT FOR INPUT CHARACTER.
	JZ	INPUT
	MOV	M,C
	MVI	C,0C0H	;RESTORE C.
	CPI	'C'-40H	;CONTROL - C?
	JZ	EXIT
	CPI	30H	;NUMBER?
	JC	NOTNUM
	CPI	3AH	;NUMBER LESS THAN 9?
	JC	SETSPD
NOTNUM	CPI	'H'	;REQUEST FOR HELP?
	JZ	HELP
	CPI	'N'	;MOVEMENT COMMAND?
	CZ	NORTH
	CPI	'S'
	CZ	SOUTH
	CPI	'E'
	CZ	EAST
	CPI	'W'
	CZ	WEST
	CPI	'M'
	CZ	MIDDLE
	CPI	'K'	;KILL CELL?
	CZ	KILL
	CPI	'L'	;GRANT LIFE?
	CZ	LIVE
	CPI	'C'	;CLEAR SCREEN?
	CZ	CLEAR
	CPI	'O'	;ONE GENERATION ONLY?
	CZ	ONEGEN
	CPI	'G'	;GO?
	JNZ	READIN
	POP	H	;SET VDM-1 IN CURRENT GENERATION POINTER.
LIFE	XRA	A	;INIT CELL COUNTER.
	ADD	M
	DAD	B	;TO GO NORTH.
	ADD	M
	INX	H	;TO GO EAST.
	ADD	M
	INX	H	;TO GO EAST.
	ADD	M
	DAD	D	;TO GO SOUTH.
	ADD	M
	DAD	D	;TO GO SOUTH.
	ADD	M
	DCX	H	;TO GO WEST.
	ADD	M
	DCX	H	;TO GO WEST.
	ADD	M
	INX	H	;TO GO EAST.
	DAD	B	;TO GO NORTH.
	ANI	7	;WHAT'S IN BOTTOM 3 BITS.
	CPI	6	;WERE THERE 2 LIVE?
	CNZ	MARK	;DO NOTHING IF 2 NEIGHBORS.
	MOV	A,H	;HAVE WE CHECKED ALL OF SCREEN?
	CPI	0D0H
	JNZ	LIFE	;CONTINUE IF NOT DONE.
	CALL	DELAY	;DELAY BETWEEN GENERATIONS.
;BIRTH/DEATH AGONY SEQUENCE.
C1	DCX	H
	MOV	A,M
	ORA	A
	CPE	LIVE
	CPO	KILL
	MOV	A,H
	CPI	VDMU-1
	JNZ	C1
; DELAY FOR VIEWING MARKED LOCATIONS.
	CALL	DELAY
; SEE IF ONLY ONE GENERATION REQUESTED.
	LDA	ONEFLAG
	ORA	A
	JNZ	READY	;STOP IF SO.
; DISPLAY CAN BE STOPPED AND ALTERED.
	CALL	GETCH	;SEE IF KEY PRESSED.
	JZ	LIFE	;CONTINUE IF NOT.
	JMP	READY	;ELSE, GET NEW COMMAND.
;
; ROUTINES THAT MARK CELLS FOR POSSIBLE ALTERATION.
MARK	CPI	1	;ARE THERE EXACTLY 3 LIVE NEIGHBOURS?
	JZ	LMARK
DMARK	MVI	A,DEADC
	ANA	M
	MOV	M,A	;CELL NOW MARKED FOR DEATH.
	RET
LIVEMARK	EQU	80H
LMARK	MVI	A,LIVEMARK
	ORA	M
	MOV	M,A	;CELL NOW MARKED FOR LIFE.
	RET
; ROUTINES FOR SEEDING THE SCREEN.
NORTH	DAD	B
	RET
SOUTH	DAD	D
	RET
EAST	INX	H
	RET
WEST	DCX	H
	RET
MIDDLE	LXI	H,VDM+220H
	RET
KILL	MVI	M,DEADC
	RET
LIVE	MVI	M,ALIVEC
	RET
;NON-ECHOING CHARACTER INPUT ROUTINE.
GETCH:	IN	KSTAT	;GET STATUS
	ANI	RDR	;SEE IF KEY PRESSED.
	RZ		;RETURN IF NOT.
	IN	KDATA	;ELSE, GET CHARACTER.
	ANI	7FH	;STRIP PARITY.
	RET
;
; DELAY ROUTINE.
DELAY	PUSH	D	;SAVE REGISTERS.
	PUSH	B
	LDA	DMULT	;GET DELAY MULTIPLIER.
	MOV	B,A	;PUT IN REG. B.
	ORA	A	;WANT MAXIMUM SPEED?
	JZ	NODEL	;SKIP DELAY IF SO.
DELA1	LXI	D,16665	;LOAD LOOP COUNT.
DELA2	DCX	D	;DECREMENT COUNT.
	MOV	A,D
	ORA	E	;ZERO YET?
	JNZ	DELA2	;CONTINUE IF NOT.
	DCR	B	;DECREMENT DELAY MULTIPLIER.
	JNZ	DELA1
NODEL	POP	B	;RESTORE REGISTERS.
	POP	D
	RET
;
SETSPD	SUI	30H	;REMOVE ASCII BIAS.
	STA	DMULT	;UPDATE DELAY MULTIPLIER.
	JMP	READY
;
ONEGEN	MVI	A,1	;SET ONEFLAG.
	STA	ONEFLAG
	MVI	A,'G'	;PUT G IN ACC.
	RET
;
HELP	CALL	CLS	;CLEAR SCREEN, INIT CURSOR.
	LXI	D,HMESS	;POINT TO MESSAGE.
	MVI	C,9	;PRINT BUFFER FUNCTION FOR CP/M.
	CALL	ENTRY	;PRINT THE MESSAGE.
WAIT	CALL	GETCH	;WAIT FOR KEY DEPRESSION.
	JZ	WAIT
	JMP	MLOOP	;THEN RESTART.
;
HMESS	DB	'AVAILABLE '
	DB	'COMMANDS '
	DB	'ARE:'
	DB	CR
	DB	LF
	DB	'H =	HELP.'
	DB	CR
	DB	LF
	DB	'C =	CLEAR '
	DB	'SCREEN.'
	DB	CR
	DB	LF
	DB	'M =	GO TO '
	DB	'MIDDLE OF '
	DB	'SCREEN.'
	DB	CR
	DB	LF
	DB	'N =	GO '
	DB	'NORTH.'
	DB	CR
	DB	LF
	DB	'S =	GO SOUTH.'
	DB	CR
	DB	LF
	DB	'E =	GO EAST.'
	DB	CR
	DB	LF
	DB	'W =	GO WEST.'
	DB	CR
	DB	LF
	DB	'G =	GRANT '
	DB	'LIFE.'
	DB	CR
	DB	LF
	DB	'O =	DO ONE '
	DB	'GENERATION '
	DB	'THEN HALT.'
	DB	CR
	DB	LF
	DB	'L =	MAKE '
	DB	'LIVE CELL.'
	DB	CR
	DB	LF
	DB	'K =	KILL CELL.'
	DB	CR
	DB	LF
	DB	'NUMBER '
	DB	'0 - 9 = '
	DB	'SET DELAY '
	DB	'(0 = FASTEST '
	DB	'SPEED).'
	DB	CR
	DB	LF
	DB	'ANY KEY = '
	DB	'HALT, AND '
	DB	'WAIT FOR '
	DB	'NEW COMMAND.'
	DB	CR
	DB	LF
	DB	CR
	DB	LF
	DB	'PRESS '
	DB	'ANY KEY '
	DB	'TO RESTART.'
	DB	'$'	;DELIMITER.
;
EXIT	LHLD	OLDSTK  ;RESTORE CP/M'S STACK.
	SPHL
	RET		;BACK TO CPM.
;
;ROUTINE TO CLEAR SCREEN AND INITIALIZE CURSOR.
CLS	LXI	H,VDM	;POINT TO SCREEN MEMORY.
	MVI	M,80H+' ' ;THIS IS THE CURSOR.
	INX	H	;BUMP POINTER.
CLS1	MVI	M,' '	;STORE BLANK.
	INX	H	;NEXT....
	MOV	A,H	;SEE IF END OF SCREEN YET.
	CPI	VDMU+10H
	JC	CLS1	;CONTINUE IF NOT.
	RET
;
ONEFLAG	DB	0	;FLAG FOR ONE GENERATION MODE.
DMULT	DB	0	;DELAY MULTIPLER.
;
	DS	12	;SPACE FOR LOCAL STACK.
NEWSTK	EQU	$	;LOCAL STACK TOP.
OLDSTK	DS	2	;SAVE CPM'S STACK HERE.
;
	END
ُصے÷‏÷ُےَs÷ے÷؟Œےےےےےےےے„ےےےlےˆےےےےôٍِِvےٍِِôٍِِôِےےےےےےےےےےHے	ےےےےے÷w÷سُے÷ُےِے÷÷÷ُ_Lےے
ےےˆےےے€ےےےےےےےےےے‎ے¾ےےûے‏‏ûے‏ےےےےےے@ےےے€ےےےےےےےےےے