*******************************************************************************
*       ;TESTER TO TAKE CARE OF YOUR MEMORY WORRIES IN A FLASH                *
*	;JUST ASSEMBLE FOR YOUR MEMORY SIZE AND DOUBLER LOCATION.             *
*	;N.B.-THIS TEST WRITES OVER CP/M ETC.,SO PROGRAM MUST BE              *
*	;TERMINATED WITH A COLDBOOT (^C) FROM KEYBOARD (OR RESET).            *
*	;Last updated  November 13, 1979   by  M.GILLILAND		      *
*******************************************************************************
BASE	SET	BOTTOM-X	;START ADDRESS OF RAM TO BE TESTED

ENDPAGE EQU	0F800H		;SET THIS TO THE LAST PAGE + 1

DESTINY	EQU	0000H		;THE TEST PROGRAM WILL GET MOVED HERE

* Note:	Be sure that the program is not within the test area.

HOLDOFF	EQU	1000		;NUMBER OF SYNC TRIES BETWEEN PATTERNS
				;IN AN ATTEMPT TO KILL MEMORY REFRESH
				;AT ABOUT 250 uS PER COUNT (4000 = 1 SEC)

DOUBLER	EQU	0F800H		;DOUBLER BASE ADDRESS
*******************************************************************************
BUFF	EQU	DOUBLER+400H	;RAM BUFFER ON DOUBLER
COMMAND	EQU	BUFF+3
WRCONT	EQU	DOUBLER+600H	;HARDWARE PORTS ON DOUBLER
WRCLK	EQU	WRCONT+1	;SYNC REGISTER
SYNCPRT	EQU	WRCONT+7	;SYNCPORT
URTSTAT	EQU	WRCONT+0AH	;UART STATUS PORT
URTDATA EQU	WRCONT+2H	;UART DATA PORT
*******************************************************************************
	ORG	100H
	JMP	MOVER           ;MOVE TESTER TO LOW MEMORY

X	EQU	$-DESTINY       ;ADDRESS OFFSET FOR BLOCK MOVE
  
STARTAD	LDA	COMMAND		;GET THE DEFAULT DOUBLER COMMAND
	ORI	4		;SET THE DENSITY BIT
	STA 	WRCONT		;SET THE DOUBLER PLO FOR SINGLE DENITY
	MVI	B,0H		;CLEAR PATTERN MODIFIER IN B REG.
	LXI	D,0H		;CLEAR PASS COUNTER
LOOP1	CALL	PASSMSG-X	;OUTPUT PASS COUNT AND PATTERN TO CRT
LOOP	LXI	H,BASE		;LOAD STARTING ADDRESS OF TEST
FILL	MOV	A,L		;MOVE LOW ADDRESS INTO ACC.
	XRA	H		;GENERATE TEST BYTE
	XRA	B		;MODIFY WITH B REG.
	MOV	M,A		;STORE TEST BYTE IN MEMORY CELL
	CALL	ENDTEST-X	;NEXT ADDRESS TO DO...SETS ZERO FLAG IF DONE
	JNZ	FILL-X		;IF NOT ZERO, CONTINUE TO GENERATE TEST BYTES
SYNC	LXI	H,WRCLK		;NOW GET READY TO SYNC ON DISK(TEST REFRESH)
	MVI	M,0		;NONEXISTENT SYNC BYTE TO LATCH ON
	LXI	H,HOLDOFF	;COUNT THE NUMBER OF SYNC TRIES
	INX	H		;CORRECT THE COUNT
     	PUSH	D		;SAVE PASS COUNTER
	LXI	D,SYNCPRT	;HARDWARE PORT ON DOUBLER
SYNLOOP	LDAX	D		;SYNC ON UP,GUYS (0FFH CLOCK IN HEADER)
	DCX	H		;DECREMENT SYNC COUNT
	MOV	A,H		;GET THE HIGH ORDER COUNT
	ORA	A		;COMPLETED NUMBER OF TRIES IN 'HOLDOFF'?
	JNZ	SYNLOOP-X	;NOT YET,TRY AGAIN
	POP	D		;RESTORE PASS COUNTER
	LXI	H,BASE		;RELOAD START ADDRESS FOR TEST CYCLE
TEST	MOV	A,L		;LOW BYTE USED TO GENERATE PATTERN
	XRA	H		;GENERATE TEST BYTE
	XRA	B		;MODIFY WITH CONTENTS OF B REG.
	MOV	C,M		;GET MEMORY DATA BYTE
	CMP	C		;COMPARE WITH MEMORY CELL CONTENTS (IN C REG.)
	CNZ	ERRMSG-X	;IF NOT EQUAL,OUTPUT ADDRESS OF FAULTY CELL
REBOOT	CALL 	CONSTAT-X	;SHOULD TEST END??
	JZ	NEXT-X		;IF ZERO FLAG,THEN NO CHARACTER: CONTINUE TEST
	CALL	CONIN-X		;GET CHARACTER
	CPI	13H		;IS IT A ^S ?
	CZ	WAIT-X		;IF YES, ENTER WAIT LOOP (STOP SCREEN SCROLL)
	CPI	3H		;IS IT A ^C ?
	JZ	EXIT-X		;IF YES, REBOOT SYSTEM 
NEXT	CALL	ENDTEST-X	;NEXT ADDRESS TO DO...SETS ZERO FLAG IF DONE
	JNZ	TEST-X		;CONTINUE THIS TEST LOOP,IF NOT FINISHED.
	MOV 	A,B		;HAS 1 PASS (256 PATTERN LOOPS) BEEN DONE?
	CPI	0FFH		;
	CZ	UPDATE-X	;UPDATE PASS COUNTER IF YES
	INR	B		;OTHERWISE,GENERATE NEXT PATTERN MODIFIER....
	JMP	LOOP1-X		;AND EXECUTE NEXT PATTERN LOOP
UPDATE	INX	D		;PASS COUNT + ONE
	RET
WAIT	CALL	CONSTAT-X	;HAS ANOTHER KEY BEEN PRESSED??
	JZ	WAIT-X		;IF NO (ZERO FLAG): WAIT UNTIL ENTRY
	CALL	CONIN-X 	;CLEAR UART STATUS FLAG
	RET			;AND EXIT WAIT LOOP
ENDTEST	INX	H		;NEXT ADDRESS TO DO
	MOV	A,H		;GET PAGE ADDRESS
	CPI	ENDPAGE/100H	;DONE???(SET ZERO FLAG IF YES)
	RET
*******************************************************************************
*		;PRINT OUT ROUTINES FOR TESTER
*******************************************************************************
CONSTAT	LDA	URTSTAT		;READ UART STATUS
	ANI 	2		;IS THERE A CHARACTER?
	RET			;RETURNS WITH ZERO SET IF NOT READY
CONIN	CALL	CONSTAT-X	;
	JZ	CONIN-X		;LOOP UNTIL INPUT FROM KEYBOARD
	LDA	URTDATA		;INPUT CHARACTER
	ANI	7FH		;STRIP PARITY BIT
	RET
PRINT	MOV	A,M		;GET CHARACTER IN MESSAGE STRING
	CPI	'$'		;IS IT THE END OF THE STRING?
	RZ			;RETURN IF DONE
	MOV	C,A		;PUT CHARACTER INTO C REG.
	CALL	CHAROUT-X	;OUTPUT TO SCREEN
	INX	H		;NEXT CHARACTER IN STRING
	JMP	PRINT-X		;REPEAT THE LOOP
CHAROUT	LDA	URTSTAT		;GET UART STATUS
	ANI 	1		;
	JZ	CHAROUT-X	;WAIT FOR UART READY FLAG
	MOV 	A,C		;PUT OUTPUT CHARACTER IN ACC.
	STA	URTDATA		;OUTPUT TO UART
	RET			;DONE
MOVHEX	MOV	A,D		;HIGH BYTE 
	CALL	PRNHEX-X	;HEX TO ASCII CONVERSION AND STORAGE
	MOV	A,E		;LOW BYTE 
	CALL	PRNHEX-X	;HEX TO ASCII CONVERSION AND STORAGE
	RET
PRNHEX	PUSH	PSW		;SAVE FOR LSNIBBBLE CONVERSION
	RAR!RAR!RAR!RAR!	;SHIFT MSN TO LSN
	CALL	SAVEHEX-X	;CONVERT HEX TO ASCII
	POP 	PSW		;NOW DO THE LSN
SAVEHEX	ANI	0FH		;
	ADI	30H		;SHIFT TO ASCII VALUE
	CPI	3AH		;OVER 9?
	JC	STORE-X		;SAVE IN STRING FOR OUTPUT
	ADI	7		;SHIFT TO ALPHA
STORE	MOV	M,A		;STORE IN MSG STRING LOCATION
	INX	H		;NEXT MSG LOCATION
	RET
PASSMSG	PUSH 	H		;SAVE CURRENT ADDRESS OF MEMORY CELL 
	PUSH	D		;SAVE PASS COUNT
	PUSH	B		;SAVE PATTERN MODIFIER
	LXI	H,MSG1-X	;LOCATION TO INSERT PASSCOUNT MESSAGE
	CALL	MOVHEX-X	;HEX TO ASCII CONVERSION AND STORAGE (2 BYTES)
	MOV	A,B		;GET PATTERN-TYPE INTO ACC.  
	LXI	H,MSG0-X	;LOCATION TO INSERT PATTERN-TYPE MESSAGE
	CALL	PRNHEX-X	;HEX TO ASCII CONVERSION AND STORAGE
	LXI	H,STRING1-X	;PASS COUNT MESSAGE
	CALL	PRINT-X		;OUT TO SCREEN
	POP	B		;RESTORE STATUS
 	POP	D		;
	POP	H		;
	RET
ERRMSG	PUSH	H		;SAVE STATUS OF TEST
	PUSH 	D		;
	PUSH	B		;
	MOV	D,H		;PLACE TEST CELL PAGE VALUE IN D REG.
	MOV	E,L		;PLACE LOW ORDER ADDRESS IN E REG.
	LXI	H,MSG3-X	;LOCATION TO INSERT PATTERN BYTE INTO STRING
	CALL	PRNHEX-X	;HEX TO ASCII CONVERSION AND STORAGE
	MOV	A,C		;GET MEMORY BYTE FOR DISPLAY (IN C REG.)
	LXI	H,MSG4-X	;LOCATION TO INSERT MEMORY DATA INTO STRING
	CALL	PRNHEX-X	;HEX TO ASCII CONVERSION AND STORAGE
	LXI	H,MSG2-X	;LOCATION TO INSERT MEMORY ADDRESS INTO STRING
	CALL	MOVHEX-X	;HEX TO ASCII CONVERSION AND STORAGE (2 BYTES)
	LXI	H,STRING2-X	;LOCATION OF ERROR STRING
	CALL	PRINT-X		;OUTPUT ERROR MESSAGE TO SCREEN
	POP	B		;RESTORE STATUS
	POP	D		;
	POP	H		;
	RET
EXIT	LXI	H,SIGNOFF-X	;Load the mess.
	CALL	PRINT-X		;Put it on the screen
	JMP	DOUBLER
***********************************************************************
*		;MESSAGE AREA: STAY CLEAR			      *
***********************************************************************
CR	EQU	0DH
LF	EQU	0AH
STRING1	DB	CR	
	DB	'MEMORY TEST: Pass #'
MSG1	DB	'0000 Hex; Pattern #'
MSG0	DB	'00 Hex$'
STRING2 DB	CR,LF
	DB	'** COMPARE ERROR ** Location:'
MSG2	DB	'0000 Hex, Pattern:'
MSG3	DB	'00 Hex, Memory Data:'
MSG4	DB	'00 Hex'
	DB	LF,'$'
SIGNOFF	DB	CR,LF,'Insert system diskette in Drive A to coldboot$'

BOTTOM	EQU	$

SIGNMSG	DB	CR,LF
	DB	'Welcome to the Memory Test for a DOUBLER system based at '
MSG5	DB	'0000 Hex'
	DB	CR,LF
	DB	'This version tests memory from '
MSG6	DB	'0000 Hex up to '
MSG7	DB	'0000 Hex.'
	DB	CR,LF
	DB	'Remove any diskettes and press <RETURN> to start the test'
	DB	CR,LF
	DB	'Press <^C> to terminate the test'
	DB	CR,LF
	DB	'Press <^S> to toggle the test ON/OFF'
	DB	CR,LF,'$'

******************************************************************************
*	;MOVER IS SOURCE FOR BLOCK MOVE TO PLACE TESTER AT 0000HEX	     *
*******************************************************************************

MOVER:	LXI	H,STARTAD	;BLOCK START
	LXI	B,DESTINY	;BLOCK DESTINATION=0000H
MOVIT:	MOV	A,M		;GET PROGRAM BYTE
	STAX	B		;STORE AT NEW LOCATION	
	INX 	H		;NEXT BYTE TO MOVIT
	INX	B		;NEXT LOCATION TO INSERT DATA
	MOV	A,H		;TEST FOR END OF BLOCK
	CPI	BOTTOM/100H + 1	;	
	JC	MOVIT		;IF NOT DONE, CONTINUE
				;OTHERWISE EXECUTE SIGNON
	LXI	SP,BUFF+3FH	;DEFINE STACK ADDRESS
SIGNON	LXI	D,DOUBLER	;GET CONTROLLER ADDRESS 
	LXI	H,MSG5  	;LOCATION FOR INSERT INTO SIGNON MSG
	CALL	MOVHEX-X	;HEX TO ASCII CONVERSION AND STORAGE
	LXI	D,BASE		;GET MEMORY START ADDRESS
	LXI	H,MSG6  	;LOCATION TO INSERT INTO SIGNON
	CALL	MOVHEX-X	;HEX TO ASCII CONVERSION AND STORAGE
	LXI	D,ENDPAGE	;GET MEMORY SIZE	
	LXI	H,MSG7	 	;LOCATION TO INSERT INTO SIGNON
	CALL	MOVHEX-X	;HEX TO ASCII CONVERSION AND STORAGE
	LXI	H,SIGNMSG  	;ADDRESS OF MESSAGE STRING FOR SIGNON
	CALL	PRINT-X		;OUTPUT ROUTINE 
GO	CALL	CONIN-X		;WAIT FOR KEY ENTRY (USER RESPONSE)
	CPI	3		;IS IT ^C (REBOOT)???
	JZ 	EXIT-X		;IF YES,THEN REBOOT
	CPI	0DH		;IS IT CR?
	JNZ	GO		;IF NO, WAIT FOR CR
	JMP	DESTINY		;IF YES, EXECUTE TESTER

	END	100H
