;******************************************************************************
;MEMTEST.ASM for CP/M-80. Written by Len Moskowitz and released to the
;public domain. October 1984
;Update Kurt Pieper 
;MEMTEST.180
;von 8080 auf z80 asmcode
;z80asm und slr180 Version 
;
;	     MEMTEST is a memory test program.  It tests memory with
;	twelve different patterns of bits.  It does each test addressed
;	both forwards and backwards through memory.
;	     The twelve patterns are:
;
;			Bit 76543210
;			------------
;		Pattern	0.  00000000 (00h, All zeros)
;			1.  00000001 (01h)___
;			2.  00000010 (02h)  |
;			3.  00000100 (04h)  |
;			4.  00001000 (08h)  | "Walking Ones" or
;			5.  00010000 (10h)  | "Barber Pole" pattern	
;			6.  00100000 (20h)  |
;			7.  01000000 (40h)  |
;			8.  10000000 (80h)__|  
;		       	9.  01010101 (55h, Even alternating ones)
;			10. 10101010 (AAh, Odd alternating ones)
;			11. 11111111 (FFh, All ones)
;
;	     Before the test begins, the program tells you where it resides
;	so that you can avoid overwriting it.  You are then prompted for 
;	whether you want screen updates and for the first and last addresses 
;	of the memory block to be tested.  Screen updates cause the program
;	to run much, much slower than without updates.  If you choose updates,
;	the screen will show the current address being tested and the pattern
;	being used.  The program expects the addresses to be in Hex
;	(radix 16) format.  A control C aborts the program.
;	     For each pattern, the first memory location is written to and
;	then read from.  The value read from memory is compared to the test
;	pattern. If they are identical, the test proceeds to the next memory
;	location.  If they are not the same, the address that failed is 
;	displayed along with the current test pattern and the erroneous value 
;	that the memory location contained. You are given a choice of
;	continuing or quitting.
;	     When all the locations are tested, the test goes on to the next 
;	pattern.  When all twelve patterns have been tested using forward
;	addressing (from low address to high address), the twelve are then
;	repeated using backward addressing (high address to low address).
;	If the test completes without mishap, a message confirms this and
;	the program exits to CP/M (provided you haven't overwritten it with
;	the test) via a jump to location 0000.
;	     I have provided for I/O using either the standard BDOS calls 
;	or direct console I/O via routines internal to MEMTST.  This version
;	has the internal routines commented out so that the casual user can
;	run it without change.  If you wish to make this test able to 
;	check all memory except for the block where it resides, all console
;	I/O should be done using the internal routines.  To activate them,
;	comment out the BDOS subroutines and remove the comment semicolons
;	from the internal subroutines.  You also must change the 
;	"instatusport",	"outstatusport", "inreadymask", "outreadymask", 
;	"indataport", and "outdataport" equates to reflect your system
;	requirements.
;	     If you use the BDOS calls, take pains not to ask MEMTST to test
;	the locations where the BDOS and BIOS reside.  You can find the
;	address of the BDOS by looking at locations 0006 and 0007 for a
;	standard CP/M installation. (Location 0005 is the "jump to BDOS" call
;	address.)  Testing these locations will result in an overwrittten
;	operating system and you'll have to reboot to bring the system back up.
;	Also be sure not to ask to test the memory where MEMTST resides.
;	     For 64K of memory, the test takes approximately 75 seconds to 
;	complete on a 4 megaHertz Z80A system with no wait states with no
;	screen updates.  With screen updates on, the test takes 20 Hours!
;	I therefore recommend you run without screen updates if you're testing
;	any block sized over 0200h.  If an error occurs, probe that area 
;	with updates on.
;	     A shortcoming of any test program of this type is that it can't
;	test the worst case memory access timing required for instruction
;	fetch operations.  If you find you have problems that this program does
;	not isolate, try using WORM (of which the current version is WORM21),
;	which is available via many RCPM systems.  It checks for access
;	timing sensitivity but does not do exhaustive pattern testing.  Between
;	the two programs you're likely to uncover any memory problems you might
;	have.
;
;						Len Moskowitz
;						Fair Lawn, N.J.
;						October 1984
;
;11Nov84  Renamed MEMTST10.ASM to MEMTEST1 to avoid confusion
;         with MEMTST11.ASM written by J. Kravitz, 08Dec76.
;         Ken Stritzel, Flanders, NJ  RCPM
;
;******************************************************************************
;
;****************
;*****  Equates *
;****************
cpm:	equ	0000h		;jump to CP/M address
bdos:	equ	0005h		;jump to BDOS address
conout:	equ	02h		;BDOS console out function code
conin:	equ	01h		;BDOS console in function code
constat:	equ	0bh	;BDOS get console status code
tpa:	equ	0100h		;CP/M programs start at this address
cr:	equ	0dh		;ASCII carriage return
lf:	equ	0ah		;ASCII line feed
controlc:	equ	03h	;ASCII control C
del:	equ	7fh		;ASCII delete
no:	equ	0
yes:	equ	0ffh
; Change the following equates to match your system if you are using the 
; internal I/O subroutines.  If you are using the CP/M BDOS calls you may 
; leave these as is.
outdataport:	equ	0
indataport:	equ	0
outstatusport:	equ	1
instatusport:	equ	1
outreadymask:	equ	01
inreadymask:	equ	02
memtst:	org	tpa
;	Tell him where this program resides so he can avoid it during the test.
	LD	sp,stacktop
	LD	HL,offlimits
	CALL	print
	LD	HL,repeat
	LD	C,H
	CALL	unpack
	LD	C,L
	CALL	unpack
	LD	HL,andmessage
	CALL	print
	LD	HL,endprogram
	LD	C,H
	CALL	unpack
	LD	C,L
	CALL	unpack
	LD	A,'.'
	CALL	sout
;	Ask him if he wants the screen updated continuously, and store his
;	answer in "screenupdate".
	LD	HL,screenupdateQ
	CALL	print
	CALL	sin
	CP	'Y'
	jp      z,setupdateyes
        CP      'J'
        jp      z,setupdateyes
	CP	'y'
	jp      z,setupdateyes
        CP      'j'
        jp      z,setupdateyes      
	CP	cr
	jp      z,setupdateyes
setupdateno:	
        LD	A,no
	LD	(screenupdate),A
	JP	locationprompt
setupdateyes:
        LD	A,yes
	LD	(screenupdate),A
;	This section does initialization and gets the starting and ending 
;	locations from the fellow at the keyboard.
locationprompt:	LD	HL,enterfirst
	CALL	print
	CALL	loca
	LD	(startloc),HL
	LD	HL,enterlast
	CALL	print
	CALL	loca
	CALL	crlf

;       cursor off update 30.07.21 
        PUSH    BC
        PUSH    DE
        PUSH    HL
        LD      HL,cursoroff
        CALL    print      
        POP     HL
        POP     DE
        POP     BC

	LD	E,L
	LD	D,H
	INC	DE
	LD	A,0
	LD	HL,step
	LD	(HL),A
	INC	HL
	LD	(HL),A
	LD	HL,(startloc)
	LD	C,A
	LD	B,A
;	This is the core of the memory test routine.  The section from
;	"repeat" to "doit" does the screen update if requested.
repeat:	LD	A,(screenupdate)
	CP	no
	jp      z,	doit
	PUSH	BC
	PUSH	HL
	LD	HL,currentaddress
	CALL	print
	POP	HL
	PUSH	HL
	LD	C,H
	CALL	unpack
	LD	C,L
	CALL	unpack
	LD	HL,currentpattern
	CALL	print
	POP	HL
	POP	BC
	PUSH	BC
	PUSH	HL
	LD	C,B
	CALL	unpack
	LD	A,cr
	CALL	sout
	POP	HL
	POP	BC
;	The next four lines do the memory test
doit:	LD	(HL),B		;move the pattern to memory
	LD	A,(HL)		;move it back to A
	CP	B		;compare it back to the pattern
	jp      nz,	error		;jump if they don't match
;	Check if he wants to exit and get the next test location ready.
reenter:	CALL	consolestatus;check if he pressed a key
	jp      z,	nocontrolc	;if not jump around the next few lines
	CALL	sin		;if he did, take the character in
	;SIN exits if it was control C
nocontrolc:	LD	A,(step);check the "backward" flag
	AND	80h		;if it's set
	jp      nz,	backwards	; jump around the forward code
	INC	HL		;we're going forward so increment HL
	JP	compareend	; and jump around the backward code
backwards:	DEC	HL	;we're going backward so decrement HL
;	This section checks the next address to be tested with the end 
;	location.  If we haven't hit the end location yet, we loop to repeat.
;	If we have hit it we fall through.
compareend:	LD	A,L
	CP	E
	jp      nz,	repeat
	LD	A,H
	CP	D
	jp      nz,	repeat
;	See if we've completed the last pattern.  If we have, then jump
;	to "checkforback" to see if we've done the backward addressing yet.
;	If this is not the last pattern, go onto the next one by incrementing
;	the step counter in register C and STEP (which holds the step and the
;	"backward" flag.
checkstep:	LD	A,0bh
	CP	C
	jp      z,checkforback
	INC	C
	LD	A,(screenupdate)
	CP	no
	jp      z,noupdate
	CALL	crlf
noupdate:	LD	HL,step
	LD	A,80h
	AND	(HL)
	jp      z,backflagnotset
	LD	A,80h
	OR	C
	LD	(HL),A
	JP	setnewpattern
backflagnotset:	LD	(HL),C
;	Check what step we're going to do and set the new pattern accordingly.
setnewpattern:	LD	A,1
	CP	C
	jp      nz,	not1
	LD	B,1
	JP	loadpattern
not1:	LD	A,9h
	CP	C
	jp      nz,	not9
	LD	B,0aah
	JP	loadpattern
not9:	jp      nc,	lessthan9
	LD	A,0ah
	CP	C
	jp      nz,	not10mustbe11
	LD	B,55h
	JP	loadpattern
not10mustbe11:	LD	B,0ffh
	JP	loadpattern
lessthan9:	LD	A,B
	RLCA	
	LD	B,A
loadpattern:	LD	HL,pattern
	LD	(HL),B
	LD	HL,(startloc)
	JP	repeat
;	We get to this next section when we've completed the last pattern.  If
;	the "backward" flag (bit 7 of STEP) is not set, it sets it, swaps the
;	starting and ending locations and starts the background addressing
;	cycle.  If the flag was set, we're done so we exit via ENDOK
checkforback:	LD	A,(screenupdate)
	CP	no
	jp      z,	updateno
	CALL	crlf
updateno:	LD	A,(step)
	AND	80h
	jp      nz,	endok
	LD	A,80h
	LD	(step),A
	LD	HL,startloc
	LD	E,(HL)
	INC	HL
	LD	D,(HL)
	DEC	DE
	LD	HL,(endloc)
	LD	(startloc),HL
	LD	A,0
	LD	C,A
	LD	B,A
	JP	repeat
;	ENDOK prints the message "OK" and exits to CP/M.
endok:	CALL	crlf
	LD	A,'O'
	CALL	sout
	LD	A,'K'
	CALL	sout
        LD      HL,cursoron       ; update 30.07.21
        CALL    print  
        JP      cpm
              
;        ld      de, msg
;        ld      c,9
;        call    5
;        JP      cpm
;msg:    db                  1bh, '[?25h'
;        db                  '$'
;	JP	cpm

;	ERROR prints the address of the memory cell that read back erroneous
;	data, the expected pattern and the bad data.  It then offers you
;	the choice of continuing or exiting to CP/M.
error:	CALL	crlf
	LD	D,A
	PUSH	AF
	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	HL,errorat
	CALL	print
	POP	HL
	PUSH	HL
	LD	C,H
	CALL	unpack
	LD	C,L
	CALL	unpack
	LD	HL,patternis
	CALL	print
	LD	C,B
	CALL	unpack
	LD	HL,memoryvalue
	CALL	print
	LD	C,D
	CALL	unpack
	CALL	crlf
	LD	HL,continueQ
	CALL	print
	CALL	sin
        CP      'J'               ; update 30.07.21
        jp      z,        setuptorepeat
	CP	'Y'
	jp      z,	setuptorepeat
        CP      'j'      
        jp      z,        setuptorepeat
	CP	'y'
	jp      z,	setuptorepeat
	CP	cr
	jp      z,	setuptorepeat
        LD      HL,cursoron
        CALL    print
	JP	cpm
setuptorepeat:	LD	A,cr
	CALL	sout
	POP	HL
	POP	DE
	POP	BC
	POP	AF
	JP	reenter
;*****************
;***** MESSAGES  *
; Update 30.07.21*
;*****************
;
errorat:	DEFB	'  *** Fehler am Standort (location) : ',0
patternis:	DEFB	'      Muster ist : ',0
memoryvalue:	DEFB	'      Speicherwert ist : ',0
continueQ:	DEFB	'Weitermacher? (J/N) : ',0 
enterfirst:	DEFB	cr,lf,'Geben Sie die erste Adresse ein  : ',0
enterlast:	DEFB	cr,lf,'Geben Sie die letzte Adresse ein : ',0
currentaddress:	DEFB	'Aktuelle Adresse : ',0
currentpattern:	DEFB	'      Aktuelles Muster : ',0
screenupdateQ:	DEFB	cr,lf,'Bildschirm-Updates? (Laeuft mit Update viel langsammer'
	        DEFB	') (J/N) : ',0
offlimits:	DEFB    1bh, '[2J'
                DEFB    cr,lf,'MEMTEST - Version (z180) '
                DEFB	cr,lf,'Der Kern dieses Programms liegt zwischen ',0
andmessage:	DEFB	' und ',0
cursoron:       DEFB    1bh, '[?25h',0          
cursoroff:      DEFB    1bh, '[?25l',0 
;
;****************
;***** MESSAGES *
;****************
;
;errorat:        db      '  *** Error at location : ',0
;patternis:      db      '     Pattern is : ',0
;memoryvalue:    db      '     Memory value is : ',0
;continueQ       db      'Continue? (Y/N) : ',0 
;enterfirst:     db      cr,lf,'Enter first address : ',0
;enterlast:      db      cr,lf,'Enter last address : ',0
;currentaddress: db      'Current address : ',0
;currentpattern: db      '     Current pattern : ',0
;screenupdateQ:  db      cr,lf,'Screen updates? (Runs much slower with updates'
;                db      ') (Y/N) : ',0
;offlimits:      db      cr,lf,'The core of this program resides between ',0
;andmessage:     db      ' and ',0

;*******************
;***** SUBROUTINES *
;*******************
;	CONSOLESTATUS checks to see if a key was pressed.  If a key was
;	pressed, the A register will contain 0FFh.  Otherwise it will have
;	00h.
;	***** BDOS I/O routine
consolestatus:	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	C,constat
	CALL	bdos
	OR	A
	POP	HL
	POP	DE
	POP	BC
	RET	
;	***** Internal I/O routine
;
;consolestatus:	in	instatusport
;		ani	inreadymask
;		jz	notready
;		mvi	a,0ffh
;		ret
;      notready:mvi	a,0
;		ret
;
;
;	CRLF prints a carriage return and a line feed. Preserves all
;	registers.
crlf:	PUSH	AF
	XOR	A
	ADD	A,cr
	CALL	sout		
	ADD	A,lf
	CALL	sout
	POP	AF
	RET	
;	HXTOASCI converts the lower nibble of the value in the A register
;	into an ASCII numeric or alphabetic character.  It expects the most
;	significant nibble to be zeroed.
hxtoasci:	CP	0ah
	jp      c,	numeric
	SUB	A,9
	ADD	A,40h
	RET	
numeric:	ADD	A,30h
	RET	
;	PRINT prints the string pointed to by registers HL until a zero
;	(0h) is encountered.
;
print:	LD	A,(HL)
	CP	0
	ret     z                 ;8080 rz
print1:	CALL	sout
	INC	HL
	LD	A,(HL)
	CP	0
	jp      nz,	print1
	RET	
;	SIN gets one character from the console and echoes it back to the 
;	console.  Character ends up in register A.
;
;	***** BDOS I/O version
sin:	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	C,conin
	CALL	bdos
	CP	controlc
	jp      z,ctrlc	;cpm
	POP	HL
	POP	DE
	POP	BC
	RET	

ctrlc:  LD      HL,cursoron
        CALL    print
        JP      cpm

;	***** Internal I/O version
;sin:		in	instatusport
;		ani	inreadymask
;		jz	sin
;		in	indataport
;		cpi	controlc
;		jz	cpm
;		push	psw
;		call	sout
;		pop	psw
;		ret
;	SOUT outputs whatever is the A register to the console.  Returns with
;	register A cleared to zero.
;
;	***** BDOS I/O version
sout:	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	E,A
	LD	C,conout
	CALL	bdos
	POP	HL
	POP	DE
	POP	BC
	XOR	A
	RET	
;	***** Internal I/O version
;sout:		push	psw
;	sout1:	in	outstatusport
;		ani	outreadymask
;		jz	sout1
;		pop	psw
;		out	outdataport
;		xra	a
;		ret
;	UNPACK takes what ever is in register C (which is supposed to be
;	an address or memory pattern), unpacks it into two hex digits, 
;	converts them to ASCII and outputs them to the console.  Modifies
;	A register only.
unpack:	LD	A,C
	AND	0f0h
	RRCA	
	RRCA	
	RRCA	
	RRCA	
	CALL	hxtoasci
	CALL	sout
	LD	A,C
	AND	0fh
	CALL	hxtoasci
	CALL	sout
	RET	
;***************
;***** STORAGE *
;***************
;	Storage goes here.  The subroutines after this section can be written 
;	over by MEMTST if you want to test those locations.  They are only 
;	used in the initialization section at the top of the program.
screenupdate:	DEFS	1
endloc:	DEFS	2
startloc:	DEFS	2
step:	DEFS	1
pattern:	DEFS	1
stack:	DEFS	32
stacktop:	equ	$
endprogram:	equ	$
;	LOCA takes four ASCII characters in from the console (expecting them
;	to be 0-9 or A-F), converts them to hex, packs them into two bytes,
;	and stores them in "endloc" and registers HL.
loca:	CALL	sin
	CALL	pack
	LD	H,C
	CALL	sin
	CALL	pack
	LD	L,C
	LD	(endloc),HL
	RET	
;	PACK expects an ASCII character in register A.  If it's an alphabetic
;	character (A-F) it converts it to a hex digit in the range of A hex 
;	to F hex.  If it is a number it just strips off the most significant
;	nibble leaving a hex digit in the range from 0 to 9.
pack:	CP	41h
	jp      c,	label8
	ADD	A,09h
label8:	AND	0fh
	ADD	A,A
	ADD	A,A
	ADD	A,A
	ADD	A,A
	LD	C,A
	CALL	sin
	CP	del
	jp      nz,	norubout
	ADD	A,' '
	CALL	sout
	CALL	sin
	JP	pack
norubout:	CP	41h
	jp      c,	label9
	ADD	A,09h
label9:	AND	0fh
	ADD	A,C
	LD	C,A
	RET	

END