;
;	DOS MEMORY TEST
;
;	A COMBINATION OF ROWPAT AND GALPAT/GALCOL
;
;	ASSEMBLE USING ASM IN ALTAIR DOS
;
;
SETSTK:	LXI  SP,STACK
	DI
	MVI  A,3
	OUT  020
	MVI  A,021
	OUT  020	INIT ACIA
;
; FIND ALL 4K BLOCKS PRESENT IN THE SYSTEM
; (BLOCK 4 TO BLOCK 15)
;
FINDEM:	LXI  B,BRDBUF	POINT TO BOARD BUFFER
	MVI  E,4	FIRST BLOCK TO TRY
;
; CONVERT BLOCK NUMBER TO A 16-BIT START ADDRESS
;
.F	XRA  A
	MOV  L,A
	MOV  A,E	GET BLOCK NUMBER
	RLC
	RLC
	RLC
	RLC		...SHIFT INTO 4 MSB
	MOV  H,A	[HL] = ADDRESS OF FIRST BYTE OF BLOCK
;
; SEE IF THE FIRST BYTE OF THE BLOCK EXISTS AS RAM
;
	MOV  A,M	GET THE BYTE
	MOV  D,A	SAVE IT
	CMA		MAKE COMPLEMENT
	MOV  M,A	TRY TO STORE COMPLEMENT
	MOV  A,M	...AND THEN READ IT BACK
	CMA		RESTORE TO ORIGINAL (MAYBE)
	CMP  D		COMPARE TO KNOWN GOOD ORIGINAL
	JNZ  +G		 NO GOOD - TRY NEXT 4K BLOCK
;
; FOUND A RAM BOARD, SAVE BOARD (BLOCK) NUMBER IN BOARD BUFFER
;
	MOV  A,E	GET BOARD NUMBER
	STAX B		SAVE IN BUFFER
	INX  B		POINT TO NEXT POSITION
;
; INCREMENT TO NEXT 4K BLOCK, STOP LOOKING IF ALL  BLOCKS TRIED
;
.G	MVI  A,15
	INR  E		NEXT BLOCK NUMBER
	CMP  E		OVER THE TOP ?
	JP   -F		 NO  - GO TEST THIS NEW BLOCK
	MVI  A,0200	 YES - MARK END IN BUFFER
	STAX B
;
;
;
; ASK USER FOR CHOICE OF AUTOMATIC OR SELECTION MODE
;
MODE:	CALL PRNMSG	PRINT "AUTO MODE (Y/N)?"
	DW   MAUTO
	CALL GETSTG	GET ANSWER
	MVI  A,"Y"
	CMP  M		DO THEY WANT AUTO MODE ?
	JNZ  SELECT	 NO - THEN LET THEM SELECT
;
; AUTO TEST SEQUENCE STARTS HERE
;
	LXI  H,DROPLS
	SHLD DROPPT	SET DROP LIST POINTER TO START
	XRA  A
	DCX  H
	MOV  M,A	CLEAR DROP COUNT
	DCX  H
	MOV  M,A	...AND ERROR FLAG
.A	LXI  H,BRDBUF	POINT TO BOARD BUFFER
	SHLD BRDPT	SAVE POINTER FOR LOOPING
;
; AUTO SEQUENCE MAIN LOOP
;
; CONTROL-C CAUSES AUTOMATIC DUMP OF DROP BUFFER
;
AUTO:	IN   16
	RRC
	JNC  +B
     	IN   17
	ANI  0177
	CPI  3		GOT A CONTROL-C ?
	CZ   CDROP	  YES - SHOW WHO DIED
;
; TEST ERROR FLAG TO SEE WHETHER OR NOT WE DROP THE BLOCK JUST TESTED
;
.B	LDA  ERRFLG	GET ERROR FLAG
	ORA  A		...AND TEST IT
	JZ   +C
;
; NONZERO ERROR FLAG MEANS WE DROP THIS BLOCK
;
	LHLD DROPPT	POINT TO NEXT DROP LIST SLOT
	XCHG
	LHLD BLKST	GET BLOCK START ADDRESS
	XCHG		[HL] = DROP LIST POINTER, [DE] = BLOCK START
	MOV  M,E	SAVE LOW BYTE
	INX  H
	MOV  M,D	SAVE HIGH BYTE
	INX  H		[HL] NOW POINTS TO NEXT DROP LIST SLOT
	SHLD DROPPT	SAVE NEW POINTER
	LXI  H,DROPCT
	INR  M		INCREMENT DROP COUNT
	LHLD BRDPT	GET BOARD POINTER
	DCX  H		BACK UP TO BOARD JUST TESTED
	MVI  M,0	...AND LOG IT OUT
;
; GET NEXT BOARD NUMBER AND TEST FOR END OF BUFFER OR DROPPED BOARD
;
.C	XRA  A
	STA  ERRFLG	CLEAR ERROR FLAG
  	LHLD BRDPT	GET BOARD POINTER
	MOV  A,M	...AND BOARD NUMBER
	INX  H
	SHLD BRDPT	UPDATE AND SAVE NEW POINTER
	ORA  A		TEST BOARD NUMBER
	JM   -A		RESTART IF END OF BUFFER
	JZ   -C		TRY NEXT ONE IF DROPPED BOARD
;
; CONVERT BOARD NUMBER TO 4K BLOCK ADDRESS
;
	RLC
	RLC
	RLC
	RLC		BOARD NUMBER TO 4 MSB
	MVI  L,0
	MOV  H,A	MAKE A 16-BIT BLOCK ADDRESS
	SHLD BLKST	SAVE AS BLOCK START ADDRESS
	ADI  16		ADD 4K TO START
	MOV  H,A
	SHLD BLKEND	SAVE AS BLOCK END ADDRESS + 1
;
; FIX STACK SO RETURNS COME TO "AUTO" TO DO NEXT BOARD
;
	LXI  H,AUTO
	PUSH H
;
; PRINT THE RANGE THAT THIS SEQUENCE TESTS
;
	CALL PRNMSG	PRINT "TESTING "
	DW   MTRNG
	LHLD BLKST	GET BLOCK START ADDRESS
	CALL NUMASC	MAKE ASCII AND PRINT IT
	CALL PRNMSG	PRINT " TO "
	DW   MFILLR
	LHLD BLKEND	GET BLOCK END ADDRESS
	CALL NUMASC	MAKE ASCII AND PRINT IT
;
; DO GALPAT TEST IN 256-BYTE CHUNKS
; PATTERNS ARE 377, THEN 000
;
	CALL PRNMSG	PRINT "GALPAT"
	DW   MGALP
	LHLD BLKST	GET BLOCK START
.G	SHLD MEMBOT	SAVE AS "CHUNK" START
	INR  H		ADD 256
	MVI  L,0
	SHLD MEMTOP	...AND THAT'S THE "CHUNK" END +1
	MVI  A,0377	ALL ONES BACKGROUND PATTERN
	CALL GALPAT	TEST THE "CHUNK"
	XRA  A		ALL ZEROS BACKGROUND PATTERN
	CALL GALPAT	TEST THE SAME CHUNK AGAIN
	LHLD BLKEND	[DE] = "CHUNK" END, [HL] = BLOCK END
	CALL COMPAR	DONE ALL 16 "CHUNKS" IN THE BLOCK ?
	JZ   WAIT	 YES - GO KILL TIME
	XCHG			[HL] = NEW "CHUNK" START
	JMP  -G				GO TEST NEW "CHUNK"
;
; WAIT HERE FOR 1 MINUTE TO CHECK REFRESH
;
WAIT:	CALL PRNMSG	PRINT "WAITING"
	DW   MWAIT
	LXI  B,500	NUMBER OF TIMES TO DO 120 ms LOOP
.W	LXI  D,10000	NUMBER OF TIMES TO DO 12 us LOOP
.X	DCX  D			2.5 us
	MOV  A,D		2.5 us
	ORA  E			2.0 us
	JNZ  -X			5.0 us
	DCX  B
	MOV  A,B
	ORA  C
	JNZ  -W
;
; SEE IF GALPAT RESULT (ALL ZEROS) SURVIVED DELAY
;
	CALL PRNMSG	PRINT "RECHECK"
	DW   MRCHK
	LHLD BLKEND	GET BLOCK END
	XCHG		...IN [DE]
	LHLD BLKST	...AND BLOCK START IN [HL]
	XRA  A		THE RESULT WE'RE LOOKING FOR
.R	CMP  M		TEST GALPAT RESULT BYTE
	CNZ  RFERR	 NO - REPORT REFRESH ERROR
	INX  H
	CALL COMPAR	CHECKED WHOLE BLOCK ?
	JC   -R		 NO - THEN DO IT !
;
; DO GALCOL WITH ALL ONES, THEN ALL ZEROS ON THE WHOLE BLOCK
;
GC:	CALL PRNMSG	PRINT "GALCOL"
	DW   MGALC
	LHLD BLKST	GET BLOCK START
	SHLD MEMBOT	SAVE FOR GALCOL
	LHLD BLKEND	GET BLOCK END
	SHLD MEMTOP	SAVE FOR GALCOL
	MVI  A,0377	ALL ONES PATTERN
	CALL GALCOL	DO THE TEST
	XRA  A		ALL ZEROS PATTERN
	CALL GALCOL	TEST THE BLOCK AGAIN
;
; WALK PATTERNS THROUGH THE BLOCK IN 256-BYTE "CHUNKS"
; FIRST WRITE ONES, WALK ZEROS, THEN WRITE ZEROS, WALK ONES
;
	CALL PRNMSG	PRINT "WALKING PATTERNS"
	DW   MWALKS
	LHLD BLKST	GET BLOCK START
.Z	SHLD MEMBOT	SAVE AS "CHUNK" START
	INR  H		ADD 256
	MVI  L,0
	SHLD MEMTOP	RESULT IS "CHUNK" END + 1
	MVI  A,0377	GET ALL ONES BACKGROUND PATTERN
	CALL WALK	TEST THE CHUNK
	XRA  A		ALL ZEROS BACKGROUND PATTERN
	CALL WALK	TEST AGAIN
	LHLD BLKEND	[DE] = "CHUNK" END, [HL] = BLOCK END
	CALL COMPAR	DONE ALL "CHUNKS" IN THE BLOCK ?
	RZ		 YES - GO TO NEXT BOARD IN AUTO SEQUENCE;
	XCHG		 NO  - [HL] = NEW "CHUNK" START
	JMP  -Z		       GO TEST NEW "CHUNK"
;
; THIS ROUTINE PRINTS OUT THE DROP LIST
;
CDROP:	LDA  DROPCT		GET DROP COUNT
	ORA  A			DID WE LOSE ANYBODY ?
	JNZ  +C
	CALL PRNMSG		  NO - THEN SAY SO !!!
	DW   NODROP
	RET
.C	CALL PRNMSG		  YES - TELL THEM WHAT THE NUMBERS MEAN
	DW   HDROP
	LXI  H,DROPLS		POINT TO DROP LIST
.D	MOV  E,M		GET LOW BYTE
	INX  H
	MOV  D,M
	INX  H		[HL] NOW POINTS TO NEXT DROP LIST SLOT
	XCHG		WE NEED ADDRESS IN [HL] FOR CALL
	CALL NUMASC
	CALL PRNMSG	CONVERT AND PRINT ADDRESS AND DO CRLF
	DW   MCRTLF
	XCHG		RESTORE [HL] AND [DE]
	DCR  A		ONE LESS ADDRESS TO DISPLAY
	JNZ  -D		DO THEM ALL
	RET		...AND THEN GO HOME
;
;
;
; THIS IS THE ENTRY POINT FOR "SELECT" MODE
; USER DEFINES ADDRESS RANGE, PATTERN, AND TEST TYPE
;
SELECT:	CALL PRNMSG	ASK FOR LOW ADDRESS LIMIT
	DW   MLOMEM
	CALL GETSTG	GET ANSWER
	CALL ASCNUM	MAKE BINARY
	SHLD MEMBOT	SAVE LOW LIMIT
	CALL PRNMSG	ASK FOR UPPER LIMIT
	DW   MHIMEM
	CALL GETSTG	GET ANSWER
	CALL ASCNUM	MAKE BINARY
	MOV  A,L
	ORA  H		UPPER LIMIT OF ZERO ?
	JZ   MODE	 YES - THEN RESTART
	INX  H		 NO  - THEN GO ONE PAST END
	SHLD MEMTOP		...AND SAVE END+1
;
; DISPLAY TEST OPTIONS AND GET USER'S CHOICE
;
.O	CALL PRNMSG	SHOW OPTIONS, ASK FOR CHOICE
	DW   MOPTN
	CALL GETSTG	GET THE ANSWER
	CALL ASCNUM	...IN BINARY
	MOV  A,L
	ORA  H		WAS IT ZERO ?
	JZ   SELECT	 YES - GO ASK FOR LIMITS AGAIN
	MOV  A,H
	ORA  A		TEST NUMBER > 255 ?
	JNZ  -O		 YES - GIVE THE JERK ANOTHER CHANCE
	ORA  L
	CPI  4		TEST NUMBER > 3 ?
	JNC  -O		 YES - SOME PEOPLE NEVER LEARN !!
	MOV  B,A	SAVE TEST NUMBER IF OK
;
; ASK USER TO DEFINE A PATTERN TO BE WRITTEN
;
	CALL PRNMSG	ASK FOR PATTERN
	DW   MGTPAT
	CALL GETSTG	GET HIS ANSWER
	CALL ASCNUM	...IN BINARY
	MOV  C,L	HIDE THE LOW BYTE
;
; BRANCH TO THE CHOSEN TEST
;
	LXI  D,SELECT
	PUSH D		WE GO TO "SELECT" AT END OF TEST
	DCR  B		ADJUST TEST NUMBER
	MOV  A,B
	RLC		...FOR TABLE JUMP
	ADD  B
	MOV  E,A
	MVI  D,0	MAKE A 16-BIT INDEX
	LXI  H,BTAB	GET TABLE BASE
	MOV  A,C	GET PATTERN READY
	DAD  D		[HL] = ADDRESS OF JUMP
	PCHL		BRANCH TO JUMP TO TEST
BTAB:	JMP  GALPAT
	JMP  GALCOL
	JMP  WALK
	RET		THIS SHOULD'NT GET USED
;
;
;
;
;*********************************************************************
;
;	ERROR PROCESSING ROUTINES
;
;
; ERROR IN REFRESH (GALPAT RESULT BAD AFTER 1-MINUTE DELAY)
;
RFERR:	CMA
	STA  ERRFLG	MAKE ERROR FLAG NONZERO
	CMA		RESTORE [A]
      	CALL PRNMSG	PRINT "REFRESH ERROR AT "
	DW   MRFSH	
	PUSH H		SAVE ERROR ADDRESS FOR EXIT
	CALL NUMASC	MAKE ASCII AND PRINT IT
	CALL PRNMSG
	DW   MRFSH2	PRINT "EXPECTED 252, READ "
	POP  H		RESTORE POINTER TO BAD DATA
	PUSH H		...BUT DON'T CHANGE STACK
	MOV  L,M	GET BAD DATA
	MVI  H,0
	CALL NUMASC	MAKE ASCII AND PRINT IT
	CALL PRNMSG
	DW   MCRTLF	 ...FOLLOWED BY CRLF
	POP  H		GET ERROR ADDRESS BACK
	RET		CONTINUE TESTING
;
; ERROR IN WALKING PATTERNS
;
PATERR:	CALL PRNMSG
	DW   MRER1	PRINT "ERROR AT ADDRESS "
	PUSH H		SAVE BAD ADDRESS
	CALL NUMASC	CONVERT TO ASCII AND PRINT IT
	CALL PRNMSG
	DW   MRER2	PRINT " : READ "
	POP  H		GET BAD ADDRESS
	PUSH H		...BUT DON'T CHANGE STACK
	MOV  L,M	GET BAD DATA
	MVI  H,0
	CALL NUMASC	MAKE ASCII AND PRINT
	CALL PRNMSG
	DW   MRER3	PRINT " , EXPECTED "
	MOV  L,A	GET GOOD DATA
	MVI  H,0
	CALL NUMASC	MAKE ASCII AND PRINT
	LHLD LASTWR	GET "LAST WRITTEN" ADDRESS
	CALL PRNMSG
	DW   MRER4	PRINT "LAST ADDRESS WRITTEN WAS"
	CALL NUMASC	CONVERT AND PRINT "LAST WRITTEN" ADDRESS
	PUSH PSW
	LDA  TPATT	[A] = TEST PATTERN
	CALL PRNMSG
	DW   MRER5	PRINT " PATTERN WAS "
	MVI  H,0
	MOV  L,A
	CALL NUMASC	CONVERT AND PRINT TEST PATTERN
	MVI  A,0377
	STA  ERRFLG	SET ERROR FLAG
	POP  PSW
	POP  H		RESTORE CALLER'S [HL]  (BAD ADDRESS)
	RET		CONTINUE TESTING
;
;
; ERROR IN GALPAT OR GALCOL
;
ERROR1:	CALL PRNMSG	PRINT "OTHER CELL"
	DW   MERROC
	JMP  COMERR	GO TO COMMON ERROR PROCESSING
ERROR2:	CALL PRNMSG	PRINT "TEST CELL"
	DW   MERRTC
COMERR:	PUSH H
	PUSH D
	PUSH B
	PUSH PSW	PROTECT THE INNOCENT
	CALL PRNMSG	PRINT "TEST CELL = "
	DW   MTCELL
	XCHG
	CALL NUMASC
	XCHG
	CALL PRNMSG	PRINT "OTHER CELL = "
	DW   MOCELL
	CALL NUMASC
	CALL PRNMSG	PRINT "PATTERN STORED = "
	DW   MSTPAT
	MOV  L,B
	MVI  H,0
	CALL NUMASC
	CALL PRNMSG	PRINT "PATTERN READ = "
	DW   MRDPAT
	MOV  L,A
	MVI  H,0
	CALL NUMASC
	CALL PRNMSG	PRINT CRLF
	DW   MCRTLF
	MVI  A,0377
	STA  ERRFLG	SET ERROR FLAG
	POP  PSW
	POP  B
	POP  D
	POP  H		EVERYBODY BACK FROM EXILE
	RET
;
;
;
;
;*********************************************************************
;
;	THESE ARE THE ACTUAL TEST ROUTINES
;
;********************************************************************
;
; GALPAT
;
GALPAT:	MOV  B,A	SAVE PATTERN
	CALL BAKPAT	WRITE BACKGROUND PATTERN
GPSTRT:	LHLD MEMBOT
	XCHG		[DE] = TEST CELL ADDRESS, [HL] = MEMTOP
GPNCEL:	PUSH D
	LHLD MEMBOT	[HL] = ADDRESS OF OTHER CELL
	LDAX D		GET CONTENTS OF TEST CELL
	CMA		COMPLEMENT IT
	STAX D		...AND STORE IT BACK
GPLOOP:	POP  D		GET BACK ADDRESS OF TEST CELL
	PUSH D
	CALL COMPAR	SEE IF TEST ADDRESS = OTHER ADDRESS
	JZ   GPSKIP	 YES - SKIP TESTING
	MOV  A,M	LOAD OTHER CELL
	CMP  B		EQUAL TO PATTERN ?
	CNZ  ERROR1	 NO - REPORT ERROR
	LDAX D		READ BACK TEST CELL
	CMA		BACK TO ORIGINAL STATE
	CMP  B		EQUAL TO PATTERN ?
	CNZ  ERROR2	 NO -REPORT ERROR
GPSKIP:	INX  H		BUMP OTHER ADDRESS
	XCHG		[DE] = MEMTOP
	LHLD MEMTOP
	XCHG
	CALL COMPAR	AT TOP OF BLOCK ?
	JNZ  GPLOOP	 NO - CONTINUE
	POP  D		 YES - GET BACK TEST ADDRESS
	LDAX D		LOAD TEST CELL
	CMA		COMPLEMENT BACK TO ORIGINAL
	STAX D		AND STORE BACK
	INX  D		NEXT TEST ADDRESS
	CALL COMPAR	AT TOP OF BLOCK ?
	JNZ  GPNCEL	 NO - CONTINUE
	RET
;
;
; GALCOL
;
GALCOL:	MOV  B,A	SAVE PATTERN
	CALL BAKPAT	WRITE BACKGROUND
	LHLD MEMBOT
GCNCEL:	PUSH H		SAVE TEST CELL ADDRESS
	MOV  A,M	GET CONTENTS OF TEST CELL
	CMA		COMPLEMENT IT
	MOV  M,A	STORE IT BACK
GCLOOP:	MVI  E,64
	MVI  D,0
	DAD  D
	XCHG		[DE] = [HL] + 64
	LHLD MEMTOP
	CALL COMPAR
	XCHG
	JM   GCINCR
	JZ   GCINCR	UP TEST CELL ADDRESS IF NEW [DE] < MEMTOP
	POP  D
	PUSH D
	LDAX D		GET TEST CELL
	CMA
	CMP  B		IS COMPLEMENT = PATTERN ?
	CNZ  ERROR1	 NO - REPORT ERROR
	MOV  A,M	TEST OTHER CELL
	CMP  B		EQUAL TO PATTERN ?
	CNZ  ERROR2	 NO - REPORT ERROR
	JMP  GCLOOP	TRY NEXT OTHER CELL
GCINCR:	POP  H		GET TEST CELL ADDRESS
	MOV  A,M	GET CONTENTS
	CMA		COMPLEMENT BACK TO ORIGINAL
	MOV  M,A	RESAVE
	CALL INCM64	INCREMENT TEST ADDRESS MOD 64
	MOV  A,M	EQUAL TO PATTERN ?
	CMP  B
	CNZ  ERROR1
	CALL INCM64
	MOV  A,M
	CMP  B
	CNZ  ERROR1
	CALL INCM64
	ANI  077	TEST MOD 64
	RZ		RETURN IF WRAPPED TO START ADDRESS
	JMP  GCNCEL	ELSE DO NEXT TEST CELL
;
;
; WALKING PATTERNS
;
WALK:	CALL BAKPAT	WRITE FULL BACKGROUND
	LHLD MEMBOT	[HL] = FIRST BYTE OF BLOCK ADDRESS
	PUSH H		SAVE FOR INSTANT RESET
	CMA		COMPLEMENT OF BACKGROUND IS TEST PATTERN
	STA  TPATT	SAVE PATTERN
;
; WRITE TEST PATTERN
;
.W	MOV  M,A	STORE TEST PATTERN
	PUSH H		SAVE "LAST WRITTEN" ADDRESS
	SHLD LASTWR	SAVE "LAST WRITTEN" ADDRESS
	INX  H
	CMA		RESET [A] TO BACKGROUND PATTERN
;
; TEST BACKGROUND PATTERN FROM ("LAST WRITTEN" + 1) TO (END OF BLOCK)
;
.X	CALL COMPAR	TESTED ALL REMAINING BACKGROUND ?
	JZ   WKSET	 YES - GO TEST PATTERN BYTES
	CMP  M		 NO  - TEST BACKGROUND BYTE
	CNZ  PATERR		BAD - REPORT ERROR
	INX  H
	JMP  -X
;
; SET UP FOR NEXT SECTION
;
WKSET:	XCHG		[HL] = LAST ADDRESS IN BLOCK + 1
	POP  D		[DE] = "LAST WRITTEN" ADDRESS
	XTHL		[HL] = FIRST ADDRESS IN BLOCK
	PUSH D		"LAST WRITTEN" TO STACK
	CMA		GET TEST PATTERN BACK
;
; TEST PATTERN BYTES FROM (START OF BLOCK) TO ("LAST WRITTEN")
;
.Y	CMP  M		TEST A BYTE
	CNZ  PATERR	ERROR IN WALKED TEST PATTERN
	CALL COMPAR	TESTED ALL WRITTEN BYTES ?
	INX  H
	JNZ  -Y		 NO - THEN DO IT !!
;
; SET UP FOR NEXT PATTERN BYTE
;
	POP  H		[HL] = "LAST WRITTEN" ADDRESS
	POP  D		[DE] = LAST ADDRESS IN BLOCK + 1
	INX  H		[HL] = NEXT ADDRESS TO WRITE
	CALL COMPAR	DONE THE WHOLE THING ?
	RZ		 YES - THAT'S ALL
	PUSH H
	LHLD MEMBOT
	XTHL		MAKE SURE A COPY OF MEMBOT IS ON THE STACK
	JMP  -W
;
;
;
;**********************************************************************
;
;	SUPPORT ROUTINES
;
;*********************************************************************
;
;
; GENERATE BACKGROUND PATTERN (CONTAINED IN [A])
;
BAKPAT:	LHLD MEMTOP
	XCHG
	LHLD MEMBOT	[HL] = MEMBOT, [DE] = MEMTOP
BACKGR:	CALL COMPAR
	RZ		RETURN WHEN DONE
	MOV  M,A	STORE PATTERN
	INX  H
	JMP  BACKGR
;
;
; INCREMENT [HL] MOD 64
;
INCM64:	INX  H
	MOV  A,L
	ANI  077	TEST FOR WRAP-AROUND
	RNZ
	MOV  A,L	SUBTRACT 64 TO ADJUST
	SUI  0100
	MOV  L,A
	RNC
	DCR  H		BORROW IF NEEDED
	RET
;
;
; PRINT MESSAGE POINTED TO BY ADDRESS FOLLOWING CALL
;
PRNMSG:	XTHL		GET RETURN ADDRESS
	PUSH D
	MOV  E,M
	INX  H
	MOV  D,M
	INX  H
	XCHG		[HL] = ADDRESS OF MESSAGE
	CALL PRNSTG	PRINT THE MESSAGE
	XCHG
	POP  D
	XTHL		RESTORE [HL] AND RETURN ADDRESS
	RET
;
;
; PRINT STRING POINTED TO BY [HL] UNTIL NULL TERMINATOR
;
PRNSTG:	PUSH PSW
PSNEXT:	MOV  A,M	GET A CHAR FROM THE STRING
	ORA  A		IS IT THE TERMINATOR ?
	JZ   PSFINI	 YES - END OF STRING
	CALL PRNCHA	 NO  - PRINT IT
	INX  H		POINT TO NEXT VICTIM
	JMP  PSNEXT	AND GO BACK TO TRY TO PRINT
PSFINI:	POP  PSW
	RET
;
;
; PRINT A CHARACTER (ACIA, CHANNEL 20 OCTAL)
;
PRNCHA:	PUSH PSW	
PCWAIT:	IN   16		GET STATUS
	ANI  2		CAN WE SEND ?
	JZ   PCWAIT	 NO - WE'LL WAIT
	POP  PSW	GET DATA BACK
	OUT  17		...AND SEND IT
	RET
;
;
;
; READ A CHARACTER STRING INTO IOBUFR UNTIL RETURN
;
GETSTG:	PUSH PSW
GSSTRT:	LXI  H,IOBUFR	POINT TO BUFFER
GSNEXT:	CALL GETCHA	GET A CHARACTER
	CALL PRNCHA	...ECHO IT
	CPI  13		IS IT RETURN ?
	JZ   GSFINI	 YES - THAT'S ALL
	CPI  "@"	 NO  - IS IT "CLEAR BUFFER" ?
	JZ   GSSTRT		YES - START OVER
	MOV  M,A		NO  - PUT CHAR IN BUFFER
	INX  H		POINT TO NEXT BUFFER POSITION
	JMP  GSNEXT	...AND GET ANOTHER CHAR
GSFINI:	MVI  A,10	PRINT LF
	CALL PRNCHA
	XRA  A
	MOV  M,A	PUT ZERO TERMINATOR IN BUFFER
	POP  PSW
	LXI  H,IOBUFR	RETURN WITH [HL] POINTING TO STRING
	RET
;
;
; GET A CHARACTER FROM ACIA, CHANNEL 20 OCTAL
;
GETCHA:	IN   16		GET STATUS
	RRC		ANYTHING THERE ?
	JNC  GETCHA	 NO - WAIT
	IN   17		 YES- GET IT
	ANI  0177	STRIP PARITY
	RET
;
;
;
; CONVERT [HL] TO ASCII DIGIT STRING (OCTAL)
;
NUMASC:	PUSH B
	PUSH D
	PUSH PSW	HIDE EVERYBODY
	LXI  D,STGNUL	POINT TO NULL AT BUFFER END
	XRA  A
	STAX D		MAKE SURE IT'S STILL A ZERO
NADIV:	DCX  D		MOVE POINTER BACK
	MVI  A,8	FOR RADIX 8
	CALL DIVBYT
	ADI  "0"	ADD ASCII ZERO
	STAX D		PUT ASCII DIGIT IN BUFFER
	MOV  A,L
	ORA  H		[HL] EMPTY YET ?
	JNZ  NADIV	 NO - GO DO ANOTHER DIGIT
	XCHG		[HL] = POINTER TO FIRST DIGIT
	POP  PSW
	POP  D
	POP  B		RESTORE ALL
	CALL PRNSTG	GO PRINT THE DIGIT STRING
;
;
; CONVERT ASCII DIGIT STRING TO BINARY IN [HL]
; ENTER WITH [HL] POINTING TO ASCII STRING
;
ASCNUM:	PUSH PSW
	PUSH D
	PUSH B
	XCHG		GET POINTER TO STRING IN [DE]
	LXI  H,0	RESULT = 0
ANLOOP:	LDAX D		GET NEXT CHAR
	CPI  "0"
	JM   ANFINI	STOP IF < ASCII ZERO
	SUI  "0"	DROP ASCII BIAS
	MOV  B,A	SAVE DIGIT
	MVI  A,8	ASSUMED RADIX 8
	CMP  B		QUIT IF [B] >= [A]
	JZ   ANFINI
	JC   ANFINI
	DAD  H
	DAD  H
	DAD  H		[HL] = RESULT * RADIX  (8)
	MOV  A,L
	ADD  B		ADD IN NEW DIGIT
	MOV  L,A
	JNC  ANNCRY
	INR  H		ADD IN CARRY IF NEEDED
ANNCRY:	INX  D
	JMP  ANLOOP	GO DO NEXT CHAR
ANFINI:	POP  B
	POP  D
	POP  PSW	COME ON BACK
	RET
;
;
;
; DIVIDE [HL] BY [A]
; QUOTIENT RETURNED IN [HL], REMAINDER IN [A]
;
DIVBYT:	PUSH B
	PUSH D
	MOV  B,A	SAVE DIVISOR
	LXI  D,020	[D] = 0, [E] = BIT COUNT
DBSHFT:	DAD  H		SHIFT [HL] LEFT
	MOV  A,D
	RAL
	CMP  B
	JC   DBSKIP
	SUB  B		SUBTRACT DIVISOR FROM DIVIDEND
	INR  L
DBSKIP:	MOV  D,A
	DCR  E
	JNZ  DBSHFT
	POP  D
	POP  B
	RET
;
;
;
; COMPARE [HL] TO [DE]
;
COMPAR:	PUSH B
	MOV  B,A
	MOV  A,H
	SUB  D
	JNZ  CPFINI
	MOV  A,L
	SUB  E
CPFINI:	MOV  A,B
	POP  B
	RET
;
;
;
;
;******************************************************************
;
;	MESSAGE STRINGS
;
;*****************************************************************
;
;
MAUTO:	DB	13,10,10,"AUTO MODE (Y/N) ? ",0
MTRNG:	DB	"TESTING ",0
MFILLR:	DB	" TO ",0
MGALP:	DB	13,10,"GALPAT / ",0
MWAIT:	DB	"WAITING / ",0
MRCHK:	DB	"RECHECK / ",0
MGALC:	DB	"GALCOL / ",0
MWALKS:	DB	"WALKING PATTERNS",13,10,0
MLOMEM:	DB	"LOW ADDRESS ? ",0
MHIMEM:	DB	"HIGH ADDRESS ? ",0
MOPTN:	DB	13,10,"TEST OPTIONS ARE:",13,10
	DB	" 1- GALPAT",13,10
	DB	" 2- GALCOL",13,10
	DB	" 3- WALKING PATTERNS",13,10,10
	DB	"ENTER TEST NUMBER: ",0
MGTPAT:	DB	"ENTER PATTERN: ",0
;
;
MERRTC:	DB	7,7,7,13,10,"ERROR READING TEST CELL",13,10,0
MERROC:	DB	7,7,7,13,10,"ERROR READING OTHER CELL",13,10,0
MTCELL:	DB	13,10,"TEST CELL = ",0
MOCELL:	DB	", OTHER CELL = ",0
MSTPAT:	DB	" BYTE STORED = ",0
MRDPAT:	DB	", BYTE READ = ",0
MCRTLF:	DB	13,10,0
MRFSH:	DB	7,7,7,13,10,"REFRESH ERROR AT ",0
MRFSH2:	DB	": EXPECTED 000, READ ",0
MRER1:	DB	7,7,7,13,10,"ERROR AT ADDRESS ",0
MRER2:	DB	": READ ",0
MRER3:	DB	", EXPECTED ",0
MRER4:	DB	13,10,"LAST BYTE WRITTEN WAS ",0
MRER5:	DB	", PATTERN WAS ",0
NODROP:	DB	13,10,"NO BOARDS DROPPED",13,10,0
HDROP:	DB	13,10,"STARTING ADDRESS(ES) OF 4K"
	DB	" BLOCK(S) DROPPED:",7,7,7,13,10,0
;
;
;
;********************************************************************
;
;	RAM STORAGE AND STACK
;
;********************************************************************
;
;
	DS   256	STACK SPACE
;
STACK:	DS	0	STACK JUST BELOW RAM DATA
ERRFLG:	DS	1	BLOCK ERROR FLAG
DROPCT:	DS	1	NUMBER OF BLOCKS DROPPED
DROPLS:	DS	28	DROP BUFFER
DROPPT:	DS	2	POINTER TO NEXT FREE SLOT IN "DROPLS"
BRDBUF:	DS	15	BOARD BUFFER
	DB	0200	TERMINATOR FOR BOARD BUFFER
BRDPT:	DS	2	BOARD BUFFER POINTER
BLKST:	DS	2	BLOCK START ADDRESS
BLKEND:	DS	2	BLOCK END ADDRESS
MEMBOT:	DS	2	CHUNK START ADDRESS
MEMTOP:	DS	2	CHUNK END ADDRESS
STGBUF:	DS	16	BUFFER FOR NUMASC
STGNUL:	DB	0	MARKER FOR END OF STGBUF
IOBUFR:	DS	80	INPUT BUFFER FOR GETSTG
TPATT:	DS	1	LAST PATTERN WRITTEN
LASTWR:	DS	2	LAST ADDRESS WRITTEN
;
	END
