;		WORM.ASM ver 2.1 (revised 10/9/81)
;
;Jim Eccleston July 19 1980 - program originally written
;
;Keith Petersen, W8SDZ, October 9, 1981 - fixed bug which
;	caused program to print one trap error at start.
;	Added REPORT conditional assembly so addresses
;	won't be reported unless there is an error.
;	Added equates for console ports.
;
;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 sub-
;routines. One of the subroutines reports the location of the
;worm and detects and reports any errors in the test bytes. The
;other subroutine 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 reports.  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.
;
stport	equ	20h		;console status port
txrdy	equ	04h		;console putput mask
daport	equ	21h		;console data port
;
report	equ	0	;<--make non-zero for address reporting
;
	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
	jmp	worm+1
;
dest	equ	08h		;destination
;
source	equ	$
offset	equ	dest-source	;reloc amount
;
stwrm	equ	$+offset	;Start of relocated code (08h)
	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
;
	if	report
	call	adout		;Print address
	endif
;
	if	not report
	nop ! nop ! nop		;Don't print address
	endif
;
	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		;save character
;
lp	equ	$+offset
	in	stport		;get console status
	ani	txrdy		;ready for character?
	jz	lp		;no, loop and wait
	pop	psw		;get character
	out	daport		;output to console
	pop	b		;restore bc
	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

