<<< c86cmd.a86 >>>
; * * * * * * * * * * * * * * *  version 2.8  * * * * * * * * * * * * * * *
; [32c] remove check for 0-length filename
; [32b] fix minor bugs
; [32a]	fix prompt to show default drive and user
;	RonB, 09/13/84
; * * * * * * * * * * * * * * *  version 2.7  * * * * * * * * * * * * * * *
; [fdc]	Fix small glitch w/CMLEVL that issued msg spuriously sometimes.
; []	Introduced CMLEVL flag to intercept "empty" command-options
;	Reset by PRSERR, SET in 86KERMIT KERMIT:
;		B.E.; EIBEN at DEC-MARLBORO 2-May-84
; [30c] Isolate ANSI escape sequences for machine independence.
; [30b] Make DEL work like BS and ^X like ^U in command input.
;	RonB, 04/18/84
; * * * * * * * * * * * * * * *  version 2.6  * * * * * * * * * * * * * * *
; [28d] Improve filename special character processing
;	RonB, 03/27/84
; [25]	Move logic for "seteol" and "escape" (from KERSYS) into here so those 
;	routines need not use internal CMD routines and variables.  For this 
;	purpose add 2 parse routines and codes: "cmcha" and "cmnum".  The point
;	of this is to keep calls to CMD modular since I want to eventually
;	replace the whole thing.
;	R. Garland	9-Mar-1984
; * * * * * * * * * * * * * * *  version 2.1  * * * * * * * * * * * * * * *
;  [9]	Fix filename parsing, and add wildcard ability.
;	RonB,12/26/83
;  [8]	Show choices for ambiguous keywords, finish keyword on '?'
;	RonB,12/26/83
; * * * * * * * * * * * * * * *  version 2.0  * * * * * * * * * * * * * * *
; This module contains all the routines and storage necessary for the
; command parser.  The command parser approximates that of the Tops-20
; COMND% JSYS.  This code is adapted from the IBM PC Kermit code which
; was adapted from the CP/M-80 Kermit code.
; COMND definitions.

cmcfm	equ	01H
cmkey	equ	02H
cmifi	equ	03H
cmofi	equ	04H
cmtxt	equ	05H
cmcha	equ	06H
cmnum	equ	07H

	DSEG $			; Resume the data segment.

; COMND storage.
 
cmer00	db	bell,'?Program error -- Invalid COMND call$'
cmer01	db	bell,'?Ambiguous command$'
cmer02	db	bell,'?Illegal input file spec$'
cmer03	db	bell,'?Unrecognized instruction$'
cmer04	db	bell,'?Invalid command or operand$'
cmer05	db	bell,'?Missing command-option$'
cmin00	db	' Confirm with carriage return$'
cmin01  db      ' Input file spec (possibly wild) $'
cmin02	db	' One of the following:$'			;[8]
 
cmlevl	db	0		;0 at main-level, 1 otherwise
cmstat	db	0		;What is presently being parsed.
cmaflg	db	0		;Non-zero when an action char has been found.
cmccnt	db	0		;Non-zero if a significant char is found.
cmsflg	db	0		;Non-zero when the last char was a space.
cmostp	dw	0		;Old stack pointer for reparse.
cmrprs	dw	0		;Address to go to on reparse.
cmprmp	dw	0		;Address of prompt.
cmptab	dw	0		;Address of present keyword table.
cmhlp	dw	0		;Address of present help.
cmdbuf	rb	80H		;Buffer for command parsing.
cmfcb	dw	0		;Pointer to FCB.
cmfcb2	dw	0		;Pointer to position in FCB.
cmcptr	dw	0		;Pointer for next char input.
cmdptr	dw	0		;Pointer into the command buffer.
cmsiz	dw	0		;Size info of user input.
cmkptr	dw	0		;Pointer to keyword.
cmsptr	dw	0		;Place to save a pointer.
cmchr	db	0		;Save char when checking ambiguity.
cmten	dw	10		;the number "10"

spchar	db	'!#$%&()+-/@\^`|~0000' ;Valid special characters ;[8][28d]

;	This set of routines provides a user oriented way of parsing
;	commands.  It is similar to that of the COMND JSYS in TOPS-20.
 
	CSEG $			;Resume coding.
 
;	This routine prints the prompt in DX and specifies the reparse
;	address.
 
prompt:	pop	bx		;Get the return address.
	push	bx		;Put it on the stack again.
	mov	cmrprs, bx	;Save as addr to go to on reparse.
	mov	bx, 0		;Clear out register.
	add	bx, sp		;Get the present stack pointer.
	mov	cmostp, bx	;Save for later restoral.
	mov	cmprmp, dx	;Save pointer to the prompt.
	mov	bx, offset cmdbuf
	mov	cmcptr, bx	;Initialize the command pointer.
	mov	cmdptr, bx
	mov	cmaflg, 0	;Zero the flags.
	mov	cmlevl, 0	;[fdc]Including the level-flag
	mov	cmccnt, 0
	mov	cmsflg, 0FFH
reprompt:							;[32a] begin
	call	tcrlf
repmt2:	mov	dx, cmprmp	;Print the prompt.
	call	tmsg
	mov	dl,defdrv	;Print the default drive and user
	add	dl,'A'
	call	bout
	mov	al,defusr
	cbw
	or	ax,ax		;Only print the user number if nonzero
	jz	repmt3
	call	nout
repmt3:	mov	dl,'>'
	call	bout						;[32a] end
	ret

 
;	This address is jumped to on reparse.

repars:	mov	sp, cmostp	;new sp <-- old sp
	mov	bx, offset cmdbuf
	mov	cmdptr, bx
	mov	cmsflg, 0FFH
	mov	bx, cmrprs	;Get the reparse address.
	jmp	bx		;Go there.
 
;	This address can be jumped to on a parsing error.
 
prserr:	mov	ah, cmlevl	;What level are we in?
	cmp	ah, 0		;
	jz	prser1		;skip error-message
	mov	dx, offset cmer05 ;we're out of main-commands
	call	tcrmsg		;and got an empty option
	mov	cmlevl, 0	;reset level-flag
prser1:	mov	sp, cmostp	;Set new sp to old one.
	mov	bx, offset cmdbuf
	mov	cmcptr, bx	;Initialize the command pointer.
	mov	cmdptr, bx
	mov	cmaflg, 0	;Zero the flags.
	mov	cmccnt, 0
	mov	cmsflg, 0FFH
	call	reprompt					;[32a]
	mov	bx, cmrprs
	jmp	bx

 
;	This routine parses the specified function in AH. Any additional
;	information is in DX and BX.
;	Returns +1 on success
;		+4 on failure (assumes a JMP follows the call)
 
comnd:	mov	cmstat, ah	;Save what we are presently parsing.
	call	cminbf		;Get chars until an action or a erase char.
	mov	ah, cmstat	;Restore 'ah' for upcoming checks.
	cmp	ah, cmcfm	;Parse a confirm?
	jz	cmcfrm		;Go get one.
	cmp	ah, cmkey	;Parse a keyword?
	jnz	cm1
	jmp	cmkeyw		;Try and get one.
cm1:	cmp	ah, cmifi	;Parse an input file spec?
	jnz	cm2
	jmp	cmifil		;Go get one.
cm2:	cmp	ah, cmofi	;Output file spec?
	jnz	cm3
	jmp	cmofil		;Go get one.
cm3:	cmp	ah, cmtxt	;Parse arbitrary text.
	jnz	cm4
	jmp	cmtext
cm4:	cmp	ah, cmcha	;[25] parse a single character?
	jnz	cm5		;[25]
	jmp	cmchar		;[25] go do it.
cm5:	cmp	ah, cmnum	;[25] parse a (decimal) number?
	jnz	cm99		;[25]
	jmp	cmnumr		;[25] go do it.
cm99:	mov	dx, offset cmer00 ;"?Unrecognized COMND call" [25]
	call	tcrmsg
	ret

; This routine gets a confirm.

cmcfrm:	call	cmgtch		;Get a char.
	cmp	ah, 0		;Is it negative (a terminator;a space or
				;a tab will not be returned here as they
				;will be seen as leading white space)?
	js	cmcfr0
	ret			;If not, return failure.
cmcfr0:	and	ah, 7FH		;Turn off the minus bit.
	cmp	ah, esc		;Is it an escape?
	jne	cmcfr2
	mov	dl, bell	;Get a bell.
	call	bout		;Output the char.
	mov	cmaflg, 0	;Turn off the action flag.
	mov	bx, cmcptr	;Move the pointer to before the escape.
	dec	bx
	mov	cmcptr, bx
	mov	cmdptr, bx
	dec	cmccnt		;Decrement the char count.
	jmp	cmcfrm		;Try again.
cmcfr2:	cmp	ah, '?'		;Curious?
	jne	cmcfr3
	mov	dx, offset cmin00 ;Print something useful.
	call	tmsg
	call	reprompt	;Reprint the prompt		;[32a]
	mov	bx, cmdptr	;Get the pointer into the	buffer.
	mov	ah, '$'		;Put a $ there for printing.
	mov	[bx], ah
	mov	bx, cmcptr
	dec	bx		;Decrement & save the buffer pointer.
	mov	cmcptr, bx
	mov	dx, offset cmdbuf
	call	tmsg
	mov	cmaflg, 0	;Turn off the action flag.
	jmp	repars		;Reparse everything.

cmcfr3:								;[8] begin
cmcfr4:	jmp	rskp


;	This routine parses a keyword from the table pointed
;	to in DX.  The format of the table is as follows:
;
;	addr:	db	n	;Where n is the # of entries in the table.
;		db	m	;M is the size of the keyword.
;		db	'string$' ;Where string is the keyword.
;		dw	ab	;Where ab is data to be returned.
;
;	The keywords must be in alphabetical order.

cmkeyw:	mov	cmhlp, bx	;Save the help string.
	mov	cmptab, dx	;Save the beginning of keyword table.
	mov	bx, dx
	mov	ch, [bx]	;Get number of entries in table.
	inc	bx
	mov	dx, cmdptr	;Save command pointer.
	mov	cmsptr, dx	;Save pointer's here.
cmky1:	cmp	ch, 0		;Any commands left to check?
	jne	cmky2
	ret
cmky2:	dec	ch
	mov	cl, 0		;Keep track of how many chars read in so far.
	call	cmgtch		;Get a char.
	cmp	ah, 0		;Do we have a terminator?
	jns	cmky2x 
	jmp	cmky4		;Negative number means we do.
cmky2x:	inc	bx		;Point to first letter of keyword.
	inc	cl		;Read in another char.
	mov	al, [bx]		      
	cmp	ah, 'a'		;Less than a?
	jl	cmky21		;If so, don't capitalize.
	cmp	ah, 'z'+1	;More than z?
	jns	cmky21
	and	ah, 137O	;Capitalize the letter.
cmky21:	cmp	ah, al
	je	cmky3
	jg	cmky2y
	jmp	cmky41		;Fail if ah preceeds al alphabetically.
cmky2y:	jmp	cmky6		;Not this keyword - try the next.
cmky3:	inc	bx		;We match here, how 'bout next char?
	mov	al, [bx]
	cmp	al, '$'		;End of keyword?
	jne	cmky3x
	jmp	cmky7		;Succeed.
cmky3x:	mov	dl, al		;Save al's char here.
	call	cmgtch
	inc	cl		;Read in another char.
	mov	al, dl
	cmp	ah, 'a'
	jl	cmky31
	cmp	ah, 'z'+1
	jns	cmky31
	and	ah, 137O
cmky31:	cmp	ah, esc+80H	;Escape Recognition (escape w/minus bit on)?
	je	cmky3y
	cmp	ah, '?'+80H	;A question mark?
	je	cmky3y
	cmp	ah, ' '+80H	;A space?
	je	cmky3y
	cmp	ah, cr+80H	;Carriage return?
	je	cmky3y
	jmp	cmky38
cmky3y:	mov	cmkptr, bx	;Save bx here.
	mov	cmsiz, cx	;Save size info.
	mov	cmchr, ah	;Save char for latter.
	call	cmambg		;See if input is ambiguous or not.
		jmp	cmky32	;Succeeded (not ambiguous).
	mov	ah, cmchr
	cmp	ah, esc+80H	;Escape?

; Display keyword choices and reparse if ambiguous		;[8] begin
	je	cmky3a
	cmp	ah, ' '+80H	;Space?
	jne	cmky3b
cmky3a:	dec	cmdptr		;If so, back up over it.
cmky3b:	mov	dx, offset cmin02 ;'One of the following:'
	call	tcmsgc
	mov	bx, cmkptr	;Find beginning of current keyword
	mov	cx, cmsiz
cmky3c:	dec	bx		;We are 'cl' characters into it
	dec	cl
	jnz	cmky3c
	inc	bx
	mov	cmkptr, bx	;Save beginning of keyword
cmky3d:	mov	dl, tab		;Precede each keyword with a tab
	call	bout
	mov	dx,cmkptr	;and display the keyword
	call	tmsg
	mov	bx, cmkptr	;Move to the next keyword
cmky3e:	inc	bx
	cmp	byte ptr [bx], '$'
	jnz	cmky3e
	add	bx,4		;Bypass '$', 2-byte return value, next length
	mov	di, cmkptr	;Get previous keyword for comparison
	mov	cmkptr, bx	;and save beginning of this keyword
	mov	cx, cmsiz	;Get number of characters to match
	dec	ch		;Are we at end of table?
	js	cmky3g		;  Yes, quit displaying
	mov	cmsiz, cx
cmky3f:	dec	cl
	jz	cmky3d		;This keyword also matches to 'cl' places
	mov	ah,[bx]		;Compare this keyword to last
	cmp	ah,[di]
	jne	cmky3g
	inc	di
	inc	bx
	jmps	cmky3f
cmky3g:	jmp	cmky50		;Not equal or end of table, redisplay prompt
								;[8] end

cmky32:	mov	cx, cmsiz	;Restore info.
	mov	bx, cmkptr	;Our place in the keyword table.
cmk32a:	cmp	cmchr, ' '+80H	;Space?			;[8]
	je	cmky35
	cmp	cmchr, cr+80H	;Carriage return?
	je	cmky35
	dec	cmcptr		;Pointer into buffer of input.
	mov	dx, cmcptr
cmky33:	mov	ah, [bx]	;Get next char in keyword.
	cmp	ah, '$'		;Are we done yet?
	jz	cmky34
	mov	di,dx
	mov	[di], ah
	inc	bx
	inc	dx
	inc	cmccnt
	jmp	cmky33
cmky34:	push	bx		;Save pointer to return value	;[8]
	mov	ah, ' '
	mov	di, dx
	mov	[di], ah	;Put a blank in the buffer.
	inc	dx
	mov	cmdptr, dx						;[8] begin
	cmp	cmchr, '?'+80H	;Question mark?
	jne	cmk34a
	mov	ah, '?'
	mov	di,dx
	mov	[di], ah
	inc	dx
	inc	cmccnt
	push	dx
	mov	dl, 08H		;Erase question mark from display
	call	bout
	pop	dx
cmk34a:	mov	cx, cmcptr	;Remember where we were (for printing below).
	mov	cmcptr, dx	;Update our pointers.		;[8] end
	mov	ah, '$'
	mov	di, dx
	mov	[di], ah	;Add '$' for printing.
	mov	dx, cx		;Point to beginning of filled in data.
	call	tmsg
	pop	bx		;Recover pointer to return value ;[8]
	inc	bx		;Point to address we'll need.
	mov	bx, [bx]
	cmp	cmchr, 0BFH	;Question mark?		;[8] begin
	je	cmk34b
	mov	cmaflg, 0 	;If esc, turn off action flag
	mov	cmsflg, 0FFH	;   and pretend they typed a space
cmk34b:	jmp	rskp						;[8] end

cmky35:	mov	ah, [bx]	;Find end of keyword.	
	inc	bx
	cmp	ah, '$'
	jne	cmky35	
	mov	bx, [bx]	;Address of next routine to call.
	jmp	rskp

cmky38:	cmp	ah, al
	jne	cmky6		;Go to end of keyword and try next.
	jmp	cmky3
	   
cmky4:	and	ah, 7FH		;Turn off minus bit.
	cmp	ah, '?'		;Need help?
	je	cmky5
	cmp	ah, ' '		;Just a space - no error.
	je	cmky51
	cmp	ah, cr
	je	cmky51
	cmp	ah, esc		;Ignore escape?
	je	cmky43
cmky41:	mov	dx, offset cmer03
	call	tcrmsg
	jmp	prserr		;Parse error - give up.

cmky43:	mov	dl, bell	;Ring a bell.
	call	bout
	mov	bx, cmcptr
	dec	bx
	mov	cmcptr, bx
	mov	cmdptr, bx
	dec	cmccnt		;Don't count the escape.
	mov	cmaflg, 0	;Reset action flag.
	inc	ch		;Account for a previous 'dec'.
	jmp	cmky1		;Start over.

cmky5:	mov	dx,cmhlp	;Print the help text.
	call	tcmsgc
cmky50:	call	reprompt	;Reprint the prompt		;[32a]
	mov	bx,cmdptr	;Get pointer into buffer.
	mov	al, '$'
	mov	[bx], al	;Add dollar sign for printing.
	mov	dx, offset cmdbuf
	call	tmsg
	mov	bx, cmdptr	;[8] begin
	mov	cmcptr, bx
	mov	dx, offset cmdbuf
	sub	bx, dx
	mov	cmccnt, bl
	mov	cmaflg, 0	;Turn off the action flag.
	jmp	repars

cmky51:	jmp	prserr

cmky6:	inc	bx		;Find end of keyword.
	mov	al, [bx]
	cmp	al, '$'
	jne	cmky6	      
	add	bx, 3		;Beginning of next command.
	mov	dx, cmsptr	;Get old cmdptr.
	mov	cmdptr, dx	;Restore.
	mov	cmsflg, 0FFH
	jmp	cmky1		;Keep trying.

cmky7:	call	cmgtch		;Get char.
	cmp	ah, 0
	js	cmky71		;Ok if a terminator.
	dec	bx
	jmp	cmky6		;No match - try next keyword.
cmky71:	mov	cmchr, ah						;[8] begin
	jmp	cmk32a


; See if keyword is ambiguous from what the user has typed in.

cmambg:	cmp	ch, 0		;Any keywords left to check?
	jne	cmamb0
	ret			;If not then not ambiguous.
cmamb0:	inc	bx		;Go to end of keyword ...
	mov	al, [bx]	;So we can check the next one.
	cmp	al, '$'
	jne	cmamb0
	add	bx, 4		;Point to start of next keyword.
	dec	cl		;Don't count escape.
	mov	dx, cmsptr	;Buffer with input typed by user.
cmamb1:	mov	ah, [bx]	;Keyword char.	
	mov	di, dx
	mov	al, [di]	;Input char.
	cmp	al, 'a'		;Do capitalizing.
	jl	cmam11
	cmp	al, 'z'+1
	jns	cmam11
	and	al, 137O
cmam11:	cmp	ah, al		;Keyword bigger than input (alphabetically)?
	jle	cmamb2		;No - keep checking.
	ret			;Yes - not ambiguous.
cmamb2:	inc	bx		;Advance one char.
	inc	dx
	dec	cl
	jnz	cmamb1
	jmp	rskp		;Fail - it's ambiguous.

; Parse an input file spec.

cmifil:	mov	wldflg, 0	;Set to no wildcards.		;[9]
	mov	bx, dx		;Get the fcb address in bx.
	mov	cmfcb, bx	;Save it.
	mov	ch, 0		;Initialize char count.
	mov	ah, 0
	mov	[bx], ah	;Set the drive to default to current.
	inc	bx
	mov	cmfcb2, bx
	mov	cl, ' '
cmifi0:	mov	[bx], cl	;Blank the FCB.
	inc	bx
	inc	ah
	cmp	ah, 0BH		;Twelve?
	jl	cmifi0
cmifi1:	call	cmgtch		;Get another char.
	cmp	ah, 0		;Is it an action character.
	jns	cmifi2
	and	ah, 7FH		;Turn off the action bit.
	cmp	ah, '?'		;A question mark?
	jne	cmif12
	mov	cmaflg, 0	;Blank the action flag.
; '?' is a legal character in wildcard filenames.		;[9] begin
; Make ESC take its place by giving info instead of beeping.	;[32b]
	mov	wldflg, 0FFH	;Say we have a wildcard.
	inc	cmdptr
	jmp	cmifi8		;Accept a '?'
cmif12:	cmp	ah, esc		;An escape?
	jne	cmif13
	dec	cmdptr
cmf12a:	mov	cmaflg, 0	;Turn off the action flag	;[9] end
	dec	cmcptr		;Decrement the buffer pointer.
	dec	cmccnt		;Decrement count.
	mov	dx, offset cmin01 ;Help message.
	call	tmsg
	call	reprompt	;Reprint the prompt		;[32a]
	mov	bx, cmdptr
	mov	al, '$'
	mov	[bx], al	;Put in dollar sign for printing.
	mov	dx, offset cmdbuf
	call	tmsg
	jmp	repars

cmif13:	mov	ah, ch		;It must be a terminator.
; The check for 0-length filenames will be performed by the	;[32c]
; caller so as to allow the file specification to be optional.
	cmp	ah, 0DH
	js	cmf3y
	jmp	cmifi9		;If too long complain.
cmf3y:	jmp	rskp		;Otherwise we have succeeded.
cmifi2:	cmp	ah, '.'
	jne	cmifi3
	inc	ch
	mov	ah, ch
	cmp	ah, 1H		;Any chars yet?
	     	jnz cmf2x
	jmp	cmifi9		;No, give error.
cmf2x:	cmp	ah, 0AH		;Tenth char?
      	js	cmf2y
	jmp	cmifi9		;Past it, give an error.
cmf2y:	mov	dl, 9H
	mov	dh, 0
	mov	bx, cmfcb
	add	bx, dx		;Point to file type field.
	mov	cmfcb2, bx
	mov	ch, 9H		;Say we've gotten nine.
	jmp	cmifi1		;Get the next char.
cmifi3:	cmp	ah, ':'
	jne	cmifi4
	inc	ch
	cmp	ch, 2H		;Is it in right place for a drive?
	je	cmif3x
	jmp	cmifi9		;If not, complain.
cmif3x:	mov	ch, 0		;Reset char count.
	mov	bx, cmfcb2
	dec	bx
	mov	ah, [bx]	;Get the drive name.
	cmp	ah,'A'		;Make sure it's in range A-P	;[9] begin
	jb	cmif3y
	cmp	ah,'P'
	jbe	cmif3z
cmif3y:	jmp	cmifi9
cmif3z:	sub	ah,'@'		;Get the drive number.		;[9] end
	mov	cmfcb2, bx
 	mov bx, cmfcb
	mov	[bx], ah	;Put it in the fcb.
	jmp	cmifi1
cmifi4:	cmp	ah, '*'
	jne	cmifi7
	mov	ah, ch
	cmp	ah, 8H		;Is this in the name or type field?
	jz	cmifi9		;If its where the dot should be give up.
	jns	cmifi5		;Type.
	mov	cl, 8H		;Eight chars.
	jmp	cmifi6
cmifi5:	mov	cl, 0CH		;Three chars.
cmifi6:	mov	wldflg, 0FFH	;Remember we had a wildcard.
	mov	bx, cmfcb2	;Get a pointer into the FCB.
	mov	ah, '?'
	mov	[bx], ah	;Put a question mark in.
	inc	bx
	mov	cmfcb2, bx
	inc	ch
	mov	ah, ch
	cmp	ah, cl
	jl	cmifi6		;Go fill in another.
	jmp	cmifi1		;Get the next char.
cmifi7:

cmif7x:	cmp	ah,'0'
	jb	cmif8x
	cmp	ah,'9'
	jbe	cmifi8
	cmp	ah,'A'
	jb	cmif8x
	cmp	ah,'Z'
	jbe	cmifi8
	cmp	ah,'a'
	jb	cmif8x
	cmp	ah,'z'
	ja	cmif8x						;[9] end
	and	ah, 137O	;Capitalize.
cmifi8:	mov	bx, cmfcb2	;Get the pointer into the FCB.
	mov	[bx], ah	;Put the char there.
	inc	bx
	mov	cmfcb2, bx
	inc	ch
	jmp	cmifi1

cmif8x:	push	es		;Check list of special characters
	mov	cx, ds		;   which are legal in filenames
	mov	es, cx		;Scan uses ES register.
	mov	di, offset spchar ;Special chars.
	mov	cx, 20		;Twenty of them.
	mov	al, ah		;Char is in al.
	repnz	scasb		;Search string for input char.
	cmp	cx, 0		;Was it there?
	pop	es
	jnz	cmifi8
 
cmifi9:	mov	dx, offset cmer02
	call	tcrmsg
	ret
 
cmofil:	jmp	cmifil		;For now, the same as CMIFI.

; Parse arbitrary text up to a CR.  Put chars into data buffer sent to
; the host (pointed to by BX).	 Return updated pointer in BX and 
; input size in AH.

cmtext:	mov	cmptab, bx	;Save pointer to data buffer.
	mov	cl, 0		;Init the char count.
cmtxt1:	call	cmgtch		;Get a char.
	cmp	ah, 0		;Terminator?
	jns	cmtxt5		;Nope, put into the buffer.
	and	ah, 07FH
	cmp	ah, esc		;An escape?
	jne	cmtxt2
	mov	dl, bell	;Ring a bell.
	call	bout
	mov	cmaflg, 0	;Reset action flag.
	dec	cmcptr		;Move pointer to before the escape.
	dec	cmdptr
	dec	cmccnt		;Decrement count.
	jmp	cmtxt1		;Try again.
cmtxt2:	cmp	ah, '?'		;Asking a question?
	jz	cmtx2y						;[32b]
	cmp	ah, ' '		;Space?				;[32b]
	jz	cmtxt3
	cmp	ah, ff		;Formfeed?
	jne	cmtx2x
	call	clrscr
cmtx2x:	mov	ah, cl		;Return count in AH.
	mov	bx, cmptab	;Return updated pointer.
	jmp	rskp
cmtx2y:	inc	cmdptr						;[32b]
cmtxt3:	mov	cmaflg, 0	;Reset action flag to zero.
cmtxt5:	inc	cl		;Increment the count.
	mov	bx, cmptab	;Pointer into destination array.
	mov	[bx], ah	;Put char into the buffer.
	inc	bx
	mov	cmptab, bx
	jmp	cmtxt1


cminbf:	push	dx
	push	bx
	mov	cx, dx		;Save value here too.
	mov	ah, cmaflg	;Is the action char flag set?
	cmp	ah, 0
	je	cminb1
	jmp	cminb9		;If so get no more chars.
cminb1:	inc	cmccnt		;Increment the char count.
	call	bin
	mov	ah, al		;Keep char in 'ah'.
	mov	bx, cmcptr	;Get the pointer into the buffer.
	mov	[bx], ah	;Put it in the buffer.
	inc	bx
	mov	cmcptr, bx
	cmp	ah, 15h		;Is it a ^U?
	je	cmnb12						;[30b]
	cmp	ah, 18h		;     or ^X?			;[30b]
	jne	cminb2
cmnb12:	call	clrlin						;[30c]
	call	repmt2		;Reprint the prompt (no crlf)	;[32a]
	mov	bx, offset cmdbuf
	mov	cmcptr, bx	;Reset the point to the start.
	mov	cmccnt, 0	;Zero the count.
	mov	dx, cx		;Preserve original value of	dx.
	jmp	repars		;Go start over.
cminb2:	cmp	ah, 08h		;Is it a backspace?		;[30b]
	jz	cminb3
	cmp	ah, 7fh		;     or delete?		;[30b]
	jne	cminb4
	mov	dx, offset delstr
	call	tmsg
cminb3:	mov	ah, cmccnt	;Decrement the char count by two.
	dec	ah
	dec	ah
	cmp	ah, 0		;Have we gone too far?
	jns	cmnb32		;If not proceed.
	mov	dl, bell	;Ring the bell.
	call	bout
	jmp	cmnb12		;Go reprint prompt and reparse.
cmnb32:	mov	cmccnt, ah	;Save the new char count.
	mov	dx, offset clrspc ;Erase the character.
	call	tmsg
	mov	bx, cmcptr	;Get the pointer into the buffer.
	dec	bx		;Back up in the buffer.
	dec	bx
	mov	cmcptr, bx
	jmp	repars		;Go reparse everything.
cminb4:	cmp	ah, '?'		;Is it a question mark.
	jz	cminb6
	cmp	ah, esc		;Is it an escape?
	jz	cminb6
	cmp	ah, cr		;Is it a carriage return?
	jz	cminb5
	cmp	ah, lf		;Is it a line feed?
	jz	cminb5
	cmp	ah, ff		;Is it a formfeed?
	jne	cminb7
	call	clrscr
cminb5:	mov	ah, cmccnt	;Have we parsed any chars yet?
	cmp	ah, 1
	jnz	cminb6
	jmp	prserr		;If not, just start over.
cminb6:	mov	cmaflg, 0FFH	;Set the action flag.
	jmp	cminb9
cminb7:	jmp	cminb1		;Get another char.

cminb9:	pop	bx
	pop	dx
	ret

cmgtch:	push	cx
	push	bx
	push	dx
cmgtc1:	mov	ah, cmaflg
	cmp	ah, 0		;Is it set.
	jne	cmgt10
	call	cminbf		;If the action char flag is not set get more.
cmgt10:	mov	bx, cmdptr	;Get a pointer into the buffer.
	mov	ah, [bx]	;Get the next char.
	inc	bx
	mov	cmdptr, bx
	cmp	ah, ' '		;Is it a space?
	jz	cmgtc2
	cmp	ah, tab		;Or a tab?
	jne	cmgtc3
cmgtc2:	mov	ah, cmsflg	;Get the space flag.
	cmp	ah, 0		;Was the last char a	space?
	jne cmgtc1		;Yes, get another char.
	mov	cmsflg, 0FFH	;Set the space flag.
	mov	ah, ' '
	pop	dx
	pop	bx
	jmp	cmgtc5
cmgtc3:	mov	cmsflg, 0	;Zero the space flag.
	pop	dx
	pop	bx
	cmp	ah, esc
	jz	cmgtc5
	cmp	ah, '?'		;Is the user curious?
	jz	cmgtc4
	cmp	ah, cr
	jz	cmgtc4
	cmp	ah, lf
	jz	cmgtc6						;[8]
	cmp	ah, ff
	je	cmgtc6						;[8]
	pop	cx
	ret			;Not an action char, just return.
cmgtc6:	mov	ah, cr		;Convert lf & ff to cr		;[8]
cmgtc4:	dec	cmdptr
cmgtc5:	or	ah, 80H		;Make the char negative to indicate
	pop	cx		;it is a terminator.
	ret

; Parse a single character	;[25] start
;	this is for setting the escape character

cmchar:
	call	cmgtch		;get a char
	cmp	ah, 0
	jns	cmchr1		;go if not negative
	and	ah, 7FH		;turn off sign bit
	cmp	ah, '?'		;user curious?
	jne	cmchr0		;no - an error
	mov	dx, bx		;help string pointer was in bx
	call	tmsg		;print help stuff		;[32a]
	call	reprompt	;Reprint the prompt		;[32a]
	mov	bx, cmdptr
	mov	al, '$'
	mov	[bx], al	;add a "$" to what was typed
	mov	dx, offset cmdbuf
	call	tmsg		;type it again
	dec	cmcptr		;but don't leave "$" ..
	dec	cmccnt		;in buffer
	mov	cmaflg, 0	;turn off action flag
	jmp	repars		;try again
cmchr0:	mov	dx, offset erms20
	call	tcrmsg		;"illegal value" error
	ret
cmchr1:	mov	temp, ax	
	call	cmcfrm		;get a confirm
	 jmp	cmchr0		;or else complain
	mov	ax, temp
	mov	bl, ah		;return the character
	jmp	rskp

;	parse a (decimal) number.  Maximum allowed value in dx
cmnumr:	
	mov	temp1, 001H	;initial multiplier of 1
	mov	temp2, dx	;storage for maximum
	mov	temp, 0		;zero running sum
	call	cmgtch		;get a char
	cmp	ah, 0
	jns	cmnum1		;go if not negative
	and	ah, 7FH		;turn off sign bit
	cmp	ah, '?'		;user curious?
	jne	cmnum0		;no - an error
	mov	dx, bx		;help string pointer was in bx
	call	tmsg		;print help stuff		;[32a]
	call	reprompt	;Reprint the prompt		;[32a]
	mov	bx, cmdptr
	mov	al, '$'
	mov	[bx], al	;add a "$" to what was typed
	mov	dx, offset cmdbuf
	call	tmsg		;type it again
	dec	cmcptr		;but don't leave "$" ..
	dec	cmccnt		;in buffer
	mov	cmaflg, 0	;turn off action flag
	jmp	repars		;try again
	call	cmcfrm		;get character (or confirm)
	 jmp	cmnum1		;got a character
				;fall through - too early for confirm
cmnum0:	mov	dx, offset erms20
	call	tcrmsg		;"illegal value" message
	ret
cmnum1:	sub	ah, 030H	;ASCII -> binary
	jl	cmnum0		;too small
	cmp	ah, 09H
	jg	cmnum0		;too big
	mov	bl, ah
	mov	bh, 0		;get number in low part of bx
	mov	ax, temp	;get running sum
	mul	temp1		;multiply by decimal place value
	add	ax, bx		;add in this digit
	cmp	ax, temp2	;over the maximum
	jg	cmnum0		;yes - error
	mov	temp, ax	;save running sum
	mov	ax, temp1	;get multiplier
	mul	cmten		;multiply multiplier by 10
	mov	temp1, ax	;save it
	call	cmcfrm		;get another character
	 jmp	cmnum1		;not terminator - process it
	mov	bx, temp	;get value of number
	jmp	rskp		;return success
						;[25] end
<<< c86fil.a86 >>>
; * * * * * * * * * * * * * * *  version 2.9  * * * * * * * * * * * * * * *
; [34c]	Add sorted wildcard SENDs with starting filename
; * * * * * * * * * * * * * * *  version 2.8  * * * * * * * * * * * * * * *
; [32c]	Allow replacement of output filename with trailer from RECEIVE.
; * * * * * * * * * * * * * * *  version 2.7  * * * * * * * * * * * * * * *
; [31]	Fix display of file renaming.
;	RonB, 05/05/84
; [30c] Isolate ANSI escape sequences for machine independence.
; [29g]  Add 8th bit quoting
;	RonB, 04/15/84
; * * * * * * * * * * * * * * *  version 2.6  * * * * * * * * * * * * * * *
; [28d]  Improve input filename processing, allow valid special chars
;	RonB, 03/27/84
; [23]	Modification to GTCEOF to fix ASCII mode transfer
; * * * * * * * * * * * * * * *  version 2.4  * * * * * * * * * * * * * * *
; [Rg]	^X/^Z file interruption.  Slight mod to GTNFIL.	Rg, 2/84
; * * * * * * * * * * * * * * *  version 2.1  * * * * * * * * * * * * * * *
;  [par] Added calls to set parity, strip parity on input if
;	other than none parity is called for.
;	 JD, 2/84
;  [16]	Add file-mode ASCII or BINARY processing.
;	RonB,01/02/84
;  [11]	Capitalize and parse filename being received.
;	RonB,12/27/83
;  [9]	Fix filename parsing, and add wildcard ability.
;	RonB,12/26/83
; * * * * * * * * * * * * * * *  version 2.0  * * * * * * * * * * * * * * *
	CSEG $

;	Get the file name (including host to micro translation)

gofil:	cld
	push	ds
	pop	es
	mov	si, offset data
	mov	di, offset fcb
	mov	al,0
	stosb
	mov	cx,11
	mov	al,' '
	rep stosb
	mov	cx,24
	mov	al,0
	rep stosb
	mov	di, offset fcb+1
	mov	ah,0
gofil1:	lodsb			;Get a filename character
	cmp	al,'.'
	je	gofil2
	cmp	al,0
	je	gofil4
	cmp	ah,8
	jae	gofil1
	call	gofl20		;Capitalize, and replace strange chars ;[11]
	stosb
	inc	ah
	jmps	gofil1
gofil2:	mov	di, offset fcb+9
	mov	ah,0
gofil3:	lodsb			;Get a file type character
	cmp	al,'.'
	je	gofil4
	cmp	al,0
	je	gofil4
	cmp	ah,3
	jae	gofil4
	call	gofl20		;Capitalize, and replace strange chars ;[11]
	stosb
	inc	ah
	jmps	gofil3
gofil4:	cmp	byte ptr fcb+1,' ' ;Any chars in first field?	;[32c] begin
	jne	gofil5		; if not, set filename to '&'
	mov	byte ptr fcb+1,'&'
gofil5:	mov	si, offset fcb2+11 ;Replace with RECEIVE trailer
	mov	di, offset fcb+11
	mov	temp2, di
	mov	ah,' '
gofl5b:	mov	al,[si]		;Get character from replacement filename
	cmp	al,'?'
	je	gofl5c
	mov	[di],al		;If not wild, simply replace existing letter
	cmp	al,' '
	je	gofl5c
	mov	ah,'-'		; and replace subsequent spaces with '-'
gofl5c:	cmp	byte ptr [di],' '
	jne	gofl5d		;Replace spaces in filename with filler char
	mov	[di],ah		; either a space or a '-'.
	cmp	ah,' '
	jne	gofl5d
	mov	temp2,di	;Mark location where last space occurred
gofl5d:	dec	di
	dec	si
	cmp	si,offset fcb2+8
	jne	gofl5e
	mov	ah,' '
gofl5e:	cmp	si,offset fcb2
	jae	gofl5b
	sub	temp2,di
	dec	temp2
	cmp	temp2,8
	jbe	gofl5f
	mov	temp2,8
gofl5f:	call	clrfln
	mov	dx, offset fcb	;Print the file name.
	call	tfile						;[32c] end
	cmp	flwflg, 0	;Is file warning on?
	jnz	gofl5x
	jmp	gofil9		;If not, just proceed.
gofl5x:	mov	dx, offset fcb
	call	openf		;See if the file exists.
	cmp	al, 0FFH	;Does it exist?
	jnz	gofil7
	jmp	gofil9		;If not create it.

gofil7:	mov	dx, offset scrfr ;Move cursor.
	call	poscur						;[30c]
	mov	dx, offset infms5 ;Inform the user we are renaming the file.
	call	tmsg
	mov	cx, temp2	;Get the length of the file name. ;[32c] begin
	mov	al, 0		;Says if first field is full.
gofil8:	cmp	cl, 8		;Is the first field full?
	jne	gofl81
	mov	al, 0FFH	;Set a flag saying so.
gofl81:	mov	bx, offset fcb	;Get the FCB.
	add	bx, cx		;Add in the character number.
	mov	ah, '&'
	mov	[bx], ah	;Replace the char with an ampersand.
	push	ax
	push	bx
	push	cx
	mov	dx, offset fcb	;See if the file exists.
	call	openf
	pop	cx
	pop	bx
	cmp	al, 0FFH	;Does it exist?
	pop	ax
	jz	gofl89		;If not create it.
	cmp	al, 0		;Get the flag.
	jz	gofl83
	dec	cl		;Decrement the number of chars.
	cmp	cl, 0
	jz	gofl88		;If no more, die.
	jmp	gofl81
gofl83:	inc	cl		;Increment the number of chars.
	jmp	gofil8						;[32c] end

gofl88:	mov	dx, offset screrr
	call	poscur						;[30c]
	mov	dx, offset ermes4 ;Tell the user that we can't rename it.
	call	tmsg
	ret

gofl89:	push	dx
	mov	dx, offset fcb	;Print the file name.		;[31]
	call	tfile						;[31]
	pop	dx
gofil9:	mov	dx, offset fcb	;Delete the file if it exists.
	call	delete
	mov	dx, offset fcb	;Now create it.
	call	create
	cmp	al, 0FFH	;Is the disk full?
	je	gofl9x
	jmp	rskp
gofl9x:	mov	dx, offset screrr ;Position cursor.
	call	poscur						;[30c]
	mov	dx, offset erms11
	call	tmsg
	ret

; Make sure character in al is a legal filename character:	;[11] begin
; Mask 8th bit, capitalize, and replace all illegal
; special characters with '#'

gofl20:	and	al, 7Fh		;mask eighth bit		;[28d]
	cmp	al, '0'		;Check for digit
	jb	gofl21
	cmp	al, '9'
	jbe	gofl23
	cmp	al, 'A'		;Check for uppercase letter
	jb	gofl21
	cmp	al, 'Z'
	jbe	gofl23
	cmp	al, 'a'		;Check for lowercase letter
	jb	gofl21
	cmp	al, 'z'
	ja	gofl21
	and	al, 5Fh		;Capitalize lowercase
	jmps	gofl23
gofl21:	push	di
	mov	di, offset spchar ;Special chars.
	mov	cx, 20		;Twenty of them.
	repne	scasb		;Search string for input char.
	je	gofl22
	mov	al, '#'		;Replace illegal characters with '#'
gofl22:	pop	di						;[28d] end 
gofl23:	ret							;[11] end


; Get next filename from sorted directory list.  Return skip on success,
; plain return at end.

getfil:	cmp	cxzflg, 'Z'	;[Rg] file interrupt flag set to 'Z'? ;[34c]
	je	gtfl9
	mov	si,dindex	;Any more entries in list?
	cmp	si,dircnt
	jae	gtfl9
	cld
gtfl4:	push	ds
	pop	es
	mov	ds,word ptr membuf
	mov	cl,4
	shl	si,cl
	inc	si
	push	si
	mov	di,offset fcb2+1
	mov	cl,11
	repe	cmpsb
	pop	si
	jae	gtfl6		;If above starting filename, use it
	push	es
	pop	ds
	inc	dindex
	mov	si,dindex
	cmp	si,dircnt
	jb	gtfl4
	mov	dx,offset erms28 ;No filenames below starting filename
	call	tcrmsg
	jmps	gtfl9
gtfl6:	mov	di,offset fcb+1
	mov	cx,11
	rep	movsb		;Move the name to the FCB
	push	es
	pop	ds
	inc	dindex		;Point to next entry
	call	getopn
	jmp	rskp
gtfl9:	mov	wldflg, 0	;Reset wild card flag.
	ret							;[34c] end

; open the file for sending

getopn:	mov	filflg, 0FFH	;Nothing in the DMA.
	mov	eoflag, 0	;Not the end of file.
	mov	dx, offset fcb
	call	openf		;Open the file.
	ret

;	Output the chars in a packet.

ptchr:	mov	temp1, ax	;Save the	size.
	mov	bx, offset data	;Beginning of received packet data.
	mov	outpnt, bx	;Remember where we are.
	mov	ch, rquote	;Quote char.
ptchr1:	dec	temp1		;Decrement # of chars in packet.
	jnl	pt1
	jmp	rskp		;Return successfully if done.
pt1:	dec	chrcnt		;Decrement number of chars in dta.
	jns	ptchr2		;Continue if space left.
	call	outbuf		;Output it if full.
	 jmp	r		; Error return if disk is full.
ptchr2:	mov	bx, outpnt	;Get position in packet data buffer.
	mov	ah, [bx]	;Grab a char
	inc	bx
	mov	outpnt, bx	;and bump pointer.
	mov	al, 00h		;First assume no 8th bit	;[29g] begin
	cmp	ebquot, 'N'	;No 8th bit if we can't quote
	je	ptch21
	cmp	ebquot, 'Y'	;  or if we can but aren't.
	je	ptch21
	cmp	ah, ebquot	;Is this the 8th bit quote character?
	jne	ptch21
	mov	ah, [bx]	;Get the quoted character
	inc	bx
	mov	outpnt, bx
	dec	temp1		;Decrement # of chars in packet.
	mov	al, 80h		;Set the 8th bit.		;[29g] end
ptch21:	cmp	ah, ch		;Is it the quote char?
	jne	ptchr4		;If not proceed.
	mov	ah, [bx]	;Get the quoted character
	inc	bx
	mov	outpnt, bx	;and bump pointer.
	dec	temp1		;Decrement # of chars in packet.
	mov	dl, ah		;Save the parity bit in dl.	;[29g] begin
	and	dl, 80H
	and	ah, 7FH		;Turn off the parity bit.
	cmp	ah, ch		;Is it the quote char?
	je	ptchr3		;If so just go write it out.
	cmp	ebquot, 'N'	;No 8th bit if we can't quote
	je	ptch22
	cmp	ebquot, 'Y'	;  or if we can but aren't.
	je	ptch22
	cmp	ah, ebquot	;Is this the 8th bit quote character?
	je	ptchr3		;If so, just go write it out.
ptch22:	add	ah, 40H		;Make it a control char again.	;[29g] end
	and	ah, 7FH		;Modulo 128.
ptchr3:	or	ah, dl		;Or in the parity bit.
ptchr4:	or	ah, al		;Or in the quoted 8th bit.	;[29g]
	mov	bx, bufpnt	;Destination buffer.
	mov	[bx], ah	;Store it.
	inc	bx
	mov	bufpnt, bx	;Update the pointer
	jmp	ptchr1		;and loop to next char.


	; output the buffer, reset bufpnt and chrcnt

outbuf:	push	bx
	push	cx
	mov	dx, offset fcb
	call	soutr		;Write the record.
	pop	cx
	pop	bx
	cmp	al, 0		;Successful.
	jz	outbf1
	cmp	al, 1
	jz	outbf0
	mov	dx, offset screrr
	call	poscur						;[30c]
	mov	dx, offset erms17 ;Record length exceeds DTA.
	call	tmsg
	ret
outbf0:	mov	dx, offset screrr
	call	poscur						;[30c]
	mov	dx, offset erms11 ;Disk full error.
	call	tmsg
	ret
outbf1:	mov	bx, offset dma	;Addr for beginning.
	mov	bufpnt, bx	;Store addr for beginning.
	mov	ax, bufsiz-1	;Buffer size.
	mov	chrcnt, ax	;Number of chars left.
	jmp	rskp


;	Get the chars from the file.

gtchr:	mov	ch, squote	;Keep quote char in c.
	cmp	filflg, 0	;Is there anything in the DMA?
	jz	gtchr0		;Yup, proceed.
	mov	cl, 0		;No chars yet.
	call	inbuf
	 jmp	gtceof		;No more chars, go return EOF.
gtchr0:	mov	al, spsiz	;Get the maximum packet size.
	sub	al, 5		;Subtract the overhead.
	mov	ah, 0
	mov	temp1, ax	;Number of chars we're to get.
	mov	bx, offset filbuf ;Where to put the data.
	mov	cbfptr, bx	;Remember where we are.
	mov	cl, 0		;No chars.
gtchr1:	dec	temp1		;Decrement the number of chars left.
	jns	gtchr2		;Go on if there is more than one left.
	mov	al, cl		;Return the count in A.
	mov	ah, 0
	jmp	rskp
gtchr2:	mov	ax, chrcnt
	dec	ax
	jl	gtchr3
	mov	chrcnt, ax
	jmp	gtchr4
gtchr3:	call	inbuf		;Get another buffer full.
	jmp	gtceof
	cmp	chrcnt, 0
	jne	gtchr4
	sub	cl, 2		;Don't count controllified Z.
	mov	al, cl
	mov	ah, 0
	jmp	rskp
gtchr4:	mov	bx, bufpnt	;Position in DMA.
	mov	ah, [bx]	;Get a char from the file.
	inc	bx
	mov	bufpnt, bx
	cmp	ebquot, 'N'	;Can we not do 8th bit quoting? ;[29g] begin
	je	gtch41
	cmp	ebquot, 'Y'	;Or are we not?
	je	gtch41
	mov	dh, ah
	and	ah, 7Fh
	and	dh, 80h		;Is the 8th bit set?
	je	gtch41		;If not, no need for quoting
	dec	temp1		;Decrement the number of characters left
	mov	dh, ebquot	;Insert 8th bit quote char. in packet buffer
	mov	bx, cbfptr
	mov	[bx], dh
	inc	cbfptr
	inc	cl		;Count the character
gtch41:	mov	dl, ah		;Save the char.		;[29g] end
	and	dl, 80H		;Turn off all but parity.
	and	ah, 7FH		;Turn off the parity.
	cmp	ah, ' '		;Compare to a space.
	jl	gtchr5		;If less then its a control char, handle it.
	cmp	ah, del		;Is the char a delete?
	jz	gtchr5		;Go quote it.
	cmp	ah, ch		;Is it the quote char?
	je	gtch42		;If so, insert it in the buffer ;[29g] begin
	cmp	ebquot, 'N'	;Can we not do 8th bit quoting?
	je	gtchr8
	cmp	ebquot, 'Y'	;Or are we not?
	je	gtchr8
	cmp	ah, ebquot	;Is this the 8th bit quote character?
	jne	gtchr8		;If not, proceed
gtch42:	dec	temp1		;Decrement the chars remaining. ;[29g] end
	mov	bx, cbfptr	;Position in character buffer.
	mov	[bx], ch	;Precede char with send quote.
	inc	cbfptr
	inc	cl		;Increment the char count.
	jmp	gtchr8
gtchr5:	or	ah, dl		;Turn on the parity bit.
	cmp	ah, ('Z'-100O)	;Is it a ^Z?
	jne	gtchr7		;If not just proceed.
	cmp	binflg, 0	;ASCII file?			;[16] begin
	je	gtceof		;If so, terminate
	cmp	eoflag, 0	;EOF flag set?			;[16] end
	jz	gtchr6		;If not just go on.
	mov	bx, bufpnt
	mov	ax, chrcnt
	mov	dh, al		;Get number of chars left in DMA.
gtch51:	dec	dh
	jns	gtch52		;Any chars left?
	mov	chrcnt, 0	;If not, say so.
	mov	al, cl		;Return the count in A.
	mov	ah, 0
	jmp	rskp
gtch52:	mov	ah, [bx]	;Get the next char.
	inc	bx		;Move the pointer.
	cmp	ah, ('Z'-100O)	;Is it a ^Z?
	jz	gtch51		;If so see if they rest are.

gtchr6:	mov	ah, ('Z'-100O)	;Restore the ^Z.
gtchr7:	xchg	ah, al
	mov	ah, 0
	mov	temp2, ax	;Save the char.
	dec	temp1		;Decrement char counter.
	mov	bx, cbfptr	;Position in character buffer.
	mov	[bx], ch	;Put the quote in the buffer.
	inc	cbfptr
	inc	cl		;Increment the char count.
	mov	ax, temp2	;Get the control char back.
	xchg	al, ah
	add	ah, 40H		;Make the non-control.
	and	ah, 7FH		;Modulo 200 octal.
gtchr8:	or	dl, dl		;Do we have parity?		;[29g]
	jz	gtch81		;If not, just send it.		;[29g]
	or	ah, dl		;Or in the parity bit.
	cmp	parflg,parnon	;[par] no parity?
	je	gtch81		;[par] yes, keep going
	and	ah,7fh		;[par] else turn off parity from file
;[par]*** should probably mention that we're losing eighth bit here
	push	ax						;[29g] begin
	push	cx
	mov	dx, offset scrhi ;mention that high bit is being lost
	call	poscur						;[30c]
	mov	dx, offset hibit
	call	tmsg
	pop	cx
	pop	ax						;[29g] end
gtch81:	mov	bx, cbfptr	;Position in character buffer.
	mov	[bx], ah	;Put the char in the buffer.
	inc	cbfptr
	inc	cl		;Increment the char count.
	jmp	gtchr1		;Go around again.

gtceof:	cmp	cl, 0		;Had we gotten any data?
	je	gteof0		;Nope.
	mov	filflg,0FFh	;[23] fix ASCII extra buffers at eof
	mov	eoflag,0FFh	;[23]
	mov	al, cl
	mov	ah, 0
	jmp	rskp
gteof0:	mov	ah, 0FFH	;Get a minus one.
	ret

;Input the next DMA buffer.

inbuf:	mov	ah, eoflag	;Have we reached the end?
	cmp	ah, 0
	jz	inbuf0
	ret			;Return if set.
inbuf0:	push	bx
	push	cx
	mov	bx, offset dma	;Set the r/w buffer pointer.
	mov	bufpnt, bx
	mov	dx, offset fcb
	call	sinr
	cmp	al, 0		;End of file?
	je	inbuf1		;Still have data left.
	mov	eoflag, 0FFH	;Set End-of-file.
	mov	filflg, 0	;Buffer not empty.
	mov	chrcnt, 0	;Say no characters.
	pop	cx
	pop	bx
	ret

inbuf1:	mov	al, 80H		;Use as counter for number of chars read.
	pop	cx
	pop	bx
	cmp	filflg, 0	;Ever used DMS?
	jnz	inbf21		;Nope, then don't change count.
	dec	al		;Fix boundary error.
inbf21:	mov	ah, 0		;Zero the flag (buffer not empty).
	mov	chrcnt, ax	;Number of chars read from file.
	mov	filflg, 0	;Buffer not empty.
	jmp	rskp


	DSEG $

temp1	dw	0
temp2	dw	0
dma	rb	80H
filbuf	rb	60H		;Character buffer.
cpfcb	rb	25H		;Save FCB in case of "*".
rdbuf	rb	80H
cnt	dw	0
fcb	rb	36
fcb2	rb	12		;replacement receive filename	;[32c]
chrcnt	dw	0		;Number of chars in the file buffer.
filcnt	dw	0		;Number of chars left to fill.
outpnt	dw	0		;Position in packet.
bufpnt	dw	0		;Position in file buffer.
fcbptr	dw	0		;Position in FCB.
datptr	dw	0		;Position in packet data buffer.
cbfptr	dw	0		;Position in character buffer.
siz	dw	0		;Size of data from gtchr.
filflg	db	0		;Non-zero when nothing in DMA buffer.
filsiz	rw	02H		;Double word for filesize (in bytes.)
eoflag	db	0		;EOF flag;non-zero on EOF.
binflg	db	0		;ASCII/Binary flag - 0 if ASCII file	;[16]
wldflg	db	0		;Assume no "*" in fn.
<<< c86ker.a86 >>>
; * * * * * * * * * * * * * * *  version 2.9  * * * * * * * * * * * * * * *
;
; [35]  Change file names in INCLUDE statements, fdc, 5 Jun 85
;
; [34]
; (c)	Make memory allocation global, add sorted SEND capability.
; (b)	Add LOCAL TYPE command to display files on screen
; (a)	Fix directory file size calculation errors
;	RonB, 11/13/84
; * * * * * * * * * * * * * * *  version 2.8  * * * * * * * * * * * * * * *
; [33] Fix printer on hanging system problem by letting CP/M handle the
;	interrupts from the 7201 that we don't care about.  Thanks to
;	Paul Ford, U. of Chicago Graduate School of Business
;	WBC3, 10/1/84
; [32]
; (e)	Change all LEA xx,yy instructions to MOV xx,OFFSET yy
; (d)	Add LOCAL and REMOTE command table entries, implementing LOCAL
;	DELETE, DIRECTORY and SPACE commands (KERMIT,KERUTL)
; (c)	Fix RECEIVE FILENAME to be different from GET (KERMIT,KERCMD,KERFIL)
; (b)	Fix minor bugs (KERMIT,KERCMD)
; (a)	Add SET option for default drive and user (KERMIT,KERCMD,KERUTL)
;	RonB, 09/20/84
;* * * * * * * * * * * * * * * version 2.7 * * * * * * * * * * * * * * *
; [31]	Fix display of file rename
;	RonB, 05/05/84
;[fdc]	Add help message about "?" when starting up.
;	Add help message about "<esc-char>?" when connecting.
;	Fix glitch in Bernie's command level flagging (KERCMD).
;	F. da Cruz, Columbia
;
;[]	Prefixed all ermes'sages with bell and changed ermes3 (secondary
;	command didn't parse) from "?Not confirmed" to "?Unrecognized
;	command-option".
;	Prettied up (TABs between INSTR and AC's) and fixed SET command
;	using RSKP convention - B.Eiben, EIBEN at DEC-Marlboro, 2-May-84
; [30]
; (e)	Recombine KERSYS and KERMIT.
; (d)	Add SET PORT command, currently unimplemented (KERMIT,KERIO)
; (c)	Isolate all machine dependencies in KERIO.
; (b)	Make DEL work like BS and ^X like ^U in command input (KERCMD).
; (a)	Add keyboard DEL key alteration for APC (KERIO).
;	RonB, 04/18/84
; [29]
; (g)	Add 8th bit quoting (KERPRO,KERFIL,KERSYS).
; (f)	Add QUIT command, synonymous to EXIT (KERSYS).
; (e)	Move logging code to terminal module, make it a SET command,
;	add quit/resume (^Q/^R) to make it more standard (KERTRM,KERSYS)
; (d)	Expand receive buffer and check for packet overruns (KERPRO)
; (c)	Clear FCB prior to opening or creating a file (KERUTL)
; (b)	Add TAKE file processing, initially from KERMIT.INI
;	(KERMIT,KERSYS,KERUTL)
; (a)	Send error packet whenever a fatal error occurs (KERPRO)
;	RonB, 04/08/84
;* * * * * * * * * * * * * * * version 2.6 * * * * * * * * * * * * * * *
; [28]
; (e)	Add local stack for use in interrupt handling (KERIO)
; (d)	Improve input filename processing, allow valid special chars
;	(KERFIL,KERCMD)
; (c)	Make disk full error messages more accurate (KERSYS)
; (b)	Include filename in file-not-found error message (KERPRO,KERSYS)
; (a)	Clear attribute bits from filename before sending (KERPRO)
;	RonB, 03/27/84
; [27]	add "Kermit-86" to interrupt messages.	Rg 20-Mar-1984
; [26]	Move terminal emulation (TELNET) to a separate module: KERTRM.
;	This is to "modularize" terminal emulation. Rg
;
; [25]	Move logic for "seteol" and "escape" from KERSYS into KERCMD so those 
;	routines need not use internal CMD routines and variables.  For this 
;	purpose add 2 parse routines and codes: "cmcha" and "cmnum".  Also
;	eliminate the use of some KERCMD text strings in KERPRO.  The point
;	of this is to keep calls to CMD modular since I want to eventually
;	replace the whole thing.
;	R. Garland	9-Mar-1984
; [24]
; (a)	Add terminal session logging (KERMIT,KERSYS,KERUTL)
; (b)	Allow escape character to local-echo (KERMIT)
;	RonB, 03/15/84
;
; [23]	Fix ASCII-mode-junk-at-end-of-file bug.  (KERFIL)	Rg
;* * * * * * * * * * * * * * * version 2.5 * * * * * * * * * * * * * * *
; [22]
; (a) - Cosmetics - changed FILE-Warning to Warning, parallel to CP/M
;	Version and makes SET FI Binary painless
; (b) - made this version 2.5 to stop confusion -B.Eiben DEC Marlboro
;	7-March-84
; [21]
; (a) - Add SET TIMER ON/OFF option, default is OFF (KERSYS,KERPRO)
; (b) - Change SET FILE-MODE to SET FILE-TYPE to match VAX/VMS (KERSYS)
; (c) - Move all Set/Show processing to KERSYS (KERMIT,KERSYS)
;	RonB, 03/05/84
; [20]
; (a) -	Fix version & send/receive header for APC (KERSYS,KERPRO)
; (b) - Add Break processing & set clock rate for NEC (KERIO)
; (c) - Add escape character help in telnet mode (KERMIT,KERSYS)
; (d) - Add a pseudo time-out to PRTOUT so it doesn't loop forever (KERIO)
; (e) - Clean up environment better on KABORT (KERPRO,KERUTL)
;	RonB, 03/02/84
;* * * * * * * * * * * * * * * version 2.4 * * * * * * * * * * * * * * *
; [19]	(let Bill's and Jeff's changes be 17 and 18)
; (a) - Add flow control for input comm port buffer.  This is primarily to
;  allow smooth scrolling and "Hold Screen" to work on the Rainbow.  Add
;  associated Set, Show, and Help for same.  (KERSYS and KERIO)
; (b) - Clear screen at beginning and end of program. (KERSYS and KERIO)
; (c) - Give "bdos" mnemonic to interrupt 224. (KERUTL)
; (d) - Change telnet to check keyboard between comm port reads. (KERMIT)
;  Woops - can't get this dumb simple thing to work.  Save for later.
; (e) - Put in Break transmission in connect mode. (KERMIT,KERIO)
; (f) - Put in ^X/^Z file interruption. (KERSYS, KERPRO, KERFIL)
; (g) - Put in timeouts for packet receive routines. (KERPRO)
; [Rg] R. Garland, 2/84,	OC.GARLAND%CU20B@COLUMBIA-20.ARPA
;	Columbia Univ.		OC.GARLAND@CU20B.BITNET  
;* * * * * * * * * * * * * * * version 2.3 * * * * * * * * * * * * * * *
; Fill in the missing parity routines to allow this program to work with
; IBM mainframes and other systems that require parity (edit marked "[par]").
; Include RonB's fixes for places where previous edit broke the NEC APC.
; Jeff Damens, Columbia, 6 Feb 84
;* * * * * * * * * * * * * * * version 2.2 * * * * * * * * * * * * * * *
; Added CFIBFs (buffer clearing) where necessary, put conditional assembly
; code all in two modules (86KERIO and 86KERSYS), created new module
; (86KERSYS), added CTLPRT routine to print out control chars for the SHOW
; command, made SERINI called once at the beginning of the program and nowhere
; else and added user protocol timeouts (hitting a <CR>).
; Bill Catchings:  12:36pm  Thursday, 19 January 1984
;* * * * * * * * * * * * * * *  version 2.1  * * * * * * * * * * * * * *
;  [B.E. 3-Jan-83] added DTR-code (in 86kermit and 86kerio) for RAINBO
;
;  [16]	Add file-mode ASCII or BINARY processing.
;  [15]	Clear screen lines before displaying packet debug.
;  [14]	Fix nout to print decimal. (KERPRO)
;  [13]	Use control-Z's for filler in partial sectors instead of nulls. (KERPRO)
;  [12]	Allow user abort from the keyboard in the send and receive routines.
;  [11]	Capitalize and parse filename being received. (KERFIL)
;  [10]	Correct missing elements in the Show display.
;  [9]	Fix filename parsing, and add wildcard ability. (KERCMD,KERPRO,KERFIL)
;  [8]	Show choices for ambiguous keywords, finish keyword on '?' (KERCMD)
;  [7]	Do tab expansion and suppress nulls while in telnet mode. (KERUTL)
;  [6]	Add support for changing baud rate.
;  [5]	Put OFF/ON command table in alphabetical order.
;  [4]	Change default escape character to '^' because NEC keyboard doesn't
;	generate a control-\.
;  [3]	Change "esc,'[H'" to "esc,'[1;1H'" to get around bug in NEC BIOS.
;	This should not affect operation on the Rainbow.
;  [2]	Add a de-initialization routine for the serial port, to restore
;	changed interrupt vectors, etc. (KERIO)
;  [1]	Add I/O support for the NEC Advanced Personal Computer (KERIO)
;	RonB,12/23/83
;
;
; * * * * * * * * * * * * * * *  version 2.0  * * * * * * * * * * * * * * *
; KERMIT - KL10 Error-free Reciprocal Micro Interconnect over TTY-lines
;
;	Kermit Protocol Version 2
;
;	Based on the KERMIT Protocol.
;
;	Copyright (C) 1983 William B. Catchings III

; This program implements the Kermit Protocol developed at Columbia
; University.  This version is being written specifically for the
; DEC Rainbow 100.  It will hopefully take advantage of much of what
; was learned in implementing the CP/M-80, DEC-20 and other versions.

; Things to do:
;	Add new features from Kermit-80, like local CP/M functions
;	Make all commands the same as those in Kermit-80

; Now all system dependencies have been isolated in 86KERIO, which
; will be implemented as a separate module for each system.  This
; eliminates source code ballooning when other systems are added
; which have (for example) non-ANSI screen controls and terminal
; emulation capabilities, as well as specific port drivers.
; Information about what routines are needed in the 86KERIO module
; is given in the 86KERMIT.HLP and 86KERIO.HLP files.
;
; So far the systems supported are:
;	NEC APC		in 86KERIO.APC
;	DEC Rainbow	in 86KERIO.RB
;
; PIP 86KERIO.A86=86KERIO.???	- will make system-dependent routines available
; ASM86 86KERMIT $PZ		- will assemble without listing
; GENCMD 86KERMIT		- will LOAD
; REN KERMIT.CMD=86KERMIT.CMD	- will make it "real" after testing

	TITLE	'Kermit'

TRUE	EQU	1
FALSE	EQU	0

; Definitions:

soh	EQU	01O
bell	EQU	07O
tab	EQU	11O
lf	EQU	12O
ff	EQU	14O
cr	EQU	15O
xon	EQU	21O
xoff	EQU	23O
esc	EQU	33O
del	EQU	177O

parevn	EQU	00H		;Even parity.			;[21c] begin
parmrk	EQU	01H		;Mark parity.
parnon	EQU	02H		;No parity.
parodd	EQU	03H		;Odd parity.
parspc	EQU	04H		;Space parity.
ibmpar	EQU	parmrk		;IBM's parity (mark).
defpar	EQU	parnon		;Default parity (none).
defeco	EQU	00H		;Default is echo off		;[21a]
defesc	EQU	'\'-100O	;Escape char is Control backslash by default.
				;note: generated by INS key on the APC.
deftmr	EQU	00H		;Default is timer off		;[21a]
deflog	EQU	00H		;Default is logging off		;[24a]
floxon	EQU	1		;[19a]
flonon	EQU	0		;[19a]
diasw	EQU	00H		;Default is diagnostics off.	;[21c] end


; The actual program:

	CSEG			;Start coding!

	call	getdrv		;Save initial disk and user	;[32a] begin
	mov	cpmdrv,al
	mov	defdrv,al
	call	getusr
	mov	cpmusr,al
	mov	defusr,al					;[32a] end

;	The allocation amount of 245 pages was arrived at by trial and
;	error.  This figure causes Concurrent CP/M on the NEC APC to only
;	allocate one additional memory block.  (Since the blocks are 4K
;	each, one would expect 256...)

	mov	cx,245
start1:	push	cx
	mov	word ptr membuf+2, cx	;Allocate 4K (or less) bytes of memory
	mov	dx, offset membuf	;   for sorting filenames
	call	allmem
	pop	cx
	cmp	al,0
	loopne	start1
	cmp	cx,0
	jne	reboot
	mov	dx, offset erms25	;Not enough memory available:
	call	tcrmsg			;   local dir, del, and type will
	mov	word ptr membuf+2, 0	;   not be enabled, nor will wildcard
					;   sends.		;[34c] end

reboot:	pushf			;Push flags on CCP's stack.	;[12]
	pop	ax		;flags in AX.
	cli							;[32b]
	mov	bx, ds		;Set SS to DS.
	mov	ss, bx
	mov	sp, offset stack ;Set up the stack pointer.
	push	ax		;Restore the flags.
	popf

	call	inidma		;Set DMA base and offset	;[34c] begin
	call	serini		;Do any necessary serial port initialization.

	call	dspver		;Clear screen and print version header
	mov	dx, offset hlpmsg ;[fdc] Give help message about "?"
	call	tmsgcr		;[fdc]

; This is the main KERMIT loop.  It prompts for and gets the user's commands.

kermit:	mov	dx, offset kerm
	call	prompt		;Prompt the user.
	mov	dx, offset comtab
	mov	bx, offset tophlp
	mov	ah, cmkey
	call	comnd
	 jmp	kermt2		;Tell them about the error.
	mov	cmlevl, 1	;we're going into options
	call	bx		;Call the routine returned.
	 jmp	kermt3		;Tell them about the error.
	cmp	extflg, 0	;Check if the exit flag is set.
	je	kermit		;If not, do it again.
	call	serfin		;clean up serial port environment ;[2]
	mov	dl,cpmdrv	;reset disk and user		;[32a] begin
	call	setdrv		; to initial defaults
	mov	dl,cpmusr
	call	setusr						;[32a] end
	call	haltf
	retf			;Just in case!			;[32b]
 
kermt2:	mov	dx, offset ermes1 ;Give an error.
	call	tcrmsg
	jmp	kermit

kermt3:	mov	dx, offset ermes3 ;Give an error.
	call	tcrmsg
	jmp	kermit
 

; This is the EXIT command.  It leaves KERMIT and returns to CP/M.
 
exit:	mov	 ah, cmcfm
	call	comnd		;Get a confirm.
	 jmp	r
	mov	extflg, 1	;Set the exit flag.
	jmp	rskp

; This is the HELP command.
 
help:	mov	ah, cmcfm
	call	comnd		;Get a confirm.
	 jmp	r
	mov	dx, offset tophlp ;Print some help.
	call	tmsg
	jmp	rskp


; LOCAL - parse and perform local command			;[32d] begin

loccmd:	mov	dx, offset loctab ;Parse a keyword from the local table.
	mov	bx, offset lochlp
	mov	ah,cmkey
	call	comnd
	 jmp	r
	call	bx		;Call the specific local routine
	 jmp	r		;We got an error
	jmp	rskp


; DIRECTORY of local files

locdir:	mov	ah, cmifi	;Parse an input filename
	mov	dx, offset fcb
	call	comnd
	 jmp	r
	push	ax		;Save length of filename
	mov	ah, cmcfm	;Confirm it
	call	comnd
	 jmp	r
	pop	ax
	cmp	ah, 0		;Read in any chars?
	jne	locd1
	mov	di, offset fcb+1
	call	wldfcb		;If not, set filename to all wild
locd1:	cmp	byte ptr fcb,0	;Replace unspecified drive with default
	jne	locd2
	mov	al,defdrv
	inc	al
	mov	byte ptr fcb,al
locd2:	call	dirutl		;perform directory action (in KERUTL)
	 jmp	rskp		;On error a message has already been issued
	jmp	locs2		;jump to display space remaining


; ERASE local files

locera:	mov	ah, cmifi	;Parse an input filename
	mov	dx, offset fcb
	call	comnd
	 jmp	r
	push	ax		;Save length of filename
	mov	ah, cmcfm	;Confirm it
	call	comnd
	 jmp	r
	pop	ax
	cmp	ah, 0		;Read in any chars?
	jne	loce1
	mov	dx, offset erms26 ;Illegal (blank) filename
	call	tcrmsg
	 jmp	rskp
loce1:	call	erautl		;Erase utility (in KERUTL)
	 jmp	rskp		;On error a message has already been issued
	jmp	rskp


; TYPE local files

loctyp:	mov	ah, cmifi	;Parse an input filename
	mov	dx, offset fcb
	call	comnd
	 jmp	r
	push	ax		;Save length of filename
	mov	ah, cmcfm	;Confirm it
	call	comnd
	 jmp	r
	pop	ax
	cmp	ah, 0		;Read in any chars?
	jne	loct1
	mov	dx, offset erms26 ;Illegal (blank) filename
	call	tcrmsg
	 jmp	rskp
loct1:	call	typutl		;Type utility (in KERUTL)
	 jmp	rskp		;On error a message has already been issued
	jmp	rskp


; SPACE remaining on disk

locsiz:	mov	ah, cmifi	;Parse an input filename
	mov	dx, offset fcb
	call	comnd
	 jmp	r
	push	ax		;Save length of filename
	mov	ah, cmcfm	;Confirm it
	call	comnd
	 jmp	r
	pop	ax
	cmp	ah, 0		;Read in any chars?
	je	locs1
	mov	dx, offset erms24 ;At most only a drive code should be entered
	call	tcrmsg
	 jmp	rskp
locs1:	cmp	fcb,0
	jne	locs2
	mov	al,defdrv
	inc	al
	mov	fcb,al
locs2:	call	spcutl		;Space utility (in KERUTL)
	 jmp	rskp		;On error a message has already been issued
	jmp	rskp


; Fill a file control block filename with ?'s

wldfcb:	mov	ax,ds		;Fill filename with ?'s.
	mov	es,ax
	mov	cx,11
	mov	al,'?'
	rep stosb
	mov	wldflg,0FFh	;Set wildcard flag
	ret


; REMOTE - parse and perform remote command
;	(not yet implemented)

remcmd:	mov	ah,cmtxt	;Parse arbitrary text up to a CR
	mov	bx, offset data
	call	comnd
	 jmp	r
	mov	dx, offset infms6 ;Tell the user that it's not yet implemented.
	call	tcrmsg
	jmp	rskp

;	mov	dx, offset remtab ;Parse a keyword from the remote table.
;	mov	bx, offset remhlp
;	mov	ah,cmkey
;	call	comnd
;	 jmp	r
;	call	bx		;Call the specific remote routine
;	 jmp	r		;We got an error
;	jmp	rskp						;[31b] end


; FINISH - tell remote KERSRV to exit.

finish:	mov	ah,cmcfm	;Parse a confirm.
	call	comnd
	 jmp	r
	mov	ah, 'F'
	call	gensen		;Send the finish command.
	jmp	rskp

; BYE - tell remote KERSRV to logout and then exit to CP/M.

bye:	mov	ah,cmcfm	;Parse a confirm.
	call	comnd
	 jmp	r
	mov	ah, 'L'
	call	gensen		;Send the logout command.
	mov	extflg,1	;Set exit flag.
	jmp	rskp

; LOGOUT- tell remote KERSRV to logout.

logout:	mov	ah,cmcfm	;Parse a confirm.
	call	comnd
	 jmp	r
	mov	ah, 'L'
	call	gensen		;Send the logout command.
	jmp	rskp

; RECEIVE - Receive a file or files from the remote Kermit.	;[32c] begin
; A filespec can optionally be specified to rename the received file.

rec:	mov	ah, cmofi	;Parse an output filename
	mov	dx, offset fcb2
	call	comnd
	 jmp	r
	push	ax		;Save length of filename
	mov	ah, cmcfm	;Confirm it
	call	comnd
	 jmp	r
	pop	ax
	cmp	ah, 0		;Read in any chars?
	jne	rec1
	mov	di, offset fcb2+1
	call	wldfcb		;If not, set filename to all wild
rec1:	cmp	byte ptr fcb2,0	;Also make default drive a ?.
	jne	rec2
	mov	byte ptr fcb2,'?'
rec2:	call	read		;Do the actual protocol work.
	jmp	rskp						;[32c] end

; GET - Get a file or files from the remote server Kermit.

get:	mov	di, offset fcb2
	mov	byte ptr [di],'?'
	inc	di
	call	wldfcb		;Receive any filename as is	;[32c]
	mov	ah, cmtxt	;Parse an arbitrary text string.
	mov	bx, offset data	;Where to put the parsed text.
	call	comnd
	 jmp	r
	cmp	ah, 0		;Read in any chars?
	jne	get1		;If not give an error.
	mov	dx, offset ermes5
	call	tcrmsg
	jmp	rskp
get1:	mov	al, ah
	mov	ah, 0
	mov	argbk1, ax	;Remember number of chars we read.
	mov	ah, '$'		;Used for printing.
	mov	[bx], ah
	call	init		;Paint screen and initialize file buffers.
	call	cfibf		;Clear any stacked NAKs.
	call	clrfln		;Prepare to print filename.
	mov	dx, offset data	;Print file name.
	call	tmsg
	mov	argblk, 0	;Start at packet zero.
	mov	ah, 'R'		;Receive init packet.
	call	spack		;Send the packet.
	 jmp	r
	call	read1		;Join the read code.
	jmp	rskp

; SEND - Send a file or files to the remote Kermit.

sencom:	mov	ah, cmifi	;Parse an input file spec.
	mov	dx, offset fcb	;Give the address for the FCB.
	call	comnd
	 jmp	r		;Give up on bad parse.
	cmp	ah,0		;Check for null filename	;[32c] begin
	jne	$+5
	 jmp	r						;[32c] end
	cmp	wldflg,0FFh
	jne	sen7
	mov	ah, cmifi	;Parse first file to send	;[34c] begin
	mov	dx, offset fcb2
	call	comnd
	 nop
	 nop
	 nop
	mov	bx,offset fcb2+1
	mov	cx,11
sen4:	cmp	byte ptr [bx],'?' ;Replace wildcards with spaces 
	jne	sen5		  ;   in starting filename
	mov	byte ptr [bx],' '
sen5:	inc	bx
	loop	sen4
	mov	wldflg,0FFh	;Show original filename as wild
sen7:	mov	ah, cmcfm
	call	comnd		;Get a confirm.
	 jmp	r		;Didn't get a confirm.
	call	send
	jmp	rskp

; SET - Set some Kermit parameter.

setcom:	mov	dx, offset settab ;Parse a keyword from the set table.
	mov	bx, offset sethlp
	mov	ah,cmkey
	call	comnd
	 jmp	r
	call	bx		;Call the specific set routine in KERSYS.
	 jmp	r		;We got an error
	jmp	rskp


; STATUS - Give some statistics on the connection.

status:	jmp	show		;Make STATUS and SHOW synonymous for now ;[32b]

; TAKE - Specify file which will supply command input.		;[29b] begin

take:	mov	ah, cmifi	;Parse an input file spec.
	mov	dx, offset tkfcb ;Give the address for the FCB.
	call	comnd
	 jmp	r		;Give up on bad parse.
	cmp	ah,0		;Check for null filename	;[32c] begin
	jne	$+5
	 jmp	r						;[32c] end
	mov	ah, cmcfm
	call	comnd		;Get a confirm.
	 jmp	r		;Didn't get a confirm.
	mov	tkflg, 1	;Turn on command file input
	mov	tkptr, 0	;Indicate file not yet open
	jmp	rskp						;[29b] end

; TRANSMIT file(s) with no protocol				;[32e] begin

txmit:	mov	ah,cmtxt	;Parse arbitrary text up to a CR
	mov	bx, offset data
	call	comnd
	 jmp	r
	mov	dx, offset infms6 ;Tell the user that it's not yet implemented.
	call	tcrmsg
	jmp	rskp						;[32e] end

; Set parity for character in Register AL.

dopar:	cmp	parflg, parnon	;No parity?
	je	parret		;Just return
	cmp	parflg, parevn	;Even parity?
	jne	dopar0
	and	al, 7FH		;Strip parity.
	jpe	parret		;Already even, leave it.
	or	al, 80H		;Make it even parity.
	jmp	parret
dopar0:	cmp	parflg, parmrk	;Mark parity?
	jne	dopar1
	or	al, 80H		;Turn on the parity bit.
	jmp	parret
dopar1:	cmp	parflg, parodd	;Odd parity?
	jne	dopar2
	and	al, 7FH		;Strip parity.
	jpo	parret		;Already odd, leave it.
	or	al, 80H		;Make it odd parity.
	jmp	parret
dopar2:	and	al, 7FH		;Space parity - turn off parity bit.
parret:	ret



; The following are the SET command subroutines		;[21c] begin
;	except for Baud rate and Port selection, which are
;	isolated in the system dependent modules.

; Sets debugging mode on and off.

debset:	mov	dx, offset ontab
	mov	bx, offset onhlp
	mov	ah, cmkey
	call	comnd
	 jmp	r
	mov	temp1, bx
	mov	ah, cmcfm
	call	comnd		;Get a confirm.
	 jmp	r		;Didn't get a confirm.
	mov	bx, temp1
	mov	debug, bl	;Set the debug flag.
	jmp	rskp

; Sets the default disk and user number for file operations	;[32a] begin
; Entry must be in one of the following forms:
;	d:	= go to drive d (A through P)
;	u:	= go to user u (0 through 15)
;	du:	= go to drive d and user u
;	:	= go to the defaults when Kermit was loaded
; Whenever a drive is specified, even if it is the same as the current
; default drive, the drive is logged in so that disks can be swapped
; without exiting Kermit to type control-C.

defset:	mov	ah,cmtxt	;Get the du: specification
	mov	bx, offset data
	call	comnd
	 jmp	r
	mov	byte ptr [bx],0	;Mark the end of input
	mov	ah,cmcfm	;Confirm the input
	call	comnd
	 jmp	r
	mov	newdrv,0FFh	;set inputs to show no entry
	mov	newusr,0FFh
	mov	bx, offset data	;analyze the input
	mov	al,[bx]
	cmp	al,'a'		;check for lower case drive specification
	jb	defs10
	cmp	al,'p'
	jbe	defs05
	jmp	deferr
defs05:	sub	al,'a'
	jmps	defs15
defs10:	cmp	al,'A'		;check for upper case drive specification
	jb	defs25
	cmp	al,'P'
	ja	deferr
	sub	al,'A'
defs15:	mov	newdrv,al	;save the new drive specification
defs20:	inc	bx
	mov	al,[bx]
defs25:	cmp	al,':'		;input must terminate with required colon
	je	defs50
	cmp	al,'0'		;check for user number digit
	jb	deferr
	cmp	al,'9'
	ja	deferr
	sub	al,'0'
	mov	dl,al
	cmp	newusr,0FFh	;is this the first digit?
	jne	defs30
	mov	newusr,dl	; yes, just store the digit
	jmps	defs20
defs30:	mov	al,newusr	; otherwise append digit to current value
	mov	dh,10
	mul	dh
	add	al,dl
	mov	newusr,al
	jmps	defs20
defs50:	inc	bx		;we've seen a colon, it must be followed
	cmp	byte ptr [bx],0	; by the null we stored earlier
	jne	deferr
	mov	al,newusr	;are we setting a new user number?
	cmp	al,0FFh
	jne	defs60		; yes, check its value
	cmp	al,newdrv	; otherwise if neither drive nor user
	jne	defs70		;  was specified and yet we saw the colon,
	mov	al,cpmdrv	;  then return to the initial CP/M defaults.
	mov	newdrv,al
	mov	al,cpmusr
defs60:	cmp	al,15		;make sure user is in range
	ja	deferr
	mov	defusr,al	;save new user value
	mov	dl,al
	call	setusr
defs70:	mov	al,newdrv	;are we setting a new drive?
	cmp	al,0FFh
	je	defs90
	mov	defdrv,al	;save new drive value
	call	rstdsk		;reset disk system to log in drive
	mov	dl,defdrv	; then select new default
	call	setdrv
defs90:	jmp	rskp
deferr:	mov	dx, offset erms23
	call	tcrmsg
	jmp	rskp						;[32a] end

; Set end-of-line character

eolset:							;[25] begin
	mov	dx, 01FH	;maximum value of number allowed. (31)
	mov	bx, offset eolhlp ;help string for parser
	mov	ah, cmnum	;look for a decimal number
	call	comnd		;call the parser
	 jmp	r
	mov	seol, bl	;set the eol character
	jmp	rskp

escape:
	mov	bx, offset eschlp ;help string for parser
	mov	ah, cmcha	;parser code for single character
	call	comnd		;call parser
	 jmp	r
	mov	escchr, bl	;set the character
	jmp	rskp
								;[25] end

;	This is the SET file-type (ASCII or BINARY) command	;[16] begin

fmset:	mov	dx, offset fmtab
	mov	bx, offset fmhlp
	mov	ah, cmkey
	call	comnd
	 jmp	r
	mov	temp1, bx
	mov	ah, cmcfm
	call 	comnd		;Get a confirm.
	 jmp	r		;Didn't get a confirm.
	mov	bx, temp1
	mov	binflg, bl	;Set the file-type flag.
	jmp	rskp						;[16] end

;	This is the SET Warning command.

filwar:	mov	dx, offset ontab
	mov	bx, offset onhlp
	mov	ah, cmkey
	call	comnd
	 jmp	r
	mov	temp1, bx
	mov	ah, cmcfm
	call	comnd		;Get a confirm.
	 jmp	r		;Didn't get a confirm.
	mov	bx, temp1
	mov	flwflg, bl	;Set the file warning flag.
	jmp	rskp

;		Set flow-control flag			[19a] Begin

setflo:	mov	dx, offset flotab ;set up ON/OFF response
	mov	bx, offset flohlp
	mov	ah, cmkey
	call	comnd		;get response
	jmp	r
	mov	temp1, bx	;save response
	mov	ah, cmcfm
	call	comnd		;get confirm
	jmp	r
	mov	bx, temp1
	mov	floctl, bl	;set flag
	jmp	rskp						;[19a] end

;	This is the SET IBM command.

ibmset:	mov	dx, offset ontab
	mov	bx, offset onhlp
	mov	ah, cmkey
	call	comnd
	 jmp	r
	mov	temp1, bx
	mov	ah, cmcfm
	call	comnd		;Get a confirm.
	 jmp	r		;Didn't get a confirm.
	mov	bx, temp1
	mov	ibmflg, bl	;Set the IBM flag.
	cmp	bl, 0		;Turning on or off?
	je	ibmst1		;If off, set parity & local echo to defaults.
	mov	parflg, ibmpar	;Set IBM parity.		;[21a] begin
	mov	ecoflg, 1	;Set local echo on.
	mov	tmrflg, 1	;Set timer on.
	jmps	ibmst2
ibmst1:	mov	parflg, defpar	;Set default parity.
	mov	ecoflg, defeco	;Set local echo to default.
	mov	tmrflg, deftmr	;Set timer to default.
ibmst2:	jmp	rskp							;[21a] end


;	This is the LOCAL echo SET subcommand.

local:	mov	dx, offset ontab
	mov	bx, offset onhlp
	mov	ah, cmkey
	call	comnd
	 jmp	r
	mov	temp1, bx	;Save the parsed value.
	mov	ah, cmcfm
	call	comnd		;Get a confirm.
	 jmp	r		;Didn't get a confirm.
	mov	bx, temp1
	mov	ecoflg, bl	;Set the local echo flag.
	jmp	rskp


;	This is the LOG filename SET command

log:	mov	ah, cmofi	;Get an output filename
	mov	dx, offset lfcb	;For a log file
	call	comnd
	 jmp	r
	cmp	ah,0		;Check for null filename	;[32c] begin
	jne	$+5
	 jmp	r						;[32c] end
	mov	ah, cmcfm	;Confirm it
	call	comnd
	 jmp	r
	mov	logflg, 0FFh	;Turn logging on
	jmp	rskp


;	This is the SET Parity command.

setpar:	mov	dx, offset partab
	mov	bx, offset parhlp
	mov	ah, cmkey
	call	comnd
	 jmp	r
	mov	temp1, bx
	mov	ah, cmcfm
	call	comnd		;Get a confirm.
	 jmp	r		;Didn't get a confirm.
	mov	bx, temp1
	mov	parflg, bl	;Set the parity flag.
	jmp	rskp

;	This is the SET Timer command.			;[21a] begin

tmrset:	mov	dx, offset ontab
	mov	bx, offset onhlp
	mov	ah, cmkey
	call	comnd
	 jmp	r
	mov	temp1, bx
	mov	ah, cmcfm
	call	comnd		;Get a confirm.
	 jmp	r		;Didn't get a confirm.
	mov	bx, temp1
	mov	tmrflg, bl	;Set the timer flag.
	jmp	rskp					;[21a] end
							;[21c] end

;	The following are display subroutines used primarily	;[21c] begin
;	in the SHOW command.

								;[10] begin
;	This routine matches a table value and prints the corresponding
;	keyword.  On entry:
;			al = value to match
;			bx = beginning address of table

tabprt:	mov	ah, 0		;make it a word comparison
	mov	ch, [bx]	;get the table size
	inc	bx
tabp1:	mov	dl, [bx]	;get the offset to the value
	inc	bx		;point to beginning of the keyword
	mov	dh, 0
	mov	si, dx
	cmp	ax, 1[bx+si]	;does the value match?
	je	tabp2		;yes, go print the keyword at [bx].
	add	bx, dx		;otherwise go to next entry
	add	bx, 3
	dec	ch		;any more entries
	jnz	tabp1		;if so, go check the next
	mov	bx, offset erms20 ;else say value was not found
tabp2:	mov	dx, bx
	call	tmsg		;display the keyword
	ret
								;[10] end

; This routine prints out the escape character in readable format.
;	Call CTLPRT with any char in DL for similar output.

escprt:	mov	dl, escchr
ctlprt:	cmp	dl, ' '
	jge	escpr2
	push	dx
	mov	dx, offset esctl ;Print "Control-"
	call	tmsg
	pop	dx
	add	dl, 040H	;Make it printable.
escpr2:	call	bout
	ret

;	This routine prints "is on" if prior comparison was nonzero
;	and otherwise prints "is off"

ponoff:	jz	ponof2		;If not say so.
	mov	dx, offset onstr ;Say on.
	jmp	ponof3
ponof2:	mov	dx, offset offstr ;Say off.
ponof3:	call	tmsg
	ret

; SHOW - Show the state of the parameters available from the SET command.

show:	mov	ah,cmcfm	;Parse a confirm.
	call	comnd
	 jmp	r
	call	tcrlf

	call	shobd		;Display baud rate (system dependent)

	mov	dx, offset debst ;Debugging string.
	call	tcrmsg
	cmp	debug, 0	;Is the debugging flag on?
	call	ponoff

	mov	dx, offset eolst ;End of line string.		;[10] begin
	call	tcrmsg
	mov	dl, seol
	call	ctlprt						;[10] end

	mov	dx, offset escst ;Escape string.
	call	tcrmsg
	call	escprt		;Print the escape char.

	mov	dx, offset fmst	;File type string. [22d]	;[16] begin
	call	tcrmsg
	mov	al, binflg	;Print the keyword corresponding to the
	mov	bx, offset fmtab ;current value of binflg
	call	tabprt						;[16] end

	mov	dx, offset flostr				;[19a] start
	call	tcrmsg		;
	mov	al, floctl	;show the current
	mov	bx, offset flotab ;setting for flow-control
	call	tabprt						;[19a] end

	mov	dx, offset ibmst ;IBM string.
	call	tcrmsg
	cmp	ibmflg, 0	;Is the IBM flag on?
	call	ponoff

	mov	dx, offset locst ;Get the address of the local echo string.
	call	tcrmsg
	cmp	ecoflg, 0	;Is the local echo flag on?
	call	ponoff		;Print on or off.

	mov	dx, offset logst ;Logging string.
	call	tcrmsg
	mov	dx, offset lfcb	;Print the log filename.
	call	tfile
	cmp	logflg, 0	;Is the logging flag on?
	call	ponoff

	mov	dx, offset parst ;Parity string.
	call	tcrmsg
	mov	al, parflg	;Print the keyword corresponding ;[10] begin
	mov	bx, offset partab ;to the current value of parflg
	call	tabprt

	call	shoprt		;Port selection (system dependent) ;[30d]

	mov	dx, offset tmrst ;Timer on/off string		;[21a] begin
	call	tcrmsg
	cmp	tmrflg, 0	;Is the timer on?
	call	ponoff		;Print on or off.		;[21a] end

	mov	dx, offset filst ;File warning string.
	call	tcrmsg
	cmp	flwflg, 0	;Is the file warning flag on?
	call	ponoff

	call	tcrlf		;Print a crlf
	jmp	rskp						;[21c] end


dspver:	call	clrscr		;Home cursor and clear the screen ;[30c] begin
	mov	dx, offset scrhdr ;Position to the version line.
	call	poscur
	call	bldon		;Turn on bold (highlighted) display
	mov	dx, offset system ;Print the system identification
	call	tmsg
	mov	dx, offset versio ;Print the Kermit version header.
	call	tmsg
	call	bldoff		;Turn off the highlight	;[30c] end
	ret


	DSEG			;Data segment.

	ORG	100H		;Leave room for system page.

	RW	100		;hundred word or so stack.
stack	RW	2

	; COMND tables

comtab	db	22
	db	3,'BYE$'
	dw	bye
	db	7,'CONNECT$'
	dw	telnet
	db	6,'DELETE$'					;[32d] begin
	dw	locera
	db	9,'DIRECTORY$'
	dw	locdir
	db	5,'ERASE$'
	dw	locera						;[32d] end
	db	4,'EXIT$'
	dw	exit
	db	6,'FINISH$'
	dw	finish
	db	3,'GET$'
	dw	get
	db	4,'HELP$'
	dw	help
	db	5,'LOCAL$'					;[32d]
	dw	loccmd						;[32d]
	db	6,'LOGOUT$'
	dw	logout
	db	4,'QUIT$'					;[29f]
	dw	exit						;[29f]
	db	7,'RECEIVE$'
	dw	rec
	db	6,'REMOTE$'					;[32d]
	dw	remcmd						;[32d]
	db	4,'SEND$'
	dw	sencom
	db	3,'SET$'
	dw	setcom
	db	4,'SHOW$'
	dw	show
	db	5,'SPACE$'					;[32d]
	dw	locsiz						;[32d]
	db	6,'STATUS$'
	dw	status
	db	4,'TAKE$'					;[29b]
	dw	take						;[29b]
	db	8,'TRANSMIT$'					;[32e]
	dw	txmit						;[32e]
	db	4,'TYPE$'					;[34b]
	dw	loctyp						;[34b]

settab	db	14
	db	4,'BAUD$'
	dw	bdset						;[6] end
	db	9,'DEBUGGING$'
	dw	debset
	db	12,'DEFAULT-DISK$'				;[32a] begin
	dw	defset						;[32a] end
	db	11,'END-OF-LINE$'
	dw	eolset
	db	6,'ESCAPE$'
	dw	escape
	db	9,'FILE-TYPE$'			;[16] begin [21b]
	dw	fmset					;[16] end
	db	13,'FLOW-CONTROL$'				;[19a]
	dw	setflo						;[19a]
	db	3,'IBM$'
	dw	ibmset
	db	10,'LOCAL-ECHO$'
	dw	local
	db	3,'LOG$'					;[24a]
	dw	log						;[24a]
	db	6,'PARITY$'
	dw	setpar
	db	4,'PORT$'					;[30d]
	dw	prtset
	db	5,'TIMER$'					;[21a]
	dw	tmrset						;[21a]
	db	7,'WARNING$'
	dw	filwar

loctab	db	5						;[32d] begin
	db	6,'DELETE$'
	dw	locera
	db	9,'DIRECTORY$'
	dw	locdir
	db	5,'ERASE$'
	dw	locera
	db	5,'SPACE$'
	dw	locsiz						;[32d] end
	db	4,'TYPE$'					;[34b]
	dw	loctyp						;[34b]
 
ontab	db	2		;Two entries.
	db	3,'OFF$'					;[5] begin
	dw	0000H
	db	2,'ON$'
	dw	0001H						;[5] end
 
yestab	db	2		;Two entries.
	db	2,'NO$'
	dw	0000H
	db	3,'YES$'
	dw	0001H

flotab	db	2		;[19a] start
	db	4,'NONE$'
	dw	flonon
	db	8,'XON/XOFF$'
	dw	floxon		;[19a] end

partab	db	5		;Five entries.
	db	4,'EVEN$'
	dw	parevn
	db	4,'MARK$'
	dw	parmrk
	db	4,'NONE$'
	dw	parnon
	db	3,'ODD$'
	dw	parodd
	db	5,'SPACE$'
	dw	parspc

fmtab	db	2		;Two entries.			;[16] begin
	db	5,'ASCII$'
	dw	00H
	db	6,'BINARY$'
	dw	01H						;[16] end

hlpmsg	db	cr,lf,'Type ? at any point for help$'
cfrmes	db	' Confirm with carriage return $'
filhlp	db	' Input file spec (possibly wild) $'

tophlp	db	cr,lf,'      Basic Commands                            Other Commands'
	db	cr,lf,'CONNECT to host as a terminal             DELETE local files'
	db	cr,lf,'RECEIVE file(s) from host                 DIRECTORY of local files'
	db	cr,lf,'SEND file(s) to host                      HELP by giving this message'
	db	cr,lf,'SET a Kermit parameter                    LOCAL generic command'
	db	cr,lf,'SHOW the parameter values                 QUIT (same as EXIT)'
	db	cr,lf,'EXIT to CP/M                              SPACE remaining on disk'
	db	cr,lf,'                                          STATUS of Kermit'
	db	cr,lf,'      Server Commands                     TAKE command input from file'
	db	cr,lf,'GET file(s) from host                     TYPE local file on screen'
	db	cr,lf,'SEND file(s) to host'
	db	cr,lf,'REMOTE generic command'
	db	cr,lf,'FINISH running Kermit on the host'
	db	cr,lf,'LOGOUT the host'
	db	cr,lf,'BYE to host (LOGOUT, then EXIT)'
	db	'$'

sethlp	db	cr,lf,'BAUD rate'				;[6]
	db	cr,lf,'DEBUGGING displays of transferred packets'
	db	cr,lf,'DEFAULT-DISK for file operations'	;[32a]
	db	cr,lf,'END-OF-LINE character in packets'
	db	cr,lf,'ESCAPE character from terminal mode'
	db	cr,lf,'FILE-TYPE (ASCII or BINARY)'		;[16][21b]
	db	cr,lf,'FLOW-CONTROL (NONE or XON/XOFF)'		;[19a]
	db	cr,lf,'IBM mainframe communications mode'
	db	cr,lf,'LOCAL-ECHO echoing (half-duplex)'
	db	cr,lf,'LOG filename for terminal session logging' ;[24a]
	db	cr,lf,'PARITY type'
	db	cr,lf,'PORT to communicate on'			;[30d]
	db	cr,lf,'TIMER for packet retransmission'		;[21a]
	db	cr,lf,'WARNING for received filename conflicts'
	db	'$'

lochlp	db	cr,lf,'DELETE local files'			;[32d] begin
	db	cr,lf,'DIRECTORY of local files'
	db	cr,lf,'SPACE remaining on disk'
	db	cr,lf,'TYPE local files on screen'		;[34b]
	db	'$'						;[32d] end

flohlp	db	cr,lf,'NONE	XON/XOFF$'
onhlp	db	cr,lf,'OFF	ON$'
yeshlp	db	cr,lf,'NO	YES$'
parhlp	db	cr,lf,'NONE   MARK   ODD   EVEN   SPACE$'
fmhlp	db	cr,lf,'ASCII	BINARY$'			;[16]
eolhlp	db	cr,lf,'Decimal digit between 0 and 31$'
eolerr	db	cr,lf,'Illegal end-of-line character$'
timhlp	db	cr,lf,'Decimal digit between 1 and 94$'
timerr	db	cr,lf,'Illegal timeout value$'
esctl	db	'Control-$'
eschlp	db	cr,lf,'Enter literal value (ex: Ctrl ])  $'
inthlp	db	'Kermit-86: Interrupt - type'		;[27]
	db	cr,lf,'	?   to display this message'	;[20c] begin
	db	cr,lf,'	B   to send a Break signal to the port'
	db	cr,lf,'	C   to return to the Kermit-86> prompt'
	db	cr,lf,'	L   to toggle terminal session logging'	;[24a]
	db	cr,lf,'	Q   to quit terminal session logging'	;[29e]
	db	cr,lf,'	R   to resume terminal session logging'	;[29e]
	db	cr,lf,'	$'
inthl2	db	'   to send a $'
inthl3	db	' character to the port$'			;[20c] end
onstr	db	' is on$'
offstr	db	' is off$'
flostr	db	'Flow control: $'				;[19a]
bdst	db	'Baud rate: $'
debst	db	'Debug mode$'
eolst	db	'End-of-line character: $'
escst	db	'Escape char: $'
fmst	db	'File type is $'				;[16][21b]
ibmst	db	'IBM flag$'
locst	db	'Local echo$'
logst	db	'Logging to $'					;[24a]
parst	db	'Parity: $'
tmrst	db	'Timer$'					;[21a]
timmes	db	'Timeout is $'
filst	db	'Warning$'

clrspc	db	' ',10O,'$'		;Clear space.

versio	db	' CP/M-86 Kermit-86 - V2.9'
	db	cr,lf,lf,'$'
kerm	db	'Kermit-86 $'					;[32a]
pktlin	db	cr,'Number of packets:   '
	db	cr,lf,'Number of retries:   '
	db	cr,lf,'File name:  $'
spmes	db	'Spack:  $'
rpmes 	db	'Rpack:  $'
hibit	db	'Warning - Non-ASCII char$'
ender	db	bell,bell,'$'
inms01	db	cr,lf,'[Kermit-86: Connecting to host...' ;[27][fdc]
	db	cr,lf,'  Type $'			;[fdc]
inms02	db	' C to return to PC, $'			;[fdc]
inms25	db	' ? for help]$'				;[fdc]
inms03	db	'[Kermit-86: Back at PC]$'			;[27]
infms3	db	'Completed    $'
infms4	db	'Failed       $'
infms5	db	'Renaming file to $'
infms6	db	'%Function not implemented$'
infms7	db	'Interrupted  $'				;[19f]
infms8	db	'File interrupt (^X)  $'			;[19f]
infms9	db	'File group interrupt (^Z)  $'			;[19f]
infm10	db	' Type: ^X to interrupt file,  ^Z to '		;[19f][30c]
	db	'interrupt group,  ^C to "abort".$'		;[19g][30c]
infm11	db	'[Kermit-86: Logging to $'			;[24a][27]
infm12	db	']$'						;[24a]
infm13	db	'[Kermit-86: Logging terminated]$'		;[24a][27]
timoms	db	bell,'Timeout$'
ermes1	db	bell,'?Unrecognized command$'
ermes2	db	bell,'?Illegal character$'
ermes3	db	bell,'?Unrecognized command option$'
ermes4	db	bell,'?Unable to rename file$'
ermes5	db	bell,'?No receive file specification given$'
ermes7	db	bell,'?Unable to receive initiate$'
ermes8	db	bell,'?Unable to receive file name$'
ermes9	db	bell,'?Unable to receive end of file$'
erms10	db	bell,'?Unable to receive data$'
erms11	db	bell,'?Disk directory area full$'			;[28c]
erms14	db	bell,'?Unable to receive an acknowledgement from the host$'
erms15	db	bell,'?Unable to find file $'			;[28b]
erms17	db	bell,'?Disk data area full$'				;[28c]
erms18	db	bell,'?Unable to tell host that session is finished$'
erms19	db	bell,'?Unable to tell host to logout$'
erms20	db	bell,'?Illegal value$'				;[10]
erms21	db	'* Aborted *$'					;[12]
erms22	db	bell,'?Cannot open log file $'			;[24a]
erms23	db	bell,'?Specify new default drive and (optional) user number as DU:$' ;[32a]
erms24	db	bell,'?Specify drive as D:$'			;[32d]
erms25	db	bell,'?Not enough memory for sort buffer:'	;[34c]
	db	cr,lf,'   Local DIRECTORY, DELETE, and TYPE operations'
	db	cr,lf,'   and wildcard SENDs will not be supported.$'
erms26	db	bell,'?Illegal filename$'
erms27	db	bell,'?Memory buffer overflow$'
erms28	db	bell,'?No filenames above specified starting name$'	;[34c]

; Cursor addressing items (row,col)			;[19b],[30c] begin

scrhdr	db	3,1		;Place for version header
scrnp	db	5,22		;Place for number of packets.
scrnrt	db	6,22		;Place for number of retries.
scrfln	db	7,12		;Place for file name.
screrr	db	13,1		;Place for error messages.
scrst	db	5,53		;Place for "Complete".
scrrpr	db	14,1		;Place for prompt.
scrhi	db	7,53		;8th bit on in character.
scrfr	db	8,1		;Rename file.			;[31]
scrsp	db	9,1		;Send packet
scrrp	db	11,1		;Receive
scrint	db	6,53		;[19f] interrupt acknowledge
scrhlp	db	24,1		;[19f] help line

	; Impure storage

extflg	db	0		;Exit flag;non zero to exit.
ecoflg	db	defeco		;Echo flag; non zero for local echoing.
escchr	db	defesc		;Escape character for the connect command.
debug	db	diasw		;Are we in debug mode?
tmrflg	db	deftmr		;Is the timer enabled?
cpmdrv	db	0		;Initial drive and user		;[32a] begin
cpmusr	db	0
defdrv	db	0		;Current default drive and user
defusr	db	0
newdrv	db	0		;Storage for resetting drive and user
newusr	db	0						;[32a] end


	ESEG			;Extra segment.

	INCLUDE C86CMD.A86	;Get the COMND routines.
	INCLUDE C86FIL.A86	;Get the file routines.

;Note, the following file does not exist.  You have to copy one of the system
;dependent files like C86XRB.A86 to a file of this name.
	INCLUDE C86XXX.A86	;Get the I/O routines

	INCLUDE C86PRO.A86	;Get the protocol routines.
	INCLUDE C86TRM.A86	;Get terminal emulation [26]
	INCLUDE C86UTL.A86	;Get the utility routines.

	DSEG $			;Resume data segment.

	DB	0		;Is this really necessary?
				;No, but without it you need to specify
				;link options to GENCMD so as to make the
				;load address allow for uninitialized storage.
				;This bypasses that necessity by including
				;all uninitialized storage in the load
				;module. - RonB

	END
<<< c86pro.a86 >>>
; C86PRO.A86
; * * * * * * * * * * * * * * * * version 3.0 * * ** * * * * * * * * * * * *
; Allows packet echo - CGL
; * * * * * * * * * * * * * * *  version 2.9  * * * * * * * * * * * * * * *
; [34c] Add sorted wildcard SENDs with initial filename
; * * * * * * * * * * * * * * *  version 2.7  * * * * * * * * * * * * * * *
; [31] Fix display on file renaming.
; RonB, 05/05/84
; [30c] Isolate ANSI escape sequences for machine independence.
; [29g]  Add 8th bit quoting.
; [29d]  Enlarge receive buffer and check for packet overruns.
; [29b]  Add TAKE processing (close command file when aborting).
; [29a]  Send error packet when aborting.
; RonB, 04/08/84
; * * * * * * * * * * * * * * *  version 2.6  * * * * * * * * * * * * * * *
; [28b]  Make file-not-found error more informative (include filename).
; [28a]  Clear attribute bits from filename before sending
; RonB, 03/27/84
; [25] - make KERCMD more modular by eliminating some use of text
; strings by other modules (KERPRO, KERSYS), and moving some
; parsing into KERCMD (ESCAPE and EOLSET logic in KERSYS)
; * * * * * * * * * * * * * * *  version 2.4  * * * * * * * * * * * * * * *
;  [21a] Add timeout enable/disable
; RonB,03/05/84
;  [20a] Fix version & send/receive header
;  [20e] Clean up environment better before rebooting in KABORT
; RonB,03/02/84
;  [19f] Add ^X/^Z file interruption - adapted from PC Kermit V1.20
;  Put in Help line for above.
;  [19g] Put in timeouts.
; R. Garland 2/84
; * * * * * * * * * * * * * * *  version 2.1  * * * * * * * * * * * * * * *
;  [14] Fix nout to print decimal.
; RonB,12/28/83
;  [13] Use control-Z's for filler in partial sectors instead of nulls.
; RonB,12/27/83
;  [12] Allow user abort from the keyboard in the send and receive routines.
; RonB,12/27/83
;  [9] Fix filename parsing, and add wildcard ability.
; RonB,12/26/83
; * * * * * * * * * * * * * * *  version 2.0  * * * * * * * * * * * * * * *
; This module contains the routines that actually implement the Kermit
; protocol.

; Packet definitions.

maxpkt equ '~'-' '+2O ;Maximum size of a packet.
maxtry equ 05O  ;Default number of retries on a packet.
imxtry equ 20O  ;Default number of retries send initiate.
drpsiz equ 5EH  ;Default receive packet size.
dspsiz equ 20H  ;Default send packet size.
dstime equ 0AH  ;Default send time out interval.
drtime equ 05H  ;Default receive time out interval.
dspad equ 00H  ;Default send padding.
drpad equ 00H  ;Default receive padding.
dspadc equ 00H  ;Default send padding char.
drpadc equ 00H  ;Default receive padding char.
dseol equ cr  ;Default send EOL char.
dreol equ cr  ;Default receive EOL char.
dsquot equ '#'  ;Default send quote char.
drquot equ '#'  ;Default receive quote char.
dqbin equ '&'  ;Default 8th-bit quote char. ;[29g]

bufsiz equ 80H  ;Size of DMA.

; A few control characters

ctlc equ 03H  ;[19f]
ctlx equ 18H  ;[19f]
ctlz equ 1AH  ;[19f]

 DSEG $

; Program storage.

belflg db 1  ;Use bell.
incmod db 0  ;Insert Character mode.
hierr db 0  ;Non-ascii char (non-zero if yes).
parflg db defpar  ;Parity flag (default none.)
flwflg db 0  ;File warning flag (default off).
ibmflg db 0  ;IBM flag (default off).
vmeflg db 0  ;VME flag (default off);
incnt dw 0  ;Number of chars read in from port.
pktptr dw 0  ;Position in receive packet.
spsiz db dspsiz  ;Send packet size.
rpsiz db drpsiz  ;Receive packet size.
stime db dstime  ;Send time out.
rtime db drtime  ;Receive time out.
spad db dspad  ;Send padding.
rpad db drpad  ;Receive padding.
spadch db dspadc  ;Send padding char.
rpadch db drpadc  ;Receive padding char.
seol db dseol  ;Send EOL char.
reol db dreol  ;Receive EOL char.
squote db dsquot  ;Send quote char.
rquote db drquot  ;Receive quote char.
ebquot db 'Y'  ;Send 8th-bit quote char.  ;[29g]
pktnum dw 0  ;Packet number.
numpkt dw 0  ;Total number of packets sent.
numrtr dw 0  ;Total number of retries.
numtry db 0  ;Number of tries on this packet.
oldtry db 0  ;Number of tries on previous packet.
cxzflg db 0  ;[19f] flag for ^X/^Z file interruption
state db 0  ;Present state of the automaton.
packet rb 4  ;Packet (data is part of it).
data rb 60H  ;Data and checksum field of packet. ;[29d]
recpkt rb 100H  ;Receive packet storage.  ;[29d]
temp dw 0
argblk dw 0  ;For subroutine arguments.
argbk1 dw 0
argbk2 dw 0
argbk3 dw 0
tickst dw 10*(7470/clckrt);[19g] "magic" formula for loops/tick
    ;[19g] CLCKRT is the system clock rate ...
    ;[19g] ... defined in KERIO
ticklp dw 0  ;[19g] Dynamic loop count for tick
tickct db dstime  ;[19g] # ticks for timeout to occur
tmodon db 0  ;[19g] flag for time-out message

 CSEG $

; Check for a user abort or interrupt during the send or receive ;[12] begin
;   modified by Rg.
kabort:
 call dbinst  ;Check if a char has been typed.
  jmp r  ;[19f]  Merrily return.
 call dbin  ;Get the character.
 cmp al, ctlc ;Abort if control-C [19f]
 je kabrt2
 cmp al, ctlx ;[19f] Is it control-x ?
 je kabrta  ;[19f] yes
 cmp al, ctlz ;[19f] control-z ?
 jne kabrtb  ;[19f] no
kabrta: add al, 100O ;[19f] make into 'X' or 'Z'
 mov cxzflg, al ;[19f] set the flag
 call intmsg  ;[19f] reassure the user
 ret   ;[19f] return
kabrtb: cmp al, cr  ;if <cr> do a timeout  ;[29a] begin
 jne kabrtc
 pop bx  ;return of kabort
 pop bx  ;receive packet pointer
    ;... next is return of inpkt
 ret   ;return to timeout routine ;[29a] end
kabrtc: cmp al, escchr ;or the escape character followed by 'C'.
 jne kabrt9
 mov cx, 1000 ;Only wait just so long for the next char.
kabrt1: push cx
 call dbin
 pop cx
 cmp al, 0
 loope kabrt1
 cmp al, ctlc ;Control-C also works here. [19f]
 je kabrt2
 and al, 137O ;Capitalize it.
 cmp al, 'C'  ;If not a 'C' then continue.
 jne kabrt9
kabrt2: cmp tkflg, 0 ;Close any command file in use ;[29b] begin
 je kabrt3
 call tkcls      ;[29b] end
kabrt3: call binst  ;Clear out extraneous input ;[29a] begin
  jmp kabrt4 ;If none, go abort this transmission.
 call bin
 jmps kabrt2
kabrt4: pop bx  ;return from kabort
 pop bx  ;receive packet pointer
 pop bx  ;return from inpkt
 pop bx  ;return from rpack
    ;... next is return from r??? to read
 mov dx, offset erms21 ;Print '* aborted *' message.
 jmp fatal      ;[29a] end
kabrt9: mov dl, bell ;beep    [19f]
 call dbout  ;    [19f]
 ret   ;return no error  [19f]


; Send the generic finish command to the remote Kermit.

finsen: mov ah, 'F'  ;Ask for the finish command.
 call gensen
 ret


; Send the generic logout command to the remote Kermit.

byesen: mov ah, 'L'  ;Ask for the logout command.
 call gensen
 ret


; This procedure processes all the generic single packet sends.

gensen: mov temp, ax ;Save the specific generic command.
 mov numtry, 0 ;Initialize count.
 call cfibf  ;Clear out input buffer of extra NAKs.
fins1: mov ah, numtry
 cmp ah, maxtry ;Too many times?
 js fins3  ;Nope, try it.
fins2: mov dx, offset erms18
 call tcrmsg
 ret

fins3: inc numtry  ;Increment number of tries.
 mov argblk, 0 ;Packet number zero.
 mov argbk1, 1 ;One piece of data.
 mov bx, offset data
 mov ax, temp ;Get the generic command.
 mov [bx], ah ;Finish running Kermit.
 mov ah, 'G'  ;Generic command packet.
 call spack
  jmp fins2 ;Tell user and die.
 call rpack  ;Get ACK.
  jmp fins1  ;Go try again.
 cmp ah, 'Y'  ;Got an ACK?
 jnz fins4
 ret   ;Yes, then we're done.
fins4: cmp ah, 'E'  ;Error packet?
 jnz fins1  ;Try sending it again.
 call error1
 ret

; Packet routines

; Send_Packet
; This routine assembles a packet from the arguments given and sends it
; to the host.
;
; Expects the following:
; AH     - Type of packet (D,Y,N,S,R,E,F,Z,T)
; ARGBLK - Packet sequence number
; ARGBK1 - Number of data characters
;
; Returns: +1 always

spack: push ax  ;Save the packet type.
 mov bx, offset packet ;Get address of the send packet.
 mov ah, soh  ;Get the start of header char.
 mov [bx], ah ;Put in the packet.
 inc bx  ;Point to next char.
 mov ax, argbk1 ;Get the number of data chars.
 xchg ah, al
 add ah, ' '+3 ;Real packet character count made printable.
 mov [bx], ah ;Put in the packet.
 inc bx  ;Point to next char.
 mov cl, ah  ;Start the checksum.
 mov ax, argblk ;Get the packet number.
 xchg ah, al
 add ah, ' '  ;Add a space so the number is printable.
 mov [bx], ah ;Put in the packet.
 inc bx  ;Point to next char.
 add cl, ah  ;Add the packet number to the checksum.
 pop ax  ;Get the packet type.
 mov [bx], ah ;Put in the packet.
 inc bx  ;Point to next char.
 add cl, ah  ;Add the type to the checksum.
 mov dx, argbk1 ;Get the packet size.
spack2: cmp dx, 0  ;Are there any chars of data?
  jz spack3  ; No, finish up.
 dec dx  ;Decrement the char count.
 mov ah, [bx] ;Get the next char.
 inc bx  ;Point to next char.
 add cl, ah  ;Add the char to the checksum.
 jmp spack2  ;Go try again.
spack3:
sp3x: mov ah, cl  ;Get the character total.
 mov ch, cl  ;Save here too (need 'cl' for shift).
 and ah, 0C0H ;Turn off all but the two high order bits.
 mov cl, 6
 shr ah, cl  ;Shift them into the low order position.
 mov cl, ch
 add ah, cl  ;Add it to the old bits.
 and ah, 3FH  ;Turn off the two high order bits.  (MOD 64)
 add ah, ' '  ;Add a space so the number is printable.
 mov [bx], ah ;Put in the packet.
 inc bx  ;Point to next char.
 mov ah, seol ;Get the EOL the other host wants.
 mov [bx], ah ;Put in the packet.
 inc bx  ;Point to next char.
 mov ah, 0  ;Get a null.
 mov [bx], ah ;Put in the packet.
 cmp debug, 0 ;debug mode.
 je spack4
 inc bx
 mov ah, '$'
 mov [bx], ah
 mov dx, offset scrsp ;Print string to move cursor.
 call poscur      ;[30c] begin
 call clreol  ;Clear current line
 mov dl, lf  ;  and next one
 call bout
 call clreol
 mov dx, offset scrsp ;Print string to move cursor.
 call poscur      ;[30c] end
 mov dx, offset spmes
 call tmsg
 mov dx, offset packet
 call tmsg  ;Debug end.
spack4: call outpkt  ;Call the system dependent routine.
 jmp rskp


; Write out a packet.

outpkt: mov dh, spad ;Get the number of padding chars.
outpk2: dec dh
 cmp dh, 0
 jl outpk3  ;If none left proceed.
 mov al, spadch ;Get the padding char.
 call prtout  ;Output it.
 jmp outpk2
outpk3: mov bx, offset packet ;Point to the packet.
outlup: mov al, [bx] ;Get the next character.
 cmp al, 0  ;Is it a null?
 jnz outlp2
 ret
outlp2: call prtout  ;Output the character.
 inc bx  ;Increment the char pointer.
 jmp outlup


; Receive_Packet
; This routine waits for a packet arrive from the host.

rpack: call inpkt  ;Read up to a carriage return.
  jmp rptimo  ; User timed us out.
rpack0: call getchr  ;Get a character.
  jmp rpack  ; Hit the carriage return, go try again.
 cmp ah, soh  ;Is the char the start of header char?
  jne rpack0  ; No, go until it is.
rpack1: call getchr  ;Get a character.
  jmp r  ; Hit the carriage return, return bad.
 cmp ah, soh  ;Is the char the start of header char?
  jz rpack1  ; Yes, then go start over.
 mov cl, ah  ;Start the checksum.
 sub ah, ' '+3 ;Get the real data count.
 mov dh, ah  ;Save it for later.
 mov al, ah  ;Swap halves.
 mov ah, 0
 mov argbk1, ax ;Save the data count.
 call getchr  ;Get a character.
  jmp r  ; Hit the carriage return, return bad.
 cmp ah, soh  ;Is the char the start of header char?
  jz rpack1  ; Yes, then go start over.
 add cl, ah  ;Add it to the checksum.
 sub ah, ' '  ;Get the real packet number.
 mov al, ah  ;Swap halves.
 mov ah, 0
 mov argblk, ax ;Save the packet number.
 call getchr  ;Get a character.
  jmp r  ; Hit the carriage return, return bad.
 cmp ah, soh  ;Is the char the start of header char?
  jz rpack1  ; Yes, then go start over.
 mov temp, ax ;Save the message type.
 ; CGL mod for VME packet echo
 cmp ah, packet + 3       ; compare with the type of this received one
 je rpack           ; if the same go back and get next
 ; end of CGL mod
 add cl, ah  ;Add it to the checksum.
 mov bx, offset data ;Point to the data buffer.
rpack2: dec dh  ;Any data characters?
  js rpack3  ; If not go get the checksum.
 call getchr  ;Get a character.
  jmp r  ; Hit the carriage return, return bad.
 cmp ah, soh  ;Is the char the start of header char?
  jz rpack1  ; Yes, then go start over.
 mov [bx], ah ;Put the char into the packet.
 inc bx  ;Point to the next character.
 add cl, ah  ;Add it to the checksum.
 jmp rpack2  ;Go get another.
rpack3: call getchr  ;Get a character.
  jmp r  ; Hit the carriage return, return bad.
 cmp ah, soh  ;Is the char the start of header char?
  jz rpack1  ; Yes, then go start over.
 sub ah, ' '  ;Turn the char back into a number.
 mov dh, cl  ;Get the character total.
 and dh, 0C0H ;Turn off all but the two high order bits.
 mov ch, cl
 mov cl, 6
 shr dh, cl  ;Shift them into the low order position.
 mov cl, ch
 add dh, cl  ;Add it to the old bits.
 and dh, 3FH  ;Turn off the two high order bits.  (MOD 64)
 cmp dh, ah  ;Are they equal?
  jz rpack4  ; If so finish up.
 call cfibf  ;Clear out any other characters on the line.
 ret   ;And return failure.

rpack4: mov ah, 0
 mov [bx], ah ;Put a null at the end of the data.
 mov ax, temp ;Get the type.
 call cfibf  ;Clear out any other characters on the line.
 jmp rskp

rptimo: call cfibf  ;On a time out clear out any remaining chars.
 ret

inpkt: mov bx, offset recpkt ;Point to the beginning of the packet.
 mov incnt, 0
inpkt2: mov al, stime ;[19g] set up timeout loop.
 mov tickct, al ;[19g]
 mov ax, tickst ;[19g] "magic" number loops/tick
 mov ticklp, ax ;[19g]
inpkta: cmp tmrflg, 0 ;Is timeout disabled?   ;[21a]
 je inpktb  ;  If so, skip tick routine  ;[21a]
 dec ticklp  ;[19g]
 cmp ticklp, 0 ;[19g] done this tick?
 jne inpktb  ;[19g] not yet
 mov ax, tickst ;[19g] reload loop count
 mov ticklp, ax ;[19g]
 call ticmsg  ;[19g] beep for debugging
 dec tickct  ;[19g]
 cmp tickct, 0 ;[19g] timed-out?
 jne inpktb  ;[19g] not yet
 call tmomsg  ;[19g] alert user
 jmp inpkt9  ;[19g] go time-out
inpktb: push bx  ;[19g][12] bx gets trashed by kb status check.
 call kabort  ;[19g] Doesn't return if user aborted.
 pop bx  ;[19g]
 call instat  ;Any char there?
  jmp inpkta  ; Go until there is one.
 call inchr  ;Get the character.
 mov [bx], al ;Put the char in the packet.
 inc bx
 inc incnt
 cmp incnt, length recpkt ;Have we overrun the input buffer? ;[29d]
 jb inpktc
 jmp inpkt  ;If so, just clear and restart
inpktc: cmp al, reol ;Is it the EOL char?  ;[29d] end
 jne inpkt2  ;If not loop for another.
 cmp incnt, 1 ;Ignore bare CR.
 jne inpkt6
 jmp  inpkt
inpkt6: cmp ibmflg, 0 ;Is this the (dumb) IBM mainframe?
 jz inpkt4  ;If not then proceed.
inpkt5: cmp state, 'S' ;Check if this is the Send-Init packet.
 jz inpkt4  ;If so don't wait for the XON.
inpkt3: call instat  ;Wait for the turn around char.
  jmp inpkt3
 call inchr
 cmp al, xon  ;Is it the IBM turn around character?
 jne inpkt3  ;If not, go until it is.
inpkt4: cmp debug, 0
 jz inpkt7  ;If not debugging don't print the packet.
 mov al, '$'  ;Get a dollar sign.
 mov [bx], al ;Put in the packet.
 inc bx  ;Point to next char.
 mov dx, offset scrrp ;Print the packet.
 call poscur      ;[30c] begin
 call clreol  ;Clear current line
 mov dl, lf  ;  and next one
 call bout
 call clreol
 mov dx, offset scrrp ;Print string to move cursor.
 call poscur      ;[30c] end
 mov dx, offset rpmes
 call tmsg
 mov dx, offset recpkt
 call tmsg

inpkt7: mov bx, offset recpkt
 mov pktptr, bx ;Save the packet pointer.
 call tmoclr  ;[19g] Clear timeout message
 jmp rskp  ;If so we are done.

inpkt9: ret   ;return failure on time out.


getchr: push bx
 mov bx, pktptr ;Get the packet pointer.
 mov ah, [bx] ;Get the char.
 inc bx
 mov pktptr, bx
 pop bx  ;Restore BX.
 cmp ah, reol ;Is it the EOL char?
 jne getcr2  ;If not return retskp.
 ret   ;If so return failure.
getcr2: jmp rskp


;This is where we go if we get an error packet.  A call to ERROR
; positions the cursor and prints the message.  A call to ERROR1
; just prints a CRLF and then the message.

error: mov state, 'A' ;Set the state to abort.
 mov dx, offset screrr
 call poscur      ;[30c]
 jmp error2
error1: call tcrlf
error2: mov bx, offset data ;Get to the string.
 add bx, argbk1 ;Add the length of the data.
 mov ah, '$'  ;Put a dollar sign at the end.
 mov [bx], ah
 mov dx, offset data ;Print the error message.
 call tmsg
 ret

; Jump here if we die during a transfer.  Print the error message in
; DX and abort.

fatal: push dx  ;Save the error message.
 mov dx, offset screrr
 call poscur      ;[30c]
 pop dx
 call tmsg
 jmp abort  ;Change the state to abort.

; Print the status message in DX, ring the bell and position of the prompt.

fnstat: push dx
 mov dx, offset scrst ;Print string to move cursor.
 call poscur      ;[30c]
 pop dx
 call tmsg
 mov dx, offset ender ;Ring them bells.
 call tmsg
 mov dx, offset scrhlp ;[19f] Cursor position
 call poscur      ;[30c]
 call clreol  ;[19f] Clear help line  ;[30c]
 mov dx, offset scrrpr ;Put cursor back
 call poscur      ;[30c]
 ret


; This routine sets up the data for init packet (either the
; Send_init or ACK packet).

rpar: mov ah, rpsiz ;Get the receive packet size.
 add ah, ' '  ;Add a space to make it printable.
 mov [bx], ah ;Put it in the packet.
 mov ah, rtime ;Get the receive packet time out.
 add ah, ' '  ;Add a space.
 mov 1[bx], ah ;Put it in the packet.
 mov ah, rpad ;Get the number of padding chars.
 add ah, ' '
 mov 2[bx], ah ;Put it in the packet.
 mov ah, rpadch ;Get the padding char.
 add ah, 100O ;Uncontrol it.
 and ah, 7FH
 mov 3[bx], ah ;Put it in the packet.
 mov ah, reol ;Get the EOL char.
 add ah, ' '
 mov 4[bx], ah ;Put it in the packet.
 mov ah, rquote ;Get the quote char.
 mov 5[bx], ah ;Put it in the packet.
 mov ah, ebquot ;Get 8th-bit quote char ;[29g] begin
 mov 6[bx], ah ;Put it in the packet.
 mov ah, '1'  ;Set single character check type
 mov 7[bx], ah ;Put it in the packet.
 mov ah, ' '  ;Set no repeat prefix
 mov 8[bx], ah ;Put it in the packet.
 mov ah, 0  ;Initialize capability byte
 cmp tmrflg, 0 ;Are we able to time out?
 je rpar1  ;  No, leave bit set to zero
 or ah, 20h  ;  Otherwise set timeout capability flag
rpar1: add ah, ' '  ;Add space to make it printable
 mov 9[bx], ah ;Put it in the packet.
 mov ah, 10  ;Ten pieces of data.  ;[29g] end
 ret

; This routine reads in all the send_init packet information.

spar: push cx  ;Save CX.
 mov cx, ax  ;Save the number of arguments.
 mov ah, [bx] ;Get the max packet size.
 sub ah, ' '  ;Subtract a space.
 mov spsiz, ah ;Save it.
 mov ax, cx  ;[19g]
 cmp al, 2  ;[19g] Fewer than two pieces?
 jge spar1  ;[19g]    ;[29g]
 jmp sparx1      ;[29g]
spar1: mov ah, 1[bx] ;[19g] Get the timeout value
 sub ah, ' '  ;[19g]
 mov stime, ah ;[19g] save it.
 mov ax, cx
 cmp al, 3  ;Fewer than three pieces?
 jge spar2      ;[29g]
 jmp sparx2      ;[29g]
spar2: mov ah, 2[bx] ;Get the number of padding chars.
 sub ah, ' '
 mov spad, ah
 mov ax, cx
 cmp al, 4  ;Fewer than four pieces?
 jge spar3      ;[29g]
 jmp sparx3      ;[29g]
spar3: mov ah, 3[bx] ;Get the padding char.
 add ah, 100O ;Re-controlify it.
 and ah, 7FH
 mov spadch, ah
 mov ax, cx
 cmp al, 5  ;Fewer than five pieces?
 jge spar4      ;[29g]
 jmp sparx4      ;[29g]
spar4: mov ah, 4[bx] ;Get the EOL char.
 sub ah, ' '
 mov seol, ah
 mov ax, cx
 cmp al, 6  ;Fewer than six pieces?
 jge spar5      ;[29g]
 jmp sparx5      ;[29g]
spar5: mov ah, 5[bx] ;Get the quote char.
 mov squote, ah
 mov ax, cx      ;[29g] begin
 cmp al, 7  ;Fewer than seven pieces?
 jge spar6
 jmp sparx6
spar6: mov ah, 6[bx] ;Get the 8th-bit quote char.
 call doquo  ;Set the quote character.
 jmp sparxx
sparx1: mov stime, dstime ;Default timeout interval
sparx2: mov spad, dspad ;Default number of padding chars
sparx3: mov spadch, dspadc ;Default pad character
sparx4: mov seol, dseol ;Default eol character
sparx5: mov squote, dsquot ;Default send quote character.
sparx6: mov ebquot, 'N' ;No 8th bit quoting.
sparxx: pop cx      ;[29g] end
 ret   ;If so we are done.

; Set 8-bit quote character based on my capabilities  ;[29g] begin
; and the other Kermit's request.

doquo: cmp ebquot,'N' ;Can I do 8-bit quoting at all?
 je dq2  ;No - so forget it.
 cmp ebquot,'Y' ;Can I do it if requested?
 jne dq0  ;No - it's a must that I do it.
 mov ebquot,ah ;Do whatever he wants.
 jmp dq2
dq0: cmp ah,'Y'  ;I need quoting - can he do it?
 je dq2  ;Yes - then all is settled.
 cmp ah,'N'  ;No - then don't quote.
 je dq1
 cmp ah,ebquot ;Both need quoting - chars must match.
 je dq2
 mov ah,'N'
dq1: mov ebquot,ah
dq2: mov ah,ebquot
 cmp ah,rquote ;Same prefix?
 je dq3  ;Not allowed, so don't do quoting.
 cmp ah,squote ;Same prefix here?
 je dq3  ;This is illegal too.
 ret
dq3: mov ebquot,'N' ;Quoting will not be done.
 ret       ;[29g] end

; These are some utility routines.

; Increment the packet number.

incpkt: inc ax  ;Increment it.
 and ax, 3FH  ;Turn off the two high order bits.
 mov pktnum, ax ;Save modulo 64 of the number.
 inc numpkt  ;Increment the number of packets.
 ret

; Check if the packet number is the present packet.

chkpeq: mov ax, argblk ;Get the packet number.
 cmp ax, pktnum ;Is it the right packet number?
 je chkpe2
 ret   ;No.
chkpe2: jmp rskp  ;Yes.

; Is the packet number one more than present.

chkpom: mov ax, pktnum ;Get the present packet number.
 inc ax  ;Increment.
 and ax, 03FH ;Account for wraparound.
 cmp ax, argblk ;Is the packet's number one more than now?
 jz chkpm2  ;Yes, success.
 ret
chkpm2: jmp rskp

; Check if the packet number is the previous packet.

chkpol: inc oldtry  ;Save the updated number of tries.
 mov ax, pktnum ;Get the present packet number.
 cmp ax, 0  ;Had we wrapped around?
 jne chkpl2
 mov ax, 64
chkpl2: dec ax  ;Decrement.
 cmp ax, argblk ;Is the packet's number one less than now?
 je chkpl3
 jmp rskp
chkpl3: ret

; Abort

abort: mov argblk, 0 ;packet number 0  ;[29a] begin
 mov argbk1, 0 ;no data
 mov ah, 'E'  ;error type
 call spack
  jmp $       ;[29a] end
 mov state, 'A' ;Otherwise abort.
 ret

; ACK the packet.

uupack: call updat  ;[19f] entry which doesn't zero argbk1
 jmp ack1  ;[19f]
upack: call updat  ;Update the number of tries.
ack: mov argbk1, 0 ;No data.  (Packet number already in argblk).
ack1: mov ah, 'Y'  ;Acknowledge packet.
 call spack  ;Send the packet.
  jmp r
 jmp rskp

updat: mov ah, numtry ;Get the number of tries.
 mov oldtry, ah ;Save it.
 mov numtry, 0 ;Reset the number of tries.
 ret

; Re-ACK the previous packet.

reack: call nretry  ;Increment and print the number of retries.
 mov numtry, 0 ;Reset the number of tries.
 mov ah, 'Y'  ;Acknowledge packet.
 call spack  ;Send the packet.
  jmp r
 jmp rskp

; NAK that packet.

nak: mov ax, pktnum ;Get the packet number we're waiting for.
 mov argblk, ax
 mov argbk1, 0
 mov ah, 'N'  ;NAK that packet.
 call spack
  jmp abort
 call nretry  ;Increment and print the number of retries.
 ret   ;Go around again.

; Print the number of retries.

nretry: inc numrtr  ;Increment the number of retries.
pretry: mov dx, offset scrnrt
 call poscur      ;[30c]
 mov ax, numrtr
 call nout  ;Write the number of retries.
 ret

; Print the number of packets.

pnmpkt: mov dx, offset scrnp ;Print string to move cursor.
 call poscur      ;[30c]
 mov ax, numpkt
 call nout  ;Write the number of packets.
 ret

; This routine prints the number in AX on the screen.

nout: push ax  ;save all registers  ;[14] begin
 push bx
 push cx
 push dx
 mov cx, 0  ;number of digits to print
 mov bx, 10  ;radix to use for dividing
nout1: mov dx, 0  ;clear high word of dividend
 div bx  ;divide dx:ax by 10
 push dx  ;push this remainder
 inc cx  ;count it
 cmp ax, 0  ;anything left in the quotient?
 jne nout1  ;   if so, keep dividing
nout2: pop dx  ;get a digit
 add dl, '0'  ;make it ascii
 push cx  ;save our count
 call bout  ;print the digit
 pop cx  ;repeat until all digits printed
 loop nout2
 pop dx  ;restore all registers
 pop cx
 pop bx
 pop ax
 ret

; Initialize file buffers and paint screen.

init:
 call dspver  ;Clear screen and display version header
 mov dx, offset scrnp ;Position to packet location
 call poscur
 mov dx, offset pktlin      ;[20a]
 call tmsg
 mov dx, offset scrhlp ;[19f] Cursor position
 call poscur  ;[19f] for help line   ;[30c]
 call revon  ;Bottom line reverse   ;[30c]
 mov dx, offset infm10 ;[19f] Help for file transfer
 call tmsg  ;[19f]
 call revoff       ;[30c]

init1: mov chrcnt, bufsiz ;Number of chars left.
 mov bufpnt, offset dma ;Addr for beginning.
 ret


;  Clear out the old filename on the screen.

clrfln: mov dx, offset scrfr ;Move cursor to file rename. ;[31] begin
 call poscur
 call clreol  ;Clear to EOL.   ;[31] end
 mov dx, offset scrfln ;Move cursor to file name position.
 call poscur      ;[30c]
 call clreol  ;Clear to EOL.   ;[30c]
 ret

; acknowledge ^X/^Z with a message  [19f] start
intmsg: mov dx, offset scrint ;position info
 call poscur  ;output it   ;[30c]
 mov dx, offset infms8 ;File message
 cmp cxzflg, 'X' ;but first check
 je intm01  ;yes it was X
 mov dx, offset infms9 ;no it was 'Z' - file group.
 cmp cxzflg, 'Z' ;or was it?
 je intm01  ;yes - go output
 call clreol  ;anything else - clear line ;[30c]
 jmps intm02      ;[30c]
intm01: call tmsg  ;output it
intm02: ret   ;goodbye [19f] end

; let the time-out clock "tick" ;[19g] start
ticmsg: mov dl, bell ;output a ...
 call dbout  ;... beep!
 ret

; notify of time-out
tmomsg: mov dx, offset scrst ;cursor position
 call poscur      ;[30c]
 mov dx, offset timoms ;timeout message
 call tmsg
 mov tmodon, 1 ;flag for message
 ret

; clear time-out message
tmoclr: cmp tmodon, 0 ;message on screen?
 jne tmocl1  ;yes - go get it
 ret   ;nothing to clear - return
tmocl1: mov dx, offset scrst ;cursor position
 call poscur      ;[30c]
 call clreol  ;clear line   ;[30c]
 mov tmodon, 0 ;indicate line is clear
 ret   ;[19g] end

; RECEIVE command

read: call init  ;Paint the screen and initialize file buffers.
 call cfibf  ;Clear out any stacked NAKs.
read1: mov numpkt, 0 ;Set the number of packets to zero.
 mov numrtr, 0 ;Set the number of retries to zero.
 mov pktnum, 0 ;Set the packet number to zero.
 mov numtry, 0 ;Set the number of tries to zero.
 mov cxzflg, 0 ;[19f] reset ^X/^Z flag
 call pretry
 mov state, 'R' ;Set the state to receive initiate.
read2: call pnmpkt
 mov ah, state ;Get the state.
 cmp ah, 'D'  ;Are we in the data send state?
 jne read3
 call rdata
 jmp read2
read3: cmp ah, 'F'  ;Are we in the file receive state?
 jne read4
 call rfile  ;Call receive file.
 jmp read2
read4: cmp ah, 'R'  ;Are we in the receive initiate state?
 jne read5
 call rinit
 jmp read2
read5: cmp ah, 'C'  ;Are we in the receive complete state?
 jne read6
 mov dx, offset infms3 ;Plus a little cuteness.
 cmp cxzflg,0 ;[19f] an interruption?
 je read59  ;[19f] no - do normal thing
 mov dx, offset infms7 ;[19f] substitute 'interrupted' message.
read59: call fnstat
 ret
read6: mov dx, offset infms4 ;Plus a little cuteness.
 call fnstat
 ret


; Receive routines

; Receive init

rinit: mov ah, numtry ;Get the number of tries.
 cmp ah, imxtry ;Have we reached the maximum number of tries?
 jl rinit2
 mov dx, offset ermes7 ;Print this error and die.
 jmp fatal

rinit2: inc ah  ;Increment it.
 mov numtry, ah ;Save the updated number of tries.
 call rpack  ;Get a packet.
  jmp nak  ; Trashed packet: nak, retry.
 cmp ah, 'S'  ;Is it a send initiate packet?
 jne rinit3  ;If not see if its an error.
 mov ebquot, 'Y' ;Initialize my 8th-bit quote flag ;[29g]
 mov ah, numtry ;Get the number of tries.
 mov oldtry, ah ;Save it.
 mov numtry, 0 ;Reset the number of tries.
 mov ax, argblk ;Returned packet number.  (Synchronize them.)
 call incpkt  ;Increment the packet number.
 mov ax, argbk1 ;Get the number of arguments received.
 mov bx, offset data ;Get a pointer to the data.
 call spar  ;Get the data into the proper variables.
 mov bx, offset data ;Get a pointer to our data block.
 call rpar  ;Set up the receive parameters.
 xchg ah, al
 mov ah, 0
 mov argbk1, ax ;Store the returned number of arguments.
 call ack1  ;ACK the packet.
  jmp abort
 mov ah, 'F'  ;Set the state to file send.
 mov state, ah
 ret
rinit3: cmp ah, 'E'  ;Is it an error packet?
 jne rinit4
 call error
rinit4: jmp abort


; Receive file

rfile: cmp numtry, maxtry ;Have we reached the maximum number of tries?
 jl rfile1
 mov dx, offset ermes8 ;Print this error and die.
 jmp fatal

rfile1: inc numtry  ;Save the updated number of tries.
 call rpack  ;Get a packet.
  jmp nak  ; Trashed packet: nak, retry.

 cmp ah, 'S'  ;Is it a send initiate packet?
 jne rfile2  ; No, try next type.
 cmp oldtry, imxtry ;Have we reached the maximum number of tries?
 jl rfil12  ;If not proceed.
 mov dx, offset ermes7 ;Print this error and die.
 jmp fatal
rfil12: call chkpol  ;Check the packet number, is it right?
  jmp nak  ;No, NAK and try again.
 mov bx, offset data ;Get a pointer to our data block.
 call rpar  ;Set up the parameter information.
 xchg ah, al
 mov ah, 0
 mov argbk1, ax ;Save the number of arguments.
 call reack  ;Re-ACK the old packet.
  jmp abort
 ret

rfile2: cmp ah, 'Z'  ;Is it an EOF packet?
 jne rfile3  ; No, try next type.
 cmp oldtry, maxtry ;Have we reached the maximum number of tries?
 jl rfil21  ;If not proceed.
 mov dx, offset ermes9 ;Print this error and die.
 jmp fatal
rfil21: call chkpol  ;Check the packet number, is it right?
  jmp nak  ;No, NAK and try again.
 mov argbk1, 0 ;No data.
 call reack  ;Re-ACK the previous packet
  jmp abort
 ret

rfile3: cmp ah, 'F'  ;Start of file?
 jne rfile4
 call chkpeq  ;Packet numbers equal?
  jmp nak  ; No, NAK it and try again.
 call incpkt  ;Increment the number of packets.
 call gofil  ;Get a file to write to.
  jmp abort
 call init1  ;Initialize all the file buffers.
 call upack  ;Update counters and ACK the packet.
  jmp abort
 mov state, 'D' ;Set the state to data receive.
 ret

rfile4: cmp ah, 'B'  ;End of transmission?
 jne rfile5
 call chkpeq  ;Packet numbers equal?
  jmp nak  ; No, NAK it and try again.
 call ack  ;ACK the packet.
  jmp abort
 mov state, 'C' ;Set the state to complete.
 ret

rfile5: cmp ah, 'E'  ;Is it an error packet?
 jne rfile6
 call error
rfile6: jmp abort


; Receive data

rdata: cmp numtry, maxtry ;Get the number of tries.
 jl rdata1
 mov dx, offset erms10 ;Print this error and die.
 jmp fatal

rdata1: inc numtry  ;Save the updated number of tries.
 call rpack  ;Get a packet.
  jmp nak  ; Trashed packet: nak, retry.

 cmp ah, 'D'  ;Is it a data packet?
 je rdat11
 jmp rdata2  ; No, try next type.
rdat11: call chkpeq  ;Packet numbers equal?
  jmp rdat12  ; No, check if previous packet.
 call incpkt  ;Increment the number of packets.
 mov ax, argbk1 ;Get the length of the data.
 cmp cxzflg, 0 ;[19f] interrupt requested?
 jne rdat1a  ;[19f] yes - skip put to file.
 call ptchr
  jmp abort  ; Unable to write out chars;abort.
 call upack  ;ACK the packet.
  jmp abort
 ret
rdat1a: mov bx, offset data ;[19f] data location
 mov ah, cxzflg ;[19f] get the ^X/^Z flag
 mov [bx], ah ;[19f] stick it in the packet
 mov argbk1, 1 ;[19f] data length is 1
 call uupack  ;[19f] ACK the packet (without zeroing argbk1)
  jmp abort  ;[19f]
 ret   ;[19f]

rdat12: cmp oldtry, maxtry ;Have we reached the maximum number of tries?
 jl rdat13  ;If not proceed.
 mov dx, offset erms10 ;Print this error and die.
 jmp fatal
rdat13: call chkpol  ;Check the packet number, is it right?
  jmp nak  ;No, NAK it and try again.
 mov argbk1, 0 ;No data.
 call reack  ;Re-ACK the previous packet.
  jmp abort
 ret

rdata2: cmp ah, 'F'  ;Start of file?
 jne rdata3  ; No, try next type.
 cmp oldtry, maxtry ;Have we reached the maximum number of tries?
 jl rdat21  ;If not proceed.
 mov dx, offset ermes8 ;Print this error and die.
 jmp fatal
rdat21: call chkpol  ;Check the packet number, is it right?
  jmp nak  ; No, NAK it and try again.
 mov argbk1, 0 ;No data.
 call reack  ;Re-ACK the previous packet
  jmp abort
 ret

rdata3: cmp ah, 'Z'  ;Is it a EOF packet?
 je rdat32
 jmp rdata4  ;Try and see if its an error.
rdat32: call chkpeq  ;Packet numbers equal?
  jmp nak  ; No, NAK it and try again.
 call incpkt  ;Increment the packet number.
 cmp cxzflg, 0 ;This file interrupted? [19f] start
 jne rdat3a  ;yes jump
 cmp argbk1, 1 ;1 byte of data in EOF packet?
 jmp rdat3b  ;no - finish up file
 mov bx, offset data ;pointer to data
 mov ah, [bx] ;get the data
 cmp ah, 'D'  ;is it D as in Discard?
 jne rdat3b  ;no - finish writing file
rdat3a: mov dx, offset fcb ;get file's fcb
 call delete  ;delete the file.
 cmp cxzflg, 'X' ;Kill one file or batch?
 jne rdat37  ;whole batch - leave flag
 mov cxzflg, 0 ;clear flag
 call intmsg  ;clear message
 jmp rdat37  ;go to clean up.  [19f] end
rdat3b: mov bx, bufpnt ;Get the dma pointer.
 mov ax, 80H
 sub ax, chrcnt ;Get the number of chars left in the DMA.
 cmp ax, 80H
 jne rdat34
 call outbuf  ;Write out buffer if no room for ^Z.
  jmp abort
 jmp rdat36  ;Go close the file.
rdat34: mov cl, 'Z'-100O ;Put in a ^Z for EOF.
 mov [bx], cl
 inc ax
 dec chrcnt
 mov cx, chrcnt
 mov temp, cx
 inc bx
rdt3: inc ax
 cmp ax, 80H
 jg rdat35  ;Pad till full.
 mov cl, 1AH  ;Use control-Z's  ;[13]
 mov [bx], cl
 inc bx
 jmp rdt3
rdat35: call outbuf  ;Output the last buffer.
  jmp abort  ; Give up if the disk is full.
rdat36: mov dx, offset fcb
 call closf  ;Close up the file.
rdat37: call upack  ;ACK the packet.
  jmp abort
 mov state, 'F'
 ret

rdata4: cmp ah, 'E'  ;Is it an error packet.
 jne rdata5
 call error
rdata5: jmp abort


; Send a file.

send: cmp wldflg,0FFh
 je send12
 mov dircnt,0 ;If not wild, just use the name in the FCB
 mov dindex,0
 call getopn  ;Open the file
 jmps send19
send12: call getwld  ;If wild, get sorted list of matching names
  jmp r  ;  on error, message already printed
 mov dindex,0 ;Start at first one
 call getfil  ;Get and open first file
  jmp r
send19: call init  ;Paint the screen and initialize file buffers.
 call cfibf  ;Clear out any stacked NAKs.
 mov pktnum, 0 ;Set the packet number to zero.
 mov numtry, 0 ;Set the number of tries to zero.
 mov numpkt, 0 ;Set the number of packets to zero.
 mov numrtr, 0 ;Set the number of retries to zero.
 call pretry  ;Print the number of retries.
 mov state,'S' ;Set the state to receive initiate.
send2: call pnmpkt  ;Print the number of packets.
 cmp state, 'S' ;Are we in the send initiate state?
 jne send3
 call sinit
 jmp send2
send3: cmp state, 'F' ;Are we in the file send state?
 jne send4
 call sfile  ;Call send file.
 jmp send2
send4: cmp state, 'D' ;Are we in the data send state?
 jne send5
 call sdata
 jmp send2
send5: cmp state, 'Z' ;Are we in the EOF state?
 jne send6
 call seof
 jmp send2
send6: cmp state, 'B' ;Are we in the eot state?
 jne send7
 call seot
 jmp send2
send7: cmp state, 'C' ;Are we in the send complete state?
 jne send8
 mov dx, offset infms3 ;Plus a little cuteness.
 cmp cxzflg, 0 ;[19f] Interrupted?
 je send7a  ;[19f] no
 mov dx, offset infms7 ;[19f] substitute "interrupted" message
send7a: call fnstat
 ret
send8: mov dx, offset infms4 ;Plus a little cuteness.
 call fnstat
 ret


; Send routines

; Send initiate


sinit: cmp numtry, imxtry ;Have we reached the maximum number of tries?
 jl sinit2
 mov dx, offset erms14
 jmp fatal
sinit2: mov ah, 'Y'  ;Reset our quote capability ;[29g] begin
 cmp parflg, parnon ;If we have parity,
 je sini21  ;  send our quote preference
 mov ah, dqbin
sini21: mov ebquot, ah ;Set our quote capability ;[29g] end
 inc numtry  ;Save the updated number of tries.
 mov bx, offset data ;Get a pointer to our data block.
 call rpar  ;Set up the parameter information.
 xchg ah, al
 mov ah, 0
 mov argbk1, ax ;Save the number of arguments.
 mov ax, numpkt ;Get the packet number.
 mov argblk, ax
 mov ah, 'S'  ;Send initiate packet.
 call spack  ;Send the packet.
  jmp abort
 call rpack  ;Get a packet.
  jmp r  ; Trashed packet don't change state, retry.
 cmp ah, 'Y'  ;ACK?
 jne sinit3  ;If not try next.
 call chkpeq  ;Is it the right packet number?
  jmp nretry  ;Increment the retries and go try again.
 call incpkt  ;Increment the packet number.
 mov ax, argbk1 ;Get the number of pieces of data.
 mov bx, offset data ;Pointer to the data.
 call spar  ;Read in the data.
 mov ah, numtry ;Get the number of tries.
 mov oldtry, ah ;Save it.
 mov numtry, 0 ;Reset the number of tries.
 mov state, 'F' ;Set the state to file send.
 ret

sinit3: cmp ah, 'N'  ;NAK?
 jne sinit4  ;If not see if its an error.
 call nretry
 ret

sinit4: cmp ah, 'E'  ;Is it an error packet?
 jne sinit5
 call error
sinit5: jmp abort


; Send file header

sfile: cmp numtry, maxtry ;Have we reached the maximum number of tries?
 jl sfile1
 mov dx, offset erms14
 jmp fatal
sfile1: inc numtry  ;Increment it.
 mov cxzflg, 0 ;[19f] clear ^X/^Z flag
 push ds
 pop es
 mov di, offset data ;Get a pointer to our data block.
 mov si, offset fcb+1 ;Pointer to file name in FCB.
 mov cx,11
sfil11: cmp cx,3  ;Separate file type with period
 jne sfil12
 mov al,'.'
 stosb
sfil12: lodsb
 and al,7Fh  ;Strip off attribute bits
 cmp al,' '  ;Printable, nonspace characters only
 jbe sfil13
 cmp al,7Fh
 jae sfil13
 stosb
sfil13: loop sfil11
 mov byte ptr [di],'$' ;Terminate filename for printing
 sub di, offset data
 mov argbk1, di ;Save number of characters in name
 call clrfln
 mov dx, offset data ;Print file name.
 call tmsg

 mov ax, pktnum ;Get the packet number.
 mov argblk, ax
 mov ah, 'F'  ;File header packet.
 call spack  ;Send the packet.
  jmp abort
 call rpack  ;Get a packet.
  jmp r  ; Trashed packet don't change state, retry.
 cmp ah, 'Y'  ;ACK?
 jne sfile2  ;If not try next.
 call chkpeq  ;Packet number right.
  jmp nretry  ;Increment the retries and go try again.
sfil14: call incpkt  ;Increment the packet number.
 call updat  ;Update the number of tries.

sfil15: mov byte ptr fcb+20h,0 ;Set the record number to zero.
 mov eoflag, 0 ;Indicate not EOF.
 mov filflg, 0FFh ;Indicate file buffer empty.
 call gtchr
  jmp sfil16  ;Error go see if its EOF.
 jmp sfil18  ;Got the chars, proceed.
sfil16: cmp ah, 0FFH ;Is it EOF?
 je sfil17
 jmp abort  ;If not give up.
sfil17: mov state, 'Z' ;Set the state to EOF.
 ret
sfil18: mov siz, ax
 mov state, 'D' ;Set the state to data send.
 ret

sfile2: cmp ah, 'N'  ;NAK?
 jne sfile3  ;Try if error packet.
 call chkpom  ;Is the packet's number one more than now?
  jmp nretry  ;Increment the retries and go try again.
 jmp sfil14  ;If so, join the ACK.

sfile3: cmp ah, 'E'  ;Is it an error packet.
 jne sfile4
 call error
sfile4: jmp abort


; Send data

sdata: cmp cxzflg, 0 ;[19f] Interrupt flag on?
 je sdata0  ;[19f] no
 mov state, 'Z' ;[19f] yes - abort sending file
 ret
sdata0: cmp numtry, maxtry ;Have we reached the maximum number of tries?
 jl sdata1
 mov dx, offset erms14
 jmp fatal
sdata1: inc numtry  ;Increment it.
 mov dx, offset data ;Get a pointer to our data block.
 mov datptr, dx
 mov dx, offset filbuf ;Pointer to chars to be sent.
 mov cbfptr, dx
 mov cx, 1  ;First char.
sdat11: mov bx, cbfptr
 mov ah, [bx]
 inc cbfptr
 mov bx, datptr
 mov [bx], ah ;Put the char in the data packet.
 inc datptr  ;Save position in data packet.
 inc cx  ;Increment the count.
 cmp cx, siz  ;Have we transfered that many?
 jle sdat11  ;If not get another.
 mov ax, siz  ;Number of char in char buffer.
 mov argbk1, ax
 mov ax, pktnum ;Get the packet number.
 mov argblk, ax
 mov ah, 'D'  ;Data packet.
 call spack  ;Send the packet.
  jmp abort
 call rpack  ;Get a packet.
  jmp r  ; Trashed packet don't change state, retry.
 cmp ah, 'Y'  ;ACK?
 jne sdata2  ;If not try next.
 call chkpeq  ;Right packet number?
  jmp nretry  ;Increment the retries and go try again.
sdat12: call incpkt  ;Increment the packet number.
 call updat  ;Update the number of tries.
 cmp argbk1,1 ;1 byte of data there?  [19f] start
 jne sdt12b  ;no - go on
 mov bx, offset data ;pointer to data
 mov ah, [bx] ;get the data
 cmp ah, 'X'  ;an 'X'?
 je sdt12a  ;yes - go
 cmp ah, 'Z'  ;or a 'Z'
 je sdt12a  ;also go
 jmp sdt12b  ;neither one - go on
sdt12a: mov cxzflg, ah ;'X' or 'Z' - set the interrupt flag [19f] end
 ret
sdt12b: call gtchr
  jmp sdat13  ;Error go see if its EOF.
 mov siz, ax  ;Save the size of the data gotten.
 ret

sdat13: cmp ah, 0FFH ;Is it EOF?
 je sdat14
 jmp abort  ;If not give up.

sdat14: mov state, 'Z' ;Set the state to EOF.
 ret
sdata2: cmp ah, 'N'  ;NAK?
 jne sdata3  ;See if is an error packet.
 call chkpom  ;Is the packet's number one more than now?
  jmp nretry  ;Increment the retries and go try again.
 jmp sdat12

sdata3: cmp ah, 'E'  ;Is it an error packet.
 jne sdata4
 call error
sdata4: jmp abort


; Send EOF

seof: cmp numtry, maxtry ;Have we reached the maximum number of tries?
 jl seof1
 mov dx, offset erms14
 jmp fatal
seof1: inc numtry  ;Increment it.
 mov ax, pktnum ;Get the packet number.
 mov argblk, ax
 mov argbk1, 0 ;No data.
 cmp cxzflg, 0 ;[19f] interrupt flag set?
 je seof1a  ;[19f] no - go on
 mov bx, offset data ;[19f] point to data
 mov ah, 'D'  ;[19f] get 'D' as in Discard
 mov [bx], ah ;[19f] stuff it into packet
 mov argbk1, 1 ;[19f] set data length of 1
seof1a: mov ah, 'Z'  ;EOF packet.
 call spack  ;Send the packet.
 jmp abort
 call rpack  ;Get a packet.
  jmp r  ; Trashed packet don't change state, retry.
 cmp ah, 'Y'  ;ACK?
 jne seof2  ;If not try next.
 call chkpeq  ;Is it the right packet number?
  jmp nretry  ;Increment the retries and go try again.
seof12: call incpkt  ;Increment the packet number.
 call updat  ;Update the number of tries.
 mov dx, offset fcb ;Close the file.
 call closf
 call getfil  ;Get and open the next file.
  jmp seof13  ; No more.
 mov state, 'F' ;Set the state to file send.
 cmp cxzflg, 'X' ;[19f] 'X' in interrupt flag?
 mov cxzflg, 0 ;[19f] meantime reset it
 jne sef13a  ;[19f] no - go on
 call intmsg  ;[19f] clear interrupt message
sef13a: ret   ;[19f] goodbye
seof13: mov state, 'B' ;Set the state to EOT.
 ret

seof2: cmp ah, 'N'  ;NAK?
 jne seof3  ;Try and see if its an error packet.
 call chkpom  ;Is the packet's number one more than now?
  jmp nretry  ;Increment the retries and go try again.
 jmp seof12

seof3: cmp ah, 'E'  ;Is it an error packet?
 jne seof4
 call error
seof4: jmp abort


; Send EOT

seot: cmp numtry, maxtry ;Have we reached the maximum number of tries?
 jl seot1
 mov dx, offset erms14
 jmp fatal
seot1: inc numtry  ;Increment it.
 mov ax, pktnum ;Get the packet number.
 mov argblk, ax
 mov argbk1, 0 ;No data.
 mov ah, 'B'  ;EOF packet.
 call spack  ;Send the packet.
  jmp abort
 call rpack  ;Get a packet.
  jmp r  ;Trashed packet don't change state, retry.
 cmp ah, 'Y'  ;ACK?
 jne seot2  ;If not try next.
 call chkpeq  ;Is it the right packet number.
  jmp nretry  ;Increment the retries and go try again.
seot12: call incpkt  ;Increment the packet number.
 call updat  ;Update the number of tries.
 mov state, 'C' ;Set the state to file send.
 ret

seot2: cmp ah, 'N'  ;NAK?
 jne seot3  ;Is it error.
 call chkpom  ;Is the packet's number one more than now?
  jmp nretry  ;Increment the retries and go try again.
 jmp seot12

seot3: cmp ah, 'E'  ;Is it an error packet.
 jne seot4
 call error
seot4: jmp abort
<<< c86trm.a86 >>>
; * * * * * * * * * * * * * * * version 2.7 * * * * * * * * * * * * * * * *
; [fdc]	Print message about how to get help during connect.
; [29e]	Move terminal session logging to terminal module.
; * * * * * * * * * * * * * * * version 2.6 * * * * * * * * * * * * * * * *
; [24b revisited]	Fix handling of double-escape character
;	RonB, 03/22/84
; [26]
;  	Move TELNET stuff from KERMIT to a this module: KERTRM.
;	This is to modularize terminal emulation.
;
;  	integrate keyboard check in-line.  This allows keyboard check
;	between port checks to work without blowing the stack.
;
;	to improve speed, let the port-to-screen routine loop for a certain
;	number of characters before checking the keyboard.  If we
;	check every time,  we lose a few characters at 9600 baud 
;	without flow control.
;
;	Mar-1984.	R. Garland - Columbia University.
;
; [24]
; (a)   Add terminal session logging (KERMIT,KERSYS,KERUTL)
; (b)   Allow escape character to local-echo (KERMIT)
;	RonB, 03/15/84
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

	cseg	$		;resume code segment

; This is the CONNECT command.  It makes the local Kermit act as a terminal.
 
telnet:	mov	ah,cmcfm
	call	comnd		;Get a confirm.
	 jmp	r		; Didn't get a confirm.
	mov	dx, offset inms01 ;Confirmed, print informatory message.
	call	tcrmsg
	call	escprt
	mov	dx, offset inms02
	call	tmsg		;[fdc] Say how to get help during connect.
	call	escprt		;[fdc]
	mov	dx, offset inms25	;[fdc]
	call	tmsgcr		;[fdc]
	cmp	logflg, 0	;Is logging specified?		;[24a] begin
	je	telnt1
	call	logopn		;If so, open the log file
	 jmp	logerr						;[24a] end

telnt1:			;Check keyboard.
	mov	teloop, cx	;initialize loop counter
	call	dbinst		;Any chars out there?
	 jmp	telntx		; If not, check serial port
	call	dbin		;Yes, then get the char.
	cmp	al, escchr	;Is it an escape char?
	jz	intchr		;If so go process it.
telnty:	call	dopar		;Set parity (if any).		;[24b]
	mov	dx, ax
	call	prtout		;Output the char to the port.
	cmp	ecoflg, 0	;Is the echo flag turned on?
	jz	telnt2		;no - go on
	and 	dl, 7FH		;Turn off the parity bit.
	call 	dbout		;Echo the character.
telntx:	mov	cx, telnct	;load count for telnt2 loop
telnt2:			;Check serial port.
	mov	teloop, cx	;save loop count
	call	instat		;Any characters available?
	 jmp	telnt1		; No, check keyboard
	call	inchr		;Get the character.
	and	al, 7FH		;Strip off parity bit.
	mov	dl, al		;Get the character.
	call	dbout		;output to screen.
	mov	cx, teloop	;get loop counter
	loop	telnt2		;loop a bunch of characters before
				;checking keyboard - for speed.
	jmp	telnt1		;go back and check keyboard.

intchr:	call	dbin		;Get a char.
	cmp	al, 0		;Is the char a null?
	jz	intchr		;If so, go until we get a char.
	mov	bl, al		;Save the actual char.
	and	al, 137O	;Convert to upper case.
			;C = close
	cmp	al, 'C'		;Is it close?
	jne	intch0
	call	logcls		;Close the log file		;[24a]
	mov	dx, offset inms03
	call	tcmsgc		;return to micro message
	jmp	rskp		;return from telnet
			;B = break
intch0:	cmp	al,'B'		;[19e] Is it "send break"?
	jne	intch1		;[19e] no.
	call	prtbrk		;[19e] send a break.
	jmp	telnt2		;[19e]
			;escape character
intch1:	cmp	bl, escchr	;Is it the escape char?
	jne	intch2		;If not, go send a beep to the user.
	mov	al, bl		;If so, uncapitalize it		;[24b]
	jmp	telnty		;go on, we are done here.		;[24b]
			;? = help
intch2:	cmp	bl, '?'		;Is it a cry for help?			;[20c]
	jne	intch3
	mov	dx, offset inthlp	;If so, display help message on screen
	call	tcrmsg
	call	escprt
	mov	dx, offset inthl2
	call	tmsg
	call	escprt
	mov	dx, offset inthl3
	call	tmsgcr
	jmp	intchr		;Keep looking for escape character.	;[20c]
			;L = toggle logging
intch3:	cmp	al, 'L'		;Is it the logging toggle?	;[24a] begin
	jne	intch4
	cmp	logflg, 0	;Is logging now off?
	jne	intc32
intc31:	mov	logflg, 0FFh	;If so, set to 'on' value.
	call	logopn		;Open log file.
		jmp	logerr
	jmp	telnt2
intc32:	mov	logflg, 0
	call	logcls		;Close the log file.
	jmp	telnt2						;[24a] end
			;Q = quit logging			;[29e] begin
intch4:	cmp	al, 'Q'
	jne	intch5
	cmp	logflg, 0	;If logging currently enabled,
	jne	intc32		;  go set flag to 'off'
	jmp	telnt2
			;R = resume logging
intch5:	cmp	al, 'R'
	jne	intch6
	cmp	logflg, 0	;If logging currently disabled,
	je	intc31		;  go set flag to 'on'
	jmp	telnt2						;[29e] end
			;error = beep!
intch6:	mov	dl, bell	;Otherwise send a beep.
	call	dbout
	jmp	telnt2

logerr:	mov	logflg, 0	;Reset logging on file error	;[24a] begin
	call	logcls		;Make sure log file is closed
	mov	dx, offset erms22 ;Print an error message.
	call	tcrmsg
	mov	dx, offset lfcb
	call	tfile
	call	tcrlf
	jmp	rskp		;And keep doing whatever we were ;[24a] end

; These are the file handling routines for terminal session logging.
; LOGOPN opens the logfile in lfcb.

logopn:	mov	byte ptr lfcb+12,0
	mov	byte ptr lfcb+13,0
	mov	byte ptr lfcb+14,0
	mov	dx, offset lfcb
	call	gtjfn		;Is file already present?
	cmp	al, 0FFh
	jne	logo4		;If so, go seek to its end & open it.
	mov	bx, offset lfcb+1 ;Don't allow wild or blank name
	cmp	byte ptr [bx], ' '
	je	logo2
	cmp	wldflg, 0
	je	logo3
logo2:	cld
	push	ds
	pop	es
	mov	di, offset lfcb	;Make it "KERMIT.LOG" instead
	mov	si, offset lognam
	mov	cx, 12
	rep movsb
logo3:	mov	dx, offset lfcb
	call	create		;Otherwise create the file.
	mov	bx, offset lfcb
	mov	byte ptr 32[bx], 0	;Zero "CR" field
	mov	bx, offset dma
	cmp	al, 0FFh	;If no more directory space
	jne	logo9
	ret			;  then return an error

logo4:	cld
	push	ds
	pop	es
	mov	di, offset lfcb+1 ;Get the unambiguous filename
	mov	si, offset dma+1
	mov	cl, 5
	shl	al, cl
	mov	ah, 0
	add	si, ax
	mov	cx, 11
	rep movsb
	mov	dx, offset lfcb
	call	openf
	mov	dx, offset lfcb	;Get existing file size
	call	sizef
	mov	bx, offset lfcb
	cmp	byte ptr 35[bx], 0
	je	logo5		;If file is too full
	ret			;  then return an error
logo5:	cmp	word ptr 33[bx], 0	;Is the file totally empty?
	je	logo8		;  if so, don't look for control-Z.
	dec	word ptr 33[bx]	;Point to last record in file
	mov	dx, offset lfcb
	call	rinr		;Read random
	mov	bx, offset dma-1 ;Look for the terminating control-Z.
	mov	cx, 80h
logo6:	inc	bx
	cmp	byte ptr [bx], 'Z'-100O
	je	logo9
	loop	logo6
	mov	dx, offset lfcb
	call	soutr		;If no control-Z, then start a new buffer
logo8:	mov	bx, offset dma
logo9:	mov	bufpnt, bx	;Save current buffer location.
	mov	logfil, 0FFh	;Flag that file is open
	mov	dx, offset infm11 ;And display status on the terminal
	call	tmsg
	mov	dx, offset lfcb	;Including the file name
	call	tfile
	mov	dx, offset infm12
	call	tmsgcr
	jmp	rskp		;Return success.

; LOGCLS - closes the log file, but only if it was open

logcls:	cmp	logfil, 0	;Is file open?
	je	logc3		;No, just return.
	cmp	bufpnt, offset dma ;Do we have an empty buffer?
	je	logc2
	mov	bx, bufpnt	;If not, fill remainder with control-Z's
	mov	cx, offset DMA+80h
	sub	cx, bx
	mov	al, 'Z'-100O
logc1:	mov	[bx], al
	inc	bx
	loop	logc1
	mov	dx, offset lfcb	;Write out last buffer
	call	soutr
logc2:	mov	dx, offset lfcb	;Close the log file
	call	closf
	mov	dx, offset infm13 ;Tell user so.
	call	tcmsgc
	mov	logfil, 0	;Show file closed.
logc3:	ret							;[24a] end



;	data specific to terminal emulation

	dseg	$

telnct	dw	10		;count of characters to take from port
				;for the screen before checking keyboard.
teloop	dw	0		;active loop counter
logflg	db	deflog		;Is logging enabled?		;[24a] begin
logfil	db	0		;Is the log file open?
lognam	db	0,'KERMIT  LOG'	;Default log filename
lfcb	db	0,'KERMIT  LOG'	;Logging file control block
	rb	24						;[24a] end
<<< c86utl.a86 >>>
;* * * * * * * * * * * * * * * version 2.9 * * * * * * * * * * * * * * *
; [34c]	Make DMA initialization and buffer allocation global
; [34b]	Add LOCAL TYPE command
; [34a]	Fix bugs in directory listing file sizes
;* * * * * * * * * * * * * * * version 2.8 * * * * * * * * * * * * * * *
; [32d]	Add routines to get disk parameters for space calculation
; [32a]	Add routines to get and set default disk and user numbers
;* * * * * * * * * * * * * * * version 2.7 * * * * * * * * * * * * * * *
; [29c]  Clear FCB prior to opening or creating a file.
; [29b]  Add TAKE command file processing.
;	RonB, 04/08/84
;* * * * * * * * * * * * * * * version 2.6 * * * * * * * * * * * * * * *
;  [24a] Add terminal session logging
;	RonB, 03/15/84
; * * * * * * * * * * * * * * *  version 2.4  * * * * * * * * * * * * * * *
;  [20e] Add regular console status check, in addition to direct I/O check.
;	RonB,03/02/84
;  [19c] Give "bdos" mnemonic for vector 224.
; * * * * * * * * * * * * * * *  version 2.1  * * * * * * * * * * * * * * *
;  [7]	Do tab expansion and suppress nulls while in telnet mode.
;	RonB,12/24/83
; * * * * * * * * * * * * * * *  version 2.0  * * * * * * * * * * * * * * *

; BDOS command codes

bdos	equ	224
reset	EQU	00H
conin	EQU	01H
conout	EQU	02H
rdrin	EQU	03H
punout	EQU	04H
lstout	EQU	05H
dconio	EQU	06H
gtiob	EQU	07H
prstr	EQU	09H
consta	EQU	0BH
resetd	EQU	0DH						;[32a]
stdrv	EQU	0EH						;[32a]
opnfil	EQU	0FH
clsfil	EQU	10H
sfirst	EQU	11H
snext	EQU	12H
delf	EQU	13H
readf	EQU	14H
writef	EQU	15H
makef	EQU	16H
gtdrv	EQU	19H						;[32a]
gtalv	EQU	1BH						;[32d]
gtrov	EQU	1DH						;[32d]
statr	EQU	1EH
gtdpb	EQU	1FH						;[32d]
stusr	EQU	20H						;[32a]
readr	EQU	21H
writer	EQU	22H
cflsz	EQU	23H
dmaset	EQU	1AH
dmabas	EQU	33H
allocm	EQU	37H						;[32d]
freem	EQU	39H						;[32d]

	DSEG $						;[29b] begin

; TAKE command file processing data area.

tkfcb	db	0,'KERMIT  INI',0,0,0,0		;FCB with default name
	rb	19
tkbuf	rb	80h				;Input buffer
tkptr	dw	0				;Buffer pointer
tkflg	db	1				;Initially enable file input

; Messages and storage for directory operations

dirm01	db	'Drive $'					;[32d] begin
dirm02	db	', User $'
dirm03	db	'    K$'
dirm04	db	' : $'		;directory column separator
dirm05	db	' files, $'
dirm06	db	'K listed$'
dirm07	db	'  $'		;displayed at start of each row
eram01	db	tab,'Delete? $'
eram02	db	tab,'...not deleted$'
eram03	db	tab,'...deleted$'
eram04	db	' $'		;displayed at start of each row
erahlp	db	cr,lf,'Respond with ''Y'' to delete this file,'
	db	cr,lf,'             ''N'' to bypass this file,'
	db	cr,lf,'         or ''ESC'' to return to the Kermit-86> prompt.$'
spcm01	db	'K (out of $'
spcm02	db	'K) remaining on drive $'
morm01	db	'--more--$'
morm02	db	'Return: next line, Space: next page, ^X: next file, ^Z: quit$'
membuf	rb	5		;memory control block
dirbuf	rb	16
dindex	dw	0		;index to first entry in row
dlngth	dw	0		;length of directory list
dircnt	dw	0		;files in directory list
dirsiz	dw	0		;kbytes in directory list
remK	dw	0		;Kbytes remaining on disk
maxK	dw	0		;Kbytes available if disk was empty
DSM	dw	0		;drive maximum allocation blocks
KPB	dw	0		;drive Kbytes Per Block		;[32d] end

	CSEG $			;Resume coding segment.

tkst:	cmp	tkptr, 0	;Is file open yet?
	jne	tkst1
	call	tkopn
	 jmp	r		;If open failure
tkst1:	mov	bx, tkptr	;Do we need a new record?
	cmp	bx, offset tkbuf+80h
	jb	tkst2
	call	tkread
	mov	bx, tkptr
tkst2:	cmp	byte ptr [bx], 1Ah	;Are we at end of file?
	je	tkst3
	jmp	rskp		;If not, say we have a character.
tkst3:	call	tkcls		;  otherwise end the file
	jmp	r

tkin:	call	tkst		;If no file input,
	 jmp	bin		;  get it from the console.
	mov	bx, tkptr
	mov	al, [bx]	;Get character from command file record.
	inc	tkptr
	cmp	al, lf		;Ignore <lf> in file
	je	tkin
	push	ax
	mov	dl, al		;Echo it to the display.
	call	bout
	pop	ax
	ret

tkopn:	mov	dx, offset tkfcb ;Open the command file
	call	fcbzer		;zero fcb trailer
	mov	cl, opnfil
	int	bdos
	inc	al
	jz	tkopn1
	mov	tkptr, offset tkbuf+80h ;If success, show we need a read
	jmp	rskp
tkopn1:	mov	tkflg, 0	;On failure, turn off file input flag.
	ret

tkread:	mov	dx, offset tkbuf ;Set dma to our location.
	call	setdma
	mov	dx, offset tkfcb
	call	sinr		;Read a record.
	cmp	al, 0
	je	tkrd1
	mov	tkbuf, 1Ah	;If failure, load buffer with EOF.
tkrd1:	mov	tkptr, offset tkbuf	;Reinitialize buffer pointer.
	call	inidma		;Reset dma to normal.
	ret

tkcls:	mov	dx, offset tkfcb ;Close command file.
	call	closf
	mov	tkflg, 0	;Turn off file input flag.
	ret							;[29b] end


; Local directory operation

dirutl:	call	getwld		;fill buffer with matching names
	 jmp	r		;on failure a message has already been printed
	cmp	dircnt,0
	jne	locd3
	 ret
locd3:	mov	dirsiz,0
	mov	ax,dircnt
	dec	ax		;calculate number of entries in each column
	shr	ax,1		; = ((count-1)/4)+1
	shr	ax,1
	inc	ax
	mov	dlngth,ax
	call	tcrlf
	mov	dx, offset dirm01 ;print header: Drive X, User nn:
	call	tcrmsg
	mov	dl,fcb		;display the drive letter
	add	dl,'A'-1
	call	bout
	mov	dx, offset dirm02
	call	tmsg
	mov	al,defusr	;display the user number
	cbw
	call	nout
	mov	dl,':'
	call	bout
	mov	dindex,0	;table entry in first column
	mov	cx,dlngth
locd4a:	push	cx
	mov	dx, offset dirm07 ;Start new row
	call	tcrmsg
	mov	si,dindex
	mov	cx,4
locd4b:	push	si
	push	cx
	push	ds
	mov	ds,word ptr membuf
	mov	cl,4		;change entry number to table offset
	shl	si,cl
	inc	si		;skip user number
	mov	cx,8		;Eight characters in filename
locd5b:	lodsb			;get a filename character
	mov	dl,al
	push	cx
	call	bout		;print it
	pop	cx
	loop	locd5b
	mov	dl,'.'		;separate filename and type with a period
	call	bout
	mov	cx,3		;Three characters in file type
locd5c:	lodsb			;get a file type character
	mov	dl,al
	push	cx
	call	bout		;print it
	pop	cx
	loop	locd5c
	lodsw			;get file size
	pop	ds
	add	dirsiz,ax	;add filesize to listing size
	mov	di, offset dirm03 ;message for file size (____K)
	mov	cx,3
locd5d:	mov	byte ptr [di],' ' ;first blank out the last size
	inc	di
	loop	locd5d
	mov	bx,10
locd5e:	mov	dx,0		;fill in current size
	div	bx
	add	dl,'0'
	mov	[di],dl
	dec	di
	cmp	ax,0
	jne	locd5e
	mov	dx, offset dirm03 ;print size message
	call	tmsg
	pop	cx
	cmp	cx,1		;follow all but last column with separator
	je	locd5f
	mov	dx, offset dirm04 ;print column separator
	push	cx
	call	tmsg
	pop	cx
locd5f:	pop	si
	add	si,dlngth
	cmp	si,dircnt
	jae	locd6
	loop	locd4b
locd6:	inc	dindex
	pop	cx
	dec	cx
	jz	locd8
	jmp	locd4a
locd8:	call	tcrlf		;end the last row
	call	tcrlf		;add a blank row
	mov	ax,dircnt	;display file count
	call	nout
	mov	dx, offset dirm05
	call	tmsg
	mov	ax,dirsiz	;display total file size
	call	nout
	mov	dx, offset dirm06
	call	tmsg
	jmp	rskp


; Erase utility function

erautl:	call	getwld
	 jmp	r		;On error a message has already been printed
	mov	dindex,0
loce3:	mov	si,dindex
	cmp	si,dircnt
	jb	loce4
	jmp	loce9
loce4:	mov	cl,4
	shl	si,cl
	inc	si
	mov	di, offset fcb+1	;FCB already has drive code
	push	ds
	pop	es
	mov	ds, word ptr membuf
	mov	cx,15
	rep	movsb
	push	es
	pop	ds
	mov	dx, offset eram04	;Start new row
	call	tcrmsg
	mov	dx, offset fcb
	call	tfile
	mov	dx, offset eram01	;'<tab>Delete? '
	call	tmsg
loce5a:	call	dbinst			;clear out all typeahead
	 jmp	loce5b
	call	dbin
	jmps	loce5a
loce5b:	call	dbin			;get response
	cmp	al,0			;NUL is ignored
	je	loce5b
	cmp	al,cr			;CR bypasses file
	je	loce5d
	cmp	al,' '			;any nonprintable char aborts
	jb	loce8
	cmp	al,7Fh
	jae	loce8
	push	ax
	mov	dl,al			;echo the printable response
	call	bout
	pop	ax
	cmp	al,'?'			;request for help?
	jne	loce5c
	mov	dx, offset erahlp	;help message
	call	tcmsgc
	jmp	loce3
loce5c:	or	al,'a'-'A'		;convert to lower case
	cmp	al,'y'
	je	loce6
loce5d:	mov	dx, offset eram02	;'    ...not deleted'
	call	tmsg
	jmp	loce7
loce6:	cmp	byte ptr fcb+14,0	;make file read-write if necessary
	je	loce6a
	mov	dx, offset fcb
	call	setatr
loce6a:	mov	dx, offset fcb		;delete file
	call	delete
	mov	dx, offset eram03 	;'    ...deleted'
	call	tmsg
loce7:	inc	dindex
	jmp	loce3
loce8:	mov	dx, offset eram02	;'    ...not deleted' when aborting
	call	tmsg
loce9:	call	tcrlf
	jmp	rskp


; Type utility function

typutl:	call	getwld
	 jmp	r		;On error a message has already been printed
	mov	dindex,0
loct3:	mov	si,dindex		;Get next filename in table
	cmp	si,dircnt
	jb	loct4
	jmp	loct9
loct4:	mov	dlngth,1
	mov	cl,4
	shl	si,cl
	inc	si
	mov	di, offset fcb+1
	push	ds
	pop	es
	mov	ds, word ptr membuf
	mov	cx,15
	rep	movsb
	push	es
	pop	ds
	call	tcrlf			;Display the filename
	mov	dx, offset fcb
	call	tfile
	call	tcrlf
	mov	dx, offset fcb
	call	openf			;Open the file
	inc	al
	jnz	loct5
	jmp	loct6b
loct5:	mov	dx, offset fcb		;Read a record
	call	sinr
	cmp	al,0
	jne	loct6
	mov	bx,offset dma
loct5a:	mov	dl,[bx]			;Get a character
	cmp	dl,1Ah
	je	loct6
	and	dl,7Fh
	push	bx
	push	dx
	call	bout			;Print the character
	pop	dx
	cmp	dl,lf
	jne	loct5b
	inc	dlngth			;Count lf characters
	cmp	dlngth,22		;If more than 22, then pause for input
	jb	loct5b
	call	more
	 jmp	loct5c
loct5b:	pop	bx
	inc	bx
	cmp	bx,offset dma+128
	jb	loct5a
	jmps	loct5
loct5c:	pop	bx
	jmps	loct6a
loct6:	mov	ax,dindex		;EOF in file
	inc	ax
	cmp	ax,dircnt
	jae	loct6a
	call	tcrlf
	call	more
	 jmp	loct6a
loct6a:	mov	dx, offset fcb		;Close file
	call	closf
	call	tcrlf
loct6b:	inc	dindex			;Move on to next file
	jmp	loct3
loct9:	jmp	rskp


more:	call	revon
	mov	dx,offset morm01	;Show --more-- message
	call	tmsg
	call	revoff
more1:	call	dbin			;Get response
	cmp	al,0			; ignore nulls
	je	more1
	push	ax
	call	clrlin
	mov	dl,tab			;This is to fool CP/M so it
	call	bout			;  gets tab stops right
	mov	dl,cr
	call	bout
	pop	ax
	and	al,7Fh
	cmp	al,'?'			;? gives help
	jne	more2
	call	revon
	mov	dx,offset morm02	;Show help message
	call	tmsg
	call	revoff
	jmps	more1
more2:	cmp	al,cr			;cr, lf go to next line
	je	more4
	cmp	al,lf
	je	more4
	mov	dlngth,0		;Everything else resets line count
	cmp	al,0Fh			;^O, ^X go to next file
	je	more6
	cmp	al,18h
	je	more6
	cmp	al,03h			;^C, ^Z, q quit
	je	more5
	cmp	al,1Ah
	je	more5
	or	al,'a'-'A'
	cmp	al,'q'
	je	more5
more4:	jmp	rskp			;Next line, page
more5:	mov	ax,dircnt		;Quit
	mov	dindex,ax
more6:	ret				;Next file


; Space remaining utility

spcutl:	call	getprm		;Get the disk parameters for calculation
	call	tcrlf
	mov	ax,remK		;Display the number of Kbytes remaining
	call	nout
	mov	dx, offset spcm01
	call	tmsg
	mov	ax,maxK		;And the total number on the drive.
	call	nout
	mov	dx, offset spcm02
	call	tmsg
	mov	dl,fcb		;Finally say which drive it is.
	add	dl,'A'-1
	call	bout
	mov	dl,':'
	call	bout
	call	tcrlf
	jmp	rskp


; get and save disk parameters which relate to block allocation and size

getprm:	mov	newdrv,0	;initialize flag
	mov	dl,fcb		;specified drive must be the default
	dec	dl		; which is true if drive was not specified
	jl	gprm2
	cmp	dl,defdrv	; or if specified drive matches the default
	je	gprm2
	mov	newdrv,0FFh	;otherwise show that we changed default
	push	dx
	call	getrov		;make sure the desired drive is write enabled
	pop	dx		; or else the data may be inaccurate
	mov	cl,dl
	mov	ax,1
	shl	ax,cl
	and	ax,bx
	jz	gprm1
	push	dx
	call	rstdsk		;if read-only, reset all drives
	pop	dx
gprm1:	call	setdrv		;select the new drive as default
gprm2:	push	es
	call	getdpb		;get address of DPB in ES:BX
	mov	cl,es:2[bx]	;get Block Shift Factor
	sub	cl,3
	mov	ax,1
	shl	ax,cl
	mov	KPB,ax		;save Kbytes Per Block
	mov	cx,es:5[bx]	;get DSM maximum block number
	mov	DSM,cx		;save for file size and allocation calculation
	inc	cx		;number of blocks includes block 0
	mul	cx
	mov	maxK,ax		;Maximum number of Kbytes
	mov	remK,ax		;Kbytes remaining
	mov	ax,es:7[bx]	;subtract directory allocation from maxK
	mov	cl,5
	shr	ax,cl
	inc	ax
	sub	maxK,ax
	call	getalv		;get address of allocation vector in ES:BX
	mov	ax,DSM		;compute length of vector = (DSM/8)+1
	mov	cl,3
	shr	ax,cl
	inc	ax
	mov	cx,ax
	mov	ax,0		;Count allocated blocks:
gprm3:	mov	dl,es:[bx]	;for each byte in vector,
	inc	bx
	push	cx
	mov	cx,8		;  for each bit in byte,
gprm4:	test	dl,1
	jz	gprm5
	inc	ax		;    if bit is set, then block is allocated
gprm5:	shr	dl,1
	loop	gprm4
	pop	cx
	loop	gprm3
	mul	KPB		;convert allocated blocks to Kbytes
	sub	remK,ax		;subtract allocated Kbytes from remK
	pop	es
	cmp	newdrv,0	;reset default drive if we changed it
	je	gprm6
	mov	dl,defdrv
	call	setdrv
gprm6:	ret


; This subroutine fills the previously allocated memory buffer with a sorted
; list of filenames matching the wild name in fcb.  On failure, if there were
; no files, it prints an appropriate error message and simply returns.  On
; success, it returns to the skip location with the number of entries
; in the list in dircnt.  Each buffer entry is 16 bytes long and contains
; the user number at offset 0, the filename (with all attribute bits stripped)
; at offset 1-11, the allocated size in Kbytes at offset 12-13, and the
; read-only and system flags at offsets 14 and 15.

getwld:	mov	byte ptr fcb+12,'?' ;Match any extent
	mov	byte ptr fcb+13,'?'
	mov	byte ptr fcb+14,'?'
	call	getprm		;get disk parameters for size calculation
	mov	dircnt,0	;zero file count and total space occupied
	mov	dx, offset fcb
	call	gtjfn		;get first filename
	cmp	al,0FFh
	jne	gwld2
	mov	dx, offset erms15 ;unable to find file
	call	tcrmsg
	mov	dx, offset fcb
	call	tfile
	 ret
gwld2:	mov	cl,5		;find file directory entry
	shl	al,cl
	mov	ah,0
	mov	si, offset dma
	add	si,ax		;pointer to filename (incl. user number)
	mov	di, offset dirbuf
	push	ds
	pop	es
	mov	ax,9[si]	;get read-only and system flags
	and	ax,8080h	;keep only attribute bits
	mov	14[di],ax	;save flags at end of buffer
	mov	cx,12
gwld2a:	lodsb
	and	al,7Fh		;get rid of all attribute bits
	stosb
	loop	gwld2a
	add	si,4		;look at allocation area
	mov	ax,0		;initialize block count
	cmp	DSM,256		;if <256 blocks, then each takes a byte
	jb	gwld2c
	mov	cx,8		;8 blocks, one word each
gwld2b:	cmp	word ptr [si],0
	je	gwld2e
	inc	si
	inc	si
	inc	ax
	loop	gwld2b
	jmps	gwld2e
gwld2c:	mov	cx,16		;16 blocks, one byte each
gwld2d:	cmp	byte ptr [si],0
	je	gwld2e
	inc	si
	inc	ax
	loop	gwld2d
gwld2e:	mul	KPB		;convert blocks to kbytes
	stosw			;save this FCB's allocation
	mov	ax,dircnt
	cmp	ax,word ptr membuf+2 ;don't exceed buffer length
	jb	gwld2f
	mov	dx,offset erms27 ; if memory exceeded, print warning
	call	tcmsgc		;     and use what info we have
	jmp	gwld4
gwld2f:	mov	si, offset dirbuf ;go back to start of filename
	mov	es,word ptr membuf ;find correct location in sorted file list
	mov	di,0
	mov	cx,dircnt	;number of entries already in list
	jcxz	gwld3e
gwld3a:	push	cx
	push	si
	push	di
	mov	cx,12
	repe	cmpsb
	jb	gwld3d		;table entry greater than this file, insert
	ja	gwld3c		;table entry less than this file, keep looking
	lodsw			;else table entry same as file
	add	es:[di],ax	; ...add this FCB's allocation to size
gwld3b:	pop	di
	pop	si
	pop	cx
	jmp	gwld3f		;go get next filename
gwld3c:	pop	di		;haven't found insert location yet
	pop	si
	pop	cx
	add	di,16
	loop	gwld3a
	jmp	gwld3e		;if greater than all entries, insert at end
gwld3d:	pop	di		;insertion point
	pop	si		;filename pointer
	pop	ax		;number of entries following insertion point
	push	si
	push	di
	mov	cl,4		;each entry occupies 16 bytes
	shl	ax,cl
	add	di,ax
	dec	di
	mov	si,di		;point to last byte of table
	add	di,16		;move last part down 16 bytes
	mov	cx,ax
	push	ds		;save filename segment
	push	es		;make all action in table segment
	pop	ds
	std			;decrement pointers after each move
	rep	movsb
	cld
	pop	ds		;restore filename segment
	pop	di		;insertion point
	pop	si		;filename pointer
gwld3e:	mov	cx,16		;insert filename in table
	rep	movsb
	inc	dircnt		;count new entry
gwld3f:	mov	dx, offset fcb	;look for another matching filename
	call	gnjfn
	cmp	al,0FFh
	je	gwld4
	jmp	gwld2		;go process next filename
gwld4:	push	ds
	pop	es
	jmp	rskp

; General output utility routines

tmsgcr:	call	tmsg		;Print the string
	call	tcrlf		;Print a CRLF.
	ret

tcrmsg:	push	dx		;Don't trash our string.
	call	tcrlf		;Print a CRLF.
	pop	dx		;Restore our string.
	call	tmsg		;Print the string
	ret

tcmsgc:	push	dx		;Don't trash our string.
	call	tcrlf		;Print a CRLF.
	pop	dx		;Restore our string.
	call	tmsg		;Print the string
	call	tcrlf		;Print a CRLF.
	ret

tcrlf:	mov	dl,cr		;print a crlf
	call	bout
	mov	dl,lf
	call	bout
	ret

tmsg:	push	bx		;Don't clobber my ACs.
	mov	cl, prstr	;Ask BDOS for string printing.
	int	bdos		;What a way to call the BDOS.
	pop	bx
	ret

tfile:	mov	bx, dx		;Print filename in [dx]'s FCB	;[24a] begin
	mov	dl, [bx]	;If explicit drive number, display it.
	cmp	dl, 0
	je	tfil1
	add	dl, 'A'-1
	push	bx
	call	bout
	mov	dl, ':'
	call	bout
	pop	bx
tfil1:	mov	cx, 11		;Now display 11 chars of filename
tfil2:	push	cx
	cmp	cx, 3		;With period before file type
	jne	tfil3
	push	bx
	mov	dl, '.'
	call	bout
	pop	bx
tfil3:	inc	bx
	mov	dl, [bx]
	cmp	dl, ' '		;Don't include spaces
	je	tfil4
	push	bx
	call	bout
	pop	bx
tfil4:	pop	cx
	loop	tfil2
	ret							;[24a] end


bout:	mov	cl, conout	;Ask BDOS for character printing.
	int	bdos
	ret

bin:	cmp	tkflg, 0	;Check for command file input.	;[29b] begin
	je	bin1
	jmp	tkin						;[29b] end
bin1:	mov	cl, conin	;Get a char from the console.
	int	bdos
	ret

binst:	cmp	tkflg, 0	;Check for command file input.	;[29b] begin
	je	binst1
	jmp	tkst						;[29b] end
binst1:	mov	cl, consta	;Console input status check	;[20e] begin
	int	bdos
	or	al, al		;Result 0 if no character ready
	jz	bins2
	jmp	rskp		;Return SKIP if character ready
bins2:	ret							;[20e] end

dbout:	cmp	dl, 0		;Skip null fillers. 
	je	dbout3
	call	logchr		;Log the character if necessary ;[24a]
	call	dotab		;Do any necessary tab expansion.
	jmp	r		; No more chars to output.
dbout2:	mov	cl, dconio	;Put a char to the console.
	int	bdos
dbout3:	ret

dbin:	push	dx
	mov	cl, dconio	;Get a char from the console without
	mov	dl, 0FFH	; interference.
	int	bdos
	pop	dx
	ret

dbinst:	push	dx
	mov	cl, dconio	;Check the console's input status.
	mov	dl, 0FEH
	int	bdos
	pop	dx
	or	al, al		;Result 0 if no character ready ;[20e] begin
	jz	dbins2
	jmp	rskp		;Return SKIP if character ready
dbins2:	ret							;[20e] end

;Log the terminal output character

logchr:	cmp	logfil, 0	;Only log if file is open	;[24a] begin
	je	logch9
	mov	bx, bufpnt	;Store the character in the buffer.
	mov	[bx], dl
	inc	bx
	cmp	bx, offset dma+80h	;Have we filled a buffer?
	jb	logch1
	push	dx		;If so, write it to file.
	mov	dx, offset lfcb
	call	soutr
	pop	dx
	mov	bx, offset dma
logch1:	mov	bufpnt, bx
logch9:	ret			;Return to output routine	;[24a] end

;Halt this program.

haltf:	mov	cl, reset	;End this program.
	int	bdos
	ret			;One never knows!

;Reset the disk system to log in drives				;[32a] begin

rstdsk:	mov	cl,resetd
	int	bdos
	call	inidma		;Concurrent CP/M also resets DMA address
	ret

;Get and set the default disk drive

getdrv:	mov	cl,gtdrv
	int	bdos
	ret			;returns drive in al (A=0 through P=15)

setdrv:	mov	cl,stdrv	;new drive number (A=0 through P=15) in dl
	int	bdos
	ret

;Get and set the default user number

getusr:	mov	cl,stusr
	mov	dl,0FFh
	int	bdos
	ret			;returns user in al (0-15)

setusr:	mov	cl,stusr	;new user number (0-15) in dl
	int	bdos
	ret							;[32a] end


; Get the address of the disk allocation vector			;[32d] begin

getalv:	mov	cl,gtalv
	int	bdos
	ret

; Get the disk read-only vector

getrov:	mov	cl,gtrov
	int	bdos
	ret

; Set file attributes according to FCB in dx

setatr:	mov	cl,statr
	int	bdos
	ret

; Get the address of the DPB

getdpb:	mov	cl,gtdpb
	int	bdos
	ret

; Allocate a block of memory.  MCB address is in dx.

allmem:	mov	cl,allocm
	int	bdos
	ret

; Free a previously allocated block of memory.  MCB address is in dx.

fremem:	mov	cl,freem
	int	bdos
	ret							;[32d] end


; Get the first file in a wild card search.

gtjfn:	mov	cl, sfirst
	int	bdos
	ret

; Get the next file in a wild card search.

gnjfn:	mov	cl, snext
	int	bdos
	ret

; Close the file pointed to by the FCB in DX.

closf:	mov	cl, clsfil
	int	bdos
	ret

; Open the file pointed to by the FCB in DX.

openf:	call	fcbzer		;clear the fcb trailer		;[29c]
	mov	cl, opnfil
	int	bdos
	ret

; Create the file pointed to by the FCB in DX.

create:	call	fcbzer		;clear the fcb trailer		;[29c]
	mov	cl, makef
	int	bdos
	ret

fcbzer:	push	bx		;Clear the end of the FCB	;[29c] begin
	push	cx		;  prior to opening or creating a file.
	mov	bx, dx
	add	bx, 12
	mov	ch, 0
	mov	cl, 23
fcbz1:	mov	[bx], ch
	inc	bx
	loop	fcbz1
	pop	cx
	pop	bx
	ret							;[29c] end

; Write a record to the file pointed to by the FCB in DX.

soutr:	mov	cl, writef
	int	bdos
	ret

; Read a record from the file pointed to by the FCB in DX.

sinr:	mov	cl, readf
	int	bdos
	ret

; Delete the file pointed to by the FCB in DX.

delete:	mov	cl, delf
	int	bdos
	ret

; Sets dma to the default buffer.  Functions that change this must call this
; function to reset it before continuing

inidma:	push	dx
	mov	dx, offset dma
	call	setdma
	pop	dx
	ret

; Sets the DMA to the offset pointed to in DX and the base in DS.

setdma:	mov	cl, dmaset
	int	bdos
	mov	dx, ds
	mov	cl, dmabas
	int	bdos
	ret

; Do random access read, write, or file size checks, FCB is in dx

rinr:	mov	cl, readr
	int	bdos
	ret

routr:	mov	cl, writer
	int	bdos
	ret

sizef:	mov	cl, cflsz
	int	bdos
	ret

; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.
 
rskp:	pop	bp
	add	bp, 3
	push	bp
	ret

 
; Jumping here is the same as a ret.
 
r:	ret
<<< c86xap.a86 >>>
; * * * * * * * * * * * * * * *  version 2.9  * * * * * * * * * * * * * * *
; [36]	Add calls to support Concurrent CP/M.
; [34]	Make BREAK be correct length (250 ms).
; * * * * * * * * * * * * * * *  version 2.7  * * * * * * * * * * * * * * *
; [30d] Add SET PORT command, currently unimplemented.
; [30c] Isolate all machine dependencies in KERIO.
; [30a] Add keyboard DEL key alteration for APC
;	RonB, 04/18/84
; * * * * * * * * * * * * * * *  version 2.6  * * * * * * * * * * * * * * *
; [28e] Switch to local stack on interrupts.
;	RonB, 03/28/84
; * * * * * * * * * * * * * * *  version 2.4  * * * * * * * * * * * * * * *
; [20b] Add PRTBRK to send break & set correct clock rate for NEC.
; [20d] Add a pseudo time-out to PRTOUT so it doesn't loop forever.
;	RonB,03/02/84
; [19a] Add XON/XOFF type flow control
; [19b]	Clear screen and beginning and end of program.
; [19e]	Add PRTBRK to send break to port (Rainbow only)
; [19g]	Put in EQU for clock rate for timing loops.
;	Rg, 2/84 	<Oc.Garland%CU20B@Columbia-20>
; * * * * * * * * * * * * * * *  version 2.3  * * * * * * * * * * * * * * *
;  [par] Added calls to set parity, strip parity on input if
;        other than none parity is called for.
;	 JD, 2/84
; * * * * * * * * * * * * * * *  version 2.2  * * * * * * * * * * * * * * *
;  [2]	Add a de-initialization routine for the serial port, to restore
;	changed interrupt vectors, etc.
;	RonB,12/23/83
;  [1]	Add I/O support for the NEC Advanced Personal Computer
;	RonB,12/23/83
; * * * * * * * * * * * * * * *  version 2.0  * * * * * * * * * * * * * * *

; This module contains all the low level communications port I/O
; routines.

; Here are the I/O routines for the NEC APC.

	CSEG $

; Clock rate *10 for timing loops	;[19g]
clckrt	equ	49		;[19g]  4.9 Mhz			;[20b]

; Interrupt vector locations, in data segment 0

mnioff	equ	84h		;sio interrupt offset
mniseg	equ	86h		;sio interrupt segment

; 8259 Interrupt controller (master)

iccmd	equ	20h		;interrupt command register
icmask	equ	22h		;interrupt mask register

; 8259 commands and masks

icEOI	equ	20h		;end of interrupt (command)
ictmof	equ	08h		;disable timer (mask)
icmnof	equ	02h		;disable RS232 (mask)

; 8253-5 Interval Timer

tmdata	equ	2bh		;baud set (chan 1)
tmcmd	equ	2fh		;baud timer command port

; 8253 Timer commands

tmch1	equ	76h		;select & init timer channel 1

; 8251A USART controller

mndata	equ	30h		;    data port
mnsts1	equ	32h		;in  status port
mnsts2	equ	34h		;in  nec special status port
mncmd	equ	32h		;out command port
mnmsk	equ	34h		;out interrupt mask port
mntdc	equ	36h		;out transmit disable port

; 8251 status port 1 bits

mninp	equ	02h		;receive ready value
mnout	equ	01h		;send ready value
mndsr	equ	80h		;data set ready

; 8251 status port 2 bits

mncts	equ	04h		;clear to send

; 8251 initialization instructions

;	command instructions

ctxe	equ	01h		;transmit enable
cdtr	equ	02h		;dtr signal high
crxe	equ	04h		;receive enable
cbrk	equ	08h		;send break
cerr	equ	10h		;error reset
crts	equ	20h		;rts signal high
cmode	equ	40h		;reset - go to mode instruction format
chunt	equ	80h		;hunt for sync characters

;	mode instructions

m1x	equ	01h		;baud rate factor: 1x
m16x	equ	02h		;                  16x
m64x	equ	03h		;                  64x
m5d	equ	00h		;data bits: 5
m6d	equ	04h		;           6
m7d	equ	08h		;           7
m8d	equ	0Ch		;           8
mpn	equ	00h		;parity: none
mpo	equ	10h		;        odd
mpe	equ	30h		;        even
m1s	equ	40h		;stop bits: 1
m15s	equ	80h		;           1.5
m2s	equ	0C0h		;           2

; 8251 interrupt mask port bits

txmsk	equ	01h		;disable transmit complete interrupt
rxmsk	equ	02h		;disable receive complete interrupt
tbemsk	equ	04h		;disable transmit buffer empty interrupt

outlmt	EQU	1000H		;Number of times to check output status
				; before giving up on send.		;[20d]

; dispatch:	Under Concurrent CP/M, releases the processor for other
;		use when waiting for either the receive or the send status
;		routines to indicate success.

dispatch:
	push	ax
	push	bx
	push	cx
	mov	cl,8Eh			; P-Dispatch
	int	224
	pop	cx
	pop	bx
	pop	ax
	ret


; Test if port is ready to send next char.  Returns RSKP if ready.
; Trashes dx.

outwt:	cmp	floctl, floxon	;are we doing flow-control?	[19a] start
	jne	outwt1		;no - go on
	cmp	xofrcv, true	;are we being held?
	je	outwt3		;yes - return status not ready
outwt1:	push	ax
	mov	dx,mnsts1
	in	al,dx
	and	al,mndsr+mnout
	sub	al,mndsr+mnout
	jnz	outwt2
	mov	dx,mnsts2
	in	al,dx
	and	al,mncts
	jnz	outwt4
outwt2:	pop	ax
outwt3:	call	dispatch	;let other CCPM processes have a chance	;[36]
	 ret
outwt4:	pop	ax
	jmp	rskp


; Output data to port. Trashes DX and prints char in AL.

outchr:	mov	dx,mndata
	out	dx,al
	ret


; Output the character in AL, checking first to make sure the port is clear.

prtout:	call	dopar		;[par] set parity
	push	dx
	push	cx						;[20d] begin
	mov	cx,outlmt
prtou2:	call	outwt		;Wait until the port is ready
	 loop	prtou2		; or too much time has passed.
	 nop
	call	outchr		;Output it.
	pop	cx						;[20d] end
	pop	dx
	ret


; Test if data is available from port.

instat:	cmp	mnchrn,0	;Any chars in the buffer?
	jne	inst2
	call	dispatch	;let other CCPM processes have a chance	;[36]
	 ret
inst2:	jmp	rskp


; Input data from port.  Preserves all registers and returns char in
; AL.  Gets the char from the ring buffer.  Assumes a char is
; already there.

inchr:	push	bx
	cli			;Disable interrupts while were are playing.
	dec	mnchrn		;Decrement the number of chars in the buffer.
	mov	bx,mnchop	;Get the pointer into the buffer.
	inc	bx		;Increment to the next char.
	cmp	bx,offset mnchrs+mnchnd ;Past the end?
	jb	inchr2
	mov	bx, offset mnchrs ;If so wrap around to the start.
inchr2:	mov	mnchop,bx	;Save the updated pointer.
	mov	al,[bx]		;Get the character.
	sti			;All done, we can restore interrupts.
	pop	bx
	cmp	parflg,parnon	;[par] no parity?
	je	inchr3		;[par] yup, don't bother stripping
	and	al,7fh		;[par] checking parity, strip off
inchr3:	cmp	floctl, floxon	;do flow-control?		[19a] start
	je	inchr4		;If yes jump
	ret
inchr4:	cmp	xofsnt, true	;Have we sent an XOFF
	je	inchr5		;Jump if yes
	ret
inchr5:	cmp	mnchrn, mntrg1	;Under the low trigger point?
	jb	inchr6		;yes - jump
	ret
inchr6:	push	ax		;save current character
	mov	al, xon
	call	prtout		;send an XON
	mov	xofsnt, false	;turn off the flag
	pop	ax		;get back character
	ret			;				[19a] end



mnax	dw	0		;Storage in CSEG		;[28e] begin
mnsp	dw	0		;  for use by interrupt handler
mnsseg	dw	0
mndseg	dw	0

; This routine handles the interrupts on input.

mnint:	cli
	mov	cs:mnax, ax	;Save interrupt stack location.
	mov	ax, sp
	mov	cs:mnsp, ax
	mov	ax, ss
	mov	cs:mnsseg, ax
	mov	ax, cs:mndseg	;Switch to our internal stack.
	mov	ss, ax
	mov	sp, offset mnstk
	push	ds		;Save all registers.
	push	es
	push	bp
	push	di
	push	si
	push	dx
	push	cx
	push	bx
	mov	ds, ax		;Get our data segment address.
	call	mnproc		;Process the character.
	mov	dx, iccmd
	mov	al, icEOI	;signal end of interrupt to controller
	out	dx, al
	pop	bx		;Restore all registers.
	pop	cx
	pop	dx
	pop	si
	pop	di
	pop	bp
	pop	es
	pop	ds
	mov	ax, cs:mnsp	;Restore the original stack.
	mov	sp, ax
	mov	ax, cs:mnsseg
	mov	ss, ax
	mov	ax, cs:mnax
	iret			;Return from the interrupt.	;[28e] end


; This routine (called by MNINT) gets a char from the serial port
; and puts it in the ring buffer.

mnproc:	mov	dx,mnsts1
	in	al,dx		;Get the port status.
	and	al,mninp	;Is a character waiting?
	jnz	mnpro2		;   Yes, go take care of it.
	 ret			;   No, just a false alarm.

mnpro2:	mov	dx,mndata
	in	al,dx		;Read the char.
	cmp	floctl, floxon	;are we doing flow-control ?	[19a] start
	jne	mnpr2b		;no - go on
	cmp	al, xoff	;is it an XOFF?
	jne	mnpr2a		;no - go on
	mov	xofrcv, true	;set the flag
	ret
mnpr2a:	cmp	al, xon		;an XON?
	jne	mnpr2b		;no
	mov	xofrcv, false	;clear the flag
	ret			;				[19a] end
mnpr2b:	cmp	mnchrn,mnchnd	;Is the buffer full?
	je	mnperr		;If so, take care of the error.
	inc	mnchrn		;Increment the character count.
	mov	bx,mnchip	;Get the buffer input pointer.
	inc	bx		;Increment it.
	cmp	bx,offset mnchrs+mnchnd ;Past the end?
	jb	mnpro3
	mov	bx, offset mnchrs ;Yes, point to the start again.
mnpro3:	mov	mnchip,bx	;Save the pointer.
	mov	[bx],al		;Put the character in the buffer.
	cmp	floctl, floxon	;do flow-control?		[19a] start
	je	mnpro4		;If yes jump
	ret
mnpro4:	cmp	xofsnt, true	;Have we sent an XOFF
	jnz	mnpro5
	ret			;return if we have
mnpro5:	cmp	mnchrn, mntrg2	;Past the High trigger point?
	ja	mnpro6		;yes - jump
	ret
mnpro6:	mov	al, xoff
	call	prtout		;send an XOFF
	mov	xofsnt, true	;set the flag
	ret			;				[19a] End

mnperr:	ret			;Just return on an error for now.


; prtbrk - send a break		;				[20b] start

prtbrk:				;
	mov	dx,mncmd	;break goes to command port
	mov	al,cbrk+crts+cerr+crxe+cdtr+ctxe ;add break to normal command
	out	dx,al
	mov	ax, 275		;.. for 275 millisec's		[34]
	call	mswait		;				[34]
	mov	al,crts+cerr+crxe+cdtr+ctxe ;RTS & DTR high, Rx & Tx enabled
	out	dx,al		;return to normal setting
	ret			;				 [19e] end

mswait:				;				[34] start
	mov	cx,5*clckrt	; inner loop count for 1 millisec.
mswai1:
	sub	cx,1		;** inner loop takes 20 clock cycles
	jnz	mswai1		;**
	dec	ax		; outer loop counter
	jnz	mswait		; wait another millisecond
	ret			;				[34] end


; serini - This routine initializes all devices that need it.
;	   Called at the start of the program.

serini:	cmp	mninit,0FFh	; must only do this initialization once
	je	serin2
	mov	mninit,0FFh

	push	es

; Make DEL key return a 7F code rather than the 18 (control-X)	;[30a] begin
; that it ordinarily does.  This involves modifying a key
; conversion table in the BIOS.  Version 1.107 and later
; contain a pointer to this table at 40:256C, but earlier
; versions which do not are still in common use, so I will
; try to find it with a direct search.

	cld
	mov	ax, 40h		;BIOS segment
	mov	es, ax
	mov	di, 2500h	;Start of BIOS
	mov	al, 0FCh	;Key gives this value
	mov	ah, 18h		;Translated to this value
	mov	cx, -1
serina:	repnz	scasb		;Look for the key value
	or	cx, cx		;Zero if search failed
	jz	serinb		; If so, do nothing
	cmp	es:[di], ah	;Translation value next?
	jne	serina		; If not, keep looking
	mov	kbpat, di	;Save the location we're patching.
	mov	al, 7Fh		;Insert a DEL character
	mov	es:[di], al
serinb:				;...and continue	;[30a] end


	mov	dx,icmask
	in	al,dx		;get current interrupt mask
	mov	mnxmsk,al	;save it for restore

;	NEC recommends that the timer be turned off during interrupt-driven
;	serial I/O, but this disables the clock and keyboard repeat.  I have
;	not had any bad results from leaving it enabled.  I will leave the
;	disabling code here in case something develops. -- RonB

;	or	al,ictmof+icmnof;mask off timer and sio interrupts
	or	al,icmnof	;mask off sio interrupt
	out	dx,al

	mov	ax,ds		;save data segment in cseg
	mov	cs:mndseg,ax	;   for use by the interrupt handler

	mov	ax,0		;point to zero page to replace
	mov	es,ax		;the sio interrupt vector
	mov	ax,es:.mniseg	;after first saving the current vector
	mov	mnxseg,ax
	mov	ax,es:.mnioff
	mov	mnxoff,ax
	cli
	mov	ax,cs
	mov	es:.mniseg,ax
	mov	ax,offset mnint
	mov	es:.mnioff,ax
	sti

	call	stmode		;set mode & baud to defaults
	call	stbaud

	mov	dx,mntdc
	mov	al,00h		;enable transmission of data
	out	dx,al

	mov	dx,mndata	;dummy read to clear buffer
	in	al,dx

	mov	dx,mnmsk
	mov	al,txmsk+tbemsk	;set interrupt mask (enable read int)
	out	dx,al

	mov	dx,icmask
	in	al,dx		;enable sio interrupts
	and	al,not icmnof
	out	dx,al

	pop	es

serin2:	ret


; serfin - this routine is used to "undo" what serini has done, called
;	   just before exiting back to cp/m.

serfin:
	cmp	mninit,0FFh	;check if initialization has been done
	jne	serfn2		;if not, don't de-initialize
	mov	mninit,0

	push	es

; Unpatch the keyboard conversion table				;[30a] begin
; Restore control-X value for DEL key from our 7F value.

	les	di, dword ptr kbpat		;Get the patch location
	or	di, di		;Did we patch it at all?
	jz	serfia		; If not, skip this
	mov	kbpat, 0	;Show no longer patched
	mov	al, 18h		;Restore control-X value
	mov	es:[di], al
serfia:				;...and continue	;[30a] end

	cli
	mov	dx,icmask
	mov	al,mnxmsk	;restore the old interrupt mask
	out	dx,al
	mov	ax,0
	mov	es,ax
	mov	ax,mnxseg	;restore sio interrupt vector
	mov	es:.mniseg,ax
	mov	ax,mnxoff
	mov	es:.mnioff,ax
	sti

	pop	es

serfn2:	ret


; This routine clears the serial port input buffer.  It is called to
; clear out excess NAKs that can result from server mode operation.

cfibf:	mov	mnchrn, 0	;Say no characters in the buffer.
	mov	mnchip, OFFSET mnchrs-1+mnchnd ;Reset input pointer.
	mov	mnchop, OFFSET mnchrs-1+mnchnd ;Reset output pointer.
	ret

; set the parity, number of data bits, and number of stop bits

stmode:	mov	dx,mncmd
	mov	al,0		;recommended reset procedure:
	out	dx,al		;three 0's followed by a cmode
	mov	al,0
	out	dx,al
	mov	al,0
	out	dx,al
	mov	al,cmode	;enable mode setting
	out	dx,al
	mov	al,m1s		;1 stop, no parity, 8 data, 16x baud
	add	al,mpn		;Note: these adds are distinct to
	add	al,m8d		;      allow the 8251 time to reset
	add	al,m16x
	out	dx,al
	mov	al,crts+cerr+crxe+cdtr+ctxe ;RTS & DTR high, Rx & Tx enabled
	out	dx,al
	ret


; set the baud rate

stbaud:	mov	al,mnbaud	;get the baud rate information
	cmp	al,12		;check for valid range (0-12)
	ja	stb02
	mov	bx,offset baudtb;get address of baud rate table
	add	al,al		;compute word offset
	mov	ah,0
	add	bx,ax
	mov	dx,tmcmd
	mov	al,tmch1	;select timer channel 1
	out	dx,al
	mov	dx,tmdata
	mov	ax,[bx]		;get value
	out	dx,al		;output low byte
	mov	al,ah
	out	dx,al		;output high byte
stb02:	ret

	dseg $

;	Serial port default parameters

mnbaud	db	6		;300 baud

;	Interval Timer values (assumes 16x baud rate mode)

baudtb	dw	0C00h		;50 baud	0
	dw	0800h		;75 baud	1
	dw	0600h		;100 baud	2
	dw	0574h		;110 baud	3
	dw	0400h		;150 baud	4
	dw	0300h		;200 baud	5
	dw	0200h		;300 baud	6
	dw	0100h		;600 baud	7
	dw	0080h		;1200 baud	8
	dw	0040h		;2400 baud	9
	dw	0020h		;4800 baud	10
	dw	0010h		;9600 baud	11
	dw	0008h		;19200 baud	12

mninit	db	0		;set to 0FFh if initialization has been done
mnxmsk 	db	0		;8259 interrupt mask storage
mnxseg	dw	0		;system sio interrupt vector
mnxoff	dw	0

mnchnd	equ	512		;Size of circular buffer.
mnchrs	rb	mnchnd		;Circular character buffer for input.
mnchip	dw	mnchrs-1+mnchnd	;Input pointer into character buffer.
mnchop	dw	mnchrs-1+mnchnd	;Output pointer into character buffer.
mnchrn	dw	0		;Number of chars in the buffer.

mntrg1	equ	128		;[19a] Low trigger point for Auto XON/XOFF
mntrg2	equ	384		;[19a] High trigger point for Auto XON/XOFF

floctl	db	1		;[19a] If floctl=floxon do Auto XON/XOFF logic
xofsnt	db	0		;[19a] set if XOFF was sent
xofrcv	db	0		;[19a] set if XOFF was recieved

	rw	32		;Interrupt stack		;[28e]
mnstk	dw	0		;bottom of stack		;[28e]

kbpat	dw	0000h,0040h	;patch location for DEL key	;[30a]


	CSEG $

; The following routines do the SET and SHOW for the machine dependent
; features of Kermit.  At present there are only two:  baud rate setting
; and port selection.

;	This is the SET BAUD rate subcommand

bdset:	mov	dx, offset bdtab
	mov	bx, offset bdhlp
	mov	ah, cmkey
	call	comnd
	 jmp	r
	mov	temp1, bx
	mov	ah, cmcfm
	call	comnd		;Get a confirm.
	 jmp	r		; Didn't get a confirm.
	mov	bx, temp1
	mov	mnbaud, bl	;Set the baud rate table index.
	call	stbaud
	jmp	rskp

;	This is the SET PORT subcommand (not implemented in APC)

prtset:	mov	ah, cmcfm
	call	comnd		;Get a confirm.
	 jmp	$+3		; Didn't get a confirm.
	mov	dx, offset infms6 ;Tell user it's not implemented
	call	tcrmsg
	jmp	rskp


; The following procedures implement the SHOW command for the system
; dependent features of baud rate and port selection.

shobd:	mov	dx, offset bdst	;Baud rate string.
	call	tcrmsg
	mov	al, mnbaud	;Print the keyword corresponding to the
	mov	bx, offset bdtab ; current value of mnbaud.
	call	tabprt
	ret

shoprt:	ret			;Port selection not implemented.


	DSEG $

bdtab	db	13		;Thirteen entries		;[6] begin
	db	3,'100$'
	dw	0002H
	db	3,'110$'
	dw	0003H
	db	4,'1200$'
	dw	0008H
	db	3,'150$'
	dw	0004H
	db	5,'19200$'
	dw	000CH
	db	3,'200$'
	dw	0005H
	db	4,'2400$'
	dw	0009H
	db	3,'300$'
	dw	0006H
	db	4,'4800$'
	dw	000AH
	db	2,'50$'
	dw	0000H
	db	3,'600$'
	dw	0007H
	db	2,'75$'
	dw	0001H
	db	4,'9600$'
	dw	000BH						;[6] end

bdhlp	db	cr,lf,'	50	100	150	300	1200	4800	19200'
	db	cr,lf,'	75	110	200	600	2400	9600$'



; The following routines do screen control.  These are isolated here because
; the screen control sequences are likely to vary from system to system, even
; though the Rainbow and APC (the only systems implemented to date) both use
; ANSI sequences for this purpose.

	CSEG $

; POSCUR - positions cursor to row and col (each 1 byte) pointed to by dx.

poscur:	mov	bx, dx		;Do ANSI cursor positioning.
	mov	cl, 10
	mov	al, [bx]	;Get row value
	sub	ah, ah
	div	cl		;units digit in ah, tens digit in al
	add	ax, '00'	;Convert both to ASCII
	mov	word ptr anspos+2, ax	;Save reversed (al,ah)
	mov	al, 1[bx]	;Do same for column value
	sub	ah, ah
	div	cl
	add	ax, '00'
	mov	word ptr anspos+5, ax
	mov	dx, offset anspos	;Print cursor positioning string.
	call	tmsg
	ret

; CLRSCR - homes cursor and clears screen.

clrscr:	mov	dx, offset apccls
	call	tmsg
	ret

; CLRLIN - clears from cursor to end of line.

clrlin:	mov	dl, cr		;Go to beginning of line
	call	bout
clreol:	mov	dx, offset ansclr ;Clear from cursor to end of line
	call	tmsg
	ret

; REVON - turns on reverse video display

revon:	mov	dx, offset ansron
	call	tmsg
	ret

; REVOFF - turns off reverse video display

revoff:	mov	dx, offset ansrof
	call	tmsg
	ret

; BLDON - turns on bold (highlighted) display

bldon:	ret

; BLDOFF - turns off bold (highlighted) display

bldoff:	ret


	DSEG $

anspos	db	esc,'[00;00H$'	;Position cursor to row and column
apccls	db	1Eh,1Ah,'$'	;Home cursor and clear screen
ansclr	db	esc,'[K$'	;Clear from cursor to end of line
ansron	db	esc,'[7m$'	;Turn on reverse video
ansrof	db	esc,'[m$'	;Turn off reverse video


; Here tab expansion is done if necessary.  If not, just return retskp.

	cseg $

dotab:	cmp	dl, tab		;A tab?
	je	dotab1
	jmp	rskp		;No, just proceed.
dotab1:	mov	curbuf, 2	;Report cursor position command.
	mov	dx, offset curbuf
	mov	cl, 7
	int	220		;Special BIOS function.
	mov	al, curbuf+2	;Column position in binary (0-79).
	and	al, 07h		;Number of spaces needed
	mov	cx, 0008h	;  = 8 - (col mod 8)
	sub	cl, al
dotab2:	push	cx
	mov	dl, ' '
	call	dbout2
	pop	cx
	loop	dotab2
	ret

	dseg $
curbuf	db	2,0,0		;command, row, column of cursor position ;[7]

delstr  db	10O,10O,'$'	;Delete string.

system	db	'        NEC Advanced Personal Computer$'	;[1][20a]
<<< c86xfj.a86 >>>
; * * * * * * * * * * * * * * *  version 2.7  * * * * * * * * * * * * * * *
; [30e] Add support for Fujitsu Micro 16s
;	Chris Barker, 01/04/85
; [30d] Add SET PORT command, currently unimplemented.
; [30c] Isolate all machine dependencies in KERIO.
; [30a] Add keyboard DEL key alteration for APC
;	RonB, 04/18/84
; * * * * * * * * * * * * * * *  version 2.6  * * * * * * * * * * * * * * *
; [28e] Switch to local stack on interrupts.
;	RonB, 03/28/84
; * * * * * * * * * * * * * * *  version 2.4  * * * * * * * * * * * * * * *
; [20b] Add PRTBRK to send break & set correct clock rate for NEC.
; [20d] Add a pseudo time-out to PRTOUT so it doesn't loop forever.
;	RonB,03/02/84
; [19a] Add XON/XOFF type flow control
; [19b]	Clear screen and beginning and end of program.
; [19e]	Add PRTBRK to send break to port (Rainbow only)
; [19g]	Put in EQU for clock rate for timing loops.
;	Rg, 2/84 	<Oc.Garland%CU20B@Columbia-20>
; * * * * * * * * * * * * * * *  version 2.3  * * * * * * * * * * * * * * *
;  [par] Added calls to set parity, strip parity on input if
;        other than none parity is called for.
;	 JD, 2/84
; * * * * * * * * * * * * * * *  version 2.2  * * * * * * * * * * * * * * *
;  [2]	Add a de-initialization routine for the serial port, to restore
;	changed interrupt vectors, etc.
;	RonB,12/23/83
;  [1]	Add I/O support for the NEC Advanced Personal Computer
;	RonB,12/23/83
; * * * * * * * * * * * * * * *  version 2.0  * * * * * * * * * * * * * * *

; This module contains all the low level communications port I/O
; routines.

; Here are the I/O routines for the Fujitsu Micro 16s

	CSEG $

; Clock rate *10 for timing loops	;[19g]
clckrt	equ	80		;[19g]  8.0 Mhz			;[30e]

; Interrupt vector locations, in data segment 0

mnioff	equ	30h		;sio interrupt offset
mniseg	equ	32h		;sio interrupt segment

; 8259 Interrupt controller (master)

iccmd	equ	00h		;interrupt command register
icmask	equ	02h		;interrupt mask register

; 8259 commands and masks

icEOI	equ	20h		;end of interrupt (command)
;ictmof	equ	08h		;disable timer (mask)
icmnof	equ	04h		;disable RS232 (mask)

; Baud Rate controller (MB 14417)

bdport	equ	0FD59h		;baud rate select (read/write)

; 8251A USART controller

mndata	equ	0FD06h		;    data port
mnsts1	equ	0FD07h		;in  status port
mncmd	equ	0FD07h		;out command port

; 8251 status port 1 bits

mninp	equ	02h		;receive ready value
mnout	equ	01h		;send ready value
mndsr	equ	80h		;data set ready

; 8251 initialization instructions

;	command instructions

ctxe	equ	01h		;transmit enable
cdtr	equ	02h		;dtr signal high
crxe	equ	04h		;receive enable
cbrk	equ	08h		;send break
cerr	equ	10h		;error reset
crts	equ	20h		;rts signal high
cmode	equ	40h		;reset - go to mode instruction format
chunt	equ	80h		;hunt for sync characters

;	mode instructions

m1x	equ	01h		;baud rate factor: 1x
m16x	equ	02h		;                  16x
m64x	equ	03h		;                  64x
m5d	equ	00h		;data bits: 5
m6d	equ	04h		;           6
m7d	equ	08h		;           7
m8d	equ	0Ch		;           8
mpn	equ	00h		;parity: none
mpo	equ	10h		;        odd
mpe	equ	30h		;        even
m1s	equ	40h		;stop bits: 1
m15s	equ	80h		;           1.5
m2s	equ	0C0h		;           2

outlmt	equ	1000h		;Number of times to check output status
				; before giving up on send.		;[20d]


; Test if port is ready to send next char.  Returns RSKP if ready.
; Trashes dx.

outwt:	cmp	floctl, floxon	;are we doing flow-control?	[19a] start
	jne	outwta		;no - go on
	cmp	xofrcv, true	;are we being held?
	jne	outwta		;no - ok go on
	ret			;held - say we're busy.	[19a] end
outwta:	push	ax
	mov	dx,mnsts1
	in	al,dx
	and	al,mndsr+mnout
	sub	al,mndsr+mnout
	jz	outwt2
outwt1:	pop	ax
	 ret
outwt2:	pop	ax
	jmp	rskp


; Output data to port. Trashes DX and prints char in AL.

outchr:	mov	dx,mndata
	out	dx,al
	ret


; Output the character in AL, checking first to make sure the port is clear.

prtout:	call	dopar		;[par] set parity
	push	dx
	push	cx						;[20d] begin
	mov	cx,outlmt
prtou2:	call	outwt		;Wait until the port is ready
	 loop	prtou2		; or too much time has passed.
	 nop
	call	outchr		;Output it.
	pop	cx						;[20d] end
	pop	dx
	ret


; Test if data is available from port.

instat:	cmp	mnchrn,0	;Any chars in the buffer?
	jnz	inst2
	 ret
inst2:	jmp	rskp


; Input data from port.  Preserves all registers and returns char in
; AL.  Gets the char from the ring buffer.  Assumes a char is
; already there.

inchr:	push	bx
	cli			;Disable interrupts while were are playing.
	dec	mnchrn		;Decrement the number of chars in the buffer.
	mov	bx,mnchop	;Get the pointer into the buffer.
	inc	bx		;Increment to the next char.
	cmp	bx,offset mnchrs+mnchnd ;Past the end?
	jb	inchr2
	lea	bx,mnchrs	;If so wrap around to the start.
inchr2:	mov	mnchop,bx	;Save the updated pointer.
	mov	al,[bx]		;Get the character.
	sti			;All done, we can restore interrupts.
	pop	bx
	cmp	parflg,parnon	;[par] no parity?
	je	inchr3		;[par] yup, don't bother stripping
	and	al,7fh		;[par] checking parity, strip off
inchr3:	cmp	floctl, floxon	;do flow-control?		[19a] start
	je	inchr4		;If yes jump
	ret
inchr4:	cmp	xofsnt, true	;Have we sent an XOFF
	je	inchr5		;Jump if yes
	ret
inchr5:	cmp	mnchrn, mntrg1	;Under the low trigger point?
	jb	inchr6		;yes - jump
	ret
inchr6:	push	ax		;save current character
	mov	al, xon
	call	prtout		;send an XON
	mov	xofsnt, false	;turn off the flag
	pop	ax		;get back character
	ret			;				[19a] end



mnax	dw	0		;Storage in CSEG		;[28e] begin
mnsp	dw	0		;  for use by interrupt handler
mnsseg	dw	0
mndseg	dw	0

; This routine handles the interrupts on input.

mnint:	cli
	mov	cs:mnax, ax	;Save interrupt stack location.
	mov	ax, sp
	mov	cs:mnsp, ax
	mov	ax, ss
	mov	cs:mnsseg, ax
	mov	ax, cs:mndseg	;Switch to our internal stack.
	mov	ss, ax
	lea	sp, mnstk
	push	ds		;Save all registers.
	push	es
	push	bp
	push	di
	push	si
	push	dx
	push	cx
	push	bx
	mov	ds, ax		;Get our data segment address.
	call	mnproc		;Process the character.
	mov	dx, iccmd
	mov	al, icEOI	;signal end of interrupt to 8259A
	out	dx, al
	pop	bx		;Restore all registers.
	pop	cx
	pop	dx
	pop	si
	pop	di
	pop	bp
	pop	es
	pop	ds
	mov	ax, cs:mnsp	;Restore the original stack.
	mov	sp, ax
	mov	ax, cs:mnsseg
	mov	ss, ax
	mov	ax, cs:mnax
	iret			;Return from the interrupt.	;[28e] end


; This routine (called by MNINT) gets a char from the serial port
; and puts it in the ring buffer.

mnproc:	mov	dx,mnsts1
	in	al,dx		;Get the port status.
	and	al,mninp	;Is a character waiting?
	jnz	mnpro2		;   Yes, go take care of it.
	 ret			;   No, just a false alarm.

mnpro2:	mov	dx,mndata
	in	al,dx		;Read the char.
	cmp	floctl, floxon	;are we doing flow-control ?	[19a] start
	jne	mnpr2b		;no - go on
	cmp	al, xoff	;is it an XOFF?
	jne	mnpr2a		;no - go on
	mov	xofrcv, true	;set the flag
	ret
mnpr2a:	cmp	al, xon		;an XON?
	jne	mnpr2b		;no
	mov	xofrcv, false	;clear the flag
	ret			;				[19a] end
mnpr2b:	cmp	mnchrn,mnchnd	;Is the buffer full?
	je	mnperr		;If so, take care of the error.
	inc	mnchrn		;Increment the character count.
	mov	bx,mnchip	;Get the buffer input pointer.
	inc	bx		;Increment it.
	cmp	bx,offset mnchrs+mnchnd ;Past the end?
	jb	mnpro3
	lea	bx,mnchrs	;Yes, point to the start again.
mnpro3:	mov	mnchip,bx	;Save the pointer.
	mov	[bx],al		;Put the character in the buffer.
	cmp	floctl, floxon	;do flow-control?		[19a] start
	je	mnpro4		;If yes jump
	ret
mnpro4:	cmp	xofsnt, true	;Have we sent an XOFF
	jnz	mnpro5
	ret			;return if we have
mnpro5:	cmp	mnchrn, mntrg2	;Past the High trigger point?
	ja	mnpro6		;yes - jump
	ret
mnpro6:	mov	al, xoff
	call	prtout		;send an XOFF
	mov	xofsnt, true	;set the flag
	ret			;				[19a] End

mnperr:	ret			;Just return on an error for now.


; prtbrk - send a break		;				[20b] start

prtbrk:
	mov	dx,mncmd	;break goes to command port
	mov	al,cbrk+crts+cerr+crxe+cdtr+ctxe ;add break to normal command
	out	dx,al
	mov	cx, 25000	;sit for a while
prtbk1:	loop	prtbk1
	mov	al,crts+cerr+crxe+cdtr+ctxe ;RTS & DTR high, Rx & Tx enabled
	out	dx,al		;return to normal setting
	ret			;				 [20b] end


; serini - This routine initializes all devices that need it.
;	   Called at the start of the program.

serini:	cmp	mninit,0FFh	; must only do this initialization once
	je	serin2
	mov	mninit,0FFh

	push	es

	mov	dx,icmask
	in	al,dx		;get current interrupt mask
	mov	mnxmsk,al	;save it for restore

;	or	al,ictmof+icmnof;mask off timer and sio interrupts
	or	al,icmnof	;mask off sio interrupt
	out	dx,al

	mov	ax,ds		;save data segment in cseg
	mov	cs:mndseg,ax	;   for use by the interrupt handler

	mov	ax,0		;point to zero page to replace
	mov	es,ax		;the sio interrupt vector
	mov	ax,es:.mniseg	;after first saving the current vector
	mov	mnxseg,ax
	mov	ax,es:.mnioff
	mov	mnxoff,ax
	cli
	mov	ax,cs
	mov	es:.mniseg,ax
	mov	ax,offset mnint
	mov	es:.mnioff,ax
	sti

;	call	stmode		;set mode & baud to defaults
	call	stbaud

	mov	dx,mndata	;dummy read to clear buffer
	in	al,dx

	mov	dx,icmask
	in	al,dx		;enable sio interrupts
	and	al,not icmnof
	out	dx,al

	pop	es

serin2:	ret


; serfin - this routine is used to "undo" what serini has done, called
;	   just before exiting back to cp/m.

serfin:
	call	clrscr		;[19b] clear screen	;[30c]

	cmp	mninit,0FFh	;check if initialization has been done
	jne	serfn2		;if not, don't de-initialize
	mov	mninit,0

	push	es

	cli
	mov	dx,icmask
	mov	al,mnxmsk	;restore the old interrupt mask
	out	dx,al
	mov	ax,0
	mov	es,ax
	mov	ax,mnxseg	;restore sio interrupt vector
	mov	es:.mniseg,ax
	mov	ax,mnxoff
	mov	es:.mnioff,ax
	sti

	pop	es

serfn2:	ret


; This routine clears the serial port input buffer.  It is called to
; clear out excess NAKs that can result from server mode operation.

cfibf:	mov	mnchrn, 0	;Say no characters in the buffer.
	mov	mnchip, OFFSET mnchrs-1+mnchnd ;Reset input pointer.
	mov	mnchop, OFFSET mnchrs-1+mnchnd ;Reset output pointer.
	ret

; set the parity, number of data bits, and number of stop bits

stmode:				;do this all in stbaud for micro16s
; set the baud rate
stbaud:	mov	al,mnbaud	;get the baud rate information
	cmp	al,6		;check for valid range (0-6)
	ja	stb03		;j/ out of valid range
	mov	bx,offset baudtb;get address of baud rate table
	mov	ah,0
	add	bx,ax
	mov	dx,bdport
	mov	al,[bx]		;get value
	out	dx,al		;output byte
	mov	al,m64x
	jne	stb02		;
	mov	al,m16x		;for 19200 bd
stb02:	push	ax
	mov	dx,mncmd
	mov	al,0		;recommended reset procedure:
	out	dx,al		;three 0's followed by a cmode
	mov	al,0
	out	dx,al
	mov	al,0
	out	dx,al
	mov	al,cmode	;enable mode setting
	out	dx,al
	pop	ax
	add	al,m1s		;1 stop, no parity, 8 data, 16x baud
	add	al,mpn		;Note: these adds are distinct to
	add	al,m8d		;      allow the 8251 time to reset
	out	dx,al
	mov	al,crts+cerr+crxe+cdtr+ctxe ;RTS & DTR high, Rx & Tx enabled
	out	dx,al

stb03:	ret

	dseg $

;	Serial port default parameters

mnbaud	db	5		;9600 baud

;	Interval Timer values (assumes 64x baud rate mode)

baudtb	db	00h		;300 baud	0
	db	11h		;600 baud	1
	db	22h		;1200 baud	2
	db	33h		;2400 baud	3
	db	44h		;4800 baud	4
	db	55h		;9600 baud	5
	db	44h		;19200 baud	6 - requires 16x rate mode

mninit	db	0		;set to 0FFh if initialization has been done
mnxmsk 	db	0		;8259 interrupt mask storage
mnxseg	dw	0		;system sio interrupt vector
mnxoff	dw	0

mnchnd	equ	512		;Size of circular buffer.
mnchrs	rb	mnchnd		;Circular character buffer for input.
mnchip	dw	mnchrs-1+mnchnd	;Input pointer into character buffer.
mnchop	dw	mnchrs-1+mnchnd	;Output pointer into character buffer.
mnchrn	dw	0		;Number of chars in the buffer.

mntrg1	equ	128		;[19a] Low trigger point for Auto XON/XOFF
mntrg2	equ	384		;[19a] High trigger point for Auto XON/XOFF

floctl	db	1		;[19a] If floctl=floxon do Auto XON/XOFF logic
xofsnt	db	0		;[19a] set if XOFF was sent
xofrcv	db	0		;[19a] set if XOFF was recieved

	rw	32		;Interrupt stack		;[28e]
mnstk	dw	0		;bottom of stack		;[28e]


	CSEG $

; The following routines do the SET and SHOW for the machine dependent
; features of Kermit.  At present there are only two:  baud rate setting
; and port selection.

;	This is the SET BAUD rate subcommand

bdset:	lea	dx, bdtab
	lea	bx, bdhlp
	mov	ah, cmkey
	call	comnd
	jmp	r
	mov	temp1, bx
	mov	ah, cmcfm
	call	comnd		;Get a confirm.
	jmp	r		; Didn't get a confirm.
	mov	bx, temp1
	mov	mnbaud, bl	;Set the baud rate table index.
	call	stbaud
	jmp	rskp

;	This is the SET PORT subcommand (not implemented in APC)

prtset:	mov	ah, cmcfm
	call	comnd		;Get a confirm.
	jmp	$+3		; Didn't get a confirm.
	lea	dx, infms6	;Tell user it's not implemented
	call	tcrmsg
	jmp	rskp


; The following procedures implement the SHOW command for the system
; dependent features of baud rate and port selection.

shobd:	lea	dx, bdst	;Baud rate string.
	call	tcrmsg
	mov	al, mnbaud	;Print the keyword corresponding to the
	lea	bx, bdtab	;   current value of mnbaud.
	call	tabprt
	ret

shoprt:	ret			;Port selection not implemented.


	DSEG $

bdtab	db	7		;Thirteen entries		;[6] begin
	db	4,'1200$'
	dw	0002h
	db	5,'19200$'
	dw	0006h
	db	4,'2400$'
	dw	0003h
	db	3,'300$'
	dw	0000h
	db	4,'4800$'
	dw	0004h
	db	3,'600$'
	dw	0001h
	db	4,'9600$'
	dw	0006h						;[6] end

bdhlp	db	cr,lf,' 300	600	1200	2400'
	db	cr,lf,'	4800	9600	19200$'



; The following routines do screen control.  These are isolated here because
; the screen control sequences are likely to vary from system to system, even
; though the Rainbow and APC (the only systems implemented to date) both use
; ANSI sequences for this purpose.

	CSEG $

; POSCUR - positions cursor to row and col (each 1 byte) pointed to by dx.

poscur:	mov	bx, dx		;Do ANSI cursor positioning.
	mov	ax, [bx]	;
	add	ax, '  '	;Add 20h to both row and column
	mov	word ptr anspos+2, ax
	lea	dx, anspos	;Print cursor positioning string.
	call	tmsg
	ret

; CLRSCR - homes cursor and clears screen.

clrscr:	lea	dx, fujcls
	call	tmsg
	ret

; CLRLIN - clears from cursor to end of line.

clrlin:	mov	dl, cr		;Go to beginning of line
	call	bout
clreol:	lea	dx, ansclr	;Clear from cursor to end of line
	call	tmsg
	ret

; REVON - turns on reverse video display

revon:	lea	dx, ansron
	call	tmsg
	ret

; REVOFF - turns off reverse video display

revoff:	lea	dx, ansrof
	call	tmsg
	ret

; BLDON - turns on bold (highlighted) display

bldon:	lea	dx, ansbon	;
	call	tmsg		;
	ret

; BLDOFF - turns off bold (highlighted) display

bldoff:	lea	dx, ansbof	;
	call	tmsg		;
	ret


	DSEG $

anspos	db	esc,'=00$'	;Position cursor to row and column
fujcls	db	1Eh,1Ah,'$'	;Home cursor and clear screen
ansclr	db	esc,'T$'	;Clear from cursor to end of line
ansron	db	esc,'G',0Ch,'$'	;Turn on reverse video (green)
ansrof	db	esc,'G',04h,'$'	;Turn off reverse video (green)
ansbon	db	esc,'G',05h,'$'	;Turn on bold (uses blue)
ansbof	db	esc,'G',04h,'$'	;Turn off bold (back to green)


; Here tab expansion is done if necessary.  If not, just return retskp.

	cseg $

dotab:	jmp	rskp		;No, just proceed.

	dseg $
curbuf	db	2,0,0		;command, row, column of cursor position

delstr	db	10O, 10O, '$'	;Delete string.

system	db	'           FUJITSU Micro 16s     $'	;[1][20a]
<<< c86xfu.a86 >>>
; STARTSYSDEP	; This is so:
;               ;     PIP LISTING=86KERMIT.LST[WSSTARTSYSDEP^ZQENDxSYSDEP^Z]
;               ; will work.
;
;
; **************************************************************************
;
; This is the i/o support module for the Future Computers FX20/FX30
; Running CP/M-86 or Concurrent CP/M. 
;
;  Tony Chabot, University of Birmingham, UK   October 1985
;  (Based on the version for the Honeywell MSE by Mark Hewitt).
;
;  This module checks which type of CP/M system it is running under,
;  and alters its performance accordingly. No separate assembly is
;  required for CP/M-86 and Concurrent.
;  It is preferable to use the KERUTL from Concurrent implementations
;  as that will work with both types of CP/M, whereas the standard
;  version of KERUTL (for CP/M-86) will not work fully with Concurrent.
;
;  This implementation allows only the modem port to be used. 
;  This is because the printer port does not have its rx interrupt request
;  line connected to the 8259 interrupt controller.
;
;  The modem port uses a 7201 Multi Protocol Serial Controller, situated
;  at address C120-C123, with the relevant addresses for the modem part
;  of it being C121 & C123.(Note - the FX20/FX30 manual incorrectly gives
;  the addresses as C122 and C123).
;  The baud rate generator is an 8253-5, timer 1.
;  The range of addresses for this device is C060-C063. The generator
;  has a 4MHz clock input (gleaned from a scope, so this is not too accurate,
;  but a frequency meter seemed to think the frequency was 1MHz +- 10% !).
;  The 7201 is programmed to divide by 16, so the baud rate divider is given
;  by
;  divider = 4000000/(16*baudrate)
;
;  The 8259 interrupt controller is programmed (by CP/M) to base its vectors
;  at 100. The relationship between the 8259 IR lines and the requesting device
;  is:
;
;	IR0	?
;	IR1	PIT (out0)
;	IR2	7201
;	IR3	?
;	IR4	?
;	IR5	Keyboard 8251 RxRdy
;	IR6	Printer  8251 TxRdy
;	IR7	Pulled up.
;
;  Only IR0, IR1 and IR5 have useful interrupt service routines, as well
;  as the predefined vectors and CP/M system call vectors. All other
;  vectors point, via some register saving code, to an illegal interrupt
;  routine. It is thus simple to patch the vector for the 7201 (at 108-10B)
;  to point to our ISR.
;
;  ** W A R N I N G **
;
;  The 7201 also handles the LAN, so if you have lan software running, it
;  is likely that this code will cause problems.
;
;
; **************************************************************************
	CSEG $

; Port base definitions

bgen	equ	0C060h		; Baud Rate Generator            (8253-5)
ictrl	equ	0C000h		; Interrupt Controller           (8259A)

; And the I/O ports themselves

bgcmd	equ	bgen+3		; Baud rate generator command port

iccmd	equ	ictrl+0		; Interrupt controller command port
icmask	equ	ictrl+1		; Interrupt controller mask register port

mdmcmd	equ	0C123h		; modem command port
mdmbg	equ	bgen+1		; Baud rate countdown value for modem port
mdmio	equ	0C121h		;modem data io port (Note-FX30 manual is wrong)
ptrcmd	equ	0C122h
;
;
;	Port selection
;
pmdm	equ	0
;
; Interrupt vectors in page 0
;
mdmvec	equ	0108h		; Interrupt vector for modem.
;
; Interrupt masks
;
immdm	equ	04h		; Mask for modem port.
;
;  Baud rate generator command words
;
mdmbsel	equ	76h		; Select modem baud rate register
;
;  Interrupt controller commands
;
iceoi	equ	20h		; end of interrupt
;
;  I/O register bits:
;
;  For 7201 'Multiprotocol Serial Communications Controller' ( a name
;  worthy of IBM !).
;
ccreg0	equ	00h		; Control instruction - select register 0
ccreg1	equ	01h		; Control instruction - select register 1
ccreg2	equ	02h		; Control instruction - select register 2
ccreg3	equ	03h		; Control instruction - select register 3
ccreg4	equ	04h		; Control instruction - select register 4
ccreg5	equ	05h		; Control instruction - select register 5
ccreg6	equ	06h		; Control instruction - select register 6
ccreg7	equ	07h		; Control instruction - select register 7

c0null	equ	00h		; Register 0 - null command
c0abort	equ	08h		; Register 0 - send abort
c0resi	equ	10h		; Register 0 - reset ext. status ints.
c0chrst	equ	18h		; Register 0 - channel reset
c0eninc	equ	20h		; Register 0 - enable int. on next character
c0rpti	equ	28h		; Register 0 - reset pending tx int./DMA req.
c0errst	equ	30h		; Register 0 - error reset
c0eoi	equ	38h		; Register 0 - end of interrupt
c0rxcrc	equ	40h		; Register 0 - reset rx CRC checker
c0txcrc	equ	80h		; Register 0 - reset tx CRC generator
c0ricrc	equ	0C0h		; Register 0 - reset idle/CRC latch

c1stien	equ	01h		; Register 1 - external/status int enable
c1txien	equ	02h		; Register 1 - transmitter interrupt enable
c1cav	equ	03h		; Register 1 - condition affects vector
c1noi	equ	00h		; Register 1 - no rx or DMA interrupts
c1i1st	equ	08h		; Register 1 - int. on 1st received character
c1iall	equ	10h		; Register 1 - int. on all received characters
c1ialp	equ	18h		; Register 1 - int on all rx'd chars, no parity
c1wrxtx	equ	20h		; Register 1 - WAIT on rx/tx
c1txbcm	equ	40h		; Register 1 - TX byte count mode enbable
c1wten	equ	80h		; Register 1 - WAIT function enable
;
;  and some useful abbreviations
;
c1norm	equ	c1ialp
;
c2dma0	equ	00h		; Register 2 - No DMA
c2dma1	equ	01h		; Register 2 - DMA mode 1
c2dma2	equ	02h		; Register 2 - DMA mode 2
c2dma3	equ	03h		; Register 2 - DMA mode 3
c2pri	equ	04h		; Register 2 - Set DMA priority
c2ack0	equ	00h		; Register 2 - Int Ack mode 0 (NV,D432)
c2ack1	equ	08h		; Register 2 - Int Ack mode 1 (NV, D432)
c2ack2	equ	10h		; Register 2 - Int Ack mode 2 (NV, D210)
c2ack4	equ	20h		; Register 2 - Int Ack mode 4 (8085 master)
c2ack5	equ	28h		; Register 2 - Int Ack mode 5 (8085 slave)
c2ack6	equ	30h		; Register 2 - Int Ack mode 6 (8086)
c2ack7	equ	38h		; Register 2 - Int Ack mode 7(8085/8259A slave)
c2rxim	equ	40h		; Register 2 - rx interrupt mask
c2syncb	equ	80h		; Register 2 - pin 10 ~RTSB or ~SYNCB

c3rxen	equ	01h		; Register 3 - receive enable
c3scli	equ	02h		; Register 3 - sync character load inhibit
c3asm	equ	04h		; Register 3 - address search mode
c3rxcrc	equ	08h		; Register 3 - receiver CRC enable
c3hunt	equ	10h		; Register 3 - enter hunt phase
c3aen	equ	20h		; Register 3 - auto enables on DCD/CTS
c3r5bit equ	00h		; Register 3 - 5 bit data
c3r6bit equ	40h		; Register 3 - 6 bit data
c3r7bit equ	80h		; Register 3 - 7 bit data
c3r8bit equ	0C0h		; Register 3 - 8 bit data
;
;  and some useful abbreviations
;
c3norm	equ	c3rxen+c3r8bit
;
c4pen	equ	01h		; Register 4 - parity enable
c4ep	equ	02h		; Register 4 - even parity
c41stp	equ	04h		; Register 4 - 1 stop bit
c415stp	equ	08h		; Register 4 - 1.5 stop bits
c42stp	equ	0C0h		; Register 4 - 2 stop bits
c48syn	equ	00h		; Register 4 - 8 bit internal sync (monosync)
c416syn	equ	10h		; Register 4 - 16 bit internal sync (bisync)
c4sdlc	equ	20h		; Register 4 - SDLC
c4exts	equ	30h		; Register 4 - External sync
c41clk	equ	00h		; Register 4 - 1x clock rate
c416clk	equ	40h		; Register 4 - 16x clock rate
c432clk	equ	80h		; Register 4 - 32x clock rate
c464clk	equ	0C0h		; Register 4 - 64x clock rate
;
;  and some useful abbreviations
;
c4norm	equ	c41stp+c416clk
;
c5txcrc	equ	01h		; Register 5 - transmitter CRC enable
c5rts	equ	02h		; Register 5 - request to send
c5poly	equ	04h		; Register 5 - CRC polynomial select
c5txen	equ	08h		; Register 5 - transmitter enable
c5sbrk	equ	10h		; Register 5 - send break
c5t5bit	equ	00h		; Register 5 - transmit 5 bit data
c5t6bit	equ	20h		; Register 5 - transmit 6 bit data
c5t7bit	equ	40h		; Register 5 - transmit 7 bit data
c5t8bit	equ	60h		; Register 5 - transmit 8 bit data
c5dtr	equ	80h		; Register 5 - data terminal ready
;
;  and some useful abbreviations
;
c5norm	equ	c5rts+c5txen+c5t8bit+c5dtr
;
cs0rxr	equ	01h		; Status register 0 - received char ready
cs0ip	equ	02h		; Status register 0 - interrupt pending
cs0tbe	equ	04h		; Status register 0 - tx buffer empty
cs0dcd	equ	08h		; Status register 0 - data carrier detect
cs0sync	equ	10h		; Status register 0 - sync status
cs0cts	equ	20h		; Status register 0 - clear to send
cs0idle	equ	40h		; Status register 0 - idle CRC latch status
cs0brk	equ	80h		; Status register 0 - break detect

cs1sent	equ	01h		; Status register 1 - all sent
cs1sdlc	equ	0Eh		; Status register 1 - SDLC residue code
cs1pe	equ	10h		; Status register 1 - parity error
cs1oe	equ	20h		; Status register 1 - overrun error
cs1fe	equ	40h		; Status register 1 - framing error
cs1eosf	equ	80h		; Status register 1 - end of SDLC frame


;	System call defs for concurrent version.
p_dispatch	equ	8Eh	; Reschedule.
f_errmode	equ	2Dh	; Set BDOS error mode.

;
; Clock rate *10 for timing loops	;[19g]
;
clckrt	equ	80			;[19g]  8.0 Mhz
;
;  Maximum number of examinations of output port to be ready before
;  rescheduling.
;
outlmt	equ	1000h

;
;  The executable code starts here
;
;
; ===========================================================================
;
;                        INITIALISATION ROUTINES
;
; ===========================================================================
;
; INTERFACE ROUTINE SERINI - Initialisation code
;
serini:	cmp	mninit, true	; Ensure that we only initialise once
	je	serin2
	mov	mninit, true

;	Get type of CP/M system.
;
	mov	cl,0ch
	int	bdos
	mov	cpmtyp, bh

;
;  Initialise the screen
;
	call	clrscr		; Clear the screen.
;
;  Disable I/O interrupts, and save the old interrupt mask.
;
	mov	dx, icmask	; read the current interrupt mask
	in	al, dx
	mov	oldmsk, al	; and save it
	or	al, immdm	; mask off i/o interrupts
	out	dx, al		; and reprogram interrupt controller
;
;  Save the system i/o interrupt vectors
;
	mov	ax, ds		; save the data segment in code segment
	mov	cs:mndseg, ax	;   for use by interrupt handler

	mov	ax, 0		; point to zero page and save both the
	mov	es, ax		;   system's i/o interrupt vectors
	mov	ax,es:.mdmvec+0	;     for the modem channel
	mov	vscoff, ax
	mov	ax, es:.mdmvec+2
	mov	vscseg, ax

;  Configure the default port
;
	mov	ax, 0		; point to zero page and set the interrupt
	mov	es, ax		;   vector for the modem/printer channel to my
				;     interrupt service routine
	mov	ax, offset isr	; set offset address
	mov	es:.mdmvec+0, ax
	mov	ax, cs		; set segment address
	mov	es:.mdmvec+2, ax

	call	setmode		; set UART mode for current port
	call	setbaud		; set the baud rate for the current port
	call	mnflush		; flush and enable the current port
	call	inton		; turn interrupts on for current port

;	If concurrent, set BDOS error mode.
;
	cmp	cpmtyp, 14h
	jne	serin2
	mov	cl, f_errmode
	mov	dl, 0FEh	; Set err mode to display and return.
	int	bdos

serin2:	ret			; initialisation over

;
; INTERFACE ROUTINE SERFIN - restore environment (as far as possible)
;                            to that which existed before we played with it
;
serfin:	cmp	mninit, true	; only deinitialise if necessary
	jne	serfn2
	mov	mninit, false
;
;  Disable i/o interrupt while we reset the vectors
;
	mov	dx, icmask	; get present interrupt mask
	in	al, dx		; and turn off all i/o interrupts
	or	al, immdm	;   from the modem channel
	out	dx, al		; reprogram the interrupt controller
;
;  Reset the i/o interrupt vectors
;
	mov	ax, 0		; point at page 0 and reset the int. vectors
	mov	es, ax
	mov	ax, vscoff	;   for the modem/printer port
	mov	es:.mdmvec+0, ax
	mov	ax, vscseg
	mov	es:.mdmvec+2, ax
;
; turn interrupts back on (or off...)
;
	mov	al, oldmsk	; restore original interrupt mask
	out	dx, al
;
;  Reset screen modes
;
	call	clrscr		; Be tidy - clear the screen.

serfn2:	ret			; deinitialisation over

;
;
; INTERNAL ROUTINE SETMODE - set the operating mode for current port's UART.
;
setmode:
	push	ax
	push	dx		; we'll need this

	mov	dx, mdmcmd	; Command port adrs.

	mov	al, c0chrst	; reset the port
	out	dx, al
	mov	al, c0resi+ccreg4 ; select register 4
	out	dx, al
	mov	al, c4norm	; 16x Clock, 1 stop bit, no parity
	out	dx, al
	mov	al, c0resi+ccreg3 ; Select register 3
	out	dx, al
	mov	al, c3norm	; 8 bits/character, RX enable
	out	dx, al
	mov	al, c0resi+ccreg5 ; select register 5
	out	dx, al
	mov	al, c5norm	; 8 bits/character, TX enable RTS and DTR
	out	dx, al
	mov	al, c0resi+ccreg1 ; select register 1
	out	dx, al
	mov	al, c1norm	; Interrupt enable
	out	dx, al
	
	pop	dx		; modes now set, restore regs. and return
	pop	ax
	ret

;
;  INTERNAL ROUTINE SETBAUD - set the baud rate of a current port.
;                             port number in cport.
;                             timer countdown table offset in cbaud.
;
setbaud:
	push	bx		; we'll be using this
	push	dx		; and this
	push	ax		; and this too

	mov	al, bdtab	; check that rate is legal
	dec	al		; pick up number of valid rates from BDTAB
	cmp	cbaud, al	;  0 <= cbaud <= [bdtab]-1
	ja	setbd2		; just return if not legal

	mov	bx, offset bdtct; get timer value
	mov	al, cbaud	; from timer countdown table
	mov	ah, 0
	add	al, al		; word offset
	add	bx, ax		; bx now points to correct value

	mov	dx, bgcmd	; dx is now baud rate generator command port

	cmp	cport, pmdm	; is it the modem port?
	jne	setbd2		; just return if not

	mov	al, mdmbsel	; set baud rate for modem port
	out	dx, al
	mov	dx, mdmbg
	jmp	setbd3

setbd3:	mov	ax, [bx]	; set the countdown value
	out	dx, al
	mov	al, ah
	out	dx, al

setbd2:	pop	ax		; baud rate set, retore regs. and return
	pop	dx
	pop	bx
	ret

;
;  INTERNAL ROUTINE MNFLUSH - enable and flush current port.
;                             Port in cport.
;
mnflush:
	push	ax		; preserve registers
	push	dx

	mov	dx, mdmio	; Modem data port adrs.
	in	al, dx		; flush the port
	in	al, dx
	in	al, dx
	mov	dx, mdmcmd	; reset any pending interrupts
	mov	al, c0errst
	out	dx, al
	mov	al, c0resi
	out	dx, al

	pop	dx		; port flushed, retore regs. and return
	pop	ax
	ret

;
; INTERNAL ROUTINE INTON - enable interrupts for the selected port
;			   (This version simply enables the modem port!)
;                          Ensure that the port selected is enabled, and
;                          that all other ports are as the system would
;                          wish them!
inton:	
	push	ax
	push	dx

	mov	dx, icmask
	in	al,dx		; Read current interrupt mask.
	and	al, not immdm	; Enable modem interrupts.
	out	dx, al

inton2:	pop	dx
	pop	ax		; interrupts now enabled - restore regs.
	ret			;   and return

	DSEG $			; Data used by initialisation/deinitialisation

mninit	db	false		; flag set when initialised
oldmsk	rb	1		; Old interrupt mask
cpmtyp	db	0		; CP/M type (0 = CP/M-86, 14h = concurrent).
;

;  Current port status
;
cport	db	pmdm		; current port number - default to modem
cbaud	db	8		; current baud rate - default to 4800
ciop	dw	mdmio		; current i/o port - default to modem
ccmdp	dw	mdmcmd		; current command/status port - default modem
;
;  Storage for system interrupt vectors
;
vscoff	rw	1		; offset for system v.24/printer int. vector
vscseg	rw	1		; seg. address for system v.24/printer int. vec
;
;  Baud rate timer countdown table
;  (The accuracy of these is uncertain as the baud rate generator clock
;  frequency was measured with a scope).
;
bdtct	dw	5000		;    50 baud, code  0
	dw	3333		;    75
	dw	2273		;   110
	dw	1667		;   150
	dw	833		;   300
	dw	417		;   600
	dw	208		;  1200
	dw	104		;  2400
	dw	52		;  4800
	dw	26		;  9600
	dw	13		; 19200

	CSEG $

; ===========================================================================
;
;                             SET COMMANDS
;
; ===========================================================================
;
;  INTERFACE ROUTINE BDSET - set baud rate for current port (cport).
;                            save current baud rate in cbaud.
;
bdset:	mov	dx, offset bdtab	; table of valid baud rates
	mov	bx, offset bdhlp	; help information for SET BAUD
	mov	ah, cmkey		; Command parser - KEYWORD lookup
	call	comnd
	  jmp	r			; error return
	mov	settmp, bx		; Normal return - save value
	mov	ah, cmcfm		; Command parser - CONFIRM
	call comnd
	  jmp	r
	mov	bx, settmp
	mov	cbaud, bl		; save the baud rate
	call	setbaud			; and set it for the current port
	jmp	rskp			; end of parsing SET BAUD command

	DSEG $

settmp	rw	1			; temporary storage for baud rate

	CSEG $
;
;  INTERFACE ROUTINE PRTSET - set the current port.
;
prtset:	mov	dx, offset potab	; table of valid port names
	mov	bx, offset pohlp	; help information for SET PORT
	mov	ah, cmkey		; Command parser - KEYWORD lookup
	call	comnd
	  jmp	r			; error return

	mov	settmp, bx		; Normal return - save value

	mov	ah, cmcfm		; Command parser - CONFIRM
	call comnd
	  jmp	r
	jmp	rskp			; end of parsing SET PORT command
;
;  Data required by the SET commands
;
	DSEG $				; SET command data
;
;  Baud rate table
;
bdtab	db	11			; number of entries
	db	3, '110$'		; size of entry, and the keyword$
	dw	02			; value returned
	db	3, '150$'
	dw	03
	db	4, '1200$'
	dw	06
	db	5, '19200$'
	dw	10
	db	4, '2400$'
	dw	07
	db	3, '300$'
	dw	04
	db	4, '4800$'
	dw	8
	db	2, '50$'
	dw	00
	db	3, '600$'
	dw	05
	db	2, '75$'
	dw	01
	db	4, '9600$'
	dw	09
;
;  Help table for baud rate setting
;
bdhlp	db	cr, lf, '    50     75    110    150    300    600'
	db	cr, lf, '  1200   2400   4800   9600  19200'
	db	'$'
;
;  Port table
;
potab	db	1
	db	5, 'MODEM$'
	dw	pmdm
;
;  Help table for port selection
;
pohlp	db	cr, lf, 'MODEM$'

	CSEG $

; ===========================================================================
;
;                              SHOW COMMANDS
;
; ===========================================================================

;
;  INTERFACE ROUTINE SHOBD - display the currently set baud rate within
;                            the SHOW command.
;
shobd:	mov	dx, offset bdst	;Baud rate string.
	call	tcrmsg
	mov	al, cbaud	;Print the keyword corresponding to the
	mov	bx, offset bdtab; current value of mnbaud.
	call	tabprt
	ret

;
;  INTERFACE ROUTINE SHOPRT - display the currently selected communication
;                             port within the SHOW command.
;
shoprt:	mov	dx, offset prtst	; Port name string
	call	tcrmsg
	mov	al, cport		; current port code
	mov	bx, offset potab	; and print the corresponding
	call	tabprt			;   textual description
	mov	dx, offset prtst2
	call	tmsg
	ret

	DSEG $

prtst	db	'Communicating via $'
prtst2	db	' port$'

	CSEG $

; ===========================================================================
;
;                                I/O ROUTINES
;
; ===========================================================================
;
;  INTERNAL ROUTINE ISR - Interrupt service routine for modem port.
;

isr:	
	cli				; disable intrerupts
	mov	cs:mnax, ax		; save ax - we will need a register
	mov	ax, sp
	mov	cs:mnsp, ax		; save current stack pointer
	mov	ax, ss
	mov	cs:mnsseg, ax		; Save current stack segment
	mov	ax, cs:mndseg		; Switch to my stack
	mov	ss, ax
	mov	sp, offset mnstk
	push	ds			; Save registers
	push	es
	push	bp
	push	di
	push	si
	push	dx
	push	cx
	push	bx
	mov	ds, ax			; set our data segment address
;
;  That's the housekeeping out of the way - now we can start
;

	mov	dx, mdmcmd		; see if char. ready at default port
	in	al, dx
	test	al, cs0rxr		; is there a character for us?
	jz	iprt3			; no - clear interrupt, and return

iprt2:	mov	dx, mdmio		; Fetch the character
	in	al, dx
	call	iproc			; Process the character in AL

iprt3:	mov	dx, iccmd		; Signal end of interrupt to
	mov	al, iceoi		;   interrupt controller
	out	dx, al

	mov	dx, ptrcmd		; Clear interrupt status at 
	mov	al, c0eoi		;   7201
	out	dx, al			;    (note we use the A channel).

	pop	bx			; Restore registers
	pop	cx
	pop	dx
	pop	si
	pop	di
	pop	bp
	pop	es
	pop	ds
	mov	ax, cs:mnsp		; restore interrupt stack
	mov	sp, ax
	mov	ax, cs:mnsseg		; restore original stack segment
	mov	ss, ax
	mov	ax, cs:mnax		; restore original AX
	iret				; all over - return

;
;  CSEG data required by interrupt service routine
;
mnax	dw	0		; temp. copy of AX
mnsp	dw	0		; interrupt stack pointer
mnsseg	dw	0		; interrupt stack segment
mndseg	dw	0		; location of our data segment

;
; INTERNAL ROUTINE IPROC - process incoming character from Rx interrupt
;                          Character in AL
;

iproc:	cmp	floctl, floxon	;are we doing flow-control ?	[19a] start
	jne	ipr2b		;no - go on
	cmp	al, xoff	;is it an XOFF?
	jne	ipr2a		;no - go on
	mov	xofrcv, true	;set the flag
	ret

ipr2a:	cmp	al, xon		;an XON?
	jne	ipr2b		;no
	mov	xofrcv, false	;clear the flag
	ret			;				[19a] end

ipr2b:	cmp	mnchrn,mnchnd	;Is the buffer full?
	je	iperr		;If so, take care of the error.
	inc	mnchrn		;Increment the character count.
	mov	bx,mnchip	;Get the buffer input pointer.
	inc	bx		;Increment it.
	cmp	bx,offset mnchrs+mnchnd ;Past the end?
	jb	ipro3
	mov	bx, offset mnchrs ;Yes, point to the start again.
ipro3:	mov	mnchip,bx	;Save the pointer.
	mov	[bx],al		;Put the character in the buffer.
	cmp	floctl, floxon	;do flow-control?		[19a] start
	je	ipro4		;If yes jump
	ret

ipro4:	cmp	xofsnt, true	;Have we sent an XOFF
	jnz	ipro5
	ret			;return if we have

ipro5:	cmp	mnchrn, mntrg2	;Past the High trigger point?
	ja	ipro6		;yes - jump
	ret

ipro6:	mov	al, xoff
	call	prtout		;send an XOFF
	mov	xofsnt, true	;set the flag
	ret			;				[19a] End

iperr:	ret			; just return on error for now

;
;  INTERFACE ROUTINE CFIBF - Clear serial port input buffer
;
cfibf:	mov	mnchrn, 0	;Say no characters in the buffer.
	mov	mnchip, OFFSET mnchrs-1+mnchnd ;Reset input pointer.
	mov	mnchop, OFFSET mnchrs-1+mnchnd ;Reset output pointer.
	ret

;
;  INTERFACE ROUTINE PRTOUT - send character in AL to current port.
;
prtout:	call	dopar		; set parity if necessary
	push	dx
	push	cx

	mov	cx, outlmt
prtou2:	call	outwait		; wait for port to be free, or timeout
	  loop	prtou2
	  nop
	call	outchr		; output the character
	pop	cx
	pop	dx
	ret

;
; INTERNAL ROUTINE OUTWAIT - test if port ready for next char to be sent.
;                            returns RSKP if ready.
;
outwait:
	cmp	floctl, floxon
	jne	outwt1
	cmp	xofrcv, true
	je	outwt3
outwt1:
	push	ax
	mov	dx, mdmcmd
	in	al, dx
	test	al, cs0tbe
	jnz	outwt4
	pop	ax
outwt3:	
	cmp	cpmtyp, 14h	; Concurrent?
	jne	outwt35		; No.
	call	dispatch	; Yes - redispatch the processor.
outwt35:
	ret

outwt4:	pop	ax
	jmp	rskp
;
;  INTERNAL ROUTINE OUTCHR - send data to a port
;
outchr:	mov	dx, mdmio
	out	dx, al
	ret

;
; INTERFACE ROUTINE INSTAT - determine if there is any data to receive.
;
instat:	cmp	mnchrn, 0		; any characters in buffer?
	jne	inst2
	ret
inst2:	jmp	rskp

;
;  INTERFACE ROUTINE INCHR - read a character from a port
;
inchr:	push	bx
	cli			;Disable interrupts while were are playing.
	dec	mnchrn		;Decrement the number of chars in the buffer.
	mov	bx,mnchop	;Get the pointer into the buffer.
	inc	bx		;Increment to the next char.
	cmp	bx,offset mnchrs+mnchnd ;Past the end?
	jb	inchr2
	mov	bx, offset mnchrs ;If so wrap around to the start.
inchr2:	mov	mnchop,bx	;Save the updated pointer.
	mov	al,[bx]		;Get the character.
	sti			;All done, we can restore interrupts.
	pop	bx
	cmp	parflg,parnon	;[par] no parity?
	je	inchr3		;[par] yup, don't bother stripping
	and	al,7fh		;[par] checking parity, strip off
inchr3:	cmp	floctl, floxon	;do flow-control?		[19a] start
	je	inchr4		;If yes jump
	ret
inchr4:	cmp	xofsnt, true	;Have we sent an XOFF
	je	inchr5		;Jump if yes
	ret
inchr5:	cmp	mnchrn, mntrg1	;Under the low trigger point?
	jb	inchr6		;yes - jump
	ret
inchr6:	push	ax		;save current character
	mov	al, xon
	call	prtout		;send an XON
	mov	xofsnt, false	;turn off the flag
	pop	ax		;get back character
	ret			;				[19a] end

;
;  INTERFACE ROUTINE PRTBRK - Send a BREAK sequence to the default port
;
prtbrk:	mov	dx, mdmcmd		; Modem port cmd byte.
	cmp	cport, pmdm		; is it modem port?
	jne	brka			; must be an error - just return

brkc:
	mov	al, c0resi+ccreg5	; Break to modem port.
	out	dx, al			; Select register 5
	mov	al, c5norm+c5sbrk	; 8 bits, TX enable, Break, RTS & DTR
	out	dx, al
	mov	ax, 275			; for 275 mS
	call	mswait
	mov	al, c0resi+ccreg5	; select register 5
	out	dx, al
	mov	al, c5norm		; 8 bits, TX enable, RTS & DTR
	out	dx, al
	ret

brka:	ret

	DSEG $
;
;  Input character queue
;
mnchnd	equ	512		;Size of circular buffer.
mnchrs	rb	mnchnd		;Circular character buffer for input.
mnchip	dw	mnchrs-1+mnchnd	;Input pointer into character buffer.
mnchop	dw	mnchrs-1+mnchnd	;Output pointer into character buffer.
mnchrn	dw	0		;Number of chars in the buffer.

mntrg1	equ	128		;[19a] Low trigger point for Auto XON/XOFF
mntrg2	equ	384		;[19a] High trigger point for Auto XON/XOFF

floctl	db	1		;[19a] If floctl=floxon do Auto XON/XOFF logic
xofsnt	db	0		;[19a] set if XOFF was sent
xofrcv	db	0		;[19a] set if XOFF was recieved
;
;  a small stack for interrupt handling
;
	rw	64		;Interrupt stack		;[28e]
mnstk	dw	0		;bottom of stack		;[28e]

	CSEG $

; ===========================================================================
;
;                             UTILITY ROUTINES
;
; ===========================================================================
;
; INTERNAL ROUTINE MSWAIT - Delay for AL milliseconds
;
mswait:				;				[34] start
	mov	cx,5*clckrt	; inner loop count for 1 millisec.
mswai1:
	sub	cx,1		;** inner loop takes 20 clock cycles
	jnz	mswai1		;**
	dec	ax		; outer loop counter
	jnz	mswait		; wait another millisecond
	ret			;				[34] end


;
; INTERNAL ROUTINE DISPATCH
;
;	Function	Redispatch processor.
;
;	Inputs		None.
;
;	Outputs		None.
;
;	Side effects	Processor is redispatched.
;			All registers preserved.
;
dispatch:
	push	ax
	push	bx
	push	cx
	mov	cl, p_dispatch
	int	bdos
	pop	cx
	pop	bx
	pop	ax
	ret


; ===========================================================================
;
;                        SCREEN CONTROL ROUTINES
;
; ===========================================================================
;
; INTERFACE ROUTINE POSCUR - positions cursor to row and col (each 1 byte)
;                            pointed to by dx.
;

poscur:	
	mov	bx, dx		;Do cursor positioning.
	mov	dx, offset scrpos	;Print cursor positioning string.
	call	tmsg
	mov	al, [bx]	;Get row value
	add	ax, 1Fh		;Convert to ASCII
	mov	dl,al
	push	bx		;(Gets clobbered by bout)
	call	bout
	pop	bx
	mov	al, 1[bx]	;Do same for column value
	add	ax, 1Fh
	mov	dl,al
	call	bout
	ret

;
; INTERFACE ROUTINE CLRSCR - homes cursor and clears screen.
;

clrscr:	mov	dx, offset scrcls
	call	tmsg
	ret
;
; INTERFACE ROUTINE CLRLIN - clears line.
;

clrlin:	mov	dl, cr		;Go to beginning of line
	call	bout
;
;	...FALL THROUGH
;
;  INTERFACE ROUTINE CLREOL - clear to end of line
;

clreol:	mov	dx, offset scrclr ;Clear from cursor to end of line
	call	tmsg
	ret

;
; INTERFACE ROUTINE REVON - turns on reverse video display
;

revon:	mov	dx, offset scrron
	call	tmsg
	ret

;
; INTERFACE ROUTINE REVOFF - turns off reverse video display
;

revoff:	mov	dx, offset scrrof
	call	tmsg
	ret
;
; INTERFACE ROUTINE BLDON - turns on bold (highlighted) display
;

bldon:	mov	dx, offset scrbon
	call	tmsg
	ret
;
; INTERFACE ROUTINE BLDOFF - turns off bold (highlighted) display
;

bldoff:	mov	dx, offset scrbof
	call	tmsg
	ret


	DSEG $

scrpos	db	esc, 'Y$'	;Position cursor to row and column
scrcls	db	esc, 'E$'	;Home cursor and clear screen
scrclr	db	esc, 'K$'	;Clear from cursor to end of line
scrron	db	esc, 'p$'	;Turn on reverse video
scrrof	db	esc, 'q$'	;Turn off reverse video
scrbon	db	esc, 'm$'	; Bold on (Actually underline).
scrbof	db	esc, 'n$'	; Bold off

	CSEG $
;
; INTERFACE ROUTINE DOTAB - do tab expansion if necessary
;
dotab:	jmp	rskp		; assume h/w does it for now

;
;  Assorted textual constants required as part of the machine interface
;
	DSEG $

delstr  db	esc,'D ',esc,'D$'	;Delete string.
system	db	'  Future Computers FX20/FX30 (TC - Oct 85)$'

	CSEG $
;
; ENDSYSDEP
;
<<< c86xr2.a86 >>>
; * * * * * * * * * * * * * * *  version 2.9  * * * * * * * * * * * * * * *
; [35a] Kermit now sets a default baud rate.
; [35b] Implemented the SET BAUD command.
; [35c] Implemented the baud rate function to the SHOW command.
;       The above was done to make operation compatable with CPM-80 version 
;       and simplify use by non computer orientated users.
;       Mark Woollard, 4/12/85, Animal and Grassland Research Institute
; * * * * * * * * * * * * * * *  version 2.8  * * * * * * * * * * * * * * *
; [34] Insert milli-second wait-loop for Break-timing - label MSWAIT:
; [33] Fix printer on hanging system problem by letting CP/M handle the
;	interrupts from the 7201 that we don't care about.  Thanks to
;	Paul Ford, U. of Chicago Graduate School of Business
; * * * * * * * * * * * * * * *  version 2.7  * * * * * * * * * * * * * * *
; [30d] Add SET PORT command, currently unimplemented.
; [30c] Isolate all machine dependencies in KERIO.
;	RonB, 04/18/84
; * * * * * * * * * * * * * * *  version 2.6  * * * * * * * * * * * * * * *
; [28e] Switch to local stack on interrupts.
;	RonB, 03/28/84
; * * * * * * * * * * * * * * *  version 2.4  * * * * * * * * * * * * * * *
; [20b] Add PRTBRK to send break & set correct clock rate for NEC.
; [20d] Add a pseudo time-out to PRTOUT so it doesn't loop forever.
;	RonB,03/02/84
; [19a] Add XON/XOFF type flow control
; [19b]	Clear screen and beginning and end of program.
; [19e]	Add PRTBRK to send break to port (Rainbow only)
; [19g]	Put in EQU for clock rate for timing loops.
;	Rg, 2/84 	<Oc.Garland%CU20B@Columbia-20>
; * * * * * * * * * * * * * * *  version 2.3  * * * * * * * * * * * * * * *
;  [par] Added calls to set parity, strip parity on input if
;	other than none parity is called for.
;	 JD, 2/84
; * * * * * * * * * * * * * * *  version 2.2  * * * * * * * * * * * * * * *
;  [2]	Add a de-initialization routine for the serial port, to restore
;	changed interrupt vectors, etc.
;	RonB,12/23/83
;  [1]	Add I/O support for the NEC Advanced Personal Computer
;	RonB,12/23/83
; * * * * * * * * * * * * * * *  version 2.0  * * * * * * * * * * * * * * *

; This module contains all the low level communications port I/O
; routines.

; The following is the I/O code for the DEC Rainbow.

		CSEG $

; Clock rate *10 for timing loops	;[19g]
clckrt	equ	48		;[19g]  4.8 Mhz 

; Interrupt vector locations, in data segment 
mnstat	EQU	042H		;Status port.
mndata	EQU	040H		;Data port.
mnctrl	EQU	002H		;Control port.

; Interrupt vector locations.  These are all in data segment 0.

mnoff	EQU	90H		;Main data port interrupt routine offset.
mnseg	EQU	92H		;Main data port interrupt routine segment.

output	EQU	04H		;Bit for output ready.
input	EQU	01H		;Bit for input ready.

outlmt	EQU	1000H		;Number of times to check output status
				; before giving up on send.		;[20d]

defbd	EQU	0BH		; [35a] Default baud rate, 0=50 baud, 1=75, 
				; 2=110, 3=134.5, 4=150, 5=200, 6=300, 7=600
				; 8=1200, 9=1800, 10=2000, 11=2400, 12=3600
				; 13=4800, 14=9600 and 15=19200

; Input data from port.  Preserves all ACs and returns char in
; AL.  Gets the char from the ring buffer.  Assumes a char is
; already there.

inchr:	push	bx
	cli			;Disable interrupts while were are playing.
	dec	mnchrn		;Decrement the number of chars in the buffer.
	mov	bx, mnchop	;Get the pointer into the buffer.
	inc	bx		;Increment to the next char.
	cmp	bx, offset mnchrs+mnchnd ;Past the end?
	jb	inchr2
	mov	bx, offset mnchrs ;If so wrap around to the start.
inchr2:	mov	mnchop, bx	;Save the updated pointer.
	mov	al, [bx]	;Get the character.
	sti			;All done, we can restore interrupts.
	pop	bx
	cmp	parflg,parnon	;[par] no parity?
	je	inchr3		;[par] yup, don't bother stripping
	and	al,7fh		;[par] checking parity, strip off
inchr3:	cmp	floctl, floxon	; do flow control?		[19a] start
	je	inchr4		;If yes jump
	ret
inchr4:	cmp	xofsnt, true	;Have we sent an XOFF
	je	inchr5		;Jump if yes
	ret
inchr5:	cmp	mnchrn, mntrg1	;Under the low trigger point?
	jb	inchr6		;yes - jump
	ret
inchr6:	push	ax		;save current character
	mov	al, xon
	call	prtout		;send an XON
	mov	xofsnt, false	;turn off the flag
	pop	ax		;get back character
	ret			;				[19a] end


; Output data to port. Trashes DX and prints char in AL.

outchr:	mov	dx, mndata
	out	dx, al
	ret


; Test if data is available from port.

instat:	cmp	mnchrn, 0	;Any chars in the buffer?
	jnz	inst2
	 ret
inst2:	jmp	rskp


; Test if port is ready to send next char.  Returns RETSKP if ready.
; Trashes dx.

outwt:	cmp	floctl, floxon	;are we doing flow-control?	[19a] start
	jne	outwta		;no - go on
	cmp	xofrcv, true	;are we being held?
	jne	outwta		;no - ok go on
	ret			;held - say we're busy.	[19a] end
outwta:	push	ax
	mov	dx, mnstat
	in	al, dx
	test	al, output
	pop	ax
	jnz	outwt2
	 ret
outwt2:	jmp	rskp


; Output the character, checking first to make sure the port is clear.

prtout:	call	dopar		;[par]
	push	dx
	push	cx						;[20d] begin
	mov	cx,outlmt
prtou2:	call	outwt		;Wait until the port is ready
	 loop	prtou2		; or too much time has passed.
	 nop
	call	outchr		;Output it.
	pop	cx						;[20d] end
	pop	dx
	ret


mnax	dw	0		;Storage in CSEG		;[28e] begin
mnsp	dw	0		;  for use by interrupt handler
mnsseg	dw	0
mndseg	dw	0

; This routine handles the interrupts on input.

mnint:	cli
	mov	cs:mnax, ax	;Save interrupt stack location.
	mov	ax, sp
	mov	cs:mnsp, ax
	mov	ax, ss
	mov	cs:mnsseg, ax
	mov	ax, cs:mndseg	;Switch to our internal stack.
	mov	ss, ax
	mov	sp, offset mnstk
	push	ds		;Save all registers.
	push	es
	push	bp
	push	di
	push	si
	push	dx
	push	cx
	push	bx
	mov	ds, ax
	call	mnproc		;Process the character.
	mov	dx, mnstat	;Get the status port.
	mov	al, 38H
	out	dx, al		;Tell the port we finished with the interrupt.
	pop	bx		;Restore all registers.
	pop	cx
	pop	dx
	pop	si
	pop	di
	pop	bp
	pop	es
	pop	ds
	mov	ax, cs:mnsp	;Restore the original stack.
	mov	sp, ax
	mov	ax, cs:mnsseg
	mov	ss, ax
	mov	ax, cs:mnax
	iret			;Return from the interrupt.	;[28e] end


; This routine (called by MNINT) gets a char from the main port
; and puts it in the infamous circular buffer.

mnproc:	mov	dx, mnstat
	in	al, dx		;Get the port status.
	test	al, input	;Any there?
	jnz	mnpro2		;Yup, go take care of it.

;[33] Begin addition
; If not a received character, simulate an interrupt transferring
; control to the CPM routine. Let it handle worrisome things like
; someone turning on the printer.

	 pushf			; Save flags, like an int.
	 callf dword ptr mnoldo	; Call CPM's routine.
	 ret			; Now back to MNINT.

;[33] End addition

mnpro2:	mov	al, 1		;Point to RR1.
	out	dx, al
	in	al, dx		;Read RR1.
	mov	ah, al		;Save it.
	mov	al, 30H		;Reset any errors.
	out	dx, al
	mov	dx, mndata
	in	al, dx		;Read the char.
	cmp	floctl, floxon	;are we doing flow-control ?	[19a] start
	jne	mnpr2b		;no - go on
	cmp	al, xoff	;is it an XOFF?
	jne	mnpr2a		;no - go on
	mov	xofrcv, true	;set the flag
	ret
mnpr2a:	cmp	al, xon		;an XON?
	jne	mnpr2b		;no
	mov	xofrcv, false	;clear the flag
	ret			;				[19a] end
mnpr2b:	cmp	mnchrn, mnchnd	;Is the buffer full?
	je	mnperr		;If so, take care of the error.
	inc	mnchrn		;Increment the character count.
	mov	bx, mnchip	;Get the buffer input pointer.
	inc	bx		;Increment it.
	cmp	bx, offset mnchrs+mnchnd ;Past the end?
	jb	mnpro3
	mov	bx, offset mnchrs ;Yes, point to the start again.
mnpro3:	mov	mnchip, bx	;Save the pointer.
	mov	[bx], al	;Put the character in the buffer.
	cmp	floctl, floxon	;do flow control?		[19a] start
	je	mnpro4		;If yes jump
	ret
mnpro4:	cmp	xofsnt, true	;Have we sent an XOFF
	jnz	mnpro5
	ret			;return if we have
mnpro5:	cmp	mnchrn, mntrg2	;Past the High trigger point?
	ja	mnpro6		;yes - jump
	ret
mnpro6:	mov	al, xoff
	call	prtout		;send an XOFF
	mov	xofsnt, true	;set the flag
	ret			;				[19a] End

mnperr:	ret			;Just return on an error for now.


; prtbrk - send a break		;				[19e] start

prtbrk:				;
	mov	dx, mnstat	;status reg. address for port
	mov	al, 15H		;select reg. 5
	out	dx, al		;
	mov	al, 0FAH	;8 bits, TX, Break, RTS, & DTR
	out	dx, al		;Turn Break on
	mov	ax, 275		;.. for 275 millisec's		[34]
	call	mswait		;				[34]
	mov	al, 15H		;select reg. 5
	out	dx, al		;
	mov	al, 0EAH	;same as above without Break
	out	dx, al		;turn it off
	ret			;				 [19e] end

mswait:				;				[34] start
	mov	cx,5*clckrt	; inner loop count for 1 millisec.
mswai1:
	sub	cx,1		;** inner loop takes 20 clock cycles
	jnz	mswai1		;**
	dec	ax		; outer loop counter
	jnz	mswait		; wait another millisecond
	ret			;				[34] end
;
; Init the 7201 for 8 bits, no parity, and 1 stop bit.

serini:	call	ansmod		;Switch from VT52 to ANSI mode	;[30c]

	mov	ax, ds
	mov	cs:mndseg, ax	;Save the data segment somewhere in CSEG.
	push	ds		;Save the data segment.
	mov	ax, 0
	mov	ds, ax		;We want DSEG = 0.
	cli			;Turn off interrupts.
	mov	bx, .mnoff	;[33] Get original interrupt offset.
	mov	es, .mnseg	;[33] Get original interrupt segment.
	mov	ax, offset mnint;Point to the interrupt routine offset.
	mov	.mnoff, ax	;Put in the main port interrupt offset addr.
	mov	ax, cs		;Get our code segment.
	mov	.mnseg, ax	;Put in the main port interrupt segment addr.
	sti			;Restore interrupts.
	pop	ds		;Restore data segment.

	mov	mnoldo, bx	;[33] Stash original serial interrupt offset.
	mov	mnolds, es	;[33] Stash original segment.

	mov	dx, mnstat	;Point to status port.
	mov	al, 18H
	out	dx, al		;Reset the port.
	mov	al, 14H
	out	dx, al		;Select register 4.
	mov	al, 44H		;16X clock, 1 stop bit, no parity.
	out	dx, al
	mov	al, 13H
	out	dx, al		;Select register 3.
	mov	al, 0C1H	;8 bits/char, RX enable.
	out	dx, al
	mov	al, 15H
	out	dx, al		;Select register 5.
	mov	al, 0EAH	;8 bits/char, TX enable, RTS and DTR.
	out	dx, al
	mov	al, 11H
	out	dx, al		;Select register 1.
	mov	al, 18H
	out	dx, al		;Enable interrupt processing on this port.
	mov	dx, mnctrl	;point to comm control port
	mov	al, 0F0H	;set RTS & DTR high
	out	dx, al
	mov	al,defbd	; [35a] Get default baud
	mov	dl,al		; Save it
	mov	cl,4		; Move low nibble into high
	shl	al,cl		;
	or	al,dl		; Replace low nibble
	out	6,al		; Write to baud control port
	mov	baudrt,al	; Save it for show baud         ;[35a] end
	ret

serfin:	
	call	clrscr		;[19b] clear screen		;[30c]

	ret			;Nothing to deinitialize on Rainbow.

; This routine clears the serial port input buffer.  It is called to
; clear out excess NAKs that can result from server mode operation.

cfibf:	mov	mnchrn, 0	;Say no characters in the buffer.
	mov	mnchip, offset mnchrs-1+mnchnd ;Reset input pointer.
	mov	mnchop, offset mnchrs-1+mnchnd ;Reset output pointer.
	ret

	DSEG $

mnchnd	equ	256		;[19a] Size of circular buffer.
mntrg1	equ	64		;[19a] Low trigger point for Auto XON/XOFF
mntrg2	equ	192		;[19a] High trigger point for Auto XON/XOFF

floctl	db	1		;[19a] If floctl=floxon do Auto XON/XOFF logic
xofsnt	db	0		;[19a] set if XOFF was sent
xofrcv	db	0		;[19a] set if XOFF was recieved

mnchrn	DW	0		;[19a] Number of chars in the buffer.
mnchrs	RB	mnchnd		;Circular character buffer for input.
mnchip	DW	mnchrs-1+mnchnd	;Input pointer into character buffer.
mnchop	DW	mnchrs-1+mnchnd	;Output pointer into character buffer.

mnoldo	RW	1		;[33] CPM's 7201 interrupt vector offset
mnolds	RW	1		;[33] and segment.

	rw	32		;Interrupt stack		;[28e]
mnstk	dw	0		;bottom of stack		;[28e]

baudrt	rb	1		;Current baud rate

	CSEG $

; The following routines do the SET and SHOW for the machine dependent
; features of Kermit.  At present there are only two:  baud rate setting
; and port selection.

;	This is the SET BAUD rate subcommand (not implemented in Rainbow)

bdset:	mov	ah, cmcfm
	call	comnd			;Get a confirm.
	jmp	bdset1			; Didn't get a confirm.
	jmp	bdset2			; [35b] Go and ask for baud
bdset1:	mov	dx, offset ermes3	; Tell user unknown command option
	call	tcrmsg
	jmp	rskp
bdset2:	mov	dx, offset bdlst	; Print list of baud speeds
	call	tmsg
bdstrt:	mov	cl,1			; Get a response from keyboard
	int	224
	and	al,5fH			; Force upper case
	cmp	al,'A'
	jnb	bdset3			; AL >= 'A'...
	jmp	bdset4			; Response too low
bdset3:	cmp	al,'P'
	jle	bdset5			; AL <= 'P'...
bdset4:	mov	dx,offset rtrmss	; Incorrect response so delete char
	call	tmsg			; typed and make keyboard beep
	jmp	bdstrt			; Go and try again...
bdset5:	sub	al,'A'			; Convert to internal baud rate code
	mov	dl,al			; Save AL
	mov	cl,4			; Move lower nibble into upper
	shl	al,cl
	or	al,dl			; replace lower nibble
	out	6,al			; Write to baud port
	mov	baudrt,al		; Save for SHOW BAUD         ;[35b] end
	jmp	rskp

;	This is the SET PORT subcommand (not implemented in Rainbow)

prtset:	mov	ah, cmcfm
	call	comnd		;Get a confirm.
	 jmp	$+3		; Didn't get a confirm.
	mov	dx, offset infms6 ;Tell user it's not implemented
	call	tcrmsg
	jmp	rskp


; The following procedures implement the SHOW command for the system
; dependent features of baud rate and port selection.

; SHOW BAUD command

shobd:	mov	dx, offset bdst		;[35c] Print 'Baud rate:'
	call	tcrmsg
	mov	al,baudrt		; Get current baud rate
	and	al,0FH			; Mask off high nibble
	mov	cl,6			; Multiply by 6
	mul	cl
	add	ax, offset bdmss2	; Set up DX as an offset into
	mov	dx,ax			; the list of baud strings
	call	tmsg			; Print the string
	ret				; 			;[35c] end

shoprt:	ret			;Port selection not implemented.




; The following routines do screen control.  These are isolated here because
; the screen control sequences are likely to vary from system to system, even
; though the Rainbow and APC (the only systems implemented to date) both use
; ANSI sequences for this purpose.

	CSEG $

; POSCUR - positions cursor to row and col (each 1 byte) pointed to by dx.

poscur:	mov	bx, dx		;Do ANSI cursor positioning.
	mov	cl, 10
	mov	al, [bx]	;Get row value
	sub	ah, ah
	div	cl		;units digit in ah, tens digit in al
	add	ax, '00'	;Convert both to ASCII
	mov	word ptr anspos+2, ax	;Save reversed (al,ah)
	mov	al, 1[bx]	;Do same for column value
	sub	ah, ah
	div	cl
	add	ax, '00'
	mov	word ptr anspos+5, ax
	mov	dx, offset anspos ;Print cursor positioning string.
	call	tmsg
	ret

; CLRSCR - homes cursor and clears screen.

clrscr:	mov	dx, offset anscls
	call	tmsg
	ret

; CLRLIN - clears from cursor to end of line.

clrlin:	mov	dl, cr		;Go to beginning of line
	call	bout
clreol:	mov	dx, offset ansclr ;Clear from cursor to end of line
	call	tmsg
	ret

; REVON - turns on reverse video display

revon:	mov	dx, offset ansron
	call	tmsg
	ret

; REVOFF - turns off reverse video display

revoff:	mov	dx, offset ansrof
	call	tmsg
	ret

; BLDON - turns on bold (highlighted) display

bldon:	mov	dx, offset ansbon
	call	tmsg
	ret

; BLDOFF - turns off bold (highlighted) display

bldoff:	mov	dx, offset ansbof
	call	tmsg
	ret

; ANSMOD - enters ANSI mode from VT52 mode

ansmod:	mov	dx, offset ansion
	call	tmsg
	ret

	DSEG $

anspos	db	esc,'[00;00H$'		;Position cursor to row and column
anscls	db	esc,'[H',esc,'[J$'	;Home cursor and clear screen
ansclr	db	esc,'[K$'		;Clear from cursor to end of line
ansron	db	esc,'[7m$'		;Turn on reverse video
ansrof	db	esc,'[m$'		;Turn off reverse video
ansbon	db	esc,'[1m$'		;Turn on bold (highlight) display
ansbof	db	esc,'[m$'		;Turn off bold display
ansion	db	esc,'<$'		;Enter ANSI mode

; Here tab expansion is done if necessary.  If not, just return retskp.

	CSEG $
dotab:	jmp rskp

	DSEG $
delstr	db	' ',10O,10O,'$'	;Delete string.

system	db	'                DEC Rainbow-100$'

bdlst	db	esc,'[H',esc,'[J','Baud rates available :'	;[35b] Baud rate list 
	db	cr,lf,cr,lf,'       A)    50'			;for SET BAUD
	db	cr,lf,'       B)    75'
	db	cr,lf,'       C)   110'
	db	cr,lf,'       D)   134.5'
	db	cr,lf,'       E)   150'
	db	cr,lf,'       F)   200'
	db	cr,lf,'       G)   300'
	db	cr,lf,'       H)   600'
	db	cr,lf,'       I)  1200'
	db	cr,lf,'       J)  1800'
	db	cr,lf,'       K)  2000'
	db	cr,lf,'       L)  2400'
	db	cr,lf,'       M)  3600'
	db	cr,lf,'       N)  4800'
	db	cr,lf,'       O)  9600'
	db	cr,lf,'       P) 19200'
	db	cr,lf,cr,lf,'Enter choice > $'

rtrmss	db	8,' ',8,7,'$'		;[35b] Delete char and beep string

bdmss2	db	'50   $'		;[35c] Used by SHOW BAUD option
	db	'75   $'
	db	'110  $'
	db	'134.5$'
	db	'150  $'
	db	'200  $'
	db	'300  $'
	db	'600  $'
	db	'1200 $'
	db	'1800 $'
	db	'2000 $'
	db	'2400 $'
	db	'3600 $'
	db	'4800 $'
	db	'9600 $'
	db	'19200$'
<<< c86xrb.a86 >>>
; * * * * * * * * * * * * * * *  version 2.8  * * * * * * * * * * * * * * *
; [34] Insert milli-second wait-loop for Break-timing - label MSWAIT:
; [33] Fix printer on hanging system problem by letting CP/M handle the
;	interrupts from the 7201 that we don't care about.  Thanks to
;	Paul Ford, U. of Chicago Graduate School of Business
; * * * * * * * * * * * * * * *  version 2.7  * * * * * * * * * * * * * * *
; [30d] Add SET PORT command, currently unimplemented.
; [30c] Isolate all machine dependencies in KERIO.
;	RonB, 04/18/84
; * * * * * * * * * * * * * * *  version 2.6  * * * * * * * * * * * * * * *
; [28e] Switch to local stack on interrupts.
;	RonB, 03/28/84
; * * * * * * * * * * * * * * *  version 2.4  * * * * * * * * * * * * * * *
; [20b] Add PRTBRK to send break & set correct clock rate for NEC.
; [20d] Add a pseudo time-out to PRTOUT so it doesn't loop forever.
;	RonB,03/02/84
; [19a] Add XON/XOFF type flow control
; [19b]	Clear screen and beginning and end of program.
; [19e]	Add PRTBRK to send break to port (Rainbow only)
; [19g]	Put in EQU for clock rate for timing loops.
;	Rg, 2/84 	<Oc.Garland%CU20B@Columbia-20>
; * * * * * * * * * * * * * * *  version 2.3  * * * * * * * * * * * * * * *
;  [par] Added calls to set parity, strip parity on input if
;	other than none parity is called for.
;	 JD, 2/84
; * * * * * * * * * * * * * * *  version 2.2  * * * * * * * * * * * * * * *
;  [2]	Add a de-initialization routine for the serial port, to restore
;	changed interrupt vectors, etc.
;	RonB,12/23/83
;  [1]	Add I/O support for the NEC Advanced Personal Computer
;	RonB,12/23/83
; * * * * * * * * * * * * * * *  version 2.0  * * * * * * * * * * * * * * *

; This module contains all the low level communications port I/O
; routines.

; The following is the I/O code for the DEC Rainbow.

		CSEG $

; Clock rate *10 for timing loops	;[19g]
clckrt	equ	48		;[19g]  4.8 Mhz 

; Interrupt vector locations, in data segment 
mnstat	EQU	042H		;Status port.
mndata	EQU	040H		;Data port.
mnctrl	EQU	002H		;Control port.

; Interrupt vector locations.  These are all in data segment 0.

mnoff	EQU	90H		;Main data port interrupt routine offset.
mnseg	EQU	92H		;Main data port interrupt routine segment.

output	EQU	04H		;Bit for output ready.
input	EQU	01H		;Bit for input ready.

outlmt	EQU	1000H		;Number of times to check output status
				; before giving up on send.		;[20d]


; Input data from port.  Preserves all ACs and returns char in
; AL.  Gets the char from the ring buffer.  Assumes a char is
; already there.

inchr:	push	bx
	cli			;Disable interrupts while were are playing.
	dec	mnchrn		;Decrement the number of chars in the buffer.
	mov	bx, mnchop	;Get the pointer into the buffer.
	inc	bx		;Increment to the next char.
	cmp	bx, offset mnchrs+mnchnd ;Past the end?
	jb	inchr2
	mov	bx, offset mnchrs ;If so wrap around to the start.
inchr2:	mov	mnchop, bx	;Save the updated pointer.
	mov	al, [bx]	;Get the character.
	sti			;All done, we can restore interrupts.
	pop	bx
	cmp	parflg,parnon	;[par] no parity?
	je	inchr3		;[par] yup, don't bother stripping
	and	al,7fh		;[par] checking parity, strip off
inchr3:	cmp	floctl, floxon	; do flow control?		[19a] start
	je	inchr4		;If yes jump
	ret
inchr4:	cmp	xofsnt, true	;Have we sent an XOFF
	je	inchr5		;Jump if yes
	ret
inchr5:	cmp	mnchrn, mntrg1	;Under the low trigger point?
	jb	inchr6		;yes - jump
	ret
inchr6:	push	ax		;save current character
	mov	al, xon
	call	prtout		;send an XON
	mov	xofsnt, false	;turn off the flag
	pop	ax		;get back character
	ret			;				[19a] end


; Output data to port. Trashes DX and prints char in AL.

outchr:	mov	dx, mndata
	out	dx, al
	ret


; Test if data is available from port.

instat:	cmp	mnchrn, 0	;Any chars in the buffer?
	jnz	inst2
	 ret
inst2:	jmp	rskp


; Test if port is ready to send next char.  Returns RETSKP if ready.
; Trashes dx.

outwt:	cmp	floctl, floxon	;are we doing flow-control?	[19a] start
	jne	outwta		;no - go on
	cmp	xofrcv, true	;are we being held?
	jne	outwta		;no - ok go on
	ret			;held - say we're busy.	[19a] end
outwta:	push	ax
	mov	dx, mnstat
	in	al, dx
	test	al, output
	pop	ax
	jnz	outwt2
	 ret
outwt2:	jmp	rskp


; Output the character, checking first to make sure the port is clear.

prtout:	call	dopar		;[par]
	push	dx
	push	cx						;[20d] begin
	mov	cx,outlmt
prtou2:	call	outwt		;Wait until the port is ready
	 loop	prtou2		; or too much time has passed.
	 nop
	call	outchr		;Output it.
	pop	cx						;[20d] end
	pop	dx
	ret


mnax	dw	0		;Storage in CSEG		;[28e] begin
mnsp	dw	0		;  for use by interrupt handler
mnsseg	dw	0
mndseg	dw	0

; This routine handles the interrupts on input.

mnint:	cli
	mov	cs:mnax, ax	;Save interrupt stack location.
	mov	ax, sp
	mov	cs:mnsp, ax
	mov	ax, ss
	mov	cs:mnsseg, ax
	mov	ax, cs:mndseg	;Switch to our internal stack.
	mov	ss, ax
	mov	sp, offset mnstk
	push	ds		;Save all registers.
	push	es
	push	bp
	push	di
	push	si
	push	dx
	push	cx
	push	bx
	mov	ds, ax
	call	mnproc		;Process the character.
	mov	dx, mnstat	;Get the status port.
	mov	al, 38H
	out	dx, al		;Tell the port we finished with the interrupt.
	pop	bx		;Restore all registers.
	pop	cx
	pop	dx
	pop	si
	pop	di
	pop	bp
	pop	es
	pop	ds
	mov	ax, cs:mnsp	;Restore the original stack.
	mov	sp, ax
	mov	ax, cs:mnsseg
	mov	ss, ax
	mov	ax, cs:mnax
	iret			;Return from the interrupt.	;[28e] end


; This routine (called by MNINT) gets a char from the main port
; and puts it in the infamous circular buffer.

mnproc:	mov	dx, mnstat
	in	al, dx		;Get the port status.
	test	al, input	;Any there?
	jnz	mnpro2		;Yup, go take care of it.

;[33] Begin addition
; If not a received character, simulate an interrupt transferring
; control to the CPM routine. Let it handle worrisome things like
; someone turning on the printer.

	 pushf			; Save flags, like an int.
	 callf dword ptr mnoldo	; Call CPM's routine.
	 ret			; Now back to MNINT.

;[33] End addition

mnpro2:	mov	al, 1		;Point to RR1.
	out	dx, al
	in	al, dx		;Read RR1.
	mov	ah, al		;Save it.
	mov	al, 30H		;Reset any errors.
	out	dx, al
	mov	dx, mndata
	in	al, dx		;Read the char.
	cmp	floctl, floxon	;are we doing flow-control ?	[19a] start
	jne	mnpr2b		;no - go on
	cmp	al, xoff	;is it an XOFF?
	jne	mnpr2a		;no - go on
	mov	xofrcv, true	;set the flag
	ret
mnpr2a:	cmp	al, xon		;an XON?
	jne	mnpr2b		;no
	mov	xofrcv, false	;clear the flag
	ret			;				[19a] end
mnpr2b:	cmp	mnchrn, mnchnd	;Is the buffer full?
	je	mnperr		;If so, take care of the error.
	inc	mnchrn		;Increment the character count.
	mov	bx, mnchip	;Get the buffer input pointer.
	inc	bx		;Increment it.
	cmp	bx, offset mnchrs+mnchnd ;Past the end?
	jb	mnpro3
	mov	bx, offset mnchrs ;Yes, point to the start again.
mnpro3:	mov	mnchip, bx	;Save the pointer.
	mov	[bx], al	;Put the character in the buffer.
	cmp	floctl, floxon	;do flow control?		[19a] start
	je	mnpro4		;If yes jump
	ret
mnpro4:	cmp	xofsnt, true	;Have we sent an XOFF
	jnz	mnpro5
	ret			;return if we have
mnpro5:	cmp	mnchrn, mntrg2	;Past the High trigger point?
	ja	mnpro6		;yes - jump
	ret
mnpro6:	mov	al, xoff
	call	prtout		;send an XOFF
	mov	xofsnt, true	;set the flag
	ret			;				[19a] End

mnperr:	ret			;Just return on an error for now.


; prtbrk - send a break		;				[19e] start

prtbrk:				;
	mov	dx, mnstat	;status reg. address for port
	mov	al, 15H		;select reg. 5
	out	dx, al		;
	mov	al, 0FAH	;8 bits, TX, Break, RTS, & DTR
	out	dx, al		;Turn Break on
	mov	ax, 275		;.. for 275 millisec's		[34]
	call	mswait		;				[34]
	mov	al, 15H		;select reg. 5
	out	dx, al		;
	mov	al, 0EAH	;same as above without Break
	out	dx, al		;turn it off
	ret			;				 [19e] end

mswait:				;				[34] start
	mov	cx,5*clckrt	; inner loop count for 1 millisec.
mswai1:
	sub	cx,1		;** inner loop takes 20 clock cycles
	jnz	mswai1		;**
	dec	ax		; outer loop counter
	jnz	mswait		; wait another millisecond
	ret			;				[34] end
;
; Init the 7201 for 8 bits, no parity, and 1 stop bit.

serini:	call	ansmod		;Switch from VT52 to ANSI mode	;[30c]

	mov	ax, ds
	mov	cs:mndseg, ax	;Save the data segment somewhere in CSEG.
	push	ds		;Save the data segment.
	mov	ax, 0
	mov	ds, ax		;We want DSEG = 0.
	cli			;Turn off interrupts.
	mov	bx, .mnoff	;[33] Get original interrupt offset.
	mov	es, .mnseg	;[33] Get original interrupt segment.
	mov	ax, offset mnint;Point to the interrupt routine offset.
	mov	.mnoff, ax	;Put in the main port interrupt offset addr.
	mov	ax, cs		;Get our code segment.
	mov	.mnseg, ax	;Put in the main port interrupt segment addr.
	sti			;Restore interrupts.
	pop	ds		;Restore data segment.

	mov	mnoldo, bx	;[33] Stash original serial interrupt offset.
	mov	mnolds, es	;[33] Stash original segment.

	mov	dx, mnstat	;Point to status port.
	mov	al, 18H
	out	dx, al		;Reset the port.
	mov	al, 14H
	out	dx, al		;Select register 4.
	mov	al, 44H		;16X clock, 1 stop bit, no parity.
	out	dx, al
	mov	al, 13H
	out	dx, al		;Select register 3.
	mov	al, 0C1H	;8 bits/char, RX enable.
	out	dx, al
	mov	al, 15H
	out	dx, al		;Select register 5.
	mov	al, 0EAH	;8 bits/char, TX enable, RTS and DTR.
	out	dx, al
	mov	al, 11H
	out	dx, al		;Select register 1.
	mov	al, 18H
	out	dx, al		;Enable interrupt processing on this port.
	mov	dx, mnctrl	;point to comm control port
	mov	al, 0F0H	;set RTS & DTR high
	out	dx, al
	ret

serfin:	
	call	clrscr		;[19b] clear screen		;[30c]

	ret			;Nothing to deinitialize on Rainbow.

; This routine clears the serial port input buffer.  It is called to
; clear out excess NAKs that can result from server mode operation.

cfibf:	mov	mnchrn, 0	;Say no characters in the buffer.
	mov	mnchip, offset mnchrs-1+mnchnd ;Reset input pointer.
	mov	mnchop, offset mnchrs-1+mnchnd ;Reset output pointer.
	ret

	DSEG $

mnchnd	equ	256		;[19a] Size of circular buffer.
mntrg1	equ	64		;[19a] Low trigger point for Auto XON/XOFF
mntrg2	equ	192		;[19a] High trigger point for Auto XON/XOFF

floctl	db	1		;[19a] If floctl=floxon do Auto XON/XOFF logic
xofsnt	db	0		;[19a] set if XOFF was sent
xofrcv	db	0		;[19a] set if XOFF was recieved

mnchrn	DW	0		;[19a] Number of chars in the buffer.
mnchrs	RB	mnchnd		;Circular character buffer for input.
mnchip	DW	mnchrs-1+mnchnd	;Input pointer into character buffer.
mnchop	DW	mnchrs-1+mnchnd	;Output pointer into character buffer.

mnoldo	RW	1		;[33] CPM's 7201 interrupt vector offset
mnolds	RW	1		;[33] and segment.

	rw	32		;Interrupt stack		;[28e]
mnstk	dw	0		;bottom of stack		;[28e]


	CSEG $

; The following routines do the SET and SHOW for the machine dependent
; features of Kermit.  At present there are only two:  baud rate setting
; and port selection.

;	This is the SET BAUD rate subcommand (not implemented in Rainbow)

bdset:	mov	ah, cmcfm
	call	comnd		;Get a confirm.
	 jmp	$+3		; Didn't get a confirm.
	mov	dx, offset infms6 ;Tell user it's not implemented
	call	tcrmsg
	jmp	rskp

;	This is the SET PORT subcommand (not implemented in Rainbow)

prtset:	mov	ah, cmcfm
	call	comnd		;Get a confirm.
	 jmp	$+3		; Didn't get a confirm.
	mov	dx, offset infms6 ;Tell user it's not implemented
	call	tcrmsg
	jmp	rskp


; The following procedures implement the SHOW command for the system
; dependent features of baud rate and port selection.

shobd:	ret			;Baud rate selection not implemented.

shoprt:	ret			;Port selection not implemented.




; The following routines do screen control.  These are isolated here because
; the screen control sequences are likely to vary from system to system, even
; though the Rainbow and APC (the only systems implemented to date) both use
; ANSI sequences for this purpose.

	CSEG $

; POSCUR - positions cursor to row and col (each 1 byte) pointed to by dx.

poscur:	mov	bx, dx		;Do ANSI cursor positioning.
	mov	cl, 10
	mov	al, [bx]	;Get row value
	sub	ah, ah
	div	cl		;units digit in ah, tens digit in al
	add	ax, '00'	;Convert both to ASCII
	mov	word ptr anspos+2, ax	;Save reversed (al,ah)
	mov	al, 1[bx]	;Do same for column value
	sub	ah, ah
	div	cl
	add	ax, '00'
	mov	word ptr anspos+5, ax
	mov	dx, offset anspos ;Print cursor positioning string.
	call	tmsg
	ret

; CLRSCR - homes cursor and clears screen.

clrscr:	mov	dx, offset anscls
	call	tmsg
	ret

; CLRLIN - clears from cursor to end of line.

clrlin:	mov	dl, cr		;Go to beginning of line
	call	bout
clreol:	mov	dx, offset ansclr ;Clear from cursor to end of line
	call	tmsg
	ret

; REVON - turns on reverse video display

revon:	mov	dx, offset ansron
	call	tmsg
	ret

; REVOFF - turns off reverse video display

revoff:	mov	dx, offset ansrof
	call	tmsg
	ret

; BLDON - turns on bold (highlighted) display

bldon:	mov	dx, offset ansbon
	call	tmsg
	ret

; BLDOFF - turns off bold (highlighted) display

bldoff:	mov	dx, offset ansbof
	call	tmsg
	ret

; ANSMOD - enters ANSI mode from VT52 mode

ansmod:	mov	dx, offset ansion
	call	tmsg
	ret

	DSEG $

anspos	db	esc,'[00;00H$'		;Position cursor to row and column
anscls	db	esc,'[H',esc,'[J$'	;Home cursor and clear screen
ansclr	db	esc,'[K$'		;Clear from cursor to end of line
ansron	db	esc,'[7m$'		;Turn on reverse video
ansrof	db	esc,'[m$'		;Turn off reverse video
ansbon	db	esc,'[1m$'		;Turn on bold (highlight) display
ansbof	db	esc,'[m$'		;Turn off bold display
ansion	db	esc,'<$'		;Enter ANSI mode

; Here tab expansion is done if necessary.  If not, just return retskp.

	CSEG $
dotab:	jmp rskp

	DSEG $
delstr	db	' ',10O,10O,'$'	;Delete string.

system	db	'                DEC Rainbow-100$'

<<< c86xtx.a86 >>>
; * * * * * * * * * * * * * * *  version 2.7  * * * * * * * * * * * * * * *
;	Tektronix 4170 version - TransEra Corporation
;	Robert Raymond, 3707 North Canyon Road, Building 4, Provo, UT 84601
; [31c] set default baud to 9600
; [31b] Changes made for 4170 port hardware
; [31a] Use ansi routines from 86keri.rb
; * * * * * * * * * * * * * * *  version 2.7  * * * * * * * * * * * * * * *
; [30d] Add SET PORT command, currently unimplemented.
; [30c] Isolate all machine dependencies in KERIO.
; [30a] Add keyboard DEL key alteration for APC
;	RonB, 04/18/84
; * * * * * * * * * * * * * * *  version 2.6  * * * * * * * * * * * * * * *
; [28e] Switch to local stack on interrupts.
;	RonB, 03/28/84
; * * * * * * * * * * * * * * *  version 2.4  * * * * * * * * * * * * * * *
; [20b] Add PRTBRK to send break & set correct clock rate for NEC.
; [20d] Add a pseudo time-out to PRTOUT so it doesn't loop forever.
;	RonB,03/02/84
; [19a] Add XON/XOFF type flow control
; [19b]	Clear screen and beginning and end of program.
; [19e]	Add PRTBRK to send break to port (Rainbow only)
; [19g]	Put in EQU for clock rate for timing loops.
;	Rg, 2/84 	<Oc.Garland%CU20B@Columbia-20>
; * * * * * * * * * * * * * * *  version 2.3  * * * * * * * * * * * * * * *
;  [par] Added calls to set parity, strip parity on input if
;        other than none parity is called for.
;	 JD, 2/84
; * * * * * * * * * * * * * * *  version 2.2  * * * * * * * * * * * * * * *
;  [2]	Add a de-initialization routine for the serial port, to restore
;	changed interrupt vectors, etc.
;	RonB,12/23/83
;  [1]	Add I/O support for the NEC Advanced Personal Computer
;	RonB,12/23/83
; * * * * * * * * * * * * * * *  version 2.0  * * * * * * * * * * * * * * *

; This module contains all the low level communications port I/O
; routines.

; Here are the I/O routines for the TEK 4170.

	CSEG $

; Clock rate *10 for timing loops	;[19g]
clckrt	equ	49		;[19g]  4.9 Mhz			;[20b]

; Interrupt vector locations, in data segment 0

mnioff	equ	200h		;HO_In_Int interrupt offset	;[31b]
mniseg	equ	202h		;HO_In_Int interrupt segment	;[31b]

;
; equates for 2661B chip -- host initialization parameters
;

HO_Data	equ	0e0h
HO_Stat	equ	0e2h
HO_Mode	equ	0e4h
HO_Cmd	equ	0e6h

HOCLKRATE	equ	02h	;sets async mode, 16x clock on host port
HOSB2	equ	0c0h	;two stop bits
HOSB1	equ	040h	;one stop bits
HOEVEN	equ	020h	;select even parity, not odd parity
HOPENB	equ	010h	;enable parity on the host

HOSTART	equ	037h	;RTS, DTR set, Rx & Tx enabled, reset errors
HOBREAK equ	4	; force break

HO_Thre	equ	01h	;one on this indicates thre, 0 is thr busy
HO_DRdy	equ	02h	;one indicates data ready, 0 no data



outlmt	EQU	1000H		;Number of times to check output status
				; before giving up on send.		;[20d]


; Test if port is ready to send next char.  Returns RSKP if ready.
; Trashes dx.

outwt:	cmp	floctl, floxon	;are we doing flow-control?	[19a] start
	jne	outwta		;no - go on
	cmp	xofrcv, true	;are we being held?
	jne	outwta		;no - ok go on
	ret			;held - say we're busy.	[19a] end
outwta:	push	ax
	mov	dx,HO_Stat	;[31b] begin
	in	al,dx
	test	al,HO_Thre	;transmit holding register empty
	pop	ax
	jnz	outwt2
	 ret
outwt2:	jmp	rskp		;[31b] end


; Output data to port. Trashes DX and prints char in AL.

outchr:	mov	dx,HO_Data	;[31b] begin
	out	dx,al
				; don't ask me why we do this:

	mov	al,HO_START	;rts and start scanning
	out	HO_Cmd,al	
	ret			;[31b] end


; Output the character in AL, checking first to make sure the port is clear.

prtout:	call	dopar		;[par] set parity
	push	dx
	push	cx						;[20d] begin
	mov	cx,outlmt
prtou2:	call	outwt		;Wait until the port is ready
	 loop	prtou2		; or too much time has passed.
	 nop
	call	outchr		;Output it.
	pop	cx						;[20d] end
	pop	dx
	ret


; Test if data is available from port.

instat:	cmp	mnchrn,0	;Any chars in the buffer?
	jnz	inst2
	 ret
inst2:	jmp	rskp


; Input data from port.  Preserves all registers and returns char in
; AL.  Gets the char from the ring buffer.  Assumes a char is
; already there.

inchr:	push	bx
	cli			;Disable interrupts while were are playing.
	dec	mnchrn		;Decrement the number of chars in the buffer.
	mov	bx,mnchop	;Get the pointer into the buffer.
	inc	bx		;Increment to the next char.
	cmp	bx,offset mnchrs+mnchnd ;Past the end?
	jb	inchr2
	lea	bx,mnchrs	;If so wrap around to the start.
inchr2:	mov	mnchop,bx	;Save the updated pointer.
	mov	al,[bx]		;Get the character.
	sti			;All done, we can restore interrupts.
	pop	bx
	cmp	parflg,parnon	;[par] no parity?
	je	inchr3		;[par] yup, don't bother stripping
	and	al,7fh		;[par] checking parity, strip off
inchr3:	cmp	floctl, floxon	;do flow-control?		[19a] start
	je	inchr4		;If yes jump
	ret
inchr4:	cmp	xofsnt, true	;Have we sent an XOFF
	je	inchr5		;Jump if yes
	ret
inchr5:	cmp	mnchrn, mntrg1	;Under the low trigger point?
	jb	inchr6		;yes - jump
	ret
inchr6:	push	ax		;save current character
	mov	al, xon
	call	prtout		;send an XON
	mov	xofsnt, false	;turn off the flag
	pop	ax		;get back character
	ret			;				[19a] end



mnax	dw	0		;Storage in CSEG		;[28e] begin
mnsp	dw	0		;  for use by interrupt handler
mnsseg	dw	0
mndseg	dw	0

; This routine handles the interrupts on input.

mnint:	cli
	mov	cs:mnax, ax	;Save interrupt stack location.
	mov	ax, sp
	mov	cs:mnsp, ax
	mov	ax, ss
	mov	cs:mnsseg, ax
	mov	ax, cs:mndseg	;Switch to our internal stack.
	mov	ss, ax
	lea	sp, mnstk
	push	ds		;Save all registers.
	push	es
	push	bp
	push	di
	push	si
	push	dx
	push	cx
	push	bx
	mov	ds, ax		;Get our data segment address.
	call	mnproc		;Process the character.
	pop	bx		;Restore all registers.
	pop	cx
	pop	dx
	pop	si
	pop	di
	pop	bp
	pop	es
	pop	ds
	mov	ax, cs:mnsp	;Restore the original stack.
	mov	sp, ax
	mov	ax, cs:mnsseg
	mov	ss, ax
	mov	ax, cs:mnax
	iret			;Return from the interrupt.	;[28e] end


; This routine (called by MNINT) gets a char from the serial port
; and puts it in the ring buffer.

mnproc:	mov	dx,HO_Stat	
	in	al,dx		;Get the port status.		[31b] start
	test	al,HO_DRdy	;Is a character waiting?
	jnz	mnpro2		;   Yes, go take care of it.
	 ret			;   No, just a false alarm.

mnpro2:	mov	dx,HO_Data
	in	al,dx		;Read the char.			[31b] end
	cmp	floctl, floxon	;are we doing flow-control ?	[19a] start
	jne	mnpr2b		;no - go on
	cmp	al, xoff	;is it an XOFF?
	jne	mnpr2a		;no - go on
	mov	xofrcv, true	;set the flag
	ret
mnpr2a:	cmp	al, xon		;an XON?
	jne	mnpr2b		;no
	mov	xofrcv, false	;clear the flag
	ret			;				[19a] end
mnpr2b:	cmp	mnchrn,mnchnd	;Is the buffer full?
	je	mnperr		;If so, take care of the error.
	inc	mnchrn		;Increment the character count.
	mov	bx,mnchip	;Get the buffer input pointer.
	inc	bx		;Increment it.
	cmp	bx,offset mnchrs+mnchnd ;Past the end?
	jb	mnpro3
	lea	bx,mnchrs	;Yes, point to the start again.
mnpro3:	mov	mnchip,bx	;Save the pointer.
	mov	[bx],al		;Put the character in the buffer.
	cmp	floctl, floxon	;do flow-control?		[19a] start
	je	mnpro4		;If yes jump
	ret
mnpro4:	cmp	xofsnt, true	;Have we sent an XOFF
	jnz	mnpro5
	ret			;return if we have
mnpro5:	cmp	mnchrn, mntrg2	;Past the High trigger point?
	ja	mnpro6		;yes - jump
	ret
mnpro6:	mov	al, xoff
	call	prtout		;send an XOFF
	mov	xofsnt, true	;set the flag
	ret			;				[19a] End

mnperr:	ret			;Just return on an error for now.


; prtbrk - send a break		;				[31b] start

prtbrk:
	mov	dx,HO_Cmd	;break goes to command port
	mov	al,HO_START+HO_BREAK	;add break to normal command
	out	dx,al
	mov	cx, 25000	;sit for a while
prtbk1:	loop	prtbk1
	mov	al,HO_START	;normal command,RTS & DTR high, Rx & Tx enabled
	out	dx,al		;return to normal setting
	ret


; serini - This routine initializes all devices that need it.
;	   Called at the start of the program.

serini:	cmp	mninit,0FFh	; must only do this initialization once
	je	serin2
	mov	mninit,0FFh
	call	ansmod		; switch from tek mode to ansi mode

	push	es
;code could be added here 
;to tell the interrupt controller to diable host interrupt
	mov	ax,ds		;save data segment in cseg
	mov	cs:mndseg,ax	;   for use by the interrupt handler

	mov	ax,0		;point to zero page to replace
	mov	es,ax		;the sio interrupt vector
	mov	ax,es:.mniseg	;after first saving the current vector
	mov	mnxseg,ax
	mov	ax,es:.mnioff
	mov	mnxoff,ax
	cli
	mov	ax,cs
	mov	es:.mniseg,ax
	mov	ax,offset mnint
	mov	es:.mnioff,ax
	sti

	call	stmode		;set mode & baud to defaults
	call	stbaud

				;enable transmission of data
	mov	al,HOSTART	;DTR high, Rx & Tx enabled, reset error
	out	HO_Cmd,al

	in	al,HO_Data	;dummy read to clear buffer

;code could be added here 
;to tell the interrupt controller to re-enable host interrupt
	mov	dx,0ebh
	mov	al,24h			;turn on host read interrupt
	out	dx,al						;[31b] end

	pop	es

serin2:	ret


; serfin - this routine is used to "undo" what serini has done, called
;	   just before exiting back to cp/m.

serfin:
	call	clrscr		;[19b] clear screen	;[30c]

	cmp	mninit,0FFh	;check if initialization has been done
	jne	serfn2		;if not, don't de-initialize
	mov	mninit,0

	push	es

	cli
;code could be added here to assure the interrupt controller
;is restored to the state is was in when kermit started
	mov	ax,0
	mov	es,ax
	mov	ax,mnxseg	;restore sio interrupt vector
	mov	es:.mniseg,ax
	mov	ax,mnxoff
	mov	es:.mnioff,ax
	sti

	pop	es

serfn2:	ret


; This routine clears the serial port input buffer.  It is called to
; clear out excess NAKs that can result from server mode operation.

cfibf:	mov	mnchrn, 0	;Say no characters in the buffer.
	mov	mnchip, OFFSET mnchrs-1+mnchnd ;Reset input pointer.
	mov	mnchop, OFFSET mnchrs-1+mnchnd ;Reset output pointer.
	ret

; set the parity, number of data bits, and number of stop bits

stmode:	mov	dx,HO_Cmd					;[31b] start
	mov	al,0		;reset 
	out	dx,al	
	in	al,dx		;reset mode1/2 sequencer
	mov	dx,HO_Mode
	mov	al,HOCLKRATE	;init async mode, 16x baud, no parity
	add	al,HOSB1	;1 stop bit
	add	al,0ch		;8 data bits
	out	dx,al						;[31b] end
	ret


; set the baud rate

stbaud:	mov	dx,HO_Cmd					;[31b] start
	in	al,dx		;reset mode1/2 sequencer
	mov	dx,HO_Mode
	in	al,dx		;get mode1 register

				; can the next 4 lines be skipped? 

	mov	ah,al		;save it
	in	al,dx		;get mode2 register to reset sequence
	mov	al,ah		; write back old contents of mode 1
	out	dx,al
				; next out will set mode 2 - baud rate

	mov	al,mnbaud	;get the baud rate information
	cmp	al,15		;check for valid range (0-15)
	ja	stb02
	or	al, 70h		;internal baud clock, 16x
	out	dx,al						;[31b] end
stb02:	ret

	dseg $

;	Serial port default parameters

mnbaud	db	0dh		;9600 baud  [31c]

mninit	db	0		;set to 0FFh if initialization has been done
mnxseg	dw	0		;system host interrupt vector
mnxoff	dw	0

mnchnd	equ	512		;Size of circular buffer.
mnchrs	rb	mnchnd		;Circular character buffer for input.
mnchip	dw	mnchrs-1+mnchnd	;Input pointer into character buffer.
mnchop	dw	mnchrs-1+mnchnd	;Output pointer into character buffer.
mnchrn	dw	0		;Number of chars in the buffer.

mntrg1	equ	128		;[19a] Low trigger point for Auto XON/XOFF
mntrg2	equ	384		;[19a] High trigger point for Auto XON/XOFF

floctl	db	1		;[19a] If floctl=floxon do Auto XON/XOFF logic
xofsnt	db	0		;[19a] set if XOFF was sent
xofrcv	db	0		;[19a] set if XOFF was recieved

	rw	32		;Interrupt stack		;[28e]
mnstk	dw	0		;bottom of stack		;[28e]

	CSEG $

; The following routines do the SET and SHOW for the machine dependent
; features of Kermit.  At present there are only two:  baud rate setting
; and port selection.

;	This is the SET BAUD rate subcommand

bdset:	lea	dx, bdtab
	lea	bx, bdhlp
	mov	ah, cmkey
	call	comnd
	 jmp	r
	mov	temp1, bx
	mov	ah, cmcfm
	call	comnd		;Get a confirm.
	 jmp	r		; Didn't get a confirm.
	mov	bx, temp1
	mov	mnbaud, bl	;Set the baud rate table index.
	call	stbaud
	jmp	rskp

;	This is the SET PORT subcommand (not implemented in TEK)

prtset:	mov	ah, cmcfm
	call	comnd		;Get a confirm.
	 jmp	$+3		; Didn't get a confirm.
	lea	dx, infms6	;Tell user it's not implemented
	call	tcrmsg
	jmp	rskp


; The following procedures implement the SHOW command for the system
; dependent features of baud rate and port selection.

shobd:	lea	dx, bdst	;Baud rate string.
	call	tcrmsg
	mov	al, mnbaud	;Print the keyword corresponding to the
	lea	bx, bdtab	;   current value of mnbaud.
	call	tabprt
	ret

shoprt:	ret			;Port selection not implemented.


	DSEG $

bdtab	db	16		; 16 entries		;[31d] begin
	db	3,'110$'
	dw	0003H
	db	4,'1200$'
	dw	0008H
	db	3,'135$'
	dw	0004H
	db	3,'150$'
	dw	0005H
	db	4,'1800$'
	dw	0009H
	db	5,'19200$'
	dw	000EH
	db	4,'2000$'
	dw	000AH
	db	4,'2400$'
	dw	000BH
	db	3,'300$'
	dw	0006H
	db	5,'38400$'
	dw	000FH
	db	2,'45$'
	dw	0000H
	db	4,'4800$'
	dw	000CH
	db	2,'50$'
	dw	0001H
	db	3,'600$'
	dw	0007H
	db	2,'75$'
	dw	0002H
	db	4,'9600$'
	dw	000DH

bdhlp	db	cr,lf,'	45	75	135	300	1200	2000	4800	19200'
	db	cr,lf,'	50	110	150	600	1800	2400	9600	38400$'
  								;[31d] end
;[31a] begin -- to end of file

; The following routines do screen control.  These are isolated here because
; the screen control sequences are likely to vary from system to system, even
; though the Rainbow and APC (the only systems implemented to date) both use
; ANSI sequences for this purpose.

	CSEG $

; POSCUR - positions cursor to row and col (each 1 byte) pointed to by dx.

poscur:	mov	bx, dx		;Do ANSI cursor positioning.
	mov	cl, 10
	mov	al, [bx]	;Get row value
	sub	ah, ah
	div	cl		;units digit in ah, tens digit in al
	add	ax, '00'	;Convert both to ASCII
	mov	word ptr anspos+2, ax	;Save reversed (al,ah)
	mov	al, 1[bx]	;Do same for column value
	sub	ah, ah
	div	cl
	add	ax, '00'
	mov	word ptr anspos+5, ax
	lea	dx, anspos	;Print cursor positioning string.
	call	tmsg
	ret

; CLRSCR - homes cursor and clears screen.

clrscr:	lea	dx, anscls
	call	tmsg
	ret

; CLRLIN - clears from cursor to end of line.

clrlin:	mov	dl, cr		;Go to beginning of line
	call	bout
clreol:	lea	dx, ansclr	;Clear from cursor to end of line
	call	tmsg
	ret

; REVON - turns on reverse video display

revon:	lea	dx, ansron
	call	tmsg
	ret

; REVOFF - turns off reverse video display

revoff:	lea	dx, ansrof
	call	tmsg
	ret

; BLDON - turns on bold (highlighted) display

bldon:	lea	dx, ansbon
	call	tmsg
	ret

; BLDOFF - turns off bold (highlighted) display

bldoff:	lea	dx, ansbof
	call	tmsg
	ret

; ANSMOD - enters ANSI mode from Tek mode

ansmod:	lea	dx, ansion
	call	tmsg
	ret

	DSEG $

anspos	db	esc,'[00;00H$'		;Position cursor to row and column
anscls	db	esc,'[H',esc,'[J$'	;Home cursor and clear screen
ansclr	db	esc,'[K$'		;Clear from cursor to end of line
ansron	db	esc,'[7m$'		;Turn on reverse video
ansrof	db	esc,'[m$'		;Turn off reverse video
ansbon	db	esc,'[1m$'		;Turn on bold (highlight) display
ansbof	db	esc,'[m$'		;Turn off bold display
ansion	db	esc,'%!1$'		;SelectCode Ansi mode

; Here tab expansion is done if necessary.  If not, just return retskp.

	CSEG $
dotab:	jmp rskp

	DSEG $
delstr	db	' ',10O,10O,'$'	;Delete string.

system	db	'                 Tektronix 4170$'
<<< c86xv9.a86 >>>
; * * * * * * * * * * * * * * *  version 2.9  * * * * * * * * * * * * * * *
; [35] Modify the Rainbow 100 version to handle the Victor 9000/Sirius
; Eric Zurcher, Utah State University, Logan, Utah (REHABIV@USU.BITNET)
; 07/07/86
; * * * * * * * * * * * * * * *  version 2.8  * * * * * * * * * * * * * * *
; [34] Insert milli-second wait-loop for Break-timing - label MSWAIT:
; [33] Fix printer on hanging system problem by letting CP/M handle the
;       interrupts from the 7201 that we don't care about.  Thanks to
;       Paul Ford, U. of Chicago Graduate School of Business
; * * * * * * * * * * * * * * *  version 2.7  * * * * * * * * * * * * * * *
; [30d] Add SET PORT command, currently unimplemented.
; [30c] Isolate all machine dependencies in KERIO.
;       RonB, 04/18/84
; * * * * * * * * * * * * * * *  version 2.6  * * * * * * * * * * * * * * *
; [28e] Switch to local stack on interrupts.
;       RonB, 03/28/84
; * * * * * * * * * * * * * * *  version 2.4  * * * * * * * * * * * * * * *
; [20b] Add PRTBRK to send break & set correct clock rate for NEC.
; [20d] Add a pseudo time-out to PRTOUT so it doesn't loop forever.
;       RonB,03/02/84
; [19a] Add XON/XOFF type flow control
; [19b] Clear screen and beginning and end of program.
; [19e] Add PRTBRK to send break to port (Rainbow only)
; [19g] Put in EQU for clock rate for timing loops.
;       Rg, 2/84        <Oc.Garland%CU20B@Columbia-20>
; * * * * * * * * * * * * * * *  version 2.3  * * * * * * * * * * * * * * *
;  [par] Added calls to set parity, strip parity on input if
;       other than none parity is called for.
;        JD, 2/84
; * * * * * * * * * * * * * * *  version 2.2  * * * * * * * * * * * * * * *
;  [2]  Add a de-initialization routine for the serial port, to restore
;       changed interrupt vectors, etc.
;       RonB,12/23/83
;  [1]  Add I/O support for the NEC Advanced Personal Computer
;       RonB,12/23/83
; * * * * * * * * * * * * * * *  version 2.0  * * * * * * * * * * * * * * *

; This module contains all the low level communications port I/O
; routines.

; The following is the I/O code for the Victor 9000.

                CSEG $

; Clock rate *10 for timing loops       ;[19g]
clckrt  equ     48              ;[19g]  4.8 Mhz

;[35a] begin
; Offsets of memory-mapped Victor "ports" within segment 0E000H
mdmseg  EQU     0E000H          ;Segement for memory-mapped 7201 of Victor
mnstat  EQU     042H            ;Status port A.
mndata  EQU     040H            ;Data port A.
mnctl1  EQU     000H            ;Interrupt controller.
mnctl2  EQU     001H            ;Interrupt controller.
mnclk1  EQU     023H            ;For setting baud rate
mnclk2  EQU     020H            ;      "
;[35a] end

; Interrupt vector locations.  These are all in data segment 0.

mnoff   EQU     84H             ;Main data port interrupt routine offset.  [35]
mnseg   EQU     86H             ;Main data port interrupt routine segment. [35]

output  EQU     04H             ;Bit for output ready.
input   EQU     01H             ;Bit for input ready.

outlmt  EQU     1000H           ;Number of times to check output status
                                ; before giving up on send.             ;[20d]


; Input data from port.  Preserves all ACs and returns char in
; AL.  Gets the char from the ring buffer.  Assumes a char is
; already there.

inchr:  push    bx
        cli                     ;Disable interrupts while were are playing.
        dec     mnchrn          ;Decrement the number of chars in the buffer.
        mov     bx, mnchop      ;Get the pointer into the buffer.
        inc     bx              ;Increment to the next char.
        cmp     bx, offset mnchrs+mnchnd ;Past the end?
        jb      inchr2
        mov     bx, offset mnchrs ;If so wrap around to the start.
inchr2: mov     mnchop, bx      ;Save the updated pointer.
        mov     al, [bx]        ;Get the character.
        sti                     ;All done, we can restore interrupts.
        pop     bx
        cmp     parflg,parnon   ;[par] no parity?
        je      inchr3          ;[par] yup, don't bother stripping
        and     al,7fh          ;[par] checking parity, strip off
inchr3: cmp     floctl, floxon  ; do flow control?              [19a] start
        je      inchr4          ;If yes jump
        ret
inchr4: cmp     xofsnt, true    ;Have we sent an XOFF
        je      inchr5          ;Jump if yes
        ret
inchr5: cmp     mnchrn, mntrg1  ;Under the low trigger point?
        jb      inchr6          ;yes - jump
        ret
inchr6: push    ax              ;save current character
        mov     al, xon
        call    prtout          ;send an XON
        mov     xofsnt, false   ;turn off the flag
        pop     ax              ;get back character
        ret                     ;                               [19a] end


; Output data to port. Trashes DX and prints char in AL.

;[35b] begin
outchr: push    es
        push    si
        mov     dx, mdmseg
        mov     es, dx
        mov     si, mndata
        mov     es:[si], al
        pop     si
        pop     es
        ret
 ;[35b] end

; Test if data is available from port.

instat: cmp     mnchrn, 0       ;Any chars in the buffer?
        jnz     inst2
         ret
inst2:  jmp     rskp


; Test if port is ready to send next char.  Returns RETSKP if ready.
; Trashes dx.

outwt:  cmp     floctl, floxon  ;are we doing flow-control?     [19a] start
        jne     outwta          ;no - go on
        cmp     xofrcv, true    ;are we being held?
        jne     outwta          ;no - ok go on
        ret                     ;held - say we're busy. [19a] end
outwta: push    ax
;[35c] begin
        push    es
        push    si
        mov     dx, mdmseg
        mov     es, dx
        mov     si, mnstat
        mov     al, es:[si]
        test    al, output
        pop     si
        pop     es
;[35c] end
        pop     ax
        jnz     outwt2
         ret
outwt2: jmp     rskp


; Output the character, checking first to make sure the port is clear.

prtout: call    dopar           ;[par]
        push    dx
        push    cx                                              ;[20d] begin
        mov     cx,outlmt
prtou2: call    outwt           ;Wait until the port is ready
         loop   prtou2          ; or too much time has passed.
         nop
        call    outchr          ;Output it.
        pop     cx                                              ;[20d] end
        pop     dx
        ret


mnax    dw      0               ;Storage in CSEG                ;[28e] begin
mnsp    dw      0               ;  for use by interrupt handler
mnsseg  dw      0
mndseg  dw      0

; This routine handles the interrupts on input.

mnint:  cli
        mov     cs:mnax, ax     ;Save interrupt stack location.
        mov     ax, sp
        mov     cs:mnsp, ax
        mov     ax, ss
        mov     cs:mnsseg, ax
        mov     ax, cs:mndseg   ;Switch to our internal stack.
        mov     ss, ax
        mov     sp, offset mnstk
        push    ds              ;Save all registers.
        push    es
        push    bp
        push    di
        push    si
        push    dx
        push    cx
        push    bx
        mov     ds, ax
        mov     dx, mdmseg      ;[35]
        mov     es, dx          ;[35]
        call    mnproc          ;Process the character.
;[35d] begin
        mov     si, mnctl1      ;Point to the interrupt controller
        mov     al, 061H
        mov     es:[si], al     ;Re-enable interrupts
        mov     si, mnstat      ;Get the status port.
        mov     al, 38H
        mov     es:[si], al     ;Tell the port we finished with the interrupt.
;[35d] end
        pop     bx              ;Restore all registers.
        pop     cx
        pop     dx
        pop     si
        pop     di
        pop     bp
        pop     es
        pop     ds
        mov     ax, cs:mnsp     ;Restore the original stack.
        mov     sp, ax
        mov     ax, cs:mnsseg
        mov     ss, ax
        mov     ax, cs:mnax
        iret                    ;Return from the interrupt.     ;[28e] end


; This routine (called by MNINT) gets a char from the main port
; and puts it in the infamous circular buffer.

mnproc: mov     si, mnstat      ;[35] Check port status
        mov     al, es:[si]     ;[35]
        test    al, input       ;Any there?
        jnz     mnpro2          ;Yup, go take care of it.

;[33] Begin addition
; If not a received character, simulate an interrupt transferring
; control to the CPM routine. Let it handle worrisome things like
; someone turning on the printer.
;[35e] begin
;Unlike the Rainbow, Victor CP/M does not normally handle these interrupts.
;Let's just return, lest we wreak havoc...
;        pushf                  ; Save flags, like an int.
;        callf dword ptr mnoldo ; Call CPM's routine.
;[35e] end
         ret                    ; Now back to MNINT.

;[33] End addition

;[35f] begin
mnpro2: mov     al, 1           ;Point to RR1.
        mov     es:[si], al
        mov     al, es:[si]     ;Read RR1.
        mov     ah, al          ;Save it.
        mov     al, 30H         ;Reset any errors.
        mov     es:[si], al
        mov     si, mndata
        mov     al, es:[si]     ;Read the char.
;[35f] end
        cmp     floctl, floxon  ;are we doing flow-control ?    [19a] start
        jne     mnpr2b          ;no - go on
        cmp     al, xoff        ;is it an XOFF?
        jne     mnpr2a          ;no - go on
        mov     xofrcv, true    ;set the flag
        ret
mnpr2a: cmp     al, xon         ;an XON?
        jne     mnpr2b          ;no
        mov     xofrcv, false   ;clear the flag
        ret                     ;                               [19a] end
mnpr2b: cmp     mnchrn, mnchnd  ;Is the buffer full?
        je      mnperr          ;If so, take care of the error.
        inc     mnchrn          ;Increment the character count.
        mov     bx, mnchip      ;Get the buffer input pointer.
        inc     bx              ;Increment it.
        cmp     bx, offset mnchrs+mnchnd ;Past the end?
        jb      mnpro3
        mov     bx, offset mnchrs ;Yes, point to the start again.
mnpro3: mov     mnchip, bx      ;Save the pointer.
        mov     [bx], al        ;Put the character in the buffer.
        cmp     floctl, floxon  ;do flow control?               [19a] start
        je      mnpro4          ;If yes jump
        ret
mnpro4: cmp     xofsnt, true    ;Have we sent an XOFF
        jnz     mnpro5
        ret                     ;return if we have
mnpro5: cmp     mnchrn, mntrg2  ;Past the High trigger point?
        ja      mnpro6          ;yes - jump
        ret
mnpro6: mov     al, xoff
        call    prtout          ;send an XOFF
        mov     xofsnt, true    ;set the flag
        ret                     ;                               [19a] End

mnperr: ret                     ;Just return on an error for now.


; prtbrk - send a break         ;                               [19e] start

prtbrk:                         ;
;[35g] begin
        push    es              ;save registers
        push    si
        mov     dx, mdmseg      ;Point to "port" segment
        mov     es, dx
        mov     si, mnstat      ;status reg. address for port
        mov     al, 15H         ;select reg. 5
        mov     es:[si], al
        mov     al, 0FAH        ;8 bits, TX, Break, RTS, & DTR
        mov     es:[si], al     ;Turn Break on
        mov     ax, 275         ;.. for 275 millisec's          [34]
        call    mswait          ;                               [34]
        mov     al, 15H         ;select reg. 5
        mov     es:[si], al
        mov     al, 0EAH        ;same as above without Break
        mov     es:[si], al     ;turn it off
        pop     si
        pop     es
        ret                     ;                                [19e] end
;[35g] end
mswait:                         ;                               [34] start
        mov     cx,5*clckrt     ; inner loop count for 1 millisec.
mswai1:
        sub     cx,1            ;** inner loop takes 20 clock cycles
        jnz     mswai1          ;**
        dec     ax              ; outer loop counter
        jnz     mswait          ; wait another millisecond
        ret                     ;                               [34] end
;
; Init the 7201 for 8 bits, no parity, and 1 stop bit.

serini:  ;call ansmod ;Switch from VT52 to ANSI mode ;[30c][35] removed

        cmp     mninit, 0FFH
        je      serin2
        mov     mninit, 0FFH
        mov     ax, ds
        mov     cs:mndseg, ax   ;Save the data segment somewhere in CSEG.
        push    ds              ;Save the data segment.
        mov     ax, 0
        mov     ds, ax          ;We want DSEG = 0.
        cli                     ;Turn off interrupts.
        mov     bx, .mnoff      ;[33] Get original interrupt offset.
        mov     es, .mnseg      ;[33] Get original interrupt segment.
        mov     ax, offset mnint;Point to the interrupt routine offset.
        mov     .mnoff, ax      ;Put in the main port interrupt offset addr.
        mov     ax, cs          ;Get our code segment.
        mov     .mnseg, ax      ;Put in the main port interrupt segment addr.
        pop     ds              ;Restore data segment.

        mov     mnoldo, bx      ;[33] Stash original serial interrupt offset.
        mov     mnolds, es      ;[33] Stash original segment.

;[35h] begin
        push    es              ;Save registers
        push    si
        mov     dx, mdmseg      ;Get "port" segment
        mov     es, dx
        mov     si, mnstat      ;Point to status port.
        mov     al, 18H         ;Reset the port.
        mov     es:[si], al
        mov     al, 14H         ;Select register 4.
        mov     es:[si], al
        mov     al, 48H         ;16X clock, 1.5 stop bits, no parity.
        mov     es:[si], al
        mov     al, 13H         ;Select register 3.
        mov     es:[si], al
        mov     al, 0C1H        ;8 bits/char, RX enable.
        mov     es:[si], al
        mov     al, 15H         ;Select register 5.
        mov     es:[si], al
        mov     al, 0EAH        ;8 bits/char, TX enable, RTS and DTR.
        mov     es:[si], al
        mov     al, 11H         ;Select register 1.
        mov     es:[si], al
        mov     al, 18H         ;Enable interrupt processing on this port.
        mov     es:[si], al
        mov     si, mnctl2      ;point to 8259 interrput controller
        mov     al,es:[si]
        and     al, 0FDH        ;Enable interrupt processing
        mov     es:[si], al
        mov     si, mnctl1
        mov     al, 061H
        mov     es:[si], al     ;Clears outstanding requests
        sti                     ;Restore interrupts.
        pop     si
        pop     es
        call    stbaud
serin2: ret

serfin:
        call    clrscr          ;[19b] clear screen             ;[30c]
        cmp     mninit, 0FFH
        jne     serfn2
        mov     mninit, 0
        cli
        push    es
        push    si
        mov     dx, mdmseg      ;Victor
        mov     es, dx          ;Victor
        mov     si, mnctl2      ;point to 8259 interrput controller
        mov     al,es:[si]      ;Enable interrupt processing
        or      al, 02H
        mov     es:[si], al
        mov     si, mnstat
        mov     al, 01
        mov     es:[si], al
        mov     al, 00
        mov     es:[si], al
        mov     ax, mnoldo      ;[33] Get original interrupt offset.
        mov     es, mnolds      ;[33] Get original interrupt segment.
        push    ds              ;Save the data segment.
        mov     dx, 0
        mov     ds, dx          ;We want DSEG = 0.
        mov     .mnoff, ax      ;Put in the main port interrupt offset addr.
        mov     .mnseg, es      ;Put in the main port interrupt segment addr.
        pop     ds              ;Restore data segment.
        pop     si
        pop     es
        sti

serfn2: ret
;[35h] end

; This routine clears the serial port input buffer.  It is called to
; clear out excess NAKs that can result from server mode operation.

cfibf:  mov     mnchrn, 0       ;Say no characters in the buffer.
        mov     mnchip, offset mnchrs-1+mnchnd ;Reset input pointer.
        mov     mnchop, offset mnchrs-1+mnchnd ;Reset output pointer.
        ret

        DSEG $

mnchnd  equ     256             ;[19a] Size of circular buffer.
mntrg1  equ     64              ;[19a] Low trigger point for Auto XON/XOFF
mntrg2  equ     192             ;[19a] High trigger point for Auto XON/XOFF

floctl  db      1               ;[19a] If floctl=floxon do Auto XON/XOFF logic
xofsnt  db      0               ;[19a] set if XOFF was sent
xofrcv  db      0               ;[19a] set if XOFF was recieved

mnchrn  DW      0               ;[19a] Number of chars in the buffer.
mnchrs  RB      mnchnd          ;Circular character buffer for input.
mnchip  DW      mnchrs-1+mnchnd ;Input pointer into character buffer.
mnchop  DW      mnchrs-1+mnchnd ;Output pointer into character buffer.

mnoldo  RW      1               ;[33] CPM's 7201 interrupt vector offset
mnolds  RW      1               ;[33] and segment.
mninit  DB      0               ;set to 0FFH if initialization has been done

        rw      32              ;Interrupt stack                ;[28e]
mnstk   dw      0               ;bottom of stack                ;[28e]


        CSEG $

; The following routines do the SET and SHOW for the machine dependent
; features of Kermit.  At present there are only two:  baud rate setting
; and port selection.

;[35i] begin
;       This is the SET BAUD rate subcommand

; set the baud rate

stbaud: mov     al,mnbaud       ;get the baud rate information
        cmp     al,15           ;check for valid range (0-15)
        ja      stb02
        mov     bx,offset baudtb;get address of baud rate table
        add     al,al           ;compute word offset
        mov     ah,0
        add     bx,ax
        push    es
        push    si
        mov     ax, mdmseg
        mov     es, ax
        mov     si, mnclk1
        mov     al, 036H
        mov     es:[si], al
        mov     si, mnclk2
        mov     ax, [bx]
        mov     es:[si], al
        mov     es:[si], ah
        pop     si
        pop     es
stb02:  ret

        dseg $

;       Serial port default parameters

mnbaud  db      13              ;9600 baud

;       Interval Timer values (assumes 16x baud rate mode)

baudtb  dw      061Bh           ;50 baud        0
        dw      0412h           ;75 baud        1
        dw      02C6h           ;110 baud       2
        dw      0245h           ;135 baud       3
        dw      0209h           ;150 baud       4
        dw      0104h           ;300 baud       5
        dw      0082h           ;600 baud       6
        dw      0041h           ;1200 baud      7
        dw      002Bh           ;1800 baud      8
        dw      0027h           ;2000 baud      9
        dw      0021h           ;2400 baud      10
        dw      0016h           ;3600 baud      11
        dw      0010h           ;4800 baud      12
        dw      0008h           ;9600 baud      13
        dw      0004h           ;19200 baud     14
        dw      0002h           ;38400 baud     15

        CSEG $

bdset:  mov     dx, offset bdtab
        mov     bx, offset bdhlp
        mov     ah, cmkey
        call    comnd
         jmp    r
        mov     temp1, bx
        mov     ah, cmcfm
        call    comnd           ;Get a confirm.
         jmp    r               ; Didn't get a confirm.
        mov     bx, temp1
        mov     mnbaud, bl      ;Set the baud rate table index.
        call    stbaud
        jmp     rskp
;[35i] end

;This is the SET PORT subcommand (not implemented in Victor at this time)

prtset: mov     ah, cmcfm
        call    comnd           ;Get a confirm.
         jmp    $+3             ; Didn't get a confirm.
        mov     dx, offset infms6 ;Tell user it's not implemented
        call    tcrmsg
        jmp     rskp


; The following procedures implement the SHOW command for the system
; dependent features of baud rate and port selection.

;[35j] begin
shobd:  mov     dx, offset bdst ;Baud rate string.
        call    tcrmsg
        mov     al, mnbaud      ;Print the keyword corresponding to the
        mov     bx, offset bdtab ; current value of mnbaud.
        call    tabprt
        ret

        DSEG $

bdtab   db      16              ;Thirteen entries               ;[6] begin
        db      3,'110$'
        dw      0002H
        db      4,'1200$'
        dw      0007H
        db      3,'135$'
        dw      0003H
        db      3,'150$'
        dw      0004H
        db      4,'1800$'
        dw      0008H
        db      5,'19200$'
        dw      000EH
        db      4,'2000$'
        dw      0009H
        db      4,'2400$'
        dw      000AH
        db      3,'300$'
        dw      0005H
        db      4,'3600$'
        dw      000BH
        db      5,'38400$'
        dw      000FH
        db      4,'4800$'
        dw      000CH
        db      2,'50$'
        dw      0000H
        db      3,'600$'
        dw      0006H
        db      2,'75$'
        dw      0001H
        db      4,'9600$'
        dw      000DH                                           ;[6] end

bdhlp:
db cr,lf,'      50      110     150     600     1800    2400    4800    19200'
db cr,lf,'      75      135     300     1200    2000    3600    9600    38400$'

        CSEG $
;[35j] end
shoprt: ret                     ;Port selection not implemented.




; The following routines do screen control.  These are isolated here because
; the screen control sequences are likely to vary from system to system, even
; though the Rainbow and APC (the only systems implemented to date) both use
; ANSI sequences for this purpose.

        CSEG $

; POSCUR - positions cursor to row and col (each 1 byte) pointed to by dx.

;[35k] begin
poscur:
        mov     si, dx
        mov     dx, offset anspos ; move prefix string
        mov     cl, prstr
        int     bdos
        xor     dx,dx
        mov     dl, [si]
        add     dl,01FH         ; this is the row
        mov     cl,dconio       ; no checking please
        int     bdos
        mov     dl, 1[si]
        add     dl,01FH         ; this is the column
        mov     cl,dconio
        int     bdos
        ret
;[35k] end

; CLRSCR - homes cursor and clears screen.

clrscr: mov     dx, offset anscls
        call    tmsg
        ret

; CLRLIN - clears from cursor to end of line.

clrlin: mov     dl, cr          ;Go to beginning of line
        call    bout
clreol: mov     dx, offset ansclr ;Clear from cursor to end of line
        call    tmsg
        ret

; REVON - turns on reverse video display

revon:  mov     dx, offset ansron
        call    tmsg
        ret

; REVOFF - turns off reverse video display

revoff: mov     dx, offset ansrof
        call    tmsg
        ret

; BLDON - turns on bold (highlighted) display

bldon:  mov     dx, offset ansbon
        call    tmsg
        ret

; BLDOFF - turns off bold (highlighted) display

bldoff: mov     dx, offset ansbof
        call    tmsg
        ret

; ANSMOD - enters ANSI mode from VT52 mode

;[35l] begin    ; Just keep Victor in VT52 (actually Heath-19) mode
ansmod: ;mov    dx, offset ansion
        ;call   tmsg
        ret

        DSEG $

anspos  db      esc,'Y$'                ;Position cursor to row and column
anscls  db      esc,'H',esc,'J$'        ;Home cursor and clear screen
ansclr  db      esc,'K$'                ;Clear from cursor to end of line
ansron  db      esc,'p$'                ;Turn on reverse video
ansrof  db      esc,'q$'                ;Turn off reverse video
ansbon  db      esc,'($'                ;Turn on bold (highlight) display
ansbof  db      esc,')$'                ;Turn off bold display
ansion  db      '$'                     ;Enter ANSI mode
;[35l] end

; Here tab expansion is done if necessary.  If not, just return retskp.

        CSEG $
dotab:  jmp rskp

        DSEG $
delstr  db      ' ',10O,10O,'$' ;Delete string.

system  db      '                  Victor 9000$' ;[35]
