$PAGINATE
$title(Arnold 5 test)
$subtitle(Command processor for the disk test)
$copyright(Copyright (c) 1989, 1990, Amstrad plc.)
$pagewidth=131


        DEFSEG  FloppyDiskTest, CLASS=CODE

        SEG     FloppyDiskTest

        PUBLIC  cmndproc,cpabandon
        PUBLIC  syshelpcmnd,sysseterror,syssetloop,sysquit   ;used in MESSAGES
        PUBLIC  printerror

        EXTERN  KeyboardRead            ;in SUPPORT

        EXTERN  chkbreak                ;in FLOPPY
        EXTERN  outnewline              ;in FLOPPY
        EXTERN  outchar                 ;in FLOPPY
        EXTERN  printmessage            ;in FLOPPY
        EXTERN  sysmessage              ;in FLOPPY

        EXTERN  .KeyTranslateTable      ;in FLOPMESS
        EXTERN  syscmndtab              ;in FLOPMESS

        EXTERN  cpstack                 ;in TESTVARS
        EXTERN  ?loopcount              ;in TESTVARS
        EXTERN  defbase                 ;in TESTVARS
        EXTERN  ?maxloop                ;in TESTVARS
        EXTERN  ?errorcount             ;in TESTVARS
        EXTERN  ?maxerror               ;in TESTVARS
        EXTERN  ?cmndtable              ;in TESTVARS
        EXTERN  autodefenable           ;in TESTVARS
        EXTERN  ?curcmndchar            ;in TESTVARS
        EXTERN  cpbuffer                ;in TESTVARS
        EXTERN  lencpbuffer             ;in TESTVARS

        include "equates.inc"

;==============
cpautodefaults:
;==============
;
; set the auto default state
; when auto defaults is set then the default cmnds are automatically run
; when the cmnd processor is envoked, after defaults have been run
; the cp autoamatically exits
; note: auto defaults are disabled when CPABANDON is invoked
;
; entry: no conditions
;  exit: af corrupt
;	all other regs preserved
;
        ld      a,low true
	ld	(autodefenable),a ;enable auto defaults
;
	ret

;=========
cpabandon:
;=========
;
; crash cp stack
; disables auto defaults
;
; entry: no conditions
;  exit: hl de restored
;	 stack crashed
;	 af corrupt
;
	xor	a
	ld	(autodefenable),a ;disable auto defaults
;
	ld	sp,(cpstack)	;restore stack
	pop	de
	pop	hl
;confirm <_ eq cmndproc>        ;re-run cmnd processor

;========
cmndproc:
;========
;
; execute lines of cmnds
;
; entry: hl = cmnd table
;  exit: psw a bc corrupt
;	 all other regs as per options run
;
	push	hl
	push	de
	ld	(cpstack),sp	;save stack level for crash routine
;
	inc	hl		;step past help message number
	inc	hl		;step past addr of default cmnd string
	inc	hl
        ld      (?cmndtable),hl  ;save addr of cmnd table
;
; issue cmnd prompt
;
cp20:	ld	hl,1
        ld      (?maxloop),hl    ;always default max loop count to 1 for each new cmnd
;
	call	printcounts	;print current loop & error counts
;
; deal with auto defaults
;
	ld	a,(autodefenable)
	or	a		;zero false if auto defaults enabled
	jr	nz,cp70		;if enabled run defaults
;
; no auto defaults to print command prompt & read a cmnd line
;
	call	printuseopts	;print user cmnds
;
	ld	hl,cpbuffer	;cmnd processor input buffer
	ld	(hl),lencpbuffer-1 ;user may enter this many chars
	call	getbuffer	;read a cmnd line
	inc	hl		;pointer to char count
;
cp25:	ld	de,0
        ld      (?errorcount),de ;zeroise error count
        ld      (?loopcount),de  ;zeroise loop count
;
	ld	b,(hl)		;b = number of parameters on line
	inc	hl
	ex	de,hl		;buffer addr in DE
;
	call	outnewline	;keep things nice with a new line
;
; validate cmnd line
;
	call	fndnonspace	;do we have a non-space char
	jr	z,cp70		;if empty cmnd line try running defaults
;
	call	parsecmnd	;check we have a line of valid parameters
	jr	nc,cp20		;loop if not
;
; process cmnds
;
cp30:	push	de
	push	bc
;
; execute parameters
;
cp40:	call	printcounts	;print current loop & error counts
	call	chkbreak	;look for & process break
	call	cpchar		;read next parameter from cmnd line (puts char in C)
	jr	z,cp50		;jump if no more cmnds
;
	ld	hl,syscmndtab
	call	runpara		;see if system option
;
	ld	a,c		;get back cmnd
        ld      hl,(?cmndtable)  ;get addr of user cmnd table
	call	nc,runpara	;if not system option then may be user option
	jr	cp40		;run next cmnd
;
; reached end of line
;
cp50:	pop	bc		;recover char count in B
;
; loop
;
        ld      hl,(?loopcount)
	inc	hl		;mark 1 more loop done
        ld      (?loopcount),hl
	ex	de,hl		;save
;
        ld      hl,(?maxloop)    ;get max loop count
	ld	a,h
	or	l
	jr	z,cp60		;jump if infinite loop
;
	call	cphlde		;see if reached maxloop
	jr	nz,cp60		;jump if must loop again
;
; reached maxloop - if running auto defaults then exit otherwise get
; a new command line
;
	ld	a,(autodefenable)
	or	a		;zero false if running auto defaults
	jp	nz,sysquit	;exit if auto defaults
;
	pop	de
	jr	cp20		;otherwise get a new cmnd line
;
cp60:	pop	de
	jr	cp30		;process cmnd line again
;
; run defaults
;
cp70:   ld      hl,(?cmndtable)
	dec	hl
	ld	a,(hl)		;msb of default cmnd string
	dec	hl
	ld	l,(hl)		;lsb of default cmnd string
	ld	h,a		;hl = ref addr of default cmnd string
	ld	a,(hl)		;get first byte
	or	a		;zero implies no defaults
	jr	z,cp20		;get next cmnd line if no defaults
;
; print defaults string
;
	ld	a,sysrundefaults ;'Running defaults'
	call	printdefaults	;print defaults
	jr	cp25		;process default cmnds
;

;=========
parsecmnd:
;=========
;
; check that all the options on the cmnd line are valid
; sets up loop count
;
; entry: hl = ref cmnd table
;	 de = ref first cmnd line
;	  b = count of bytes in cmnd line
;  exit: if all cmnds are valid
;	    then carry true
;	 if any invalid cmnds
;           then carry false
;	always
;	   psw hl corrupt
;	   all other regs preserved
;
	push	de
	push	bc
;
        ld      hl,(?maxloop)
	push	hl		;save current loop count
;
; check each cmnd is valid
;
_10pc:  call    cpchar          ;get next char (puts char in C)
        jr      z,_20pc         ;fin if all parameters valid
;
; see if system option
;
	ld	hl,syscmndtab
	call	parsec30	;see if system option
        jr      nc,_15pc        ;jump if not a system cmnd
;
; found a system cmnd - if its the loop cmnd then set the loop count
; so that the count displayed on the status line gets updated as soon as we run an option
;
	ld	a,c
        cp      'L'             ;was it loop_
        jr      nz,_10pc        ;loop if not
;
	ex	(sp),hl		;save new loop count
        jr      _10pc
;
; see if user option
;
_15pc:  ld      hl,(?cmndtable)  ;assume not system option
	ld	a,c		;get cmnd char
	call	nc,parsec30	;see if user option
        jr      c,_10pc         ;loop if valid parameter
;
; we have found an invalid parameter
;
	call	outnewline
	ld	a,c
	call	outchar		;print char in error
	ld	a,sysbadcpcmnd	;'is not a cmnd'
	call	sysmessage
;
	pop	hl		;discard new loop count
;
	pop	bc
	pop	de
	ret			;fin, carry false
;
; all cmnds processed ok - set new max loop count & exit
;
_20pc:  pop     hl              ;get new max loop count
        ld      (?maxloop),hl    ;save it
;
	pop	bc
	pop	de
	scf
	sbc	a,a		;zero false, carry true
	ret			;fin, carry true
;
;========
parsec30:
;========
;
; see if user option is in given cmnd table
; if so  and  option requires a parameter then read number from cmnd buffer
;
; entry: hl = ref cmnd table for look up
;	 de = ref current position in cmnd buffer
;	  b = bytes remaining in cmnd buffer
;  exit: de b updated
;	 psw hl corrupt
;
; see if user cmnd
;
pc15:	call	cmndlookup	;lookup cmnd routine
	ret	nc		;fin if not found
;
; if cmnd option has bit 7 set then read associated number
;
	or	a
	scf			;assume number not required
	ret	p		;fin if number not required
;
	jp	readnumber	;read  and  valid associated number

;=======
runpara:
;=======
;
; run specified parameter
;
; entry: a = cmnd parameter
;	 b = bytes reamining in cmnd buffer
;        c = drives to test  and  read after write flag
;	de = ref next byte in cmnd buffer
;	hl = ref cmnd table to use
;  exit: b de updated
;	 psw a hl corrupt
;	 all other regs preserved
;
        ld      (?curcmndchar),a ;save cmnd letter for errormessage
;
	call	cmndlookup	;get cmnd routine
	ret	nc		;fin if cmnd not in table
;
	push	hl		;put routine addr on stack
;
	or	a		;does routine require a number
	scf			;in case no number required
	call	m,readnumber	;if so read one
	jr	nc,rp10		;jump if invalid number
;
	ex	de,hl		;de = parameter (if any)
	ex	(sp),hl		;push buffer pointer restore routine addr
	ex	de,hl		;de = routine to run, hl = parameter
;
	push	bc		;save count of bytes remaining
;
	call	pcdeinstruction	;run parameter
;
	pop	bc
	pop	de
	scf			;mark parameter run ok
	ret
;
rp10:	pop	hl		;restore stack
	ret

;======
cpchar:
;======
;
; get next char from cmnd buffer
;
; entry: (de) = ref next char in buffer
;	    b = count of chars remaining in buffer
;  exit: c = a
;	 all other regs as per paragetchar
;
	call	fndnonspace	;skip separators
	call	nz,paragetchar	;get a char if any
;
	ld	c,a		;char in C
	ret

;============
printuseopts:
;============
;
; print user option letters
;
; entry: no conditions
;  exit: psw bc hl corrupt
;	 all other regs preserved
;
	ld	a,syscommand	;'Command ('
	call	sysmessage
;
        ld      hl,(?cmndtable)  ;user cmnd table
	ld	b,(hl)		;loop count in B
	inc	hl
	jr	puo20		;jump into loop
;
puo10:	ld	a,(hl)		;get nex letter
	and	7Fh		;mask out parameter bit
	call	outchar		;print letter
;
	inc	hl
	inc	hl
	inc	hl		;move to next entry
;
puo20:	djnz	puo10
;
	ld	a,syselq	;'ELQ):_'
	jp	sysmessage

;===============
pchlinstruction:
;===============
;
; execute a pchl
;
	jp	(hl)

;===============
pcdeinstruction:
;===============
;
; execute a pcde instruction
;
	push	de
	ret

;======
cphlde:
;======
;
; compare hl and de
; cpu flags set according to hl - de
;
; entry: hl  and  de set up accordingly
;  exit: all cpu flags as per hl - de
;	 all regs preserved
;
	push	bc
	ld	b,a		;save A
;
	ld	a,h
	cp	d		;compare ls bytes
	jr	nz,chd10	;jump if ms bytes differ
;
	ld	a,l
	cp	e		;compare ls byte
;
chd10:	ld	a,b		;restore A
	pop	bc
	ret

;===========
syshelpcmnd:
;===========
;
; system help command
; craskes stack so user can see help cmnd
;
; entry: no conditions
;  exit: stack crashed to cmnd processor
;
        ld      hl,(?cmndtable)  ;get addr of user cmnd table
	dec	hl
	dec	hl
	dec	hl
	ld	a,(hl)		;message number of help message
	call	printmessage	;print help message
;
; print system options
;
	ld	a,syshelp
	call	sysmessage	;print system cmnds
;
; now print default cmnd string
;
	inc	hl
	call	ldhlvhl		;get addr of defaults string
;
	ld	a,(hl)		;length of default string
	or	a		;null string means no defaults 
;
	ld	a,sysnodefaults	;assume 'no defaults'
	call	z,sysmessage
;
	ld	a,sysdefaults	;'Defaults are: '
	call	nz,printdefaults ;print defaults, if any
;
;        call    outnewline
	jp	cpabandon	;crask stack

;=============
printdefaults:
;=============
;
; print cp defaults
;
; entry: hl = ref default cmnd string
;	 system message to print
;  exit: af b corrupt
;	 all other regs preserved
;
	push	hl
;
	call	sysmessage	;print required message
;
	ld	b,(hl)		;length of default sting
;
_10pd:  inc     hl
	ld	a,(hl)
	call	outchar
        djnz    _10pd           ;loop till all of string output
;
	pop	hl
	ret

;===========
sysseterror:
;===========
;
; set the error count
;
; entry: hl = required count
;  exit: all flags  and  regs preserved
;
        ld      (?maxerror),hl   ;save max number of errors allowed
;confirm <$ eq retinstruction>

;==========
syssetloop:
;==========
;
; set the loop count
; no need to do anything as the loop count is set up when parsing the cmnd line
;
; entry: hl = loop count
;  exit: all flags  and  regs preserved
;
;confirm <$ eq retinstruction>

;==============
retinstruction:
;==============
;
; guess what this does
;
	ret

;=======
sysquit:
;=======
;
; exir from cmnd processor
;
; entry: no condtions
;  exit: de hl restored
;
	ld	sp,(cpstack)	;restore stack
	pop	de
	pop	hl
	ret

;===========
printcounts:
;===========
;
; print the current values of the loop & error counts
;
; entry: no conditions
;  exit: af hl corrupt
;	 all other regs preserved
;
        exx
        push    de              ;save current cursor pos
        ld      de,00000h       ;move to (0,0)
        exx

	ld	a,syscounts
        call    sysmessage      ;print loop & error counts

        exx
        pop     de              ;restore old cursor
        exx

        ret

;=========
getbuffer:
;=========
;
; read buffered line from the console
;
; input a line of buffered text from the user
; convert up-arrow char into the relavent control char
;
; buffer format: byte 0 = n - max number of chars to input
;		 byte 1 = number of chars actually put into buffer
;		 byte 2 to n + 2 = the chars input
;
; entry: HL = ref buffer for text
;  exit: psw corrupt
;	all other regs preserved
;
;
; buffered input direct from the console
;
;
	push	de
	push	bc
;
	ld	d,0		;no chars input yet
	ld	b,(hl)		;max number of chars to input
	inc	b		;keep 1 ahead of available space in buffer
	inc	hl		;ref count of bytes read from console
	push	hl		;save
	inc	hl		;ref first position in buffer
;
; get next char
;
gb10:	call	waitkey		;get next char from keyboard
;
; look for & deal with control chars
; check for cr, bs  and  control X
;
	cp	' '		;do we have a control char
	jr	nc,gb20		;jump if not control char
;
	cp	cr		;is it carriage return?
	jr	z,gb30		;jump if so
;
	ld	e,d		;assume control-X
	cp	'X'-40h		;is it?
	jr	z,gb40		;jump if so
;
	cp	bs		;is it backspace
	ld	e,1		;assume so
	jr	z,gb40		;jump if backspace
;
	jr	gb70		;moan at all other control codes
;
gb20:	cp	rubout		;treat rub-out as backspace
	jr	z,gb40		;jump if rub-out
;
; put into buffer - if theres room
;
	dec	b		;any more room in buffer?
	jr	z,gb60		;j if not
;
	ld	(hl),a
	inc	hl
	inc	d		;inc count of chars put in buffer
;
; print the char
;
	call	outchar
	jr	gb10		;loop
;
; user pressed cr so set the number of bytes in the buffer  and  return
;
gb30:	call	outchar		;for consistancy with bdos output the cr
	pop	hl		;restore pointer to count
	ld	(hl),d		;save number of bytes input
;
	dec	hl		;restore hl
;
	pop	bc
	pop	de
	ret

; user wants to back space 1 or more times - check we are not in col 0
;
gb40:	ld	a,d
	or	a		;are we in col zero?
	jr	z,gb70		;jump if so
;
; backspace C times
;
gb50:	ld	a,bs
	call	outchar		;print backspace
	ld	a,' '
	call	outchar		;print space
	ld	a,bs
	call	outchar		;print backspace
;
	dec	hl		;move back buffer pointer 1 char
	dec	d		;mark 1 less char in buffer
	inc	b		;mark 1 more slot in buffer
	dec	e
	jr	nz,gb50		;loop if so
;
	jr	gb10
;
; no more room in buffer output a bleep
;
gb60:	inc	b		;restore B
;
gb70:	ld	a,bel
;        call    outchar         ;output a bell (don't do BELLs - CJL)
	jr	gb10		;loop
;

;=======
waitkey:
;=======
;
; Return an ASCII key value
;
        push    bc
        push    de
        push    hl
_waitInvalid
        call    KeyboardRead
        cp      0FFh
        jr      z,_waitInvalid

        push    af
        ld      bc,2000h
_waitdelay:
        dec     bc
        ld      a,b
        or      c
        jr      nz,_waitdelay
        pop     af

        ld      l,a
        ld      h,0             ;gonna use HL to index into translate table
        ld      bc,.KeyTranslateTable
        add     hl,bc
        ld      a,(hl)          ;get the ASCII value for this key number
        cp      0
        jr      z,_waitInvalid   ;some sort of invalid key (SHIFT etc)
        pop     hl
        pop     de
        pop     bc
        ret

;=======
ldhlvhl:
;=======
;
; load hl via hl
;
; entry: (hl) = ref variable to load
;  exit: hl = variable contents
;	 all flags & other regs preserved
;
	push	af
;
	ld	a,(hl)		;lsb
	inc	hl
	ld	h,(hl)		;msb
	ld	l,a
;
	pop	af
	ret

;===========
fndnonspace:
;===========
;
; return first non space (or comma) char from buffer
; force upper case
;
; entry: de = current pointer to cmnd line
;         b = bytes remaining in cmnd line
;  exit: if zero false
;	   then found none space char
;		a = char
;		de b updated
;	 if zero true
;	    then no more chars in cmnd buffer
;		 a corrupt
;	always
;		carry false
;		all other regs preserved
;
; see if any char left in buffer
;
fns10:	ld	a,b
	or	a		;any chars left in buffer?
	ret	z		;fin if not (carry false)
;
; get char from buffer  and  see if its a separator

	ld	a,(de)		;get char
	call	testsepchar	;do we have a separator
	ccf			;carry false if not
	ret	nc		;fin if not ,carry false, zero false
;
; found a separator so move to the next char
;
	dec	b		;dec count of chars remaining
	inc	de		;inc pointer
	jr	fns10		;loop

;===========
testsepchar:
;===========
;
; see if char is a parameter separator
;
; entry: no conditions
;  exit: if carry false
;	    then next char is a para separator
;		 zero true
;	 if carry true
;	    then char is not a para separator
;		 zero false
;	always
;
	cp	','		;is it a comma
	ret	z		;fin if so
;
	cp	' '		;is it a space
	ret	z		;fin if so
;
	scf			;mark not para separator
	ret

;==========
cmndlookup:
;==========
;
; lookup a letter in a cmnd table and return an associated 2 byte value
; table format:
;               byte 0        - number of entries + 1 in table
;               bytes 1 - 3   - entry 0
;		bytes n - n+2 - last entry in table
;
; each entry has the following format:
;		byte 0 - key value
;		byte 1 - lsb of associated field
;		byte 2 - msb of associated field
;
; entry: hl = addr of lookup table
;         a = key value
;  exit: if key found in table
;	   then carry true
;		hl = associated value from table
;		 a = key value from table
;        if key not found in table
;		carry false
;		a hl corrupt
;	 always
;		all other regs preserved
;
	push	bc
;
	ld	c,a		;save A
;
	ld	b,(hl)		;number of entries + 1
	inc	hl
;
cmdl10:	or	a
	dec	b		;any more entries
	jr	z,cmdl30	;jump if not
;
	ld	a,(hl)		;get value from table
	and	7Fh		;mask out parameter bit
	cp	c		;is this the right entry
	jr	z,cmdl20	;jump if so
;
	inc	hl
	inc	hl
	inc	hl
	jr	cmdl10		;loop
;
; get associated field from table
;
cmdl20:	ld	a,(hl)		;return key field from table
	inc	hl
	ld	c,(hl)		;lsb
	inc	hl
	ld	h,(hl)		;msb
	ld	l,c
	scf
;
cmdl30:	pop	bc
	ret



;==========
readnumber:
;==========
;
; read a number from the cmnd buffer
; number is terminated by the first non digit char
;
; entry: de = current position in cmnd buffer
;	  b = bytes remaining in cmnd buffer
;  exit: if carry true then
;	    valid number read
;	    hl = number
;	    de b updated
;	 if carry false then 
;	    invalid number input
;	    hl de b corrupt
;	 always
;		a corrupt
;		all other regs preserved
;
	ld	hl,0		;start off with zero
;
; get next char from buffer  and  check its a digit
;
	call	paralookchar	;lets have a look at the next char in the buffer
	scf
	ret	z		;fin if eol
;
	cp	'&'		;is it  and 
	jr	z,rn20		;if so read number in hex
;
	cp	'@'		;is it @
	jr	nz,rn05		;j if not decimal override char
;
	call	paragetchar	;read  and  discard decimal override char
	jr	rn10		; and  get the number
;
; use default base
;
rn05:	ld	a,(defbase)
	or	a		;zero false if default base is hex
	jr	nz,rn30		;jump if must read in hex
;
rn10:	call	paralookchar	;get back next char
	call	makedigit	;is char a digit
	ccf			;carry true if not digit
	ret	c		;fin if not digit
;
; read number in decimal
;
	call	paragetchar	;get next char from buffer
	call	c,makedigit	;if not eol do we have a digit
	ccf
	ret	c		;fin if eol or none digit
;
; got valid digit so add it to the number
;
	push	de
	call	hltimesten	;move number 1 power of ten
	pop	de
	ccf			;carry false if overflow
	ret	nc		;fin if number overflowed
;
	add	a,l
	ld	l,a
	adc	a,h
	sub	l
	ld	h,a		;hl = number + digit
	jr	rn10		;loop
;
; read number in hex
;
rn20:	call	paragetchar	;read  and  discard ' and ' char
;
rn30:	call	paralookchar	;get back next char
	call	makehexdigit	;is char a hex digit
	ccf			;carry true if no
	ret	c		;fin if not digit
;
	call	paragetchar	;get next char from buffer
	call	c,makehexdigit	;if not eol do we have a hex digit
	ccf
	ret	c		;fin if eol or non hex digit
;
; got valid digit so add it to the number
;
	call	hlleft4		;shift hl left 4
	ccf
	ret	nc		;fin if overflow
	add	a,l		;add hex char to lsb
	ld	l,a
	jr	rn30		;loop

;============
makehexdigit:
;============
;
; make a hex digit
;
; convert ascii hex digit into a number in the range 0 - F
;
; entry: = ascii digit
;  exit: if valid digit
;	    then carry true
;		 a = number
;	 if digit not valid
;	    then carry false
;		a corrupt
;	always
;		all other regs preserved
;
	call	makedigit	;is it a decimal digit
	ret	c		;fin if so
;
	sub	'A'-'0'-10	;convert hex char
	ccf			;carry false if less than 'A'
	ret	nc		;fin if not hex
;
	cp	'F'+1		;carry true if in range
	ret

;=========
makedigit:
;=========
;
; convert ascii digit into a number in the range 0 - 9
;
; entry: = ascii digit
;  exit: if valid digit
;	    then carry true
;		 a = number
;	 if digit not valid
;	    then carry false
;		 a = A - '0'
;	always
;		all other regs preserved
;
	sub	'0'		;convert ascii to number
	ccf			;carry false if invalid digit
	ret	nc		;fin if ascii too small
;
	cp	9+1		;carry true if valid
	ret

;==========
hltimesten:
;==========
;
; multiply HL by 10
;
; entry: no conditions
;  exit: if carry false
;	    then hl = hl * 10
;	 if carry true
;	    then number overflowed
;		 hl corrupt
;	always
;		de corrupt
;        all other regs preserved
;
	ld	d,h
	ld	e,l
;
	add	hl,hl		;hl * 2
	ret	c
	add	hl,hl		;hl * 4
	ret	c
	add	hl,de		;hl * 5
	ret	c
	add	hl,hl		;hl * 10
;
	ret

;=======
hlleft4:
;=======
;
; shift HL by left 4 bits
;
; entry: hl = number to shift
;  exit: if carry false
;	    then hl = hl shift left 4
;	 if carry true
;	    then number overflowed
;		 hl preserved
;	always
;        all other regs preserved
;
	push	hl
;
	add	hl,hl		;shift 1
	jr	c,hl410		;j if overflow
	add	hl,hl		;shift 2
	jr	c,hl410		;j if overflow
	add	hl,hl		;shift 3
	jr	c,hl410		;j if overflow
	add	hl,hl		;shift 4
	jr	c,hl410		;j if overflow
;
	ex	(sp),hl
	pop	hl		;restore stack
	ret
;
hl410:	pop	hl
	ret


;===========
paragetchar:
;===========
;
; return the next char in the cmnd line
; force upper case
;
; entry: de = current pointer to cmnd line
;         b = bytes remaining in cmnd line
;  exit: if carry true
;	   then got another char from cmnd line
;		zero false
;		a = char
;		de b updated
;	 if carry false
;           then if zero false
;		    then found parameter separator
;			 a = para separator
;	         if zero true
;		    then no more chars in cmnd buffer
;			 a corrupt
;	always
;		all other regs preserved
;
	call	paranextchar	;get next char from buffer
	ret	z		;fin if empty
;
	call	testsepchar	;see if char is a para separator
	ret	c		;fin if not a separator char
;
	or	a		;carry false, zero false
	ret

;============
paranextchar:
;============
;
; get next char from cmnd buffer
; force upper case
;
; entry: de = current position in cmnd line
;         b = number of bytes remaining in buffer
;  exit: if buffer empty
;	    then zero true
;		a corrupt
;		de b preserved
;	 if buffer not empty
;	    then zero false
;		 a = next char from buffer
;		 de b updated
;	always
;		carry false
;		all other regs preserved
;
	ld	a,b
	or	a		;any chars left in buffer?
	ret	z		;fin if not (carry false)
;
; get next char from buffer
; remember we must exit with zero false
;
	dec	b		;dec count of chars remaining (may set zero true !)
	or	a		;set zero false in case this is the last char in the buffer

	ld	a,(de)		;get char from buffer
	inc	de		;inc pointer
;
	jp	upconvert	;force upper case

;============
paralookchar:
;============
;
; get next char from cmnd buffer but do not change buffer pointer or count
; force upper case
;
; entry: de = current position in cmnd line
;         b = number of bytes remaining in buffer
;  exit: if buffer empty
;	    then zero true
;		a corrupt
;	 if buffer not empty
;	    then zero false
;		 a = next char from buffer
;	always
;		carry false
;		all other regs preserved
;
	ld	a,b
	or	a		;any chars left in buffer?
	ret	z		;fin if not (carry false)
;
; get next char from buffer
;
	ld	a,(de)		;get char from buffer
	jp	upconvert	;force upper case

;=========
upconvert:
;=========
;
; force lower case alpha chars to upper case
;
; entry: a = char to convert
;  exit: a = converted char
;	 all flags  and  other regs preserved
;
	push	bc
	push	af
;
	cp	'a'
	jp	c,uc10		;jump if less than lower case a
;
	cp	'z'+1
	jp	nc,uc10		;jump if more than lower case z
;
	and	0DFh		;mask out bit 5 to force upper case
uc10:	ld	b,a		;save char
;
	pop	af		;restore flags
	ld	a,b		;char in A
;
	pop	bc
	ret

;==========
printerror:
;==========
;
; output an error message followed by the current cmnd letter
; & loopcount value
; then increment the error count
; if count hits maxloop then abandon test
;
; entry: a = message number
;        bc de setup as required by message
; exit: carry false
;       a corrupt
;       all other regs preserved
;
	push	hl
	push	de
;
	call	printmessage	;print the message
	ld	a,syscurcmnd
	call	sysmessage	;add cmnd letter & loop count to error message
;
; inc count & see if maxerror reached
;
        ld      hl,(?errorcount)
	inc	hl		;inc count
        ld      (?errorcount),hl ;& save
	ex	de,hl		;error count in de
;
; if maxcount = 0 then its disabled
;
        ld      hl,(?maxerror)
	ld	a,h
	or	l
	jr	z,pe10		;jump if err count disabled
;
	call	cphlde		;have we reached the mac count yet?
	jr	nz,pe10		;jump if error limit not reached
;
; report error limit reached  and  abandon testing
;
	ld	a,systestabandon ;'Err limit reached: Test abandoned'
	call	sysmessage
	jp	cpabandon	;abandon testing
;
pe10:	call	printcounts	;update count on screen
;
	pop	de
	pop	hl

        jp      chkbreak

        END
