; CPSPK2.ASM
;       KERMIT - (Celtic for "FREE")
;
;       This is the CP/M-80 implementation of the Columbia University
;       KERMIT file transfer protocol.
;
;       Version 4.0
;
;       Copyright June 1981,1982,1983,1984
;       Columbia University
;
; Originally written by Bill Catchings of the Columbia University Center for
; Computing Activities, 612 W. 115th St., New York, NY 10025.
;
; Contributions by Frank da Cruz, Daphne Tzoar, Bernie Eiben,
; Bruce Tanner, Nick Bush, Greg Small, Kimmo Laaksonen, Jeff Damens, and many
; others. 
;
;       This file contains the (system-independent) routines that implement
;       the KERMIT protocol, and the commands that use them:
;       RECEIVE, SEND, FINISH, and LOGOUT.
;
; revision history:
;
;edit 11, 21-Mar-1991 by MF. After "inchr7", close TAKE-file (if any) so
;	^C will halt all processing (including commands from TAKE-files)
;	and put the user back at Kermit command-level.
;edit 10, 3-Jan-1991 by MF. Modify routine "inchr" after label "inchr5" to
;	not take retry (nonskip) return if ^X/^Z seen on the Console. This
;	will prevent multiple copies of packets being sent if user aborts
;	some files in a stream being sent via ^X and is a better fix to this
;	problem than flushing comm input before sending the "Z" packet
;	requesting the remote Kermit to discard the current file being
;	received (as implemented in CPSPK1.ASM edit of 2-jan-1991).
;edit 9, 14-Dec-1990 by MF. Modified "gofil" routine to allow for
;	specification of a drive in the local filespec for GET and
;	RECEIVE commands. Thus commands such as
;	GET HELLO.TXT B:GOODBYE.TXT
;	and
;	RECEIVE B:GOODBYE.TXT
;	now work as expected.
;edit 8, 22-Oct-1990 by MF.  Fixed bug in completion-message routine
;	"finmes" wherein the completion message was not printed if the
;	terminal was set to QUIET because the message pointer was clobbered
;	by prcrlf.
;edit 7, 14-Sep-1990 by MF.  Add hooks for SET COLLISION command.
;	Eliminate commented-out old file warning rename routine.
;	Clear communication input buffers (call flsmdm) before
;	BYE, FINISH and LOGOUT commands.
;edit 6, 9-Sep-1990 by MF.  Implemented fixes in CPKERM.BWR for
;	garbage printout during quiet transfers and for file existence/
;	rename algorithm.
;	Also implemented hooks for Remote commands.
; edit 5, 18 June 1990 by Russell Lang [rjl@monu1.cc.monash.edu.au]
;	When trying to generate a unique file name on receive, zero
;	the attribute bits between file opening attempts.  This is
;	to fix a bug which caused the unique file name to have the
;	attributes of the already existing file.  If the attribute
;	was R/O, a bdos error occured later when an attempt was made
;	to write to the file.
;
; edit 4, 27 October, 1987 By OBSchou.  Changed the rename routine to 
;	be more like the MSDOS issue.
;
; edit 3, 28 July, by OBSchou.  Added traps to NOT print to screen during
;	file transfers if quietd is non zero (ie we SET TERMINAL QUIET)
;	This hopefully speeds up transfers in systems spending an age
;	updating the screen.
;
; edit 2, 8 April, 1987 by OBSchou.  Minor edit to put drive and user number
;	in the "filename" field on the transfer screen.  This means that the
;	offset on the line foe the file name proper has moved along 4 space.  
;	Also, it writes 15 spaces AFER the xxd: string to clear the field 
;	of any prevous file.  Needed for thos terminals that cannot
;	clear to end of line...
;
; edit 1, 28 January, 1987 by OBSchou.
;	Hived off about 1/2 of CPSPKT.ASM to form two (smaller => easier
;	to handle) files.  
;
;

pk2ver:	db	'CPSPK2.ASM (11)  21-Mar-1991$'     ; name, edit number, date


;
;       Get the file name (including host to micro translation)
;       called by: rfile

gofil:	xra	a
	sta	fcb		;Set the drive to default to current.
	lxi	h,data		;Get the address of the file name.
; allow use of local name if one was given [gnn]
	lda	remlen		;[gnn] 
	ora	a		;[gnn] anything there?
	jz	gofil0		;[gnn]  no, use the one in the data packet
	lxi	h,remnam	;[gnn] yes, use this instead
	lda	remnam+1	;[MF]Get 2nd char of local filename
	cpi	':'		;[MF]Was a drive specified?
	jnz	gofil0		;[MF]No, proceed as of old
	mov	a,m		;[MF]Yes, get drive
	ani	5fh		;[MF]Force uppercase
	sui	'A'-1		;[MF]Make valid drive for fcb
	sta	fcb		;[MF]and store in fcb
	inx	h		;[MF]Skip drive and delimiter
	inx	h		;[MF]...
gofil0:				;[gnn]  continue to set up the file [gnn]
;
	shld	datptr		;Store the address.
	lxi	h,fcb+1		;Address of the FCB.
	shld	fcbptr		;Save it.
	xra	a
	sta	temp1		;Initialize the char count.
	sta	temp2
	mvi	b,' '
gofil1:	mov	m,b		;Blank the FCB.
	inx	h
	inr	a
;       cpi     0CH             ;Twelve?[5a]
	cpi	0BH		; Eleven? [5a]
	jm	gofil1
	mvi	m,0		; [5a] Specify extent 0
gofil2:	lhld	datptr		;Get the NAME field.
	mov	a,m
	cpi	'a'             ;Force upper case
	jm	gofl2a		;
	ani	5FH		;
gofl2a:	inx	h
	cpi	'.'             ;Seperator?
	jnz	gofil3
	shld	datptr		;[jd] update ptr (moved from above)
	lxi	h,fcb+9H
	shld	fcbptr
	lda	temp1
	sta	temp2
	mvi	a,9H
	sta	temp1
	jmp	gofil6

gofil3:	ora	a		;Trailing null?
	jz	gofil7		;Then we're done.
	shld	datptr		;[jd] no, can update ptr now.
	lhld	fcbptr
	mov	m,a
	inx	h
	shld	fcbptr
	lda	temp1		;Get the char count.
	inr	a
	sta	temp1
	cpi	8H		;Are we finished with this field?
	jm	gofil2
gofil4:	sta	temp2
	lhld	datptr
	mov	a,m
	inx	h
	shld	datptr
	ora	a
	jz	gofil7
	cpi	'.'             ;Is this the terminator?
	jnz	gofil4		;Go until we find it.
gofil6:	lhld	datptr		;Get the TYPE field.
	mov	a,m
	cpi	'a'             ;Force upper case
	jm	gofl6a		;
	ani	5FH		;
gofl6a:	ora	a		;Trailing null?
	jz	gofil7		;Then we're done.
;[jd] move above two lines so we don't increment pointer if char is null
	inx	h
	shld	datptr
	lhld	fcbptr
	mov	m,a
	inx	h
	shld	fcbptr
	lda	temp1		;Get the char count.
	inr	a
	sta	temp1
	cpi	0CH		;Are we finished with this field?
	jm	gofil6
gofil7:	lhld	datptr
	mvi	m,'$'           ;Put in a dollar sign for printing.
	lda	quietd		; quiet display?
	ana	a
	jnz	gofi70		; yes, so skip it.
	call	scrfln		;Position cursor
gofi70:	lxi	d,data		;Print the file name
	lda	getrxflg	;[obs 8] are we doing a get or receive?
	ana	a		;[obs 8]
	jz	gofi7a		;[obs 8] if zero, receive
	lxi	d,remnam	;[obs 8]
gofi7a: 		;[obs 8]

	call	prtstr
gofi7b:	xra	a		;[MF]Zero "discard" flag
	sta	dscflg		;[MF]...
	lda	flwflg		;Is file warning on?
	ora	a
	jz	gofil9		;If not, just proceed.
	mvi	c,openf		;See if the file exists.
	lxi	d,fcb
	call	bdos
	cpi	0FFH		;Does it exist?
	jz	gofil9		;If not create it.
;
	lda	flwflg		;[MF]Get flag again
	cpi	3		;[MF]SET COLLISION DISCARD?
	jnz	gofi7h		;[MF]No
	mvi	a,0ffh		;[MF]Yes, order rejection of the file
	sta	dscflg		;[MF]...
	jmp	rskp		;[MF]and pretend successful open
gofi7h:	push	psw		;[MF]Save Collision status
	lxi	d,infms5
	call	error3
	pop	psw		;[MF]Restore Collision status
	cpi	1		;[MF]SET COLLISION RENAME?
	jz	gofi7i		;[MF]Yes, same as SET WARNING ON
				;[MF]If we come here, SET COLLISION BACKUP
	lxi	h,fcb		;[MF]Copy original fcb to a safe place
	lxi	d,colfcb	;[MF]...
	lxi	b,33		;[MF]...
	call	mover		;[MF]...
				;[MF]and fall into rename code
gofi7i:				;[MF]
;
;	Replacement  file name renamer routine.  Incomming
;	files are renamed in this manner:
;	original file name:	filex.ext
;	first rename:		filex001.ext
;	...		...
;	ninth rename		filex009.ext
;	10th rename		fail - would we really want 10
;				files of the same name??
;
;
;	1)
;	Assume that we need to "rename" the file, so lets make sure
;	that there is a full. 8 character filename. (We make it if 
;	it does not already exist)
;		1a) If full file name, last character is to be replaced
;		by a zero.  This gives us up to no#ine renames.
;	2)open file
;		2a)If exists, increment last character by one
;		2b)if = '9' then abort
;		2c)If does not exist, got 2)
;	3)we have a valid 'renamed' file
;
;Part 1) - fill out filename part 

	mvi	c,8		; max 8 characters to test for
	mvi	a,'0'		; spaces to be replaced by a zero.
	lxi	h,fcb+8		; start at the end
gofi7c:	mov	m,a		; put a zero in here
	dcr	c		; come to the end?
	jz	gofi7d		; should not have, but just in case...
	dcx	h		; previous chararcter
	mov	a,m		; get it
	cpi	' '		; if this character a space as well, zero it
	mvi	a,'0'		; set it to ascii zero just in case...
	jz	gofi7c		;
;
; Part 2) open the file (if success, then it exists)

gofi7d:
;zero the attribute bits.  [rjl@monu1.cc.monash.edu.au]
	lxi	h,fcb+1		;[rjl]
	mvi	c,11		;[rjl]
gofi7z:	mov	a,m		;[rjl]
	ani	07fh		;[rjl]
	mov	m,a		;[rjl]
	inx	h		;[rjl]
	dcr	c		;[rjl]
	jnz	gofi7z		;[rjl]
	lxi	d,fcb
	mvi	c,openf
	call	BDOS
	inr	a		; if 0ffh returned, error (ie does not exist)
	jz	gofi7e
	lda	fcb+8		; get last character
	inr	a
	sta	fcb+8
	cpi	'9'+1		; more than '9' => too far, lets give up.
	jnz	gofi7d		; else try again
;Giving up, so lets exit
	lxi	d,erms16	;
	call	prtstr
	ret		; return to error routine

gofi7e:	lxi	d,fnbuf		; make the file name into a character string
	lxi	h,fcb+1		; point to source file name, less drive name
	mvi	c,8		; 11 characters (8+3) + dot to copy across
;
gofi7f:	mov	a,m		; get character
	stax	d
	inx	h
	inx	d
	dcr	c
	jnz	gofi7f		; loop until all done

	mvi	a,'.'		; then the dot
	stax	d
	inx	d

	mvi	c,3		; then the file extention

gofi7g:	mov	a,m
	stax	d
	inx	h
	inx	d
	dcr	c
	jnz	gofi7g		; loop until extention copied across

	mvi	a,'$'		; dollar terminate string
	stax	d
	lxi	d,fnbuf		;[MF]Point to string
	call	prtstr		; write string to console

	lda	flwflg		;[MF]Get warning (SET COLLISION) flag
	cpi	2		;[MF]SET COLLISION BACKUP?
	jnz	gofil9		;[MF]No
	lxi	h,fcb		;[MF]Yes, get new filename fcb
	lxi	d,colfcb+16	;[MF]Where to copy to for rename
	lxi	b,16		;[MF]Copy 16 bytes
	call	mover		;[MF]...
	lxi	d,colfcb	;[MF]Point to rename fcb
	mvi	c,renam		;[MF]Rename function
	call	bdos		;[MF]Try to rename original file
	cpi	0ffh		;[MF]Did we win?
	jnz	gofl82		;[MF]Yes
	lxi	d,erms16	;[MF]No, complain and bomb
	jmp	error3		;[MF]...
gofl82:	lxi	h,colfcb	;[MF]Now recopy original filename into fcb
	lxi	d,fcb		;[MF]to create new file with original name
	lxi	b,16		;[MF]...
	call	mover		;[MF]...
;
;
;Now lets make the file (create it)

gofil9:	call	makfil		; Create the file.
	jmp	gofl91		; Disk was full.
	jmp	rskp		; Success.
    
gofl91:	lxi	d,erms11
	call	error3
	ret
;
;       This is the FINISH command.  It tells the remote KERSRV to exit.
;       here from kermit

finish:	call	cfmcmd
	call	selmdm		;[MF]Select modem
	call	flsmdm		;[MF]Flush buffers
	call	selcon		;[MF]Select keyboard again
	xra	a
	sta	numtry		;Inititialize count.
	mvi	a,'1'           ;Reset block check type to single character
	sta	curchk		; .  .  .

finsh1:	lda	numtry		;How many times have we tried?
	cpi	maxtry		;Too many times?
	jm	finsh3		;No, try it.
finsh2:	lxi	d,erms18	;Say we couldn't do it.
	call	prtstr
	jmp	kermit		;Go home.

finsh3:	inr	a		;Increment the number of tries.
	sta	numtry
	xra	a
	sta	argblk		;Make it packet number zero.
	mvi	a,1
	sta	argblk+1	;One piece of data.
	lxi	h,data
	mvi	m,'F'           ;Finish running Kermit.
	mvi	a,'G'           ;Generic command packet.
	call	spack
	jmp	finsh2		; Tell the user and die.
	call	rpack		;Get an acknowledgement.
	jmp	finsh1		; Go try again.
	cpi	'Y'             ;ACK?
	jz	kermit		;Yes, we are done.
	cpi	'E'             ;Is it an error packet?
	jnz	finsh1		;Try sending the packet again.
	call	error1		;Print the error message.
	jmp	kermit
;
;       This is the LOGOUT command.  It tells the remote KERSRV to logout.
;       here from: kermit

logout:	call	cfmcmd
	call	logo		;Send the logout packet.
	jmp	kermit		;Go get another command
	jmp	kermit		; whether we succeed or not.

;       do logout processing.
;       called by: bye, logout

logo:	call	selmdm		;[MF]Select modem
	call	flsmdm		;[MF]Flush buffers
	call	selcon		;[MF]Select keyboard again
	xra	a
	sta	numtry		;Inititialize count.
	mvi	a,'1'           ;Reset block check type to single character
	sta	curchk		; .  .  .

logo1:	lda	numtry		;How many times have we tried?
	cpi	maxtry		;Too many times?
	jm	logo3		;No, try it.
logo2:	lxi	d,erms19	;Say we couldn't do it.
	call	prtstr
	ret		;Finished.

logo3:	inr	a		;Increment the number of tries.
	sta	numtry
	xra	a
	sta	argblk		;Make it packet number zero.
	mvi	a,1
	sta	argblk+1	;One piece of data.
	lxi	h,data
	mvi	m,'L'           ;Logout the remote host.
	mvi	a,'G'           ;Generic command packet.
	call	spack
	jmp	logo2		; Tell the user and die.
	call	rpack		;Get an acknowledgement
	jmp	logo1		; Go try again.
	cpi	'Y'             ;ACK?
	jz	rskp		;Yes, we are done.
	cpi	'E'             ;Is it an error packet?
	jnz	logo1		;Try sending the packet again.
	call	error1		;Print the error message.
	ret		;All done.
;
;       Packet routines

;       Send_Packet
;       This routine assembles a packet from the arguments given and sends it
;       to the host.
;
;       Expects the following:
;               A        - Type of packet (D,Y,N,S,R,E,F,Z,T)
;               ARGBLK   - Packet sequence number
;               ARGBLK+1 - Number of data characters
;       Returns: nonskip if failure
;                skip if success
;       called by: read, rinit, rfile, rdata, sinit, sfile, sdata, seof, seot,
;                  finish, logout, nak, ackp

spack:	sta	argblk+2
	lxi	h,packet	;Get address of the send packet.
	lda	sndsop		;[gnn] send start-of-pkt char.
	mov	m,a		;Put in the packet.
	inx	h		;Point to next char.
	lda	curchk		;Get current checksum type
	sui	'1'             ;Determine extra length of checksum
	mov	b,a		;Copy length
	lda	argblk+1	;Get the number of data chars.
	adi	' '+3           ;Real packet character count made printable.
	add	b		;Determine overall length
	mov	m,a		;Put in the packet.
	inx	h		;Point to next char.
	lxi	b,0		;Zero the checksum AC.
	mov	c,a		;Start the checksum.
	lda	argblk		;Get the packet number.
	adi	' '             ;Add a space so the number is printable.
	mov	m,a		;Put in the packet.
	inx	h		;Point to next char.
	add	c
	mov	c,a		;Add the packet number to the checksum.
	mvi	a,0		;Clear A (Cannot be XRA A, since we can't
				; touch carry flag)
	adc	b		;Get high order portion of checksum
	mov	b,a		;Copy back to B
	lda	argblk+2	;Get the packet type.
	mov	m,a		;Put in the packet.
	inx	h		;Point to next char.
	add	c
	mov	c,a		;Add the packet number to the checksum.
	mvi	a,0		;Clear A
	adc	b		;Get high order portion of checksum
	mov	b,a		;Copy back to B
spack2:	lda	argblk+1	;Get the packet size.
	ora	a		;Are there any chars of data?
	jz	spack3		; No, finish up.
	dcr	a		;Decrement the char count.
	sta	argblk+1	;Put it back.
	mov	a,m		;Get the next char.
	inx	h		;Point to next char.
	add	c
	mov	c,a		;Add the packet number to the checksum.
	mvi	a,0		;Clear A
	adc	b		;Get high order portion of checksum
	mov	b,a		;Copy back to B
	jmp	spack2		;Go try again.

spack3:	lda	curchk		;Get the current checksum type
	cpi	'2'             ;Two character?
	jz	spack4		;Yes, go handle it
	jnc	spack5		;No, go handle CRC if '3'
	mov	a,c		;Get the character total.
	ani	0C0H		;Turn off all but the two high order bits.
				;Shift them into the low order position.
	rlc		;Two left rotates same as 6 rights
	rlc		; .  .  .
	add	c		;Add it to the old bits.
	ani	3FH		;Turn off the two high order bits.  (MOD 64)
	adi	' '             ;Add a space so the number is printable.
	mov	m,a		;Put in the packet.
	inx	h		;Point to next char.
	jmp	spack7		;Go store eol character

;Here for 3 character CRC-CCITT

spack5:	mvi	m,0		;Store a null for current end
	push	h		;Save H
	lxi	h,packet+1	;Point to first checksumed character
	call	crcclc		;Calculate the CRC
	pop	h		;Restore the pointer
	mov	c,e		;Get low order half for later
	mov	b,d		;Copy the high order
	mov	a,d		;Get the high order portion
	rlc		;Shift off low 4 bits
	rlc		; .  .  .
	rlc		; .  .  .
	rlc		; .  .  .
	ani	0FH		;Keep only low 4 bits
	adi	' '             ;Put into printing range
	mov	m,a		;Store the character
	inx	h		;Point to next position

;Here for two character checksum

spack4:	mov	a,b		;Get high order portion
	ani	0FH		;Only keep last four bits
	rlc		;Shift up two bits
	rlc		; . .  .
	mov	b,a		;Copy back into safe place
	mov	a,c		;Get low order half
	rlc		;Shift  high two bits
	rlc		;to low two bits
	ani	03H		;Keep only two low bits
	ora	b		;Get high order portion in
	adi	' '             ;Convert to printing character range
	mov	m,a		;Store the character
	inx	h		;Point to next character
	mov	a,c		;get low order portion
	ani	3FH		;Keep only six bits
	adi	' '             ;Convert to printing range
	mov	m,a		;Store it
	inx	h		;Bump the pointer

spack7:	lda	dbgflg
	ora	a		; is debugging enabled?
	jz	spack8
	push	h		; yes. save address of end of packet
	mvi	m,0		; null-terminate the packet for display
	lda	quietd		; a quiet display?
	ana	a
	jnz	spac7a		; so dont say a thing
	call	sppos		; position cursor
	lxi	h,packet+1	; print the packet
	call	dmptxt
	lda	prnflg		; is the printer on too?
	ana	a
	jz	spac7a
	lxi	h,sstatm		; print state
	call	printm		; dumptext but to printer
	lda	state
	mov	e,a
	call	outprn
	lxi	h,princr	; cr lf to printer
	call	printm
	lxi	h,spackm
	call	printm
	lxi	h,packet+1
	call	printm
	lxi	h,princr
	call	printm
	lxi	h,princr
	call	printm

spac7a:	pop	h		; restore address of end of packet
spack8:	lda	seol		;Get the EOL the other host wants.
	mov	m,a		;Put in the packet.
	inx	h		;Point to next char.
	xra	a		;Get a null.
	mov	m,a		;Put in the packet.
;       Write out the packet.
outpkt:	call	selmdm		; Set up for output to comm port if iobyt
	lda	spad		;Get the number of padding chars.
	sta	temp1
outpk2:	lda	temp1		;Get the count.
	dcr	a
	ora	a
	jm	outpk6		;If none left proceed.
	sta	temp1
	lda	spadch		;Get the padding char.
	call	setpar		;Set parity appropriately
	mov	e,a		;Put the char in right AC.
	call	outmdm		;Output it.
	jmp	outpk2

outpk6:	lxi	h,packet	; Point to the packet.
outlup:	mov	a,m		; Get the next character.
	ora	a		; Is it a null?
	jz	outlud		; If so return success.
	call	setpar		; Set parity for the character
	mov	e,a		; Put it in right AC
	call	outmdm		; and output it.
; TAC trap: If this character is the TAC intercept character, and the TAC
; trap is enabled, we have to output it twice.  If the TAC trap is enabled,
; tacflg contains the intercept character.  (The current character cannot
; be NUL, so we don't have to worry about doubling nulls in the message)
	lda	tacflg		; get current intercept character, or zero.
	cmp	m		; compare against current data character.
	jnz	outpk8		; if different, do nothing.
	call	setpar		; match. set appropriate parity,
	mov	e,a		;  put it in the right register,
	call	outmdm		;  and output it a second time.
outpk8:
	inx	h		; Increment the char pointer.
	jmp	outlup

outlud:	call	selcon		; select console
	jmp	rskp		; and return success
;
;       Receive_Packet
;       This routine waits for a packet to arrive from the host.  It reads
;       characters until it finds a SOH.  It then reads the packet into packet.
;
;       Returns:  nonskip if failure (checksum wrong or packet trashed)
;          skip if success, with
;               A        - message type
;               ARGBLK   - message number
;               ARGBLK+1 - length of data
;       called by: rinit, rfile, rdata,
;                  sinit, sfile, sdata, seof, seot, finish, logout

rpack:	call	inpkt		;Read up to the end-of-line character
	jmp	r		; Return bad.
rpack0:	call	getchr		;Get a character.
	jmp	rpack		; Hit eol;null line;just start over.
	lxi	h,rcvsop	;[gnn] Is it receive start-of-pkt char.
	cmp	m		;[gnn]
	jnz	rpack0		; No, go until it is.
rpack1:	call	getchr		;Get a character.
	jmp	r		; Hit end of line, return bad.
	lxi	h,rcvsop	;[gnn] Is it receive start-of-pkt char.
	cmp	m		;[gnn]
	jz	rpack1		; Yes, then go start over.
	sta	packet+1	;Store in packet also
	mov	c,a		;Start the checksum.
	lda	curchk		;Get block check type
	sui	'1'             ;Determine extra length of block check
	mov	b,a		;Get a copy
	mov	a,c		;Get back length character
	sui	' '+3           ;Get the real data count.
	sub	b		;Get total length
	sta	argblk+1
	mvi	b,0		;Clear high order half of checksum
	call	getchr		;Get a character.
	jmp	r		; Hit end of line, return bad.
	lxi	h,rcvsop	;[gnn] Is it receive start-of-pkt char.
	cmp	m		;[gnn]
	jz	rpack1		; Yes, then go start over.
	sta	argblk
	sta	packet+2	;Save also in packet
	add	c
	mov	c,a		;Add the character to the checksum.
	mvi	a,0		;Clear A
	adc	b		;Get high order portion of checksum
	mov	b,a		;Copy back to B
	lda	argblk
	sui	' '             ;Get the real packet number.
	sta	argblk
	call	getchr		;Get a character.
	jmp	r		; Hit end of line, return bad.
	lxi	h,rcvsop	;[gnn] Is it receive start-of-pkt char.
	cmp	m		;[gnn]
	jz	rpack1		; Yes, then go start over.
	sta	temp1		;Save the message type.
	sta	packet+3	;Save in packet
	add	c
	mov	c,a		;Add the character to the checksum.
	mvi	a,0		;Clear A
	adc	b		;Get high order portion of checksum
	mov	b,a		;Copy back to B
	lda	argblk+1	;Get the number of data characters.
	sta	temp2
	lxi	h,data		;Point to the data buffer.
	shld	datptr
rpack2:	lda	temp2
	sui	1		;Any data characters?
	jm	rpack3		; If not go get the checksum.
	sta	temp2
	call	getchr		;Get a character.
	jmp	r		; Hit end of line, return bad.
	lxi	h,rcvsop	;[gnn] Is it receive start-of-pkt char.
	cmp	m		;[gnn]
	jz	rpack1		; Yes, then go start over.
	lhld	datptr
	mov	m,a		;Put the char into the packet.
	inx	h		;Point to the next character.
	shld	datptr
	add	c
	mov	c,a		;Add the character to the checksum.
	mvi	a,0		;Clear A
	adc	b		;Get high order portion of checksum
	mov	b,a		;Copy back to B
	jmp	rpack2		;Go get another.

rpack3:	call	getchr		;Get a character.
	jmp	r		; Hit end of line, return bad.
	lxi	h,rcvsop	;[gnn] Is it receive start-of-pkt char.
	cmp	m		;[gnn]
	jz	rpack1		; Yes, then go start over.
	sui	' '             ;Turn the char back into a number.
	sta	temp3
;Determine type of checksum

	lda	curchk		;Get the current checksum type
	cpi	'2'             ;1, 2 or 3 character?
	jz	rpack4		;If zero, 2 character
	jnc	rpack5		;Go handle 3 character
	mov	a,c		;Get the character total.
	ani	0C0H		;Turn off all but the two high order bits.
				;Shift them into the low order position.
	rlc		;Two left rotates same as six rights
	rlc		; .  .  .
	add	c		;Add it to the old bits.
	ani	3FH		;Turn off the two high order bits.  (MOD 64)
	mov	b,a
	lda	temp3		;Get the real received checksum.
	cmp	b		;Are they equal?
	jz	rpack7		;If so, proceed.
rpack9:	call	updrtr		;If not, update the number of retries.
	ret		;Return error.

;Here for three character CRC-CCITT

rpack5:	lhld	datptr		;Get the address of the data
	mvi	m,0		;Store a zero in the buffer to terminate packet
	lxi	h,packet+1	;Point at start of checksummed region
	call	crcclc		;Calculate the CRC
	mov	c,e		;Save low order half for later
	mov	b,d		;Also copy high order
	mov	a,d		;Get high byte
	rlc		;Want high four bits
	rlc		; .  .  .
	rlc		;And shift two more
	rlc		; .  .  .
	ani	0FH		;Keep only 4 bits
	mov	d,a		;Back into D
	lda	temp3		;Get first value back
	cmp	d		;Correct?
	jnz	rpack9		;No, punt
	call	getchr		;Get a character.
	jmp	r		; Hit end of line, return bad.
	lxi	h,rcvsop	;[gnn] Is it receive start-of-pkt char.
	cmp	m		;[gnn]
	jz	rpack1		; Yes, then go start over.
	sui	' '             ;Remove space offset
	sta	temp3		;Store for later check
	;...

;Here for a two character checksum and last two characters of CRC

rpack4:	mov	a,b		;Get high order portion
	ani	0FH		;Only four bits
	rlc		;Shift up two bits
	rlc		; .  .  .
	mov	b,a		;Save back in B
	mov	a,c		;Get low order
	rlc		;move two high bits to low bits
	rlc		; .  .  .
	ani	03H		;Save only low two bits
	ora	b		;Get other 4 bits
	mov	b,a		;Save back in B
	lda	temp3		;Get this portion of checksum
	cmp	b		;Check first half
	jnz	rpack9		;If bad, go give up
	call	getchr		;Get a character.
	jmp	r		; Hit end of line, return bad.
	lxi	h,rcvsop	;[gnn] Is it receive start-of-pkt char.
	cmp	m		;[gnn]
	jz	rpack1		; Yes, then go start over.
	sui	' '             ;Remove space offset
	mov	b,a		;Save in safe place
	mov	a,c		;Get low 8 bits of checksum
	ani	3FH		;Keep only 6 bits
	cmp	b		;Correct value
	jnz	rpack9		;Bad, give up
rpack7:	lhld	datptr
	mvi	m,0		;Put a null at the end of the data.
	lda	temp1		;Get the type.
	jmp	rskp
;
;       inpkt - receive and buffer packet
;       returns: nonskip if error (timeout)
;               skip if success; packet starts at recpkt (which holds the SOH)
;               and is terminated by a null.
;               console is selected in either case.
;       called by: rpack

inpkt:	lxi	h,recpkt	;Point to the beginning of the packet.
	shld	pktptr
inpkt1:	call	inchr		;Get first character
	jmp	r		;Return failure
	lxi	h,rcvsop	;[gnn] Is it receive start-of-pkt char.
	cmp	m		;[gnn]
	jnz	inpkt1		;if not, ignore leading junk
	jmp	inpkt3		;else go put it in packet

inpkt2:	call	inchr		;Get a character.
	jmp	r		; Return failure.
	lxi	h,rcvsop	;[gnn] Is it receive start-of-pkt char.
	cmp	m		;[gnn]
	jnz	inpkt3		;if not continue
	lxi	h,recpkt	;else throw away what we've got so far
	shld	pktptr		;
inpkt3:	lhld	pktptr		;
	mov	m,a		;Put the char in the packet.
	inx	h
	shld	pktptr
	mov	b,a
	lxi	d,-recpkx	;Start over if packet buffer overflow
	dad	d		;
	jc	inpkt		;buffer overflow
	lda	reol		;Get the EOL char.
	cmp	b
	jnz	inpkt2		;If not loop for another.
;[gnn]                  ***  added by Godfrey Nix   Nottingham University ***
;[gnn]                  to allow Kermit server to echo our packets back
	lxi	h,recpkt+3	;[gnn] point to packet type
	lda	packet+3	;[gnn] get the one we sent
	cmp	m		;[gnn] are they the same?
	jz	inpkt		;[gnn] yes, get another packet
;[gnn]                  *** end of patch *****
 ;...
	;...

;Begin IBM change/fdc
;This moved from OUTPK7 -- it appears that waiting until we're
;ready to send a packet before looking for turnaround character
;is long enough for it to get lost.  Better to look now.

	lda	ibmflg		;Is this the IBM?
	ora	a
	jz	inpkt6		;If not then proceed.
	lda	state		;Check if this is the Send-Init packet.
	cpi	'S'
	jz	inpkt6		;If so don't wait for the XON.
inpkt5:	call	inchr		;Wait for the turn around char.
	jmp	inpkt6
	cpi	xon		;Is it the IBM turn around character?
	jnz	inpkt5		;If not, go until it is.
inpkt6:	lhld	pktptr		;Reload packet pointer
;End IBM change/fdc.
	dcx	h		;Back up to end of line character
	mvi	m,0		;Replace it with a null to stop rpack:
	call	selcon		;We've got the packet. Return to console.

	lda	dbgflg		; Is debugging enabled?
	ora	a
	jz	inpkt7
	inx	h		; Point to next char.
	lda	quietd		; a quiet display?
	ana	a
	jnz	inpkt7		; so dont say a thing
	call	rppos		; position cursor
	lxi	h,recpkt+1	; print the packet
	call	dmptxt

	lda	prnflg		; is the printer on too?
	ana	a
	jz	inpkt7
	lxi	h,rstatm		; print state
	call	printm		; dumptext but to printer
	lda	state
	mov	e,a
	call	outprn
	lxi	h,princr	; cr lf to printer
	call	printm
	lxi	h,rpackm
	call	printm
	lxi	h,recpkt+1
	call	printm
	lxi	h,princr
	call	printm
	lxi	h,princr
	call	printm


inpkt7:	lxi	h,recpkt
	shld	pktptr		;Save the packet pointer.
	jmp	rskp		;If so we are done.

;       getchr - get next character from buffered packet.
;       returns nonskip at end of packet.
;       called by: rpack

getchr:	lhld	pktptr		;Get the packet pointer.
	mov	a,m		;Get the char.
	inx	h
	shld	pktptr
	ora	a		;Is it the null we put at the end of the packet?
	jnz	rskp		;If not return retskp.
	ret		;If so return failure.
;
;
;       inchr - character input loop for file transfer
;       returns: nonskip if timeout or character typed on console
;                       (console selected)
;               skip with character from modem in A (parity stripped
;                       if necessary; modem selected)
;               preserves bc, de, hl in either case.
;       called by: inpkt

inchr:	push	h		; save hl and bc
	push	b
	lhld	timout		;Get initial value for timeout
	shld	timval		;[jd] 
inchr0:	call	selmdm		;select modem
	call	inpmdm		;Try to get a character from the modem
	ora	a
	jz	inchr2		;if zero, nothing there.
	mov	b,a
	lda	parity		;Is the parity none?
	cpi	parnon
	mov	a,b
	jz	inchr1		;If so just return.
	ani	7FH		;Turn off the parity bit.
inchr1:	pop	b		;restore registers
	pop	h
	jmp	rskp		;take skip return, character in A

inchr2:	call	selcon		;select console
	call	inpcon		; Try to get a character from the console
	ora	a
	jz	inchr6		;If not go do timer thing
	cpi	cr		;Is it a carriage return?
	jz	inchr4		;If so return
	cpi	('Z'-100O)      ;Control-Z?
	jz	inchr5		;Yes, go flag it
	cpi	('C'-100O)      ;Control-C?
	jz	inchr7		;re-enter, he wants to get out
	cpi	('X'-100O)      ;Control-X?
	jnz	inchr6		;No, ignore it. do timer thing.
inchr5:	adi	100O		;Convert to printing range
	sta	czseen		;Flag we saw a control-Z
	jmp	inchr6		;[MF] and do timer thing
inchr4:	pop	b		; restore registers
	pop	h
	ret		;And return

inchr6:	lda	timflg		;[jd] pick up timer flag
	ora	a		;[jd] are we allowed to use timer?
	jz	inchr0		;[jd] no, don't time out
	lhld	timval		; decrement fuzzy time-out
	dcx	h		;
	shld	timval		;((timout-1) * loop time)
	mov	a,h		;(Retry if not time-out)
	ora	l		;
	jnz	inchr0		;
	call	updrtr		;Count as retry (?)
	pop	b		;restore registers
	pop	h
	ret		;and return to do retry

inchr7:	call	clrtop		;[hh] clear screen and home cursor
	lda	takflg		;[MF]Take-file in progress?
	ani	1		;[MF]...
	cnz	closet		;[MF]Yes, close it and reset TAKE-flag
				;[MF]so all processing is halted
	jmp	kermit		;[hh] then re-enter kermit

;
;       CRCCLC - Routine to calculate a CRC-CCITT for a string.
;
;       This routine will calculate a CRC using the CCITT polynomial for
;       a string.
;
;       call with: HL/ Address of null-terminated string
;       16-bit CRC value is returned in DE.
;       Registers BC and HL are preserved.
;
;       called by: spack, rpack

crcclc:	push	h		;Save HL
	push	b		;And BC
	lxi	d,0		;Initial CRC value is 0

crccl0:	mov	a,m		;Get a character
	ora	a		;Check if zero
	jz	crccl1		;If so, all done
	push	h		;Save the pointer
	xra	e		;Add in with previous value
	mov	e,a		;Get a copy
	ani	0FH		;Get last 4 bits of combined value
	mov	c,a		;Get into C
	mvi	b,0		;And make high order zero
	lxi	h,crctb2	;Point at low order table
	dad	b		;Point to correct entry
	dad	b		; .  .  .
	push	h		;Save the address
	mov	a,e		;Get combined value back again
	rrc		;Shift over to make index
	rrc		; .  .  .
	rrc		; .  .  .
	ani	1EH		;Keep only 4 bits
	mov	c,a		;Set up to offset table
	lxi	h,crctab	;Point at high order table
	dad	b		;Correct entry
	mov	a,m		;Get low order portion of entry
	xra	d		;XOR with previous high order half
	inx	h		;Point to high order byte
	mov	d,m		;Get into D
	pop	h		;Get back pointer to other table entry
	xra	m		;Include with new high order half
	mov	e,a		;Copy new low order portion
	inx	h		;Point to other portion
	mov	a,m		;Get the other portion of the table entry
	xra	d		;Include with other high order portion
	mov	d,a		;Move back into D

	pop	h		;And H
	inx	h		;Point to next character
	jmp	crccl0		;Go get next character

crccl1:	pop	b		;Restore B
	pop	h		;And HL

	ret		;And return, DE=CRC-CCITT

CRCTAB:	DW	00000H
	DW	01081H
	DW	02102H
	DW	03183H
	DW	04204H
	DW	05285H
	DW	06306H
	DW	07387H
	DW	08408H
	DW	09489H
	DW	0A50AH
	DW	0B58BH
	DW	0C60CH
	DW	0D68DH
	DW	0E70EH
	DW	0F78FH

CRCTB2:	DW	00000H
	DW	01189H
	DW	02312H
	DW	0329BH
	DW	04624H
	DW	057ADH
	DW	06536H
	DW	074BFH
	DW	08C48H
	DW	09DC1H
	DW	0AF5AH
	DW	0BED3H
	DW	0CA6CH
	DW	0DBE5H
	DW	0E97EH
	DW	0F8F7H
;
;       This is where we go if we get an error during a protocol communication.
;       error prints the error packet on line 6 or so, and aborts the
;       transfer.
;         called by: rinit, rfile, rdata, sinit, sfile, sdata, seof, seot
;       error1 print CRLF followed by the error packet.
;         called by: finish, logout
;       error2 just prints the error packet.
;       error3 positions cursor and prints error message specified in DE.
;         called by: rinit, rfile, rdata, sinit, sfile, sdata, seof,
;                    seot, parwrn, gofil, outbuf

error:	lda	quietd		; a quiet display?
	ana	a
	jnz	error0		; so dont say a thing
	lda	remtxt		;[MF]Doing a remote command?
	ora	a		;[MF]...
	jnz	error0		;[MF]Yes, don't position cursor
	call	screrr		;Position the cursor.
error0:	mvi	a,'A'           ;Set the state to abort.
	sta	state
	jmp	error2

error1:	lxi	d,crlf		;Print a CRLF.
	lda	quietd		; a quiet display?
	ana	a
	jnz	error2		; so dont say a thing
		call	prtstr
error2:	lda	argblk+1	;Get the length of the data.
	mov	c,a
	mvi	b,0		;Put it into BC
	lxi	h,data		;Get the address of the data.
	dad	b		;Get to the end of the string.
	mvi	m,'$'           ;Put a dollar sign at the end.
	lxi	d,data		;Print error message
	lda	remtxt		;[MF]Doing a remote command?
	ora	a		;[MF]...
	jnz	errr2a		;[MF]Yes, print message, quiet or not!
	lda	quietd		; a quiet display?
	ana	a
	rnz		; so dont say a thing
errr2a:	call	prtstr
	ret

error3:	lda	quietd		; a quiet display?
	ana	a
	rnz		; so dont say a thing
	lda	remtxt		;[MF]Doing a remote command?
	ora	a		;[MF]...
	jnz	err3a		;[MF]Yes, don't position cursor
	push	d		;Save the pointer to the message.
	call	screrr		;Position the cursor.
	pop	d		;Get the pointer back.
err3a:	call	prtstr		;Print error message
	ret
;
;       Set up for file transfer.
;       called by read, send.

init:	lxi	d,version	; point at Kermit's version string
	lda	quietd		; a quiet display?
	ana	a
	jnz	init1		; so dont say a thing
	call	sysscr		; fix up screen
init1:	call	selmdm		; select modem
	call	flsmdm		; purge any pending data
	call	selcon		; select console again.
	ret

;       Set state to ABORT
;       called by: rinit, rfile, rdata, sinit, sfile, sdata, seof, seot,
;                  nak, ackp

abort:	mvi	a,'A'           ;Otherwise abort.
	sta	state
	ret

;       nak - send NAK packet
;       here from: rinit, rfile, rdata
;       nak0 - update retry count and send NAK packet
;       here from: rinit, rfile, rdata, tryagn

nak0:	call	updrtr		;Update number of retries.
nak:	lda	pktnum		;Get the packet number we're waiting for.
	sta	argblk
	xra	a		;No data.
	sta	argblk+1
	mvi	a,'N'           ;NAK that packet.
	call	spack
	jmp	abort		; Give up.
	ret		;Go around again.

;       increment and display retry count
;       called by: rfile, sinit, sfile, sdata, seof, seot,
;                  nak, rpack, inchr, tryagn

updrtr:	lhld	numrtr
	inx	h		;Increment the number of retries
	shld	numrtr
	lda	remtxt		;[MF]Doing a remote server command?
	ora	a		;[MF]...
	rnz		;[MF]Yes, keep mum
	lda	quietd		; a quiet display?
	ana	a
	rnz		; so dont say a thing
	call	scrnrt		;Position cursor
	lhld	numrtr		;[MF]
call	nout		;Write the number of retries.
	ret

;       [jd] this routine prints parity warnings.  All registers are
;       saved except for a.
;       called by: sdata

parwrn:	push	b
	push	d
	push	h
	lxi	d,inms25
	call	error3
	pop	h
	pop	d
	pop	b
	ret
;[jd] end of addition

;       print message in status field.  address of message is in DE.
;       called by: read, send

finmes:	lda	quietd		; a quiet display?
	ana	a
	jz	finme0		; so do usual stuff
	push	d		;[MF]Save pointer to completion message
	call	prcrlf		; best do a new line
	pop	d		;[MF]Restore completion message pointer
	call	prtstr		; and send message
	mvi	e,space		; send a space or two
	mvi	c,dconio
	push	b
	push	d
	call	bdos
	pop	d
	pop	b
	call	bdos
	ret		; and exit back
;
;else for screaming screens...

finme0:	push	d		;Save message.
	call	scrst		;Position cursor
	pop	d		;Print the termination message
	call	prtstr
	ret		; may not want this **************

	mvi	c,4		;[2] copy across user no and drive
	lxi	h,kerm1		;[2] as we have the text already
finme1:	mov	e,m
	push	h		;[2] conout probably destroys these
	push	b
	call	conout
	pop	b
	pop	h
	inx	h		;[2] next character
	dcr	c		;[2] ah, but have we done?
	jnz	finme1		;[2] nope
	lxi	d,spac15	;[2] send 15 spaces (clears previous filename)
	call	prtstr		;[2]
	call	scrend		;Position cursor for prompt
	ret

;       Compare expected packet number against received packet number.
;       return with flags set (Z = packet number valid)
;       called by: rfile, rdata, sinit, sfile, sdata, seof, seot

compp:	lda	pktnum		;Get the packet Nr.
	mov	b,a
	lda	argblk
	cmp	b
	ret

;       Increment the packet number, modulo 64.
;       called by: rinit, rfile, rdata, sinit, sfile, sdata, seof, seot

countp:	inr	a		;Increment packet Nr.
	ani	3FH		;Turn off the two high order bits
	sta	pktnum		;Save modulo 64 of number
	lhld	numpkt
	inx	h		;Increment Nr. of packets
	shld	numpkt
	ret

;       Send an ACK-packet
;       called by: rfile, rdata, tryagn

ackp:	xra	a
	sta	numtry		;Reset number of retries
	sta	argblk+1	;No data. (The packet number is in argblk)
	mvi	a,'Y'           ;Acknowledge packet
	call	spack		;Send packet
	jmp	abort
	ret

;       ?
;       called with A/ current retry count
;       called by: rfile, rdata

tryagn:	inr	a		;Increment it.
	sta	oldtry		;Save the updated number of tries.
	lda	pktnum		;Get the present packet number.
	dcr	a		;Decrement
	ani	3FH		; modulo 64
	mov	b,a
	lda	argblk		;Get the packet's number
	cmp	b		;Is the packet's number one less than now?
	jnz	nak0		;No, NAK it and try again.
	call	updrtr		;Update the number of retries.
	call	ackp
	ret

;       Output a null-terminated string to the console.  We assume that the
;       console has been selected.  Called with HL = address of string.
;       called by: spack, inpkt

dmptxt:	mov	a,m		; get character from string
	ora	a
	rz		; done if null
	push	h		; save string address
	mov	e,a		; move character to E for outcon
	call	outcon		; output character to console
	pop	h		; restore string address
	inx	h		; point past printed character
	jmp	dmptxt		; go output rest of string


;       Output a null-terminated string to the PRINTER  We assume that the
;       console has been selected.  Called with HL = address of string.
;       called by: spack, inpkt

printm:	mov	a,m		; get character from string
	ora	a
	rz		; done if null
	push	h		; save string address
	mov	e,a		; move character to E for outcon
	call	outprn		; output character to printer
	pop	h		; restore string address
	inx	h		; point past printed character
	jmp	printm		; go output rest of string


;
;       test if character in A is the start of header character.  We get
;       the start of packet character from sohchr, which can be SET
tstsoh:	push	b		; save these registers for a bit
	mov	c,a		; we have to test if this is the character
	lda	sohchr
	cmp	c		; if zero, then it is
	mov	a,c		; restore accumulator but not flags
	pop	b
	ret		; return with flags set
;


; Little code to allow some expansion of code without changing
;  every futher address, only up to the end of this file.
;   TO BE REMOVED FRO RELEASE!

;	org ($+100h) AND 0FF00H


IF lasm
	LINK	CPSREM
ENDIF;lasm
