;
;Jim Eccleston July 19 1980
;
;Allen Mar August 8, 1980 - added 'label + offset'
;	so COM file can be generated without DDT.COM
;	Thanks to Bill Precht and Ward Christensen
;	who used it in his 'PMMIBYE2' remote CP/M
;	program...
;
;	
;**********************************************************************
;*									*
;*			W O R M T E S T					*
;*									*
;*	A serious flaw exists in most memory test routines when		*
;*	applied to Z-80 systems. A board can be tested for hours	*
;*	and never drop a bit, then when it's loaded with a program	*
;*	fail instantly. The problem is that access timing on a		*
;*	Z-80 instruction fetch (the M-1 cycle) is significantly		*
;*	more critical than on a simple read cycle. No matter how	*
;*	fancy a normal memory test gets, it never makes demands		*
;*	on memory speed that an actual program does. The only way	*
;*	to check this memory for full speed operation is to 		*
;*	actually run a program in it. Wormtest does just that.
;*					vide The M-1 Worm. P.C. July 73 *
;*									*
;**********************************************************************
;
;	The first few lines relocate the program down to low memory
;
;	There are two parts to the test program. The larger
;	portion consists of service routines, but the Worm
;	itself consists of a short 12 byte routine that
;	upon initialization breaks away from the main body
;	and crawls up through memory space giving a running
;	travelogue as he goes. If he stops talking, you know
;	something bad happened, and where.
;	The Worm acts as the main program loop. It manipulates
;	two test bytes and calls two subroutines. One of the 
;	subroutines reports the location of the worm and detects
;	and reports any errors in the test bytes. The other sub-
;	routine shifts the Worm up in memory one location and 
;	adjusts the return to begin execution for another loop.
;	There are seven instruction fetches per loop, with the
;	data manipulation instructions in complementary pairs
;	to ensure full speed testing on both ones and zeroes.
;	The instruction RST 7 is embedded in the Worm in non-
;	executable locations as traps in case a memory error
;	causes the program counter to get out of sync. The Worm
;	leaves behind a slime-trail of FF's as it travels. Any
;	execution of a trap is reported and the trap subroutine
;	attempts to return execution back to the worm.
;	The error reports indicate which data byte was bad and
;	what the erroneous value was.A "D" indicates that bits
;	were dropped, while a "P" indicates that bits were
;	picked. A trap error is flagged by a "T" followed by
;	the address. Upon execution the Worm will immediately
;	start reporting its location (by address)
;	Bad memory will trash the program or else trigger the
;	error repots. No memory looks like a string of FF's
;	and a sequential string of traps will be reported.
;	When ROM is hit, execution of the ROM program will begin,
;	so if you have the Morrows Disk Controller you will know
;	your memory is ok when the system re-boots.
;
;
;	(The following in is no longer true with this version)
;	You may find you need to use DDT to produce the .COM file
;	because of the ORG at 0008H
;
;
	org	0100h
;
; Move routine relocates WORM program down to 08H
;
start	lxi	d,dest		;Destination address
	lxi	h,source	;Source address
	mvi	b,pend-source+1	;Number of bytes to move
loop	mov	a,m		;Get byte
	stax	d		;Put byte
	inx	h		;Bump up source adr.
	inx	d		;Bump up destination adr.
	dcr	b		;Decrease byte count
	jnz	loop		;Do again if not zero count
	lxi	sp,stack	;Set up stack pointer
	lxi	b,data		;Set up data pointer
;				;No, these are not the Pointer sisters
	jmp	worm
;
dest	equ	08h		;destination
;
source	equ	$
offset	equ	dest-source	;reloc amount
;
;	org	08h		;Start of relocated code
	dw	0ffffh
	dw	0ffffh
	dw	0ffffh
	dw	0ffffh		;Rst 7s
;
;
movwrm	equ	$+offset
	pop	h		;Get address of worm + 1
	mov	d,h		;Duplicate address in d
	mov	e,l		;and e regs.
	dcx	d		;Set DE to last address of worm
	mvi	b,0ch		;Set length of worm
getwrm	equ	$+offset
	ldax	d		;;Get byte of worm 
	mov	m,a		;Move it up one location
	dcx	d		;Adjust pointers
	dcx	h
	dcr	b
	mov	a,b
	cpi	00
	jnz	getwrm
	lxi	b,data		;Restore data pointer
	inx	h		;HL to start of worm
	inx	h
	pchl			;Return to worm
	rst	7		;RST 7
;
;
rptadr	equ	$+offset
	cpi	0ffh		;Check for dropped bit error
	jnz	errdrp
	mov	a,b
	cpi	00h		;Check for picked bit error
	jnz	errpik
	jmp	rptad2		;Show the address
	rst	7
	rst	7		;Trap
;
;
trap	equ	$+offset
	mvi	a,54h		;Print a "T"
	call	output
	mvi	a,20h		;Print a " "
	call	output
	pop	h		;Recover address
	dcx	h		;Adjust address
	call	adout		;Print the address
	inx	h		;Adjust address
	pchl			;Return
;
;
rptad2	equ	$+offset
	pop	h		;Recover address
;
;	NOP THIS IF YOU HAVE A SLOW TERMINAL
;
	call	adout		;Print address
;
;	UNLESS YOU REALLY LIKE SEEING THAT ADDRESS !!
;
	inx	h		;Adjust return address
	inx	h
	inx	h
	pchl			;Return
;
;
errdrp	equ	$+offset
	mvi	b,44h		;Show it's dropped bit(s)
	jmp	errprt		;Print error message
;
;
errpik	equ	$+offset
	mvi	b,50h		;Show it's picked bit(s)
;
;
errprt	equ	$+offset
	mov	c,a		;Save error data
	mvi	a,2ah		;Print "*"
	call	output
	mvi	a,20h		;Print " "
	call	output
	mov	a,b		;Print type of error
	call	output
	mvi	a,20h		;Print " "
	call	output
	mov	a,c		;Get error data
	call	bowcl		;Print and end line
	jmp	rptad2		;Now try for address and return
;
;
adout	equ	$+offset
	push	h		;Save address
	mov	a,h
	call	bytout		;Its nybble time
	mov	a,l
	call	bowcl		;Same with cr/lf
	pop	h		;Restore address
	ret
;
;
bytout	equ	$+offset
	push	psw		;Save A
	rrc			;Shift nybble
	rrc
	rrc
	rrc
	call	nybout		;Nybble out 1
	pop	psw		;Restore A
	call	nybout		;Nybble out 2
	ret
;
;
bowcl	equ	$+offset
	call	bytout
	mvi	a,0Dh		;C/R time
	call	output
	mvi	a,0Ah		;LF time
	call	output
	ret
;
;
nybout	equ	$+offset
	ani	0fh		;Strip high nibble
	cpi	0ah		;Divide alpha v numeric
	jm	isnum		;If numeric
	adi	07h		;Add alpha offset
isnum	equ	$+offset
	adi	30h		;Add numeric offset
	call	output
	ret
;
;	************************************************
;	*					       *
;	* Insert Your DIRECT Console Output Call Here  *
;	*					       *
;	*					       *
;	* unless you want to use cp/m calls till crash *
;	************************************************
;
output	equ	$+offset
	push	b		;save bc
	push	psw
lp	equ	$+offset
	in	72h
	ani	02h
	jz	lp
	pop	psw
	out	73h
	pop	b
	ret
;
;
data	equ	$+offset
	db	00
;
;
	ds	20
stack	equ	$+offset-1
;
;
worm	equ	$+offset
	rst	7		;Trap
	ldax	b		;Move data to A; start of worm
	push	psw		;Push test data onto stack
	mvi	a,0ffh		;Move second test byte to A
	pop	b		;Pop first test byte into B
	rst	5		;call "rptadr"
	rst	7		;Trap
	rst	7
	rst	7
	nop			;"rptadr" return location
	rst	2		;call "movwrm"
;
pend	equ	$		;program end
;
	end	0100h
