; CP4TT.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 code for the TRANSMIT and CONNECT commands,
;	which communicate with a host which is not running KERMIT.
;
; revision history:
; edit 3: July 27, 1984
;	Allow assembly with LASM: to CP4TT is linked by CP4PKT, and links
;	to CP4CPM; remove exclamation points so as not to confuse LASM.
;	Add Toad Hall TACtrap to TRANSMIT command (TAC intercept character
;	is only doubled if it's data; when typed by the user, they're not
;	automatically doubled)
;
; edit 2: June 7, 1984
;	formatting and documentation; add module version number; make sure
;	console is selected when leaving intchr.
;
; edit 1: May, 1984
;	extracted from CPMBASE.M80 version 3.9; modifications are described
;	in the accompanying .UPD file.

ttver:	db	'CP4TT.ASM  (3) 27-Jul-84$'

;	This is the TRANSMIT command.  It attempts to send a file, even
;	though there is no KERMIT on the other side.
;	here from: kermit

xmit:	mvi	a,cmofi		;Parse an input file spec (non-wild).
	lxi	d,fcb		;Give the address for the FCB.
	call	comnd
	 jmp	kermit		;Give up on bad parse.
	call	cfmcmd
	call	getfil		;Open file.
	cpi	0FFH		;Succeed?
	jnz	xmit1
	lxi	d,erms15
	call	prtstr		;Display error msg.
	jmp	kermit

xmit1:	lxi	d,inms19	;Output start message.
	call	prtstr
	call	escpr		;Print the escape character.
	lxi	d,inms20	;Output 2nd part.
	call	prtstr
	call	escpr		;Print the escape character.
	lxi	d,inms21	;Print the rest.
	call	prtstr
	mvi	a,1		;Start file I/O.
	sta	fileio
	xra	a		;Clear XOFF flag.
	sta	xofflg
	; fall through into xnext...
;
;	assemble another line from the disk file.
;	here from: previous page, rexmit

xnext:	call	prtchr		; Copy characters from comm line to console
	mvi	c,consta	;  until user types anything on the console.
	call	bdos
	ora	a
	jz	xnext		; nothing at console yet.
	lda	eoflag		;EOF encountered?
	ora	a
	jnz	xend		;Yes, finish.
	xra	a		;Reset line buffer counter.
	mov	c,a
	sta	cmaflg		;Reset carriage return flag.
	lxi	d,cmdbuf	;Use comnd buffer as line buffer.
	lhld	bufpnt		; Get current buffer pointer.
	lda	chrcnt		; Get current byte count
	mov	b,a		;  in B
xmit30:	dcr	b		; Assume there's a character there
	jp	xmit2		; If there was, proceed.
	call	inbuf		; There wasn't.  Try for another buffer.
	 jmp	xmit38		; End of file.
	lhld	bufpnt		; Got another buffer.  Get new pointer in HL
	lda	chrcnt		;  and new byte count
	mov	b,a		;  in B
xmit2:	mov	a,m		;Get a character from disk buffer.
	inx	h
	ani	7FH		;Mask 7 bits.
	jz	xmit30		;Skip nulls.
	cpi	cr		;Carriage return?
	jz	xmit32
	cpi	subt		;CTRL-Z (substitute)?
	jz	xmit37
	cpi	lf		;Line feed?
	jz	xmit39
	stax	d		;Save to buffer.
	inx	d
	lda	cmaflg		;Carriage return seen?
	ora	a
	jnz	xmit31		;Yes, don't count this character.
	inr	c		;Count it.
xmit31:	jmp	xmit30		;Loop for next input byte.

; Carriage return seen.  Start discarding characters until we see a line-feed.
xmit32:	sta	cmaflg		;Mark return seen.
	jmp	xmit30		;And continue.

; Control-Z seen. Force end of file, and send the current line.
xmit37:	sta	eoflag		;Mark EOF for next line.
	; fall through...
; End of File encountered. eoflag has already been set; just send current line.
xmit38:
	; fall through...
; Linefeed seen. send the current line.
xmit39:	shld	bufpnt		;Save next buffer pointer.
	mov	a,b		;Save count of remaining characters.
	sta	chrcnt
	mov	a,c		;Save line length.
	sta	filcnt
	; fall through into rexmit...
;

;	transmit the buffered line.
;	here from: previous page, intchr

rexmit:	lda	filcnt		;Set up line length.
	sta	cmccnt
	lxi	h,cmdbuf	;Set up line buffer pointer.
	shld	cmcptr
xmit40:	call	prtchr		;Receive comm. line & display.
	lda	xofflg		;XOFF received?
	ora	a
	jnz	xmit40		;Yes, wait for XON
	lda	cmccnt		;Any characters left?
	dcr	a
	jm	xmit49		;No, next state.
	sta	cmccnt
	call	selmdm		; select modem for outmdm
	lhld	cmcptr		;Get the character to be sent
	mov	a,m
	inx	h		;Bump to next character.
	shld	cmcptr
	call	setpar		;Set parity (if any).
	mov	e,a		;Save character (with parity)
	call	outmdm		;Output it to the comm. line.
; 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	xmit41		; 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.
xmit41:
	lda	ecoflg		;Local echo?
	ora	a
	jz	xmit40		;No, continue.
	mov	a,e		;Get the character.
	ani	7FH		;Mask out parity.
	mov	e,a		;Display on console.
	call	selcon
	call	outcon
	jmp	xmit40		;Continue.

xmit49:	xra	a		;Reset last character seen.
	sta	lstchr
xmit50:	call	prtchr		;Receive comm. line & display.
	call	conchr		;Read keyboard & send.
	 jmp	xendc		;CLOSE connection.
	lda	lstchr		;Check last keyboard character.
	cpi	cr		;Carriage return?
	jz	xnext		;Yes, prepare to send next line.
	jmp	xmit50		;Continue, until carriage return.
;
;	clean up.
;   xend - end of file reached. close file, go to connect mode.
;	here from: xnext.
;   xendc - user wants out. close file, go to command mode.
;	here from: rexmit.

xend:	call	xmtoff		;Close file, etc.
	lxi	d,inms22	;Tell we're done with transmission
	jmp	telnt1		;Branch to CONNECT command.

xendc:	call	xmtoff		;Close file, etc.
	jmp	kermit		;Back to command loop.

xmtoff:	mvi	c,closf		;Close file.
	lxi	d,fcb
	call	bdos
	xra	a		;Terminate file I/O.
	sta	fileio
	ret
;
;   telnet - the CONNECT command.
;	here from: kermit
;   telnt1 - entry to connect mode from TRANSMIT command
;	here from: xend

telnet:	call	cfmcmd
	lxi	d,infms7	;Output start of message
; enter here from TRANSMIT command.
telnt1:	call	prtstr
	call	escpr		;Print the escape char.
	lxi	d,infms8	;Output some more of the message
	call	prtstr
	call	escpr		;Print the escape char again.
	lxi	d,inms8a	;Print the remainder of the message
	call	prtstr
	call	syscon		;do system-dependent stuff
chrlup:	call	prtchr		;See if char at port (send to console).
	call	conchr		;See if char at console (send to port).
	 jmp	kermit		;requested to end session - go to command loop.
	jmp	chrlup		;Go do it again.
;
;
;	prtchr - copy characters from comm line to console
;	returns: nonskip, console selected.
;	called by: xnext, rexmit, telnet
;

prtchr:	call	selmdm		; select modem port
	call	inpmdm		; try to get a character from it
	ora	a		; test character
	jnz	prtch0		; if non-zero, process it.
	call	selcon		; select console
	ret			; return.

prtch0: ani	7FH		; drop parity bit.
	jz	prtchr		; ignore null (filler)
	cpi	del		; ignore delete, too
	jz	prtchr
	cpi	xon		;Is it an XON?
	jz	prtxon		;yes
	cpi	xoff		;Is it an XOFF?
	jz	prtxof		;yes
	mov	e,a		;Set the char aside.
	lda	vtflg		;Get the VT52 emulation flag.
	cpi	1		;Is the flag set?
	jnz	prtch1		;If not, don't do this stuff.
	lda	escflg		;Get the escape flag.
	ora	a		;Are we working on an escape sequence?
	jz	prtch2		;If not, continue.
	call	vt52		;If so, work on it some more
	jmp	prtchr		;try for more characters.

prtch2:	mov	a,e		;normal text.
	cpi	esc		;Is the char an escape?
	jnz	prtch1		;If not skip on.
	mvi	a,1
	sta	escflg		;Set the escape flag: escape seen.
	jmp	prtchr		;Get another char...

prtch1:	call	sysflt		; ok to print this character (in E)?
	ora	a
	jz	prtchr		; no, skip it.
	lda	logflg		;Get the log flag.
	ora	a
	cnz	logit		;Log the char if it is set.
	call	selcon		; select console
	lda	prnflg		;Get Print parallel flag
	ora	a
	cnz	outlpt		; output to printer if flag set
	call	outcon		; output to console.
	jmp	prtchr		; go around again.

; I don't think we want to print xon/xoff - this should be
; flow control only across the link between us and the host.
; (besides, IBM host xon's don't make sense to most micros)
; remember xon/xoff state in xofflg (zero = xon, non-zero = xoff)
prtxon:	xra	a		;Yes, reset XOFF flag
prtxof:	sta	xofflg
	jmp	prtchr		; look for another character
;
;
;	logit - output character in E to log file.
;	we assume the host recognizes xon/xoff. (we probably shouldn't)
;	modem port is selected.
;	preserves de
;	called by: prtchr

logit:	lhld	bufpnt
	mov	m,e		;Store the char.
	inx	h
	shld	bufpnt
	mov	a,l
	ora	a		;Buffer full?
	rnz
	push	d
	mvi	a,xoff		;^S to stop the host while we write the buffer.
	call	setpar		; set correct parity...
	mov	e,a
	call	outmdm		; output it.
	mvi	c,writef
	lxi	d,fcb
	call	bdos
	ora	a		;Successful.
	jz	logit2
	lxi	d,erms11
	call	prtstr
logit2:	mvi	a,xon		;^Q to restart the host
	call	setpar		; set appropriate parity
	mov	e,a
	call	outmdm		; send it.
	lxi	h,buff
	shld	bufpnt		;Reset the buffer pointer.
	pop	d
	ret
;
;
;	VT52 emulation.
;	called by: prtchr
;	A/ contents of escflg (guaranteed non-zero)
;	E/ current character
;	modem is selected.
;
vt52:	cpi	1		; first character after escape?
	jnz	vt52y		; no, must be doing cursor positioning.
;
;	E contains the character that followed the escape.
;	valid characters are:
;	A - cursor up
;	B - cursor down
;	C - cursor right
;	D - cursor left
;	F - enter graphics mode (hard to do on a non-vt52)
;	G - exit graphics mode
;	H - home
;	I - reverse linefeed
;	J - erase to end of screen
;	K - erase to end of line
;	Y - cursor positioning leadin
;	Z - identify terminal as VT52
;	[ - enter hold-screen mode (not supported)
;	\ - exit hold-screen mode (not supported)
;	> - enter alternate-keypad mode? (not supported)
;	= - exit alternate-keypad mode? (not supported)
;
;	Invalid sequences are handled as the VT52 does - the escape and
;	the following character are swallowed, never to be seen again.
;	For <esc>E, the translation table may contain just '$' (no action),
;	or may be used as clear-and-home, as in the Heath/Zenith H19.
;
	mov	a,e		; get the second character of the sequence.
	cpi	'Y'		; if cursor lead-in handle it.
	jnz	vt52a		; if not, go on.
	mvi	a,2		; state = 2: row follows.
	sta	escflg		; update the flag.
	ret			; back for another character

vt52a:	cpi	'Z'		; VT52 ID query?
	jz	vt52id		; yes. claim to be one.
	cpi	'A'		;Less than an 'A'?
	jm	vtig		;Yes - ignore.
	cpi	'K'+1		;Greater than 'K'?
	jp	vtig		;Yes - ignore.
	sui	'A'		;Else make into index.
	rlc			;Multiply by four.
	rlc			;(Shift left twice.)
	lhld	pttab		;Load base addr of table.
	mov	e,a		;Move a into de pair.
	mvi	d,00H		;Zero out high byte.
	dad	d		;Double add index+offset.
	xchg			;Exchange de with hl.
	call	selcon		; select console
	call	prtstr		;and syscall.
vtig:				;Ignore escape sequence.
	xra	a		;Reset the ol' escape flag.
	sta	escflg
	ret			;Return home.

; here for <esc>Z.  Tell the host we're a VT52. (Sure we are...)
vt52id:	mvi	a,esc		; response is escape...
	call	setpar		; (need correct parity)
	mov	e,a
	call	outmdm		; (console already selected)
	mvi	a,'/'		; ... slash ...
	call	setpar		; (with parity)
	mov	e,a
	call	outmdm
	mvi	a,'K'		; ... K.
	call	setpar
	mov	e,a
	call	outmdm
	jmp	vtig		; clear escape-sequence flag and return.

; here when escflg isn't 0 or 1 - processing cursor positioning sequence.
vt52y:	cpi	2		; looking for row? (y-coordinate)
	jnz	vt52x		; no, must be column.
	mov	a,e		; yes. get coordinate
	sui	(' '-1)		; convert from ascii (1 = top line)
	sta	vtyval		; store for later
	mvi	a,3		; advance to next state (x coord)
	sta	escflg		; store it
	ret			; try for another character

; here when escflag isn't 0, 1, or 2 - it must be 3. (right?)
; E holds the last character of the cursor positioning sequence.
vt52x:	xra	a		; end of escape sequence, reset state.
	sta	escflg
	mov	a,e		; get column (' ' is left margin)
	sui	(' '-1)		; make left margin be one
	mov	c,a		; stash column in c
	lda	vtyval		; get row number
	mov	b,a		;  in b
	call	selcon		; select console
	call	csrpos		; call system-dependent cursor positioner
	ret			; all through.
;
;
;	conchr - copy character from console to comm line, processing
;	(kermit's) escape sequences.
;	Enter and exit with console selected.
;	nonskip return: transparent mode terminated.
;	skip return:	still in transparent mode.
;	called by: rexmit, telnet

conchr:	call	inpcon		;Try to get a character from the console
	ani	07FH		;Keep only 7 bits
	jz	rskp		;Null means nothing there.
	mov	e,a		;Move the char for comparison.
	sta	lstchr		;Save it
	lda	escchr		;Get the escape char.
	cmp	e		;Is it an escape char?
	jz	intchr		;If so go process it.
	call	selmdm		; select the modem
	mov	a,e		;Get the char.
	call	setpar		;Set parity (if any).
	mov	e,a		;Restore it.
	call	outmdm		;Output the char to the port.
	call	selcon		; reselect console
	lda	ecoflg		;Get the echo flag.
	ora	a		;Is it turned on?
	jz	rskp		;If not we're done here.
	mov	a,e		;Get the char.
	ani	7FH		;Turn off the parity bit.
	mov	e,a
	call	outcon		; echo the character.
	jmp	rskp		; use skip return
;
;	transparent escape character has been typed. dispatch on second
;	character. (console is still selected)
;	here from: conchr

intchr: call	inpcon		; get another character from the console
	ora	a		; zero means no character available yet.
	jz	intchr		; If so, loop until we get a char.
	mov	b,a		;Save the actual char.
	cpi	ctrlc		;is it Control-C?
	jz	contc		;yes
	ani	137O		;Convert to upper case.
	cpi	'C'		;Is it close?
	jnz	intch0		;If not proceed.
contc:	lxi	d,infms9	;Say we are back.
	call	prtstr
	call	syscls		; call system-dependent close routine
	lda	logflg		;Get the log flag.
	ora	a		;Is it set?
	rz			;Return if it is zero.
	xra	a
	sta	logflg		;Turn off the log flag.
	lxi	d,infms6	;Tell user we are closing file.
	call	prtstr
	lhld	bufpnt
	mvi	m,('Z'-100O)	;Put in a ^Z.
	mvi	c,writef	;Write the last buffer.
	lxi	d,fcb
	call	bdos
	mvi	c,closf		;Close the file.
	lxi	d,fcb
	call	bdos
	ret

;Here if not a 'C' or '^C'

intch0: cpi	'S'		;Is it status?
	jnz	inch01		;If not, proceed.
	call	stat01		;Print out the status stuff.
	jmp	rskp		;return from conchr

inch01:	cpi	'R'-100O	;Control-R?
	jz	inch02		;Yes
	cpi	'R'		;(plain) R?
	jnz	inch03		;No
inch02:	lda	fileio		;TRANSMIT in progress?
	ora	a
	jz	inch03		;No,ignore
	pop	b		;Remove return address (non-local goto)
	jmp	rexmit		;Retransmit line

inch03:	mov	a,b		;Get the char.
	cpi	'?'		;Is it a help request?
	jnz	intch1		;If not, go to the next check.
	lda	fileio		;TRANSMIT in progress?
	ora	a
	jz	inch04		;No
	lxi	d,xmthlp	;Tell about R too
	call	prtstr
inch04:	lxi	d,inthlp	;If so, get the address of the help message.
	call	prtstr
	call	sysinh		; print system-dependent help message
	lxi	d,inhlp1	; Tell about doubling the escape character
	call	prtstr
	call	escpr		;Print escape character
	lxi	d,inhlp2	;Print the rest
	call	prtstr
	jmp	intchr		;Get another char.

intch1: mov	a,b		;Get the character.
	cpi	'0'		;Is it '0', to send a null?
	jnz	intch3		;No.
	xra	a		;Yes, send an ASCII zero.
	call	setpar		; with the correct parity
	mov	e,a
	call	selmdm		; (to the modem...)
	call	outmdm
	call	selcon		; return with console selected
	jmp	rskp

intch3:	lda	escchr		;Get the escape char.
	cmp	b		;Is it the escape char?
	jnz	intchz		;If not, look for local additions
	mov	a,b		;Get the char.
	call	setpar
	mov	e,a		;Restore it.
	call	selmdm
	call	outmdm		;Output it.
	call	selcon		;We promised console would be selected...
	jmp	rskp		;Return, we are done here.

intchz:	mov	a,b		; not recognized. get saved copy back.
	call	sysint		; interpret system-dependent sequences
	 jmp	rskp		;  done. return (from conchr).
	mvi	e,'G'-100O	;Otherwise send a beep.
	call	outcon		; to the console.
	jmp	rskp
;
IF lasm
	LINK	CP4CPM
ENDIF;lasm
