[net.sources] MS-DOS Kermit sources

knutson@ut-ngp.UUCP (Jim Knutson) (10/05/84)

This and the 6 following messages contain the source code and documentation
for Kermit-MS and code for downloading the program to your micro.  The source
will build Kermit for the IBM-PC and any compatibles running DOS 1.x or 2.x,
the NEC APC, DEC Rainbow, HP-150, Zenith Z100, and Wang.

------------------- Cut here -----------------
: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting mscmd.asm'
sed 's/^X//' <<'//go.sysin dd *' >mscmd.asm
	public comnd, cmcfrm, prserr, repars, cmgtch, drives, comand, fcbcpy
	include msdefs.h

datas 	segment	public 'datas'
	extrn	flags:byte, trans:byte, fcb:byte, buff:byte
	extrn	taklev:byte, takadr:word, dosnum:byte

comand	cmdinfo	<>
cmer00  db      cr,lf,'?Program error   Invalid COMND call$'
cmer01  db      cr,lf,'?Ambiguous$'
cmer02  db      cr,lf,'?Illegal input file spec$'
cmer03	db	cr,lf,'?Invalid command$'		 ; [19e]
cmer04	db	cr,lf,'?Invalid command or operand$'     ; [1]
cmer06	db	cr,lf,'?Wildcard not allowed$'		 ; [21a]
cmer07	db	cr,lf,'?Invalid drive specificaton$'	 ; [21a]
cmin00  db      ' Confirm with carriage return$'
cmin01	db	' One of the following:',cr,lf,'$'

cmthlp	dw	0		; Text of help message for random input.
drives	db	0		; How many drives we have. [21a]
crlf    db      cr,lf,'$'
ctcmsg	db	'^C$'
prsp	db	' $'			; Print a space.
hlpmsg	dw	0			; Address of help message.
spchar	db	24H,26H,23H,40H,21H,25H,27H,28H,29H,2DH
	db	3CH,3EH,7BH,7DH,5FH,5CH,5EH,7EH,7CH,60H
spclen	equ	$-spchar
spchar2	db	24H,26H,23H,40H,21H,25H,27H,28H,29H,2DH
	db	7BH,7DH,5FH,5EH,7EH,60H
spc2len	equ	$-spchar2
escspc	db	10O,' ',10O,'$'		; Clear escape.
clrspc  db      ' ',10O,'$'             ; Clear space.
filbuf  db	60H DUP(?)		; Character buffer.
tbuff	db	80  DUP(?)
cmdstk	dw	?
datas	ends

code	segment	public
	extrn	dodel:near, ctlu:near, cmblnk:near, locate:near, takrd:near
	extrn	clearl:near
	assume	cs:code,ds:datas,es:datas

;       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)
 
CMND	PROC NEAR
comnd:  mov comand.cmstat,ah    ; Save what we are presently parsing.
	mov cmdstk,sp		; save stack ptr locally.
        call cminbf             ; Get chars until an action or a erase char.
	mov ah,comand.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.   [8]
	jnz cm4
	jmp cmtext
cm4:	mov ah,prstr		; Else give error.
	mov dx,offset cmer00	; "?Unrecognized COMND call"
	int dos
	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 ah,conout
        mov dl,bell             ; Get a bell.
        int dos
        mov ah,0
        mov comand.cmaflg,ah    ; Turn off the action flag.
        mov bx,comand.cmcptr    ; Move the pointer to before thee scape.
        dec bx
        mov comand.cmcptr,bx
        mov comand.cmdptr,bx
        dec comand.cmccnt       ; Decremrnt the char count.
        jmp cmcfrm              ; Try again.
cmcfr2: cmp ah,'?'              ; Curious?
        jne cmcfr3
        mov ah,prstr            ; Print something useful.
        mov dx,offset cmin00
        int dos
        mov ah,prstr
        mov dx,offset crlf      ; Print a crlf.
        int dos
        mov ah,prstr
        mov dx,comand.cmprmp    ; Reprint the prompt.
        int dos
        mov bx,comand.cmdptr    ; Get the pointer into the buffer.
        mov ah,'$'              ; Put a $ there for printing.
        mov [bx],ah
        mov bx,comand.cmcptr
        dec bx                  ; Decrement & save the buffer pointer.
        mov comand.cmcptr,bx
        mov ah,prstr
        mov dx,offset comand.cmdbuf
        int dos
        mov ah,0                        ; Turn off the action flag.
        mov comand.cmaflg,ah
        jmp repars              ; Reparse everything.
 
cmcfr3: cmp ah,ff                       ; Is it a form feed?
        jne cmcfr4
        call cmblnk             ; If so blank the screen.
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 comand.cmhlp,bx     ; Save the help.
        mov comand.cmptab,dx    ; Save the beginning of keyword table.
        mov bx,dx
        mov ch,[bx]             ; Get number of entries in table.
        inc bx
	mov dx,comand.cmdptr    ; Save command pointer.
	mov comand.cmsptr,dx	; Save pointer's here.
cmky1:  cmp ch,0                ; Any commands left to check?
        jne cmky2
	jmp cmky41		; no, go complain
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?    [3]
	je cmky3y
	cmp ah,' '+80H		; A space?
	je cmky3y
	cmp ah,cr+80H		; Carriage return?
	je cmky3y
	jmp cmky38
cmky3y:	mov comand.cmkptr,bx	; Save bx here.
	mov comand.cmsiz,cx	; Save size info.
	mov comand.cmchr,ah	; Save char for latter.
	call cmambg		; See if input is ambiguous or not.
	 jmp cmky32		; Succeeded (not ambiguous).
	mov ah,comand.cmchr
	cmp ah,esc+80H		; Escape?
	je cmky3z
	cmp ah,'?'+80H		; maybe question mark?
	je cmkyj1		; yes, go handle
	jmp cmky41		; Else fail.
cmky3z:	mov ah,conout		; Ring a bell.
	mov dl,bell
	int dos
	mov bx,comand.cmcptr	; Move pointer to before the escape.
	dec bx
	mov comand.cmcptr,bx
	mov comand.cmdptr,bx
	dec comand.cmccnt	; Decrement char count.
	mov bx,comand.cmkptr	; Failed - pretend user never typed ....
	mov cx,comand.cmsiz	; ... in a char.
	dec cl			; Don't count the escape.
	dec bx
	mov comand.cmaflg,0	; Reset the action flag.
	jmp cmky3		; Keep checking.
; ambiguous.  Print out all the keywords that match
cmkyj1:	mov dx,offset cmin01
	mov ah,prstr
	int dos
	mov bx,comand.cmkptr	; this is current keyword
	mov cx,comand.cmsiz	; we are cl chars into it
	mov ch,0
	sub bx,cx		; back up to beginning
	inc bx			; not counting ?
	mov comand.cmkptr,bx	; save beginning of kw
cmkyj2:	mov dl,tab		; put a tab before each keyword
	mov ah,conout
	int dos
	mov dx,comand.cmkptr	; get current keyword
	mov ah,prstr
	int dos			; print it
	mov bx,comand.cmkptr	; get keyword back
	dec bx
	mov al,[bx]		; get length
	mov ah,0
	add ax,5		; skip length, $, value, next length
	add bx,ax		; this is next keyword
	mov si,bx
	mov di,comand.cmkptr	; compare with last keyword
	mov comand.cmkptr,bx	; update this
	mov cx,comand.cmsiz
	dec ch			; are we at end of table?
	jl cmkyj3		; yes, don't go on
	mov comand.cmsiz,cx	; else update count
	mov ch,0
	dec cl			; this includes ?
	jcxz cmkyj2		; empty, just print it
	repe cmpsb		; compare to previous string
	je cmkyj2		; same, go print this one
cmkyj3:	jmp cmky50		; else go finish up

cmky32: mov cx,comand.cmsiz	; Restore info.
	mov bx,comand.cmkptr	; Our place in the keyword table.
	cmp comand.cmchr,0A0H	; Space?
	je cmky35
	cmp comand.cmchr,0BFH	; Question mark?     [3]
	je cmky35
	cmp comand.cmchr,8DH	; Carriage return?
	je cmky35
	dec comand.cmcptr	; Pointer into buffer of input.
	mov dx,comand.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 comand.cmccnt
	jmp cmky33
cmky34:	mov ah,' '
	mov di,dx
	mov [di],ah		; Put a blank in the buffer.
	inc dx
	mov cx,comand.cmcptr	; Remember where we were.
	mov comand.cmcptr,dx	; Update our pointers.
	mov comand.cmdptr,dx
	mov ah,'$'
	mov di,dx
	mov [di],ah		; Add '$' for printing.
	mov ah,prstr
	mov dx,cx		; Point to beginning of filled in data.
	int dos
	inc bx			; Point to address we'll need.
	mov bx,[bx]
	mov comand.cmaflg,0	; Turn off action flag.
	jmp rskp

cmky35:	inc bx
	mov ah,[bx]		; Find end of keyword. 
	cmp ah,'$'
	jne cmky35	
	inc bx
	mov bx,[bx]		; Address of next routine to call.
;	mov comand.cmaflg,0	; Zero the action flag.
	jmp rskp

cmky38:	cmp ah,al
	je cmky39
        jmp cmky6               ; Go to end of keyword and try next.
cmky39:	jmp cmky3		; Check next letter.
           
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,tab
	je cmky51 
	cmp ah,esc		; Ignore escape?
	je cmky43
cmky41: mov ah,prstr
        mov dx,offset cmer03
        int dos
        jmp prserr              ; Parse error - give up.

cmky43:	mov ah,conout		; Ring a bell.
	mov dl,bell
	int dos
	dec comand.cmcptr	;[ESC] don't trash BX here.
	dec comand.cmdptr	;[ESC] ditto
	dec comand.cmccnt	; Don't count the escape.
	mov comand.cmaflg,0	; Reset action flag.
	inc ch			; Account for a previous 'dec'.
	jmp cmky1		; Start over.

cmky5:	inc bx			; point to actual keyword
	mov comand.cmkptr,bx	; remember current kw
	mov cl,1		; code above expects to count ?
	mov comand.cmsiz,cx	; and size
	mov dx,comand.cmhlp
	or dx,dx		; was any help given?
	jnz cmky5a		; yes, use it
	jmp cmkyj1		; else make our own message
cmky5a:	mov ah,prstr
        int dos
cmky50:	mov ah,prstr
	mov dx,offset crlf
	int dos
	mov dx,comand.cmprmp	; Address of prompt.
	int dos
	mov bx,comand.cmdptr	; Get pointer into buffer.
	mov al,'$'
	mov [bx],al		; Add dollar sign for printing.
	mov dx,offset comand.cmdbuf
	int dos
	dec comand.cmcptr	; Don't keep it in the buffer.
	dec comand.cmccnt	; Don't conut it.
	mov comand.cmaflg,0     ; Turn off the action flag.
        jmp repars

cmky51:	cmp comand.cmcr,1	; Are bare CR's allowed?
	je cmky52		; Yes.
	mov ah,prstr
	mov dx,offset cmer04	; Complain.
	int dos
cmky52:	jmp prserr

cmky6:  inc bx                  ; Find end of keyword.
        mov al,[bx]
        cmp al,'$'
        jne cmky6             
        inc bx                  ; Beginning of next command.
        inc bx
        inc bx
	mov dx,comand.cmsptr	; Get old cmdptr.
	mov comand.cmdptr,dx	; Restore.
	mov comand.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: inc bx                  ; Get necessary data.
        mov bx,[bx]
	cmp ah,9BH		; An escape?
	jne cmky72
	mov ah,prstr
	mov dx,offset prsp      ; Print a space.
	int dos
	mov di,comand.cmcptr
	dec di
	mov ah,20H
	mov [di],ah		; Replace escape char with space.
	mov comand.cmaflg,0
	mov comand.cmsflg,0FFH	; Pretend they typed a space.
cmky72: jmp rskp

; See if keyword is unambiguous or not 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,comand.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.

cmifil:	mov hlpmsg,bx		; Address of help message.
	mov bx,dx               ; Get the fcb address in bx.
        mov comand.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 comand.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.
	js cmif1x		; Jump out of range. [21a]
        jmp cmifi2		; Ditto. [21a]
cmif1x: and ah,7FH              ; Turn off the action bit. [21a]
        cmp ah,'?'              ; A question mark?
        jne cmif12
        mov al,0
        mov comand.cmaflg,al    ; Blank the action flag.
        dec comand.cmcptr       ; Decrement the buffer pointer.
	dec comand.cmccnt	; Decrement count.
	mov ah,prstr
	mov dx,hlpmsg		; Help  message.
	int dos
	mov dx,offset crlf
	int dos
	mov dx,comand.cmprmp
	int dos
	mov bx,comand.cmdptr
	mov al,'$'
	mov [bx],al		; Put in dollar sign for printing.
	mov dx,offset comand.cmdbuf
	int dos
	jmp repars
cmif12: cmp ah,esc		; An escape?
	je cm12x
        jmp cmif13
cm12x:	mov comand.cmaflg,0	; Turn off the action flag.
	dec comand.cmcptr	; Move pointers to before the escape.
	dec comand.cmdptr
	dec comand.cmccnt	; Decrement char count.
	mov comand.cmchr,ch	; Save current character count.
	cmp ch,9		; Past '.'?
         jl cmf120		; No.
	dec ch			; Yes, don't count point.
cmf120:	mov di,comand.cmfcb2    ; Fill the rest with CP/M wildcards.
	mov ah,'?'

cmf121:	cmp ch,11		; Done?
	 jge cmf122		; Yes.
	mov [di],ah
	inc di
	inc ch
	jmp cmf121

cmf122: mov ah,sfirst		; Find first matching file?
	mov dx,comand.cmfcb	;[jd] use pointer to PASSED fcb
	int dos
	cmp al,0FFH		; Any found?
	jne cmf123		; Yes.
	 jmp cmf12b		; No, lose.
cmf123:	mov di,offset filbuf    ; Copy first file spec from DTA to buffer.
	mov bx,offset buff+1
	mov cl,11
	call fcbcpy
	mov di,offset filbuf+10H ; Get another copy (if not ambiguous).
	mov bx,offset buff+1
	mov cl,11
	call fcbcpy
	mov ah,snext		; More matching specs?
	mov dx,comand.cmfcb	;[jd] use PASSED fcb...
	int dos
	cmp al,0FFH
	 je cmf124		; Only one.
	mov di,offset filbuf+10H ; Copy second file spec.
	mov bx,offset buff+1
	mov cl,11
	call fcbcpy

cmf124:	mov si,offset filbuf	; Start comparing file names.
	mov bx,offset filbuf+10H
	mov di,comand.cmcptr	; Command buffer pointer
	mov cl,comand.cmchr	; Bypass characters typed.
	cmp cl,9		; Past '.'?
	 jl cmf125		; No.
	dec cl			; Yes, don't count point.
cmf125:	mov ch,0		; Adjust pointers.
	add si,cx
	add bx,cx
	mov ch,cl		; Update character count

cmf126:	cmp ch,11		; All done?
	jne cmf127		; No.
	 jmp cmf12a		; Yes.
cmf127:	cmp ch,8		; End of file name?
	 jne cmf128		; No.
	cmp comand.cmchr,9	; Exactly at point?
	 je cmf128		; Yes, don't output a second point.
	mov ah,'.'		; Output separator.
	mov [di],ah
	inc di
	inc comand.cmccnt
cmf128:	mov ah,[si]		; Get a character from first file spec.
	inc si
	mov al,[bx]		; Get another from second spec.
	inc bx
	cmp ah,al		; Compare.
	 jne cmf12a		; Ambiguous.
	inc ch			; Same, count.
	cmp ah,' '		; Blank?
	 je cmf129		; Yes, don't output.
	mov [di],ah
	inc di
	inc comand.cmccnt
cmf129:	jmp cmf126		; Repeat.

cmf12a:	mov comand.cmchr,ch	; Save count of characters processed.
	mov ah,'$'		; Put terminator into buffer.
	mov [di],ah
	mov comand.cmcptr,di    ; Save pointer for recognized characters.
	mov ah,prstr
	mov dx,comand.cmdptr
	int dos
	mov ch,comand.cmchr	; Characters processed.
	cmp ch,11		; Complete file name.
	 je cmf12c		; Yes, don't beep.

cmf12b:	mov ah,conout		; Beep, if not recognized.
	mov dl,bell
	int dos			; Ring the bell.
cmf12c:	jmp repars

cmif13: mov ah,ch               ; It must be a terminator.
        cmp ah,0                ; Test the length of the file name.
	jnz cmf3x
	cmp comand.cmcr,1	; Is zero length OK? [21a]
	je cmf3z		; Return successfully. [21a]
        jmp cmifi9              ; If zero complain.
cmf3x:  cmp ah,0DH
        js cmf3y
	jmp cmifi9              ; If too long complain.
cmf3y:  jmp rskp                ; Otherwise we have succeeded.
cmf3z:	push es
	mov ax,ds
	mov es,ax
	mov di,comand.cmfcb
	inc di
	mov cx,11
	mov al,'?'
	repne stosb
	pop es
	mov flags.wldflg,0FFH	; Remember we had a wildcard.
	jmp rskp
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,comand.cmfcb
        add bx,dx               ; Point to file type field.
        mov comand.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 flags.droflg,1	; Override default drive. [21a]
	mov flags.nmoflg,0	; Not so fast. [21a]
	mov bx,comand.cmfcb2
	mov al,':'		; Use for parsing drive name.
	mov [bx],al
	dec bx			; Point to drive spec.
	mov si,bx
	push es
	mov ax,ds
	mov es,ax
	mov di,offset tbuff	; Borrow this buffer.
	mov ah,prsfcb
	int dos
	pop es
	cmp al,0		; OK return code?
	je cmif3y		; Yes, keep going.
;        mov ah,[bx]		; Get the drive name.
;        sub ah,'@'              ; Get the drive number.
;	cmp ah,drives		; Did user specify a non-existant drive? [21a]
;	jle cmif3y		; Nope, so continue. [21a]
	mov dx,offset cmer07	; Fail with this error message. [21a]
	jmp cmif9x		; [21a]
cmif3y:	mov comand.cmfcb2,bx	; Put rest of filename starting here. [21a]
	mov ah,[bx]		; Pick up drive specified.
	sub ah,'@'		; Get real value.
 	mov bx,comand.cmfcb
        mov [bx],ah		; Put it in the fcb.
	push bx
	mov al,' '		; Overwrite the drive and ":".
	inc bx
	mov [bx],al
	inc bx
	mov [bx],al
	pop bx
        jmp cmifi1
cmifi4: cmp ah,'*'
        jne cmifi7
	cmp comand.cmrflg,1	; In receive mode?  [21a]
	jne cmif4x		; Jump out of range. [21a]
	mov dx,offset cmer06	; Set the error message. [21a]
	jmp cmif9x	        ; Fail - no wildcard allowed. [21a]
cmif4x: mov ah,ch		; [21a]
        cmp ah,8H		; Is this in the name or type field?
        jns cmifi5              ; Type.
        mov cl,8H               ; Say we have eight chars.
        js cmifi6		; Name field.
        jmp cmifi9		; If its where the dot should be give up.
cmifi5: mov cl,0CH              ; Three chars.
cmifi6: mov flags.wldflg,0FFH	; Remember we had a wildcard.
	mov bx,comand.cmfcb2    ; Get a pointer into the FCB.
        mov ah,'?'
        mov [bx],ah		; Put a question mark in.
        inc bx
        mov comand.cmfcb2,bx
        inc ch
        mov ah,ch
        cmp ah,cl
        jl cmifi6               ; Go fill in another.
        jmp cmifi1              ; Get the next char.
cmifi7: cmp ah,03DH		; Equals sign (wildcard)?
	jne cmif7x
	cmp comand.cmrflg,1	; In receive mode?  [21a]
	jne cmif7y		; No, so it's ok. [21a]
	mov dx,offset cmer06	; Set the error message. [21a]
	jmp cmif9x		; Fail - no wildcard allowed. [21a]
cmif7y:	mov ah,'?'		; New label. [21a]
	mov flags.wldflg,0FFH	; Say we have a wildcard.
	jmp cmifi8		; Put into FCB.
cmif7x:	cmp ah,'0'
        jl cmif8x
        cmp ah,'z'+1
        jns cmif8x
        cmp ah,'A'		; Don't capitalize non-alphabetics.
        jl cmifi8
        and ah,137O             ; Capitalize.
cmifi8: mov bx,comand.cmfcb2    ; Get the pointer into the FCB.
        mov [bx],ah             ; Put the char there.
        inc bx
        mov comand.cmfcb2,bx
	mov flags.nmoflg,1	; Overriding name from host. [21a]
        inc ch
        jmp cmifi1

cmif8x:	push es
	mov cx,ds
	mov es,cx		; Scan uses ES register.
	mov di,offset spchar    ; Special chars.
	mov cx,spclen		; How many of them.
	cmp dosnum,0		; Under version 2.0
	je cmif8y
	mov di,offset spchar2
	mov cx,spc2len
cmif8y:	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
cmif9x:	mov ah,prstr
        int dos
	mov flags.droflg,0	; Not overriding drive. [21a] 
	mov flags.nmoflg,0	; Or name to save file under. [21a]
	mov comand.cmrflg,0	; Reset this flag too. [21a]
        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).   Called with text of help message in DX.
; Return updated pointer in BX and input size in AH.

cmtext:	mov comand.cmptab,bx	; Save pointer to data buffer.   [8 start]
	mov cmthlp,dx		; Save the help message.
	mov cl,0		; Init the char count.
cmtxt1:	mov comand.cmsflg,0	; Get all spaces. [25]
	call cmgtch		; Get a char.
	test ah,80H		; is high-order bit on?
	jz cmtxt5		; Nope, put into the buffer.
	and ah,07FH
	cmp ah,' '
	je cmtxt5
	cmp ah,esc		; An escape?
	jne cmtxt2
	mov ah,conout
	mov dl,bell		; Ring a bell.
	int dos
	mov comand.cmaflg,0	; Reset action flag.
	dec comand.cmcptr	; Move pointer to before the escape.
	dec comand.cmdptr
	dec comand.cmccnt	; Decrement count.
	jmp cmtxt1		; Try again.
cmtxt2:	cmp ah,'?'		; Asking a question?
	jz cmtx30
	cmp ah,ff		; Formfeed?
	jne cmtx2x
	call cmblnk
cmtx2x: mov ah,cl		; Return count in AH.
	mov bx,comand.cmptab	; Return updated pointer.
	jmp rskp
cmtx30:	mov comand.cmaflg,0	; Reset action flag to zero.
	inc comand.cmdptr	; count the ?
	cmp cl,0		; Is "?" first char?
	jne cmtxt5		; No, just add to buffer.
	dec comand.cmcptr	;[ESC] (moved 3 lines) Don't keep in buffer.
	dec comand.cmccnt	;[ESC] Don't conut it.
	dec comand.cmdptr	;[ESC] don't count if printing help.
	mov ah,prstr		; Else, give some help.
	mov dx,cmthlp		; Address of help message.
	int dos
        mov ah,prstr
        mov dx,offset crlf      ; Print a crlf.
        int dos
        mov ah,prstr
        mov dx,comand.cmprmp    ; Reprint the prompt.
	int dos
	mov bx,comand.cmdptr	; Get the pointer into the buffer.
	mov byte ptr [bx],'$'
	mov ah,prstr
	mov dx,offset comand.cmdbuf
	int dos
	jmp cmtxt1		; And keep going.
cmtxt5: inc cl			; Increment the count.
	mov bx,comand.cmptab	; Pointer into destination array.
	mov [bx],ah		; Put char into the buffer.
	inc bx
	mov comand.cmptab,bx
	jmp cmtxt1					; [8 end]

cmgetc:	cmp taklev,0
	jne cmget1
	jmp cmge10			; no take file, get from keyboard
cmget1:	push bx
	push si
	mov bx,takadr
	mov ax,[bx].takcnt
	or ax,[bx].takcnt+2
	jnz cmget5
cmget2:	mov al,byte ptr [bx].takfcb	; get first byte of fcb
	cmp al,0ffh			; is it really a macro?
	je cmget4			; yes, better not try to close it
	cmp al,0feh			; or maybe a file handle?
	je cmget3			; yes, close w/2.0 call
	mov ah,closf
	lea dx,[bx].takfcb
	int dos
	jmp short cmget4			; skip over alternate close
cmget3:	mov bx,word ptr [bx].takfcb+1	; this is where file handle is stored
	mov ah,close2			; use 2.0 close
	int dos
cmget4:	dec taklev
	sub takadr,size takinfo
	pop si
	pop bx
	mov al,cr		; end with carriage return...
	ret
	
cmget5:	cmp [bx].takchl,0	; Any chars left in buffer?
	jne cmget6
	call takrd
cmget6:	dec [bx].takchl
	sub [bx].takcnt,1	; DEC doesn't set carry!!
	sbb [bx].takcnt+2,0
	mov si,[bx].takptr
	lodsb
	mov [bx].takptr,si
	cmp al,ctlz		; maybe control-z?
	je cmget2		; yes, close take file (has to be before pops)
	pop si
	pop bx
	cmp al,lf		; linefeed?
	jne cmget7
	cmp flags.takflg,0
	je cmgetc		; yes, ignore it
cmget7:	cmp al,';'		; maybe a semicolon?
	je cmget9
	cmp flags.takflg,0	; Echo contents of take file?
	je cmget8
	push dx
	mov dl,al
	mov ah,conout
	int dos
	pop dx
cmget8:	ret			; else just return...
; semicolon seen, ignore chars until cr
cmget9:	call cmgetc		; get a character?
	cmp al,cr		; carriage return?
	jne cmget9		; no, keep reading
	ret			; else return it

cmge10:	mov ah,coninq		; Get a char.
	cmp flags.debug,0	; in debug mode?
	je cmge11		; yes, go on
	mov ah,8		; else use read that recognizes ^C
cmge11:	int dos
	push ax			; save the char
	cmp al,bs		; backspace?
	je cmge13		; yes, skip echo
	cmp al,' '		; printable?
	jae cmge12		; yes, no translation needed
	cmp al,cr		; this is printable
	je cmge12
	cmp al,lf
	je cmge12
	cmp al,tab
	je cmge12
	mov al,' '		; else echo a space
cmge12:	mov dl,al		; put char here
	mov ah,conout
	int dos			; echo it ourselves...
cmge13:	pop ax			; and return it
	cmp al,'C'-40H		; control-C?
	je cmge15		; yes, go handle
	cmp al,';'		; semicolon?
	je cmget9		; yes, ignore rest of line...
	cmp al,tab
	jne cmge14
	mov al,' '
cmge14:	ret
cmge15:	mov dx,offset ctcmsg
	mov ah,prstr
	int dos
	mov flags.cxzflg,'C'	; remember ^C'd
	mov sp,cmdstk		; restore command stack ptr
	ret			; and fail

; Come here is user types ^W when during input.
cntrlw:	mov ah,prstr
	mov dx,offset escspc
	int dos
	dec comand.cmccnt	; Don't include it in the count.
	dec comand.cmcptr	; Back up past the ^W.
	mov cl,comand.cmccnt
	mov ch,0
	jcxz ctlw2
	pushf 
	push es
	std			; Scan backwards.
	mov ax,ds
	mov es,ax		; Point to the data area.
	mov di,comand.cmcptr	; Looking from here.
	dec di
	mov al,' '
	repe scasb		; Look for non-space.
	je ctlw1		; All spaces, nothing else to do
	inc di			; move back to non-space
	inc cx
	repne scasb		; look for a space
	jne ctlw1		; no space, leave ptrs alone
	inc di
	inc cx			; skip back over space
ctlw1:	inc di
	mov comand.cmccnt,cl	; update count
	mov cx,comand.cmcptr	; remember old ptr
	mov comand.cmcptr,di	; update pointer
	sub cx,di		; this is characters moved
	mov al,bs		; backspace
	cld
	mov di,offset tbuff	; temporary buffer
	rep stosb		; put enough spaces in
	mov byte ptr [di],'$'	; end buffer
	mov dx,offset tbuff
	mov ah,prstr
	int dos			; back up cursor
	call clearl		; clear line
	pop es
	popf
	ret			; and return
ctlw2:	mov ah,conout
	mov dl,bell
	int dos
	ret

cminbf:	push dx
	push bx
	mov cx,dx		; Save value here too.
	mov ah,comand.cmaflg	; Is the action char flag set?
	cmp ah,0
	je cminb1
	jmp cminb9		; If so get no more chars.
cminb1: inc comand.cmccnt	; Increment the char count.
	call cmgetc
	mov ah,al		; Keep char in 'ah'.
	mov bx,comand.cmcptr	; Get the pointer into the buffer.
	mov [bx],ah		; Put it in the buffer.
	inc bx
	mov comand.cmcptr,bx
	cmp ah,'W'-64		; Is it a ^W?
	jne cmnb11
	call cntrlw		; Kill the previous word.
	jmp repars
cmnb11:	cmp ah,25O		; Is it a ^U?
	jne cminb2
cmnb12: call ctlu		; Clear out the line.
	mov ah,prstr
	mov dx,comand.cmprmp	; Print the prompt.
	int dos
	mov bx,offset comand.cmdbuf
	mov comand.cmcptr,bx	; Reset the point to the start.
	mov comand.cmccnt,0	; Zero the count.
	mov dx,cx		; Preserve original value of dx.
	jmp repars		; Go start over.
cminb2: cmp ah,bs	       ; Or backspace?
	jz cminb3
	cmp ah,del		; Delete?
	jne cminb4
cminb3:	call dodel		; Delete a character.
	mov ah,comand.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 ah,conout		; Ring the bell.
	mov dl,bell
	int dos
	jmp cmnb12		; Go reprint prompt and reparse.
cmnb32: mov comand.cmccnt,ah	; Save the new char count.
	mov ah,prstr		; Erase the character.
	mov dx,offset clrspc
	int dos
	mov bx,comand.cmcptr	; Get the pointer into the buffer.
	dec bx			; Back up in the buffer.
	dec bx
	mov comand.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 cminb8
	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 cmblnk
	call locate
cminb5: mov ah,comand.cmccnt	; Have we parsed any chars yet?
	cmp ah,1
	jnz cminb6
	jmp prserr		; If not, just start over.
cminb6: mov ah,0FFH		; Set the action flag.
	mov comand.cmaflg,ah
	jmp cminb9
cminb7: jmp cminb1		; Get another char.

cminb8: mov ah,prstr		; Don't print the escape char.
	mov dx,offset escspc
	int dos
	jmp cminb6
 
cminb9: pop bx
	pop dx
	ret
 
cmgtch: push cx
	push bx
	push dx
cmgtc1: mov ah,comand.cmaflg
	cmp ah,0			; Is it set.
	jne cmgt10
	call cminbf		; If the action char flag is not set get more.
cmgt10: mov bx,comand.cmdptr	; Get a pointer into the buffer.
	mov ah,[bx]		; Get the next char.
	inc bx
	mov comand.cmdptr,bx
	cmp ah,' '		; Is it a space?
	jz cmgtc2
	cmp ah,tab		; Or a tab?
	jne cmgtc3
cmgtc2: mov ah,comand.cmsflg	; Get the space flag.
	cmp ah,0		; Was the last char a space?
	jne cmgtc1		; Yes, get another char.
	mov ah,0FFH		; Set the space flag.
	mov comand.cmsflg,ah
	mov ah,' '
	pop dx
	pop bx
	jmp cmgtc5
cmgtc3: mov al,0
	mov comand.cmsflg,al	; 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 cmgtc4
	cmp ah,ff
	je cmgtc4
	pop cx
	ret			; Not an action char, just return.
cmgtc4: dec comand.cmdptr
cmgtc5: or ah,80H		; Make the char negative to indicate
	pop cx
	ret			; it is a terminator.
CMND	ENDP

;	This address is jumped to on reparse.

PARSE	PROC NEAR 
repars: mov sp,comand.cmostp   ; new sp <-- old sp
	mov bx,offset comand.cmdbuf
	mov comand.cmdptr,bx
	mov ah,0FFH
	mov comand.cmsflg,ah
	jmp comand.cmrprs	; go back to reparse address 
 
;	This address can be jumped to on a parsing error.
 
prserr: mov sp,comand.cmostp	; Set new sp to old one.
	mov bx,offset comand.cmdbuf
	mov comand.cmcptr,bx	; Initialize the command pointer.
	mov comand.cmdptr,bx
	mov ah,0
	mov comand.cmaflg,ah	; Zero the flags.
	mov comand.cmccnt,ah
	mov comand.cmsflg,0FFH
	cmp taklev,0		; in take cmd?
	jne prser1		; yes, don't print prompt
	mov ah,prstr
	mov dx,offset crlf
	int dos
	mov ah,prstr		; Print the prompt.
	mov dx,comand.cmprmp	; Get the prompt.
	int dos
; Instead return to before the prompt call.
prser1:	jmp comand.cmrprs
PARSE	ENDP
 
;	FCB must be remembered if found "*" in filename.      [7 start]
; 	Copy from place addressed by BX to place addressed by DI.
;	Also use to get the filename to the FCB from the DTA.

FCBCPY	PROC	NEAR
	push	es
	push	si
	mov	ax,ds
	mov	es,ax		; make sure destination segment is correct
	mov	ch,0		; high-order part of length
	jcxz	fcbcp1		; zero argument (is this necessary???)
	mov	si,bx		; this is source
	rep	movsb		; copy the whole thing
fcbcp1:	pop	si
	pop	es
	ret			; and return
FCBCPY	ENDP	

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

; Jumping here is the same as a ret.
 
R	PROC	NEAR
	ret
R	ENDP

code	ends
	end
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 mscmd.asm
	/bin/echo -n '	'; /bin/ls -ld mscmd.asm
fi
/bin/echo 'Extracting mscomm.asm'
sed 's/^X//' <<'//go.sysin dd *' >mscomm.asm
	public	data, spack, rpack, rpack5, portval, port1, port2, hierr
	include msdefs.h

gettim	equ	2CH		; Get the time of day. 
maxlp	equ	100		; Use as number of times to loop (in inchr).
true	equ	1
false	equ	0
mntrgl	equ	bufsiz/4	; Low point = 1/4 of the way full.
maxpack	equ	60H		; largest packet we can handle

datas	segment	public 'datas'
	extrn	flags:byte, trans:byte, pack:byte, count:word, xofsnt:byte

port1	prtinfo	<0FFFH,0,defpar,1,0,defhand,floxon>
port2	prtinfo	<0FFFH,0,defpar,1,0,defhand,floxon>
portval	dw	port1		; Default is to use port 1.
hierr	db	0		; Non-ascii char (non-zero if yes).
spmes	db	'Spack:  $'
rpmes 	db	'Rpack:  $'
crlf    db      cr,lf,'$'
infms0  db	'Waiting .....$'
hibit	db	'Warning - Non Ascii char$'
cemsg	db	'User intervention$'
temp	dw	0
tmp	db	?,'$'
pktptr  dw	?		; Position in receive packet.
incnt	dw	?		; Number of chars read in from port.
loopct	db	?		; Loop counter.
time 	dw	?		; When we should timeout. 
	dw	?		; Want a double word.
packet  db	?,?,?,?		; Packet (data is part of it).
data	db	5AH DUP(?)	; Data and checksum field of packet.
recpkt  db	maxpack DUP(?)	; Receive packet storage (use the following).
crctab	dw	00000H
	dw	01081H
	dw	02102H
	dw	03183H
	dw	04204H
	dw	05285H
	dw	06306H
	dw	07387H
	dw	08408H
	dw	09489H
	dw	0A50AH
	dw	0B58BH
	dw	0C60CH
	dw	0D68DH
	dw	0E70EH
	dw	0F78FH

crctb2	dw	00000H
	dw	01189H
	dw	02312H
	dw	0329BH
	dw	04624H
	dw	057ADH
	dw	06536H
	dw	074BFH
	dw	08C48H
	dw	09DC1H
	dw	0AF5AH
	dw	0BED3H
	dw	0CA6CH
	dw	0DBE5H
	dw	0E97EH
	dw	0F8F7H
datas	ends

code	segment	public
	extrn	prtchr:near, clrbuf:near, outchr:near
	extrn	sppos:near, stpos:near, biterr:near, intmsg:near
	extrn	clearl:near, rppos:near, errpack:near
	assume 	cs:code, ds:datas

;	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
 
SPKT	PROC	NEAR

spack: 	push ax			; Save the packet type.
	call clrbuf		; Clear the input buffer. [20e]
	mov bx,offset packet	; Get address of the send packet.
	mov ah,trans.ssoh	; Get the start of header char.
	mov [bx],ah		; Put in the packet.
	inc bx			; Point to next char.
	mov ax,pack.argbk1	; Get the number of data chars.
	xchg ah,al
	mov al,trans.chklen	; Length of checksum.
	dec al			; Extra length of checksum.
	add ah,' '+3		; Real packet character count made printable.
	add ah,al		; Account for checksum length in count.
	mov [bx],ah		; Put in the packet.
	inc bx			; Point to next char.
	mov ch,0		; For the 16 bit checksum.
	mov cl,ah		; Start the checksum.
	mov ax,pack.argblk	; Get the packet number.
	add al,' '		; Add a space so the number is printable.
	mov [bx],al		; Put in the packet.
	inc bx			; Point to next char.
	add cx,ax		; 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.
	mov al,0
	xchg ah,al
	add cx,ax		; Add the type to the checksum.
	mov dx,pack.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 al,[bx]		; Get the next char.
	inc bx			; Point to next char.
	mov ah,0
	add cx,ax		; Add the char to the checksum.
	cmp al,0
	jns spack2
	cmp hierr,0ffH		; Printed message already?
	je spack2		; Yes, then that's it.
	push bx
	push cx
	push dx
	call biterr
	pop dx
	pop cx
	pop bx
	mov hierr,0FFH		; set err flag. 
	jmp spack2		; Go try again.
spack3:	cmp trans.chklen,2	; What kind of checksum are we using.
	je spackx		; 2 characters.
	jg spacky		; 3 characters.
	mov ah,cl		; 1 char: 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.
	jmp spackz		; Add EOL char.
spacky:	mov al,0		; Get a null.
	mov [bx],al		; To determine end of buffer.
	push bx			; Don't lose our place.
	mov bx,offset packet+1	; First checksummed character.
	call crcclc		; Calculate the CRC.
	pop bx
	push cx
	mov ax,cx		; Manipulate it here.
	and ax,0F000H		; Get 4 highest bits.
	mov cl,4
	shr ah,cl		; Shift them over 4 bits.
	add ah,' '		; Make printable.
	mov [bx],ah		; Add to buffer.
	inc bx
	pop cx			; Get back checksum value.
spackx:	push cx			; Save it for now.
	and cx,0FC0H		; Get bits 6-11.
	mov ax,cx
	mov cl,6
	shr ax,cl		; Shift them bits over.
	add al,' '		; Make printable.
	mov [bx],al		; Add to buffer.
	inc bx
	pop cx			; Get back the original.
	and cx,003FH		; Get bits 0-5.
	add cl,' '		; Make printable.
	mov [bx],cl		; Add to buffer.
	inc bx
spackz:	mov ah,trans.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 flags.debug,0	; debug mode.
	je spack4
	inc bx
	mov ah,'$'
	mov [bx],ah
	call sppos
	call clearl		; Clear to end of line.
	mov dx,offset crlf
	mov ah,prstr
	int dos
	call clearl		; Next line too.
	call sppos		; Reposition cursor.
	mov ah,prstr
	mov dx,offset spmes
	int dos
	mov dx,offset packet
	mov ah,prstr
	int dos			; debug end.
spack4: call outpkt		; Call the system dependent routine.
	 jmp r
	jmp rskp
SPKT	ENDP 

;	Write out a packet.
 
OUTPKT  PROC	NEAR
	mov dh,trans.spad	; Get the number of padding chars.
outpk2: dec dh
	cmp dh,0
	jl outpk3		; If none left proceed.
	mov ah,trans.spadch	; Get the padding char.
	call outchr		; Output it.
	 jmp r			; Say we failed. [25]
	jmp outpk2
outpk3: mov bx,offset packet	; Point to the packet.
outlup: mov ah,[bx]		; Get the next character.
	cmp ah,0		; Is it a null?
	jnz outlp2
	jmp rskp
outlp2: call outchr		; Output the character.
	 jmp r
	inc bx			; Increment the char pointer.
	jmp outlup
OUTPKT  ENDP
 
; Calculate the CRC.  Returns the CRC in CX.  Destroys: BX, AX.
crcclc:	push dx
	push si
	mov dx,0		; Initial CRC value is 0.
crc0:	mov al,[bx]		; Get the first char of the string.
	cmp al,0		; If null, then we're done.
	je crc1
	inc bx
	xor al,dl		; Xor input with lo order byte of CRC.
	mov ah,al		; Get a copy.
	and ah,0F0H		; Get hi 4 bits.
	mov cl,4
	shr ah,cl		; Right justify.
	and al,0FH		; Get lo 4 bits.
	push bx
	mov si,offset crctb2	; Low portion of CRC factor.
	mov bh,0
	mov bl,al
	add bl,al		; Get word index.
	mov cx,[si+bx]		; Low portion.
	mov si,offset crctab	; High portion of CRC factor.
	mov bh,0
	mov bl,ah
	add bl,ah		; Get word index.
	mov bx,[si+bx]
	xor bx,cx		; Add the two.
	mov cl,8
	shr dx,cl		; Shift CRC 8 bits to the right.
	xor dx,bx		; XOR table value and CRC.
	pop bx			; Retrieve index.
	jmp crc0
crc1:	mov cx,dx		; Return it in CX.
	pop si
	pop dx
	ret

; Receive_Packet
; This routine waits for a packet arrive from the host.  It reads
; chars until it finds a SOH.

RPACK	PROC	NEAR
rpack5: call inpkt		; Read up to a carriage return.
	 jmp r			;  Return bad.
rpack0: call getchr		; Get a character.
	 jmp r			;  Hit the carriage return, return bad.
	cmp al,trans.rsoh	; 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 al,trans.rsoh	; Is the char the start of header char?
	 jz rpack1		;  Yes, then go start over.
	mov ch,0		; For 16-bit checksum.
	mov cl,al		; Start the checksum.
	mov ah,0
	mov pack.argbk1,ax	; Save the data count.
	call getchr		; Get a character.
	 jmp r			;  Hit the carriage return, return bad.
	cmp al,trans.rsoh	; Is the char the start of header char?
	 jz rpack1		;  Yes, then go start over.
	mov ah,0
	add cx,ax		; Add it to the checksum.
	sub al,' '		; Get the real packet number.
	mov ah,0
	mov pack.argblk,ax	; Save the packet number.
	call getchr		; Get a character.
	 jmp r			;  Hit the carriage return, return bad.
	cmp al,trans.rsoh	; Is the char the start of header char?
	 jz rpack1		;  Yes, then go start over.
	mov ah,0
	mov temp,ax		; Save the message type. [11]
	add cx,ax		; Add it to the checksum.
; Start of change.
; Now determine block check type for this packet.  Here we violate the layered
; nature of the protocol by inspecting the packet type in order to detect when
; the two sides get out of sync.  Two heuristics allow us to resync here:
;   a. An S packet always has a type 1 checksum.
;   b. A NAK never contains data, so its block check type is LEN-2. 
	push cx
	mov cl,al
	mov ax,pack.argbk1	; Get back the size.
	sub al,34		; unchar(len) - 2, for SEQ & TYPE fields.
	mov ah,trans.chklen	; Checksum length we expect.
	cmp cl,'S'		; Is this an "S" packet?
	jne rpk0		; Nope.
	mov ah,1		; Yes, use 1 char checksum.
rpk0:	cmp cl,'N'		; Is this a NAK?
	jne rpk1		; Nope.
	mov ah,al		; So, len - 2 is checksum type.
rpk1:	mov trans.chklen,ah	; Then, this is the chksum length.
	sub al,ah		; Real size of data.
	mov dh,al		; Need it here.
	mov ah,0
	mov pack.argbk1,ax	; And here.
	pop cx	
; End of change.
	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 al,trans.rsoh	; Is the char the start of header char?
	 jz rpack1		;  Yes, then go start over.
	mov [bx],al		; Put the char into the packet.
	inc bx			; Point to the next character.
	mov ah,0
	add cx,ax		; 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 al,trans.rsoh	; Is the char the start of header char?
	 jnz rpk3x
	 jmp rpack1		;  Yes, then go start over.
rpk3x:	sub al,' '		; Turn the char back into a number.
	cmp trans.chklen,2	; What checksum length is in use.
	je rpackx		; Two character checksum.
	jg rpacky		; Three character CRC.
	mov dh,cl		; 1 char - 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,al		; Are they equal?
	 jz rpack4		; If so finish up.
	jmp rpack6		; No, we fail.
rpacky:	mov tmp,al		; Save value from packet here.
	mov ah,0		; Three character CRC.
	push bx
	mov bx,pktptr		; Where we are in the packet.
	dec bx
	mov [bx],ah		; Add null to signify end of buffer.
	mov bx,offset recpkt+1	; Where data for CRC is.
	call crcclc		; Calculate the CRC and put into CX.
	pop bx
	push cx
	mov ax,cx		; Manipulate it here.
	and ax,0F000H		; Get 4 highest bits.
	mov cl,4
	shr ah,cl		; Shift them over 4 bits.
	pop cx			; Get back checksum value.
	cmp ah,tmp		; Is what we got == what we calculated?
	jne rpack6
	call getchr		; Get next character of checsum.
	 jmp r			; Failed.	
	cmp al,trans.rsoh	; Restarting?
	 jz rpack7
	sub al,' '		; Get back real value.
rpackx:	mov tmp,al		; Save here for now.
	push cx			; Two character checksum.
	and cx,0FC0H		; Get bits 6-11.
	mov ax,cx
	mov cl,6
	shr ax,cl		; Shift them bits over.
	pop cx			; Get back the original.
	cmp al,tmp		; Are they equal?
	 jne rpack6		; No, we fail.
	call getchr		; Get last character of checsum.
	 jmp r			; Failed.	
	cmp al,trans.rsoh	; Restarting?
	 jz rpack7
	sub al,' '		; Get back real value.	
	and cx,003FH		; Get bits 0-5.
	cmp al,cl		; Do the last chars match?
	jne rpack6
rpack4: mov ah,0
	mov [bx],ah		; Put a null at the end of the data.
	mov ax,temp		; Get the type.   [11]
	xchg al,ah		; Packet type should be in AH.
	jmp rskp
rpack6:	ret
rpack7:	jmp rpack1		; For the jump out of range.
RPACK	ENDP
 
 
INPKT	PROC	NEAR
	mov bl,flags.cxzflg	; Remember original value. [20b]
	mov tmp,bl		; Store it here. [20b]
inpkt1:	mov bx,offset recpkt	; Point to the beginning of the packet.
	mov incnt,0
inpkt2:	call inchr		; Get a character.
	 jmp inpkt8		;  Return failure. [20b]
	 nop			;  Make it three bytes long. [20b] 
	mov [bx],ah		; Put the char in the packet.
	inc bx
	inc incnt
	cmp ah,trans.reol	; Is it the EOL char?
	je inpkt3		; ended by eol, keep going
	cmp incnt,maxpack	; is it too big?
	jbe inpkt2		; no, keep going
	jmp inpkt1		; else just start over
inpkt3:	cmp incnt,1		; Ignore bare CR.   [2 start]
	je inpkt1
	mov bp,portval
	cmp ds:[bp].hndflg,0	; Waiting for handshake?
	jz inpkt5		; If not then proceed.
inpkt4: call inchr		; Wait for the turn around char.
	 jmp inpkt8		;  Return failure. [20b]
	 nop			;  Make it three bytes long.  [20b]
	mov bp,portval
	cmp ah,ds:[bp].hands	; Is it the IBM turn around character?
	jne inpkt4		; If not, go until it is.
inpkt5:	cmp flags.debug,0	; In debug mode?
	je inpkt6
	mov ah,'$'
	mov [bx],ah
	call rppos
	call clearl		; Clear to end of line.
	mov dx,offset crlf
	mov ah,prstr
	int dos
	call clearl		; Next line too.
	call rppos		; Reposition cursor.
	mov ah,prstr
	mov dx,offset rpmes
	int dos
	mov dx,offset recpkt
	mov ah,prstr
	int dos			; debug end.
inpkt6:	mov bx,offset recpkt
	mov pktptr,bx		; Save the packet pointer.
	mov bl,tmp		; Get the original value. [20b]
	cmp bl,flags.cxzflg	; Did ^X/^Z flag change? [20b]
	je inpkt7		; If not, just return.  [20b]
	cmp flags.cxzflg,'E'	; Error packet?
	je inpkt9
	call intmsg 		; Else, say we saw the interrupt. [20b]
inpkt7: jmp rskp		; If so we are done.
inpkt8:	cmp flags.cxzflg,'C'	; Did the user type a ^C? [25]
	jne inpkt9
	mov pack.state,'A'
	ret
inpkt9:	cmp flags.cxzflg,'E'	; How about ^E?
	jne inpk10		; No just go on.
	mov bx,offset cemsg	; Null message for error packet.
	call errpack
	mov pack.state,'A'
	ret
inpk10:	mov bl,tmp		; Get the original value. [20b]
	cmp bl,flags.cxzflg	; Did ^X/^Z flag change? [20b]
	je inpk11		; If not, just return failure.  [20b]
	call intmsg 		; Else, say we saw the interrupt. [20b]
inpk11:	jmp r
INPKT	ENDP

inchr:	cmp flags.timflg,0	; Are timeouts turned off.
	je inchr1		; Yes, so skip this stuff.
	cmp trans.stime,0	; Don't time out?
	je inchr1		; Yes, so skip this stuff.
	mov loopct,0		; Use to check for timeout.
	mov ah,gettim		; Get the time.
	int dos
	mov time,cx
	mov time+2,dx
	mov ah,0
	mov al,trans.stime	; Timeout when getting data.
	mov cl,8
	shl ax,cl		; Move timeout to seconds field.
	add time+2,ax		; If get to this time, then timeout.
	jnc inchr1
	inc time
inchr1:	call prtchr		; Is there a character to read?
	 jmp inchr6		; Got one.
	mov dl,0FFH		; To read in a char.
	mov ah,dconio		; Is a char on the console?
	int dos
	jz inchr2		; If not go look for another char.
	mov ah,al 
	cmp ah,cr		; Is it a carriage return?
	je inchr5		; If yes, then leave.
	cmp ah,'Z'-100O		; Control-Z? [20b]
	je inchr4		; Yes - flag it. [20b]
	cmp ah,'X'-100O		; Control-X? [20b]
	je inchr4		; Yes - flag it. [20b]
	cmp ah,'E'-100O		; Control-E?
	je inchr4		; Flag it and get rest of packet.
	cmp ah,'C'-100O		; Control-C? [25]
	jne inchr2		; No, then wait for input. [25]
	add ah,100O		; Make it printable. [25]
	mov flags.cxzflg,ah	; Save it. [25]
	ret			; Return right away. [25]
inchr2:	cmp flags.timflg,0	; Are timeouts turned off?
	je inchr1		; Yes, just check for more input.
	cmp trans.stime,0	; Doing time outs?
	je inchr1		; No, just go check for more input.
	inc loopct
	cmp loopct,maxlp	; Times to go without checking time.
	jne inchr1		; Don't check yet.
	mov ah,gettim		; Get the current time.
	int dos
	mov ax,time
	sub ax,cx		; Check hours and minutes.
	jl inchr5		; Over the limit so fail.
	jg inchr3		; Under the limit, keep going.
	mov ax,time+2
	sub ax,dx		; Else, check seconds and hundreds of seconds.
	jle inchr5		; Return failure.
inchr3:	mov loopct,0		; Reset counter.
	jmp inchr1
inchr4: add ah,100O		; Make it printable. [20b]
	mov flags.cxzflg,ah	; Remember what we saw. [20b]
	jmp inchr2		; Continue getting input. [20b]
inchr5:	ret
inchr6: mov ah,al
	mov bp,portval		; Point to current port structure.
	cmp ds:[bp].parflg,parnon	; Is the parity none?	[10]
	je inchr7		; We're done.		[10]
	and ah,7FH		; Turn off the parity bit.
inchr7:	cmp ds:[bp].floflg,0	; Doing any flow control?
	jne inchr8		; Yes, check it out.
	jmp rskp		; No, just return the data.
inchr8:	cmp xofsnt,true		; Have we sent flow char (XOFF)?
	je inchr9		; Yes.
	jmp rskp		; No, just return.
inchr9:	cmp count,mntrgl	; Under the low trigger point?
	jb inchra		; Yes.
	jmp rskp		; No, just return.
inchra:	push ax
	mov bp,portval
	mov ax,ds:[bp].flowc	; Get flow control char (AH = XON, AL = XOFF).
	call outchr		; Send it (XON).
	mov xofsnt,false	; Turn off the flag.
	pop ax
	jmp rskp		; Return the character.

; Return next character in AL.
GETCHR  PROC	NEAR
	push bx
	mov bx,pktptr		; Get the packet pointer.
	mov al,[bx]		; Get the char.
	inc bx
	mov pktptr,bx
	pop bx			; Restore BX.
	cmp al,trans.reol	; Is it the EOL char?
	jne getcr2		; If not return retskp.
	ret			; If so return failure.
getcr2: jmp rskp
GETCHR  ENDP

; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.
 
RSKP	PROC	NEAR
	pop bp
	add bp,3
	push bp
	ret
RSKP	ENDP
 
; Jumping here is the same as a ret.
 
R	PROC	NEAR
	ret
R	ENDP

code	ends
	end
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 mscomm.asm
	/bin/echo -n '	'; /bin/ls -ld mscomm.asm
fi
/bin/echo 'Extracting msdefs.h'
sed 's/^X//' <<'//go.sysin dd *' >msdefs.h
verdef	macro
	db	' Kermit-MS V2.26'
	endm

BELL    EQU     07Q
TAB     EQU     11Q
LF      EQU     12Q
FF      EQU     14Q
CR      EQU     15Q
XXON     EQU     21Q
XXOFF    EQU     23Q
ESC     EQU     33Q
DEL     EQU     177Q
BS	EQU	08H
CTLZ	EQU	1AH
SOH     EQU     01H             ; Start of header char.

DOS     EQU     21H

CONIN   EQU     01H
CONOUT  EQU     02H
RDRIN   EQU     03H
PUNOUT  EQU     04H
LSTOUT  EQU     05H
DCONIO  EQU     06H
CONINQ	EQU	07H		; quiet console input
PRSTR   EQU     09H
CONSTAT EQU     0BH
SELDSK	EQU	0EH		; Select disk.  [21a]
OPENF   EQU     0FH
CLOSF   EQU     10H
SFIRST  EQU	11H
SNEXT	EQU	12H
DELF    EQU     13H
READF   EQU     14H             ; Read from the file.
WRITEF  EQU     15H
MAKEF   EQU     16H
GCURDSK	EQU	19H		; Current disk.  [21a]
SETDMA	EQU	1AH
PRSFCB	equ	29H		; parse an fcb.
DOSVER	equ	30H		; dos version #
OPEN2	EQU	3DH		; 2.0 open
CLOSE2	EQU	3EH		; 2.0 close
READF2	EQU	3FH		; 2.0 read.
LSEEK	EQU	42H		; 2.0 lseek
IOCTL	EQU	44H
WRITEF2	EQU	40H		; 2.0 write
GCD	equ	47H		; 2.0 get current directory.

PAREVN	EQU	00H		; Even parity.			[10 start]
PARMRK	EQU	01H		; Mark parity.
PARNON	EQU	02H		; No parity.	
PARODD	EQU	03H		; Odd parity.
PARSPC	EQU	04H		; Space parity.

CMKEY   EQU     01H             ; Parse a keyword.
CMIFI   EQU     02H             ; Parse an input file spec (can be wild).
CMOFI   EQU     03H             ; Parse an output file spec.
CMCFM   EQU     04H             ; Parse a confirm.
CMTXT	EQU	05H		; Parse arbitrary text up to CR.   [8]

FLOXON	EQU	1113H		; Use XON/XOFF for flow control.
FLONON	EQU	0		; Don't do flow control.
DEFHAND	EQU	XON		; Use XON as default handshake.

DMASIZ	EQU	80H		; Size of DMA.
FCBSIZ	EQU	25H

MAXTAK	EQU	05H		; Max number of TAKE's allowed. [25t]
MAXTRY  EQU	05Q		; Default number of retries on a packet.
IMXTRY  EQU	20Q		; Default number of retries send initiate.

DEFESC  EQU     ']'-100Q        ; The default escape character.
DRPSIZ  EQU     5EH             ; Default receive packet size.
DSPSIZ  EQU     50H             ; Default send packet size.
DSTIME  EQU     08H             ; Default send time out interval.
DRTIME  EQU     0DH             ; Default receive time out interval.
DSRVTM	EQU	30		; Default server timeout.
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.
DSSOH	EQU	SOH		; Default send start-of-packet char.
DRSOH	EQU	SOH		; Default receive start-of-packet char.
DSQUOT  EQU     '#'             ; Default send quote char.
DRQUOT  EQU     '#'             ; Default receive quote char.
DQBIN	EQU	'&'		; Default 8-bit prefix. [21b]
DRPT	EQU	'~'		; Default repeat prefix.
DCHKLEN	EQU	1		; Default checksum length.
DEFPAR	EQU	PARNON		; Default parity (none.) 
IBMPAR  EQU	PARMRK		; IBM's parity (mark.)		[10 end]


bufsiz	equ	2048		; size of serial input buffer

; baud rate definitions
B00455	EQU	0		; 45.5 baud
B0050	EQU	1		; 50 baud
B0075	EQU	2		; 75 baud
B0110	EQU	3		; 110 baud
B01345	EQU	4		; 134.5 baud
B0150	EQU	5		; 150 baud
B0300	EQU	6		; 300 baud
B0600	EQU	7		; 600 baud
B1200	EQU	8		; 1200 baud
B1800	EQU	9		; 1800 baud
B2000	EQU	10		; 2000 baud
B2400	EQU	11		; 2400 baud
B4800	EQU	12		; 4800 baud
B9600	EQU	13		; 9600 baud
B19200	EQU	14		; 19200 baud
B38400	EQU	15		; 38400 baud 

BAUDSIZ	EQU	16		; Number of options for baud rate.

; Structure definitions.

; Modem information.
mdminfo	struc
mddat	dw	0		; Default to port 1. [19b start]
mdstat	dw	0		; Ditto. 
mdcom	dw	0		; Here too. 
mden	db	0
mddis	db	0
mdmeoi	db	0
mdintv	dw	0		; [19b end]
mdminfo	ends

; Command information.
cmdinfo	struc	
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  db      80H DUP(0)      ; 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.
cmrflg	db	0		; Assume parsing filename for send. [21a]
cmcr	db	0		; Say whether bare CR is allowed.
cmdinfo	ends

; Flags information.
flginfo	struc
belflg	db	1		; Use bell  [17a -- DT]
comflg	db	1		; Use COM1 by default. [19b]
abfflg	db	1		; Discard incoming file if abort. [20d]
debug	db	0		; Debugging mode (default off).
flwflg  db      1               ; File warning flag (default on). [19c]
ibmflg  db      0               ; IBM flag (default off).
extflg  db      0               ; Exit flag (default off).
vtflg	db	1		; H-19 emulation.
droflg	db	0		; Override default disk drive. [21a]
nmoflg	db	0		; Override name from the F packet. [21a]
wldflg	db	0		; Assume no "*" in fn.         [7]
cxzflg	db	0		; ^X/^Z to interrupt file x-fer. [20b] 
xflg	db	0		; Seen "X" packet. [21c]
filflg  db      0               ; Non-zero when nothing in DMA buffer.
eoflag  db      0               ; EOF flag; non-zero on EOF.
getflg	db	0		; Assume normal RECEIVE (not GET). [21a]
capflg	db	0		; On if capturing data. [25]
takflg	db	0		; On if echo commands of TAKE file.
timflg	db	0		; Say if are timing out or not.
destflg	db	1		; Incoming files destination: disk or printer.
eofcz	db	0		; ^Z signals eof if non-zero.
remflg	db	0		; non-zero if in remote mode.
flginfo	ends

; Transmission parameters
trinfo	struc
maxdat	db	0		; Max packet size for send.
chklen	db	1		; Number of characters in checksum.
seol	db	dseol		; Send EOL char.
reol	db	dreol		; Receive EOL char.
ssoh	db	dssoh		; Send start-of-packet character.
rsoh	db	drsoh		; Receive start-of-packet character.
squote  db	dsquot		; Send quote character.
rquote	db	drquot		; Receive quote character.
spsiz	db	dspsiz		; Send packet size.
rpsiz	db	drpsiz		; Receive packet size.
stime	db	dstime		; Send timeout. (Don't timeout).
rtime	db	drtime		; Receive timeout.
spad	db	dspad		; Send padding.
rpad	db	drpad		; Receive padding.
spadch	db	dspadc		; Send padding char.
rpadch	db	drpadc		; Receive padding char.
ebquot	db	'Y'		; Send 8-bit quote character.
escchr	db	defesc		; Escape character.
trinfo	ends

pktinfo	struc
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.
state   db      0               ; Present state of the automaton.
argblk  dw      0               ; For subroutine arguments.
argbk1  dw      0
argbk2  dw      0
argbk3  dw      0
pktinfo	ends

takinfo	struc
takfcb	db	fcbsiz dup(0)
takbuf	db	dmasiz dup(0)
takptr	dw	0
takchl	db	0
takcnt	dw	0,0
takinfo ends

; Port Information.
prtinfo	struc
baud	dw	0		; Default baud rate.
ecoflg  db      0               ; Local echo flag (default off).
parflg  db	0		; Parity flag (default none.)  [10]
floflg	db	0		; If need flow control during file x-fer.
hndflg	db	0		; If need handshake during file x-fer.
hands	db	0		; Default handshake.
flowc	dw	0		; Do flow control with XON/XOFF.
prtinfo	ends

mkeyw	macro	key,val
	local	junk,oldval
oldval	equ	$
	db	junk,key,'$'
junk	equ	$-oldval-2
	dw	val
	endm


; definitions for terminal handler:

termarg	struc
flgs	db	?		; flags
prt	db	?		; port to use (0,1)
cols	db	?		; # columns on screen
rows	db	?		; # rows on screen
captr	dw	?		; routine to call with captured data
belld	dw	?		; bell divisor
klen	dw	?		; length of key redefinition table
ktab	dw	?		; address of key redefinition table
krpl	dw	?		; address of key replacement table
escc	db	?		; escape character
baudb	db	?		; baud rate bits.
parity	db	?		; parity
termarg	ends

; bits for flag byte
scrsam	equ	80h			; on if shouldn't redraw screen
capt	equ	40h			; capture output
emheath	equ	20h			; emulate heath
havtt	equ	10h			; have translate table
trnctl	equ	08h			; translate control chars
modoff	equ	04h			; mode line off
lclecho	equ	01h			; local echo

//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msdefs.h
	/bin/echo -n '	'; /bin/ls -ld msdefs.h
fi
/bin/echo 'Extracting msfile.asm'
sed 's/^X//' <<'//go.sysin dd *' >msfile.asm
	public	bufpnt, buff, fcb, cpfcb, chrcnt, fixfcb, init, init1,
	public	gofil, outbuf, ptchr, gtchr, gtnfil, getfil, filbuf,
	public	encode, decode, nulref, nulr, decbuf, errpack, rptq,
	public	origr, rptct, rptval, clrfln, cxmsg, biterr, intmsg,
	public	rtpos, erpos,rppos, stpos,nppos,rprpos,nrtpos,sppos,
	public	kbpos,perpos,frpos, prtscr
	include msdefs.h

rptmin	equ	3		; At least 3 of same char in a row.

; equates for screen positioning
scrfln	equ	0316H		; Place for file name.
scrkb	equ	0416H		; Place for percent transferred.
scrper	equ	0516H		; Place for Kbytes transferred.
scrst	equ	0616H		; Place for status.
scrnp	equ	0816H		; Place for number of packets.
scrnrt  equ	0916H		; Place for number of retries.
screrr  equ	0A16H		; Place for error msgs. 
scrhi	equ	0B16H		; Err when 8th bit is on.
scrfr	equ	0B16H		; Rename file.
scrint	equ	0B16H		; Acknowledge interrupt. [20b]
scrsp	equ	0C00H		; Place for send packet.
scrrp	equ	0E00H		; Place for receive packet.
scrrpr	equ	1100H		; Prompt when Kermit ends.



datas	segment	public 'datas'
	extrn	data:byte, flags:byte, trans:byte, pack:byte, hierr:byte
	extrn	dosnum:byte

outlin  db	cr,lf,cr,lf
        db      cr,lf,'           File name:'
        db      cr,lf,'  KBytes transferred:'
        db      cr,lf
        db      cr,lf
        db      cr,lf
        db      cr,lf,'   Number of packets:'
        db      cr,lf,'   Number of retries:'
        db      cr,lf,'          Last error: None'
        db      cr,lf,'        Last warning: None'
        db      '$'

ermes4  db	'Unable to rename file$'
erms10  db	'?Unable to receive data$'
erms11  db	'?Disk full$'
erms12	db	'?Unable to create file$'
erms17  db	'Record length exceeds size of buffer$'
infms5  db	'Renaming file to $'
infms7	db	'File interrupt$'
infms8	db	'File group interrupt$'
hibit	db	'Warning - Non Ascii char$'
crlf	db	cr,lf,'$'
printer	db	0,'LPT1       '
spchar	db	24H,26H,23H,40H,21H,25H,27H,28H,29H,2DH
	db	3CH,3EH,7BH,7DH,5FH,5CH,5EH,7EH,7CH,60H
spclen	equ	$-spchar	; Number of special chars.
spchar2	db	24H,26H,23H,40H,21H,25H,27H,28H,29H,2DH
	db	7BH,7DH,5FH,5EH,7EH,60H
spc2len	equ	$-spchar2
next	db	0FFH		; No next character just yet.
rptval	db	0		; Repeated character.
rptct	db	1		; Number of times it's repeated.
rptq	db	drpt		; Repeat prefix.
origr	db	drpt		; Original repeat prefix.
temp1	dw	?		; Temporary storage.
temp2	dw	?
oloc	dw	0		; Original buffer location. [21c]
osiz	dw	0		; Original buffer size. [21c]
chrcnt  dw	?		; Number of chars in the file buffer.
outpnt  dw	?		; Position in packet.
bufpnt  dw	?		; Position in file buffer.
fdtpnt	dw	?		; Pointer to within our file. 
fcbptr  dw	?		; Position in FCB.
cbfptr  dw	?		; Position in character buffer.
filsiz	dw	0		; Double word for filesize (in bytes.)
	dw	0
ofilsz	dw	0		; Original file size percent adjusted (/100).
tfilsz	dw	0		; Bytes transferred.
	dw	0
oldper	dw	?		; old percentage
oldkbt	dw	?		; old KB transferred.
wrpmsg	db	?		; non-zero if we wrote percent message
percnt	dw	100		; Number to divide by for a percent.
bufhex	dw	80H
permsg	db	cr,' Percent transferred:$'
cxzhlp	db	'^X cancels file, ^Z cancels batch'
	db	', ^E aborts protocol'
	db	', ^C aborts at once'
	db	'$'
asmsg	db	' AS '
asmln	equ	$-asmsg
filbuf  db	60H DUP(?)	; Character buffer.
buff	db	dmasiz DUP(?)	; Use as our DTA.
fcb	db	fcbsiz DUP(?)	; Use as our FCB.
cpfcb	db	fcbsiz DUP(?)	; Save FCB in case of "*".   [7]
decbuf	db	dmasiz DUP(?)	; For decoding incoming data.
datas	ends

code	segment	public
	extrn	spack:near, cmblnk:near, locate:near, nout:near
	extrn	putmod:near, poscur:near, clearl:near, fcbcpy:near
	assume  cs:code,ds:datas

; Position cursor for an error message.

ERPOS	PROC 	NEAR
	cmp flags.xflg,1	; Packet header seen? [21c start] 
	jne erp0		; No, do as normal. 
	mov dx,offset crlf
	mov ah,prstr
	int dos
	ret
erp0:	mov dx,screrr
	jmp poscur
ERPOS	ENDP

; Position cursor for number of retries message.

RTPOS	PROC 	NEAR
	cmp flags.xflg,1	; Packet header seen? [21c]
	jne rtp0		; No, do as normal.
	ret
rtp0:	mov dx,scrnrt
	jmp poscur
RTPOS	ENDP

; Reassure user that we acknowledge his ^X/^Z.

INTMSG	PROC	NEAR
	cmp flags.xflg,0	; Writing to screen?
	jne int1		; Yes. Don't do anything.
	mov dx,scrint
	call poscur
	call clearl
	mov dx,offset infms7    ; File interrupted?
	cmp flags.cxzflg,'X'	; Yes. 
	je int0
	mov dx,offset infms8	; File group interrupted.
int0:   mov ah,prstr
        int dos
int1:	ret
INTMSG	ENDP

; Print err message that found a non-standard-Ascii char in the file.

BITERR	PROC	NEAR
	cmp flags.remflg,0	; remote mode?
	jne biter1		; yes, no printing.
	push bx
	mov dx,scrhi
	call poscur
	call clearl
	mov ah,prstr
	mov dx,offset hibit
	int dos
	pop bx
biter1:	ret
BITERR	ENDP		

;  Clear out message about interrupted file.

CXMSG	PROC	NEAR
	cmp flags.xflg,0	; Writing to screen?
	jne cxm0		; Yes. Don't do anything.
	mov dx,scrint
	call poscur
	call clearl
cxm0:	ret
CXMSG	ENDP

;  Clear out the old filename on the screen. 

CLRFLN	PROC	NEAR
	mov dx,scrfln
	call poscur
	call clearl		; Clear to end of line. [19a]
	ret
CLRFLN	ENDP

; some random screen positioning functions
kbpos:	mov dx,scrkb		; KBytes transferred.
	jmp poscur
perpos:	mov dx,scrper		; Percent transferred.
	call poscur
	jmp clearl
frpos:	mov dx,scrfr		; Say renamed file.
	call poscur
	jmp clearl
stpos:	mov dx,scrst		; Print status of file transfer.
	call poscur
	jmp clearl
nppos:	mov dx,scrnp		; Number of packets sent.
	jmp poscur
rprpos:	mov dx,scrrpr		; Reprompt position.
	jmp poscur
nrtpos:	mov dx,scrnrt		; Number of retries.
	jmp poscur
sppos:	mov dx,scrsp		; Send packet location.
	jmp poscur
rppos:	mov dx,scrrp		; Receive packet location.
	jmp poscur



;	Initialize buffers and clear line.
 
INIT	PROC	NEAR
	call cmblnk
	call locate
	mov ah,prstr		; Put statistics headers on the screen.
	mov dx,offset outlin
	int dos
	mov dx,offset cxzhlp
	call putmod		; write mode line
	mov wrpmsg,0		; haven't printed the messsage yet.
	call init1
	ret
INIT	ENDP
 
INIT1	PROC	NEAR
	mov chrcnt,dmasiz	       ; Number of chars left.
	mov bufpnt,offset buff	       ; Addr for beginning.
	mov hierr,0
	ret
INIT1	ENDP

;	Output the chars in a packet.

; Called with AX = size of the data, BX = address of source.

FILEIO	PROC	NEAR 	
ptchr:  mov cx,ax
	lea ax,outbuf		; Where to put data when buffer gets full.
	jmp decode

; CX = Size of data, BX = Address of data, AX = Routine to call to
; dump data.

decode: push si
	push di
	push es
	push dx
	push ax
	mov ax,ds
	mov es,ax
	pop ax
	mov si,bx		; Source of data.
	mov bx,ax		; Coroutine to call.
	mov di,bufpnt		; Destination of data.
	mov dh,0		; assume no quote char
	cmp trans.ebquot,'N'	; no quoting?
	je decod1		; yes, keep going
	cmp trans.ebquot,'Y'	; or not doing it?
	je decod1		; yes, keep going
	mov dh,trans.ebquot	; otherwise use quote char

decod1:	mov rptct,0		; Reset.
	mov rptval,0		; Ditto.
	dec cx
	jge dcod11		; More data.
	jmp decod6		; Else, we're through.
dcod11:	dec chrcnt		; Decrement number of chars in dta.
	jns decod2		; Continue if space left.
	push cx
	push dx
	push bx
	call bx			; Output it if full.
	 jmp decod5		;  Error return if disk is full.
	 nop
	pop bx
	pop dx
	pop cx
	mov di,bufpnt
decod2:	cmp rptct,0		; Doing a repeat?
	je dcod20		; No, so go get a character.
	mov ah,0
	mov al,rptval		; Get the character we're repeating.
	jmp decod4		; And write it out to the file.
dcod20:	lodsb			; Pick up a char.
	cmp rptq,0		; Doing repeat quoting?
	je dcod21		; Nope, skip this part.
	cmp al,rptq		; Did we pick up the repeat quote char?	
	jne dcod21		; No, continue processing it.
	lodsb			; Get the size.
	dec cx			; Modify buffer count.
	sub al,20H		; Was made printable.
	mov rptct,al		; Remember how many repetitions.
	lodsb			; Get the char to repeat.
	dec cx			; Modify buffer count.
dcod21:	mov ah,00H		; Assume no 8-bit quote char. [21b start]
	cmp al,dh		; This the 8-bit quot char?
	jne decod3
	lodsb			; Get the real character.
	dec cx			; Decrement # chars in packet
	mov ah,80H		; Turn on 8-bit quot char flag. [21b end] 
decod3: cmp al,trans.squote	; Is it the quote char? [21b] [21c]
	jne decod4		; If not proceed.
	lodsb			; Get the quoted character
	dec cx			; Decrement # of chars in packet.
	or ah,al		; save parity (combine with prefix)
	and ah,80h		; only parity
	and al,7FH		; Turn off the parity bit.
	cmp al,trans.squote	; Is it the quote char? [21c]
	je decod4		; If so just go write it out.
	cmp al,dh		; This the 8-bit quot char?
	je  decod4		; If so, just go write it out
	cmp al,rptq		; Is is the repeat quote character?
	je decod4		; If so, just write it out.
	add al,40H		; Make it a control char again.
	and al,7FH		; Modulo 128.
decod4: or al,ah		; or in parity
	stosb			; store the character
	dec rptct		; Repeat counter.
	cmp rptct,0		; Write out char again?
	jg dcod41
	jmp decod1		; No, get next char.
dcod41:	mov rptval,al		; Save the char.
	jmp dcod11		; and loop to next char.
decod5:	pop bx
	pop dx			; dx is pushed twice (really)
	pop cx
	pop dx
	pop es
	pop di
	pop si
	ret
decod6:	mov bufpnt,di
	pop dx
	pop es
	pop di
	pop si
	jmp rskp		; Return successfully if done.



	; output the buffer, reset bufpnt and chrcnt
 
outbuf: cmp flags.xflg,1	; Writing to screen? [21c] 
	je outbf2		; Yes, handle specially. [21c] 
	push bx
	mov ah,writef		; The write code.
	mov dx,offset fcb
	int dos			; Write the record.
	pop bx
	cmp al,0		; Successful.
	jz outbf1
	push ax			; Remember the return code. [20d]
	call abfil		; Fix things up before aborting. [20d]
	pop ax			; Retrive return code. [20d]
	cmp al,01
	jz outbf0
	call erpos
	mov ah,prstr
	mov dx,offset erms17	; Record length exceeds dta.
	int dos
	ret
outbf0: call erpos
	mov ah,prstr		; Tell about it.
	mov dx,offset erms11	; Disk full error.
	int dos
	ret
outbf1:	add tfilsz+2,80H	; Say 128 more characters received.
	adc tfilsz,0
	call kbpr		; Print the kilobytes received.
	call perpr		; Print the percent ('?' for now).
outb11:	mov bufpnt,offset buff	; Addr for beginning.
	mov chrcnt,dmasiz-1	; Buffer size.
	jmp rskp
outbf2:	mov cx,dmasiz-1		; Number of chars to write. [21c]
	sub cx,chrcnt		; minus # of unused in buffer
	mov di,offset buff	; Where they are. [21c]
	call prtscr		; Output buffer to screen. [21c]
	jmp outb11		; Reset counter & pointer. [21c]

;  Tidy up before aborting.	[20d]
ABFIL	PROC	NEAR
	mov ah,closf		; Close the file.
	mov dx,offset fcb
	int dos
	cmp flags.abfflg,1	; Delete what got across or keep it?
	jne abfil0		; Nope, keep it.
	mov ah,delf		; Delete it.
	mov dx,offset fcb
	int dos
abfil0:	mov bx,offset erms10	; Text of message to send.
	call errpack		; Send an error packet.
	ret
ABFIL	ENDP

; General routine for sending an error packet.  Register BX should
; point to the text of the message being sent in the packet. [20f]

ERRPACK	PROC	NEAR
	mov di,offset data	; Where to put the message.
	mov al,0
errp1:	mov ah,[bx]
	cmp ah,'$'		; At end of message?
	je errp2
	inc al			; Remember number of chars in msg.
	mov [di],ah
	inc bx
	inc di
	jmp errp1
errp2:	mov ah,0
	mov pack.argbk1,ax
	mov ah,'E'		; And send an error packet.
	call spack
	 ret			; Return if succeed or fail.
	nop
	nop
	ret
ERRPACK	ENDP

;	Get the chars from the file.
 
gtchr:  cmp flags.filflg,0	; Is there anything in the DMA?
	jz gtchr0		; Yup, proceed.
	mov ah,rptq
	mov origr,ah		; Save repeat prefix here.
	mov rptct,1		; Number of times char is repeated.
	mov rptval,0		; Value of repeated char.
	call inbuf
	 jmp gtchr1		; No more chars, go return EOF.
	 nop			; Make three bytes long.
gtchr0:	lea bx,inbuf
	jmp encode
gtchr1:	mov ax,0ffffh
	ret

; encode - writes data portion of kermit packet into filbuf.
; expects BX to contain the address of a routine to refill the buffer,
; chrcnt to be the # of chars in the buffer, trans.maxdat to contain
; the maximum size of the data packet, bufpnt to contain a pointer to
; the source of the characters.
; Returns: AX/ the number of characters actually written to the buffer.

encode:	mov cl,trans.maxdat	; Maximum packet size. [21b]
	mov ch,0
	mov di,offset filbuf	; Where to put the data.
	mov si,bufpnt		; pointer into source buffer
	mov dl,trans.rquote	; send quote char
	mov dh,0		; assume no 8-bit quoting
	cmp trans.ebquot,'N'	; not doing 8-bit quoting
	je encod1
	cmp trans.ebquot,'Y'	; or can but won't?
	je encod1
	mov dh,0ffh		; remember we have to do it
encod1: dec cx			; Decrement output buffer counter.
	jge encod2		; Go on if there is more than one left.
	sub di,offset filbuf
	mov ax,di
	mov bufpnt,si		; update pointer into DMA.
	jmp rskp
encod2: dec chrcnt		; any data in buffer?
	jge encod3		; yes, skip over buffer refill.
	call bx			; Get another buffer full.
	 jmp encod8
	mov si,bufpnt		; update position in DMA.
	cmp chrcnt,0		; no characters returned?
	jne encod3		; Got some, keep going.
	jmp encod8		; none, assume eof.
encod3:	lodsb
	cmp rptq,0		; Are we doing repeat prefixing?
	je encd3x		; Nope, skip next part.
	cmp chrcnt,0		; Are we on the last character?
	jle encd31		; Yes, so there's no next character.
	cmp rptct,94		; Max number that we can put in a byte.
	je encd31		; Then that's it.
	mov ah,[si]		; Get the next character.
	cmp al,ah		; Is current char == next char?
	jne encd31
	inc rptct		; Number of times char appears.
	mov rptval,al		; Remember the character.
	inc cx			; Repeats don't take up so much buffer space.
	jmp encod1		; Keep checking for more.
encd31:	cmp rptct,1		; Were previous characters repeats?
	je encd3x		; No, so just add this char.
	cmp rptct,rptmin	; Are we within bounds for repeat prefixing?
	jge encd32		; Yes, use repeat prefixing.
	mov al,rptct
	mov ah,0
	sub si,ax		; Not enough characters to warrant it.
	mov rptval,0		; Clear out this value.
	inc cx			; Adjust output buffer pointer.
	mov al,rptq
	mov origr,al		; Save original repeat prefix.
	mov rptq,0		; Pretend we're not doing the prefixing.
	mov al,rptct
	mov ah,0
	add chrcnt,ax		; Adjust input buffer pointer.
	jmp encod1		; Reprocess those characters.
encd32:	push ax			; Do repeat prefixing - save data.
	mov al,rptq		; Add repeat prefix char.
	stosb
	dec cx			; Account for it in buffer size.
	mov al,rptct		; Get the repeat count.
	add al,20H		; Make it printable.
	stosb			; Add to buffer.
	dec cx
	pop ax			; Get back the actual character.
	mov rptct,1		; Reset repeat count.
	mov rptval,0		; And this.
encd3x:	cmp dh,0		; are we doing 8-bit quoting?
	je encod4		; no, forget this.
	test al,80h		; parity on?
	je encod4		; no, don't bother with this
	and al,7fh		; turn off parity
	push ax			; save original char for a bit
	dec cx			; decrement # of chars left
	mov al,trans.ebquot	; get quote char
	stosb			; save in buffer
	pop ax			; restore character
encod4:	mov ah,al		; save character
	and ah,80h		; only parity
	and al,7fh		; turn off parity in character
	cmp al,' '		; Compare to a space.
	jl encod5		; If less then its a control char.
	cmp al,del		; Is the char a delete?
	jz encod5		; Go quote it.
	cmp al,dl		; Is it the quote char?
	je encod6		; Yes - go add it. [21b start]
	cmp dh,0		; are we doing 8-bit quoting?
	je encd41		; no, don't translate it
	cmp al,trans.ebquot	; Is it the 8-bit quote char?
	je encod6		; Yes, just output with quote
encd41:	cmp origr,0		; Doing repeat prefixing?
	je encod7		; No, don't check for quote char.
	cmp al,origr		; Is this the repeat quote character.
	je encod6		; Yes, then quote it.
	jmp short encod7	; else don't quote it.
encod5:	add al,40h		; control char, uncontrollify
	and al,7fh
encod6:	push ax			; save the char
	dec cx
	mov al,dl
	stosb
	pop ax
encod7:	or al,ah		; put parity back
	stosb
	cmp rptct,1		; One occurence of this char?
	jne encd7x
	mov al,origr
	mov rptq,al		; Restore repeat quote char.
	jmp encod1		; Yes, so loop around for some more.
encd7x:	dec rptct		; Add another entry of this char.
	jmp encod1		; With quoting and all.
 
encod8: sub di,offset filbuf
	or di,di
	je encod9		; Nope.
	mov ax,di
	jmp rskp
encod9: mov ax,0FFFFH		; Get a minus one.
	ret


inbuf:  mov ah,flags.eoflag	; Have we reached the end?
	cmp ah,0
	jz inbuf0
	ret			; Return if set.
inbuf0:	push si
	push di
	push dx
	push bx			
	push cx
	mov bx,offset buff	; Set the r/w buffer pointer.
	mov bufpnt,bx
	mov ah,readf		; Read a record.
	mov dx,offset fcb
	int dos
	mov cx,filsiz
	cmp cx,0		; Check for 128 chars or less left.
	jne inbuf1		; Still have data left.
	mov ax,ds
	mov es,ax
	mov si,offset filsiz+2
	mov di,offset bufhex
	cmps filsiz+2,es:bufhex
	ja inbuf1		; More than 128 chars.
	mov flags.eoflag,0FFH	; Set End-of-file.
	mov cx,filsiz+2
	cmp flags.filflg,0	; Ever used DMA? [25]
	jnz inbf01
	dec cx			; Account for DEC in caller routine.
inbf01:	mov chrcnt,cx		; Return proper number of chars.
	mov flags.filflg,0	; Buffer not empty.
	pop cx
	pop bx
	pop dx
	pop di
	pop si
	jmp rskp
inbuf1:	sub filsiz+2,80H	; Sent another 128 chars.
	sbb filsiz,0		; Account for the doubleword.
	add tfilsz+2,80H	; Book keeping for the same.
	adc tfilsz,0
	push ax
	call kbpr		; Print the kilobytes sent.
	call perpr		; Print the percent sent.
	pop ax
	mov al,80H		; Use as counter for number of chars read.
	pop cx
	pop bx
	pop dx
	pop di
	pop si
	cmp flags.filflg,0	; Ever used DMA?
	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 flags.filflg,0	; Buffer not empty.
	jmp rskp

nulref:	mov chrcnt,0		; No data to return.
	jmp rskp

nulr:	ret

; Print the number of Kilobytes transferred.

kbpr:	cmp flags.remflg,0	; remote mode?
	jne kbpr1		; yes, no printing.
	mov ax,tfilsz+2
	mov bx,tfilsz
	mov cl,10
	shr ax,cl		; divide by 1024
	mov cl,6		; high order moves 16-10 = 6 bits
	shl bx,cl
	or ax,bx
	cmp ax,oldkbt		; is it the same?
	je kbpr1		; yes, skip printing
	mov oldkbt,ax		; save new # of kb
	push ax
	call kbpos		; Postion the cursor.
	pop ax
	call nout		; Print the number of KBytes transferred.
kbpr1:	ret

; Print the percent transferred.

perpr:	cmp flags.remflg,0	; remote mode?
	jne perpr5		; yes, no printing.
	mov ax,tfilsz
	or ax,tfilsz+2
	cmp ax,oldper		; same as it was before?
	je perpr5		; yes, don't bother printing.
	mov oldper,ax		; remember this for next time
	cmp ofilsz,0		; No divide by zeroes.
	je perpr5		; If not proceed.
	cmp wrpmsg,0		; did we write the percentage message?
	jne perpr1		; yes, skip this part
	call perpos		; position cursor
	mov dx,offset permsg
	mov ah,prstr
	int dos			; write out message
	mov wrpmsg,1		; init flag so we don't do it again
perpr1:	call perpos		; Position the cursor.
perpr2:	mov dx,tfilsz		; Get the high order word.
	mov ax,tfilsz+2		; Get the low order word.
	div ofilsz		; Div by percent adjusted original file size.
	cmp ax,100		; > 100% ?
	jle perpr3		; no, accept it
	mov ax,100		; else just use 100
perpr3:	call nout
	mov dl,'%'		; Load a percent sign.
perpr4:	mov ah,conout		; Print the character.
	int dos
perpr5:	ret

getfil: mov ah,0FFH
	mov flags.filflg,ah	; Nothing in the DMA.
	mov ax,0
	mov flags.eoflag,ah	; Not the end of file.
	mov bx,offset fcb+0CH
	mov [bx],ax		; Zero the current block number.
	mov bx,offset fcb+0EH
	mov [bx],ax		; Ditto for Lrecl.
	mov bx,offset fcb+20H
	mov [bx],ah		; Zero the current record (of block).
	inc bx
	mov [bx],ax		; Same for record (of file). 
	mov bx,offset fcb+23H
	mov [bx],ax
	mov ah,openf		; Open the file.
	mov dx,offset fcb
	int dos
	mov dx,word ptr fcb+18	; get file size (hi order word)
	mov filsiz,dx
	mov ax,word ptr fcb+16	; lo order word
	mov filsiz+2,ax
	div percnt		; Divide by 100.
	mov ofilsz,ax
	mov tfilsz,0		; Set bytes sent to zero.
	mov tfilsz+2,0
	mov oldkbt,-1
	mov oldper,-1
	cmp filsiz,0		; Null file?
	jne getfl0		; Nope.
	cmp filsiz+2,0		; Null file?
	jne getfl0		; Nope.
	mov flags.eoflag,0FFH	; Set EOF.
getfl0:	jmp rskp


gtnfil: cmp flags.cxzflg,'Z'	; Did we have a ^Z? [20b]
	je gtn5			; If yes, we're done sending files. [20b]
	cmp flags.wldflg,0	; Was there a "*"?		[7 start]
	je gtn5			; Nope.
	mov bx,offset cpfcb	; Get FCB from last check for file.  
	mov di,offset fcb	; Copy to FCB.
	mov cl,37		; Size of FCB.
	call fcbcpy
gtn2:	mov ah,snext
	mov dx,offset fcb	; More files?
	int dos
	cmp al,0FFH
	je gtn5
	mov bx,offset fcb
	mov di,offset cpfcb
	mov cl,37
	call fcbcpy		; Copy from FCB.
	mov di,offset fcb+1	; Get name of next file to send.
	mov bx,offset buff+1
	mov cl,11
	call fcbcpy
	call getfil		; Initialize
	 jmp r
	jmp rskp			
gtn5:	mov flags.wldflg,0	; Reset wild card flag.
	ret			 			;  [7 end]


;	Get the file name (including host to micro translation)
 
gofil:  cmp flags.xflg,1	; Remote command? [21c]
	jne goflx		; No.... [21c]
	jmp gofla		; Yes so skip this stuff. [21c]
goflx:	cmp flags.nmoflg,1	; Overriding name from other side? [21a]
	jne gofil0		; No - get the filename. [21a]
	jmp gofil7		; Yes, so ignore packet contents. [21a]
gofil0:	mov bx,offset data	; Get the address of the file name. [21a]
	mov fdtpnt,bx		; Store the address.
	mov bx,offset fcb+1	; Address of the FCB.
	mov fcbptr,bx		; Save it.
	mov ax,0
	mov temp1,ax		; Initialize the char count.
	mov temp2,ax
	cmp flags.droflg,1	; Default drive? [21a]
	je gofil1		; No - don't blank out value in FCB. [21a]
	mov si,offset fcb
	mov [si],ah		; Set the drive to default to current.
gofil1: mov ch,' '		; Moved the label. [21a]
	mov [bx],ch		; Blank the FCB.
	inc bx
	inc ah
	cmp ah,0BH		; Twelve?
	jl gofil1
gofil2: mov bx,fdtpnt		; Get the NAME field.
	mov ah,[bx]
	inc bx
	mov fdtpnt,bx
	cmp ah,'.'		; Seperator?
	jne gofil3
	mov bx,offset fcb+9H
	mov fcbptr,bx
	mov ax,temp1
	mov temp2,ax
	mov temp1,9H
	jmp gofil6
gofil3: cmp ah,0		; Trailing null?
	jz gofil7		; Then we're done.
	call verlet		; Verify that the char is legal. 
	mov bx,fcbptr
	mov [bx],ah
	inc bx
	mov fcbptr,bx
	mov ax,temp1		; Get the char count.
	inc ax
	mov temp1,ax
	cmp ax,8H		; Are we finished with this field?
	jl gofil2
gofil4: mov temp2,ax
	mov bx,fdtpnt
	mov ah,[bx]
	inc bx
	mov fdtpnt,bx
	cmp ah,0
	jz gofil7
	cmp ah,'.'		; Is this the terminator?
	jne gofil4		; Go until we find it.
gofil6: mov bx,fdtpnt		; Get the TYPE field.
	mov ah,[bx]
	inc bx
	mov fdtpnt,bx
	cmp ah,0		; Trailing null?
	jz gofil7		; Then we're done.
	call verlet		; Verify that the char is legal. 
	mov bx,fcbptr
	mov [bx],ah
	inc bx
	mov fcbptr,bx
	inc temp1		; Increment char count.
	cmp temp1,0CH		; Are we finished with this field?
	jl gofil6
gofil7:	cmp flags.remflg,0	; remote mode?
	jne gofil7a		; yes, don't print it.
	call prtfn		; Print the file name. [21a]
gofil7a:cmp flags.destflg,0	; Writing to the printer?
	jne gf7y
	push es
	mov ax,ds
	mov es,ax		; Set this up.
	mov cx,11
	mov si,offset printer
	mov di,offset fcb
	repne movsb		; Change name in FCB to be printer.
	pop es
	jmp gofil9
gf7y:	mov ah,flags.flwflg	; Is file warning on?
	cmp ah,0
	jnz gf7x
	jmp gofil9		; If not, just proceed.
gf7x:	mov ah,openf		; See if the file exists.
	mov dx,offset fcb
	int dos
	cmp al,0FFH		; Does it exist?
	jnz gf8x
	jmp gofil9		; If not create it.
gf8x:	cmp flags.remflg,0	; remote mode?
	jne gf8xa		; yes, skip printing
	call frpos		; Position cursor. 
	mov ah,prstr		; Inform the user we are renaming the file.
	mov dx,offset infms5
	int dos
gf8xa:	mov ax,temp2		; Get the number of chars in the file name.
	cmp ax,0
	jne gofil8
	mov ax,temp1
	mov temp2,ax
gofil8: mov ch,0
	mov cl,al
	mov al,0		; Says if first field is full.
	cmp cl,9H		; Is the first field full?
	jne gofl81
	mov al,0FFH		; Set a flag saying so.
	dec cl
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
	mov ah,openf		; See if the file exists.
	mov dx,offset fcb
	int dos
	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.
	cmp cl,9H		; Are we to the end?
	jl gofl81		; If not try again ; else fail. 
 
gofl88:	cmp flags.remflg,0	; remote mode?
	jne gofl88a		; yes, no printing
	call erpos		; Position cursor.
	mov ah,prstr		; Tell the user that we can't rename it.
	mov dx,offset ermes4
	int dos
gofl88a:mov bx,dx		; Tell host can't rename.  [20f]
	call errpack		; Send error packet before abort. [20f]
	ret
 
gofl89:	cmp flags.remflg,0	; remote mode
	jne gofil9		; yes, don't have to print it
	mov bx,offset fcb+0CH	; Point past the end of the file name.
	mov dh,[bx]		; Save the present contents.
	mov ah,'$'
	mov [bx],ah		; Put in a dollar sign.
	push dx
	mov ah,prstr		; Print the file name.
	mov dx,offset fcb+1
	int dos
	pop dx
	mov bx,offset fcb+0CH	; Restore over the dollar sign.
	mov [bx],dh
gofil9: mov ah,delf		; Delete the file if it exists.
	mov dx,offset fcb
	int dos
	mov ax,0
	mov si,offset fcb+0CH
	mov [si],ax		; Zero current block.
	mov si,offset fcb+0EH
	mov [si],ax		; Same for Lrecl.
	mov si,offset fcb+20H
	mov [si],ah		; Zero the current record (within block).
	inc si
	mov [si],ax		; Zero record (within file).
	mov si,offset fcb+23H
	mov [si],ax
	mov ofilsz,0		; File size unknown.
	mov tfilsz,0		; Set bytes received to zero.
	mov tfilsz+2,0
	mov oldkbt,-1
	mov oldper,-1
	mov ah,makef		; Now create it.
	mov dx,offset fcb
	int dos
	cmp al,0FFH		; Is the disk full?
	je gf9x
	jmp rskp
gf9x:	cmp flags.remflg,0	; remote mode?
	jne gf9xa		; yes, don't try printing
	call erpos		; Position cursor.
	mov ah,prstr		; If so tell the user.
	mov dx,offset erms12
	int dos
	mov bx,dx
gf9xa:	call errpack		; Send an error packet.
	ret
gofla:  cmp pack.argbk1,0	; Any data in "X" packet? [21c start]
	je gofla1		; Nothing to print. 
	mov ah,prstr
	mov dx,offset crlf
	int dos	
	mov di,offset data	; Where data is.
	mov cx,pack.argbk1	; How much data we have.
	call prtscr		; Print it on the screen.
gofla1:	mov ah,prstr
	mov dx,offset crlf
	int dos
	jmp rskp		; And done. [21c end]
FILEIO	ENDP

; Passed char of incoming filename in AH.  Verify that it is legal
; and if not change it to an "X".
verlet:	cmp ah,'0'
	jl ver2			; See if it's a legal weird char.
	cmp ah,'z'+1
	jns ver2
	cmp ah,'9'
	jle ver1		; It's between 0-9 so it's OK.
	cmp ah,'A'
	jl ver2			; Coud be a weird char.
	cmp ah,'Z'
	jle ver1		; It's A-Z so it's OK.
	cmp ah,'a'
	jl ver2
	and ah,137O		; It's a-z, capitalize.
ver1:	ret

ver2:	push es
	mov cx,ds
	mov es,cx		; Scan uses ES register.
	mov di,offset spchar	; Special chars.
	mov cx,spclen		; How many of them.
	cmp dosnum,0		; Under version 2.0
	je ver3
	mov di,offset spchar2
	mov cx,spc2len
ver3:	mov al,ah		; Char is in al.
	repnz scasb		; Search string for input char.
	pop es
	mov ah,al		; Return it in AH.
	cmp cx,0		; Was it there?
	jnz ver1		; Yes, return it.
	mov ah,'X'		; If illegal, replace with "X".
	mov flags.nmoflg,1
	ret

; Print incoming filename(s). [21a]
PRTFN	PROC	NEAR
	call clrfln		; Position cursor & blank out the line.
	mov di,offset data	; Where to put the name.
	mov bx,offset fcb	; Where it is now.
	cmp flags.droflg,0	; Drive specified?
	je prtfn1
	mov dl,[bx]		; Which one did they say?
	add dl,'@'		; Make it readable.
	mov ah,dconio		; Print the drive name. 
	int dos
	mov dl,':'
	int dos
prtfn1:	inc bx			; Point to start of filename.
	cmp flags.nmoflg,0	; Is filename in packet?
	je prtfn2		; no, keep going
	add di,pack.argbk1	; bump by length of remote name
	mov si,offset asmsg	; something to put after it
	mov cx,asmln		; length of it
	rep movsb		; add this to the buffer
prtfn2:	mov cx,8		; At most 8 letters in file name.
	mov si,bx		; this is source now
prtfn3:	lodsb			; get a letter
	cmp al,' '		; Done with name?
	je prtfn4		; yes, continue
	stosb			; else store
	loop prtfn3		; and loop thru rest
prtfn4: mov si,offset fcb+9	; Point to file type. 
	cmp byte ptr [si],' '	; is there a type?
	je prtfn5		; Nope so we're done.
	mov al,'.'		; Add the dot.
	stosb
	mov cx,3		; At most 3 letters in file type.
	rep movsb		; copy type (incl trailing spaces)
prtfn5:	mov byte ptr [di],'$'	; end the string
	mov ah,prstr		; Print the file name.
	mov dx,offset data
	int dos
	mov flags.droflg,0	; Reset flag once have the full name.
	mov flags.nmoflg,0
	ret
PRTFN	ENDP

; Print data onto the screen.  If text has no "$" in it, just print
; it.  Else, do special output for the "$".  
; Routine expects: DI = Start of buffer we are to print.
;		   CX = Number of characters to print.	 [21c]

PRTSCR	PROC	NEAR
	mov al,'$'		; This is what we're looking for.
	mov oloc,di		; Remember original buffer address. 
	mov osiz,cx		; And original size. 
	push es
	mov bx,ds
	mov es,bx		; Have ES point to data area.
prts0:	repnz scasb		; Search for "$" in the buffer.
	cmp cx,0		; Found one?
	je prts1		; No, do a regular DOS call.
	mov ah,prstr
	mov dx,oloc		; Print up to the "$". 
	int dos
	mov ah,dconio
	mov dl,'$'
	int dos			; Print the "$"
	mov oloc,di		; New starting location.
	mov osiz,cx		; New size.
	jmp prts0
prts1:	mov bx,oloc		; The buffer location.
	add bx,osiz		; Point past the data.
	mov [bx],al		; Add "$" for printing.
	mov ah,prstr
	mov dx,oloc
	int dos
	pop es
	ret
PRTSCR	ENDP

FIXFCB  PROC	NEAR	
	push ax			; Don't forget this.  [22]
	mov bx,offset fcb+18
	mov di,offset filsiz
	mov ax,[bx]
	mov [di],ax
	mov bx,offset fcb+16
	mov ax,[bx]
	mov 2[di],ax
	pop ax			; Get number of chars in last buffer full. [22]
	sub filsiz+2,ax		; Get real file size.
	sbb filsiz,0
	mov bx,offset fcb+18
	mov di,offset filsiz
	mov ax,[di]
	mov [bx],ax
	mov bx,offset fcb+16
	mov ax,2[di]
	mov [bx],ax
	ret
FIXFCB	ENDP

; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.
 
RSKP	PROC	NEAR
	pop bp
	add bp,3
	push bp
	ret
RSKP	ENDP
 
; Jumping here is the same as a ret.
 
R	PROC	NEAR
	ret
R	ENDP

code	ends
	end
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msfile.asm
	/bin/echo -n '	'; /bin/ls -ld msfile.asm
fi

knutson@ut-ngp.UUCP (Jim Knutson) (10/05/84)

: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting mskerm.asm'
sed 's/^X//' <<'//go.sysin dd *' >mskerm.asm
	public	prompt, dosnum, curdsk, swchar
	include msdefs.h
;******************** Version 2.26 ********************************** 
; KERMIT, Celtic for "free" 
;
; The name "Kermit" is a registered trade mark of Henson Associates, Inc.,
; used by permission.
; 
;       Kermit-MS Program Version 2.26, July 26, 1984
; 
;       Based on the Columbia University KERMIT Protocol.
; 
;       Copyright (C) 1982,1983,1984 Trustees of Columbia University
;
;       Daphne Tzoar, Jeff Damens
;       Columbia University Center for Computing Activities
;       612 West 115th Street
;       New York, NY  10025
; 
; Special thanks to Frank da Cruz, Bill Catchings, Steve Jensen, Herm Fischer,
; Vace Kundakci, and Bernie Eiben for their help and contributions.

makseg	equ	26H
deffcb	equ	5cH
setblk	equ	4AH
exec	equ	4BH
env	equ	2CH		; environment address in psp
terma	equ	10		; termination address in psp
cline	equ	80H		; offset in psp of command line
namsiz	equ	20		; Bytes for file name and size.
maxnam	equ	10
chmod	equ	43H		; chmod call (used to test for file existence)

STACK   SEGMENT PARA STACK 'STACK'
        DW      100 DUP(0)      ; Initialize stack to all zeros.
STK	EQU	THIS WORD
STACK   ENDS
 
datas   segment public 'datas'
	extrn	buff:byte, comand:byte, flags:byte, pack:byte, trans:byte
	extrn	fcb:byte, cpfcb:byte, prmptr:word, inichk:byte
	extrn	machnam:byte
	public	takadr,taklev

versio	label	byte
	verdef
	db	cr,lf
	db	'Type ? for help',cr,lf
	db	'$'
tmp	db	?,'$'
crlf    db      cr,lf,'$'
ermes1  db      cr,lf,'?Unrecognized command$'
ermes3  db      cr,lf,'?Not confirmed$'
erms30	db	cr,lf,'Passed maximum nesting level for TAKE command$'
erms31	db	cr,lf,'Take file not found$'
erms32	db	cr,lf,'File(s) not found$'
erms33	db	cr,lf,'CHKDSK program not found on current disk$'
erms34	db	cr,lf,'This command works only for DOS 2.0 and above$'
erms35	db	cr,lf,'Must specify program name$'
erms36	db	cr,lf,'Could not free memory$'
erms37	db	cr,lf,'Unable to execute program$'
infms1	db	'Really erase *.*? $'
infms8	db	cr,lf,'File(s) erased$'
tmsg5	db	cr,lf,'[closing log file]',cr,lf,'$' ; [jd]
filhlp1 db      ' Command file specification $'
filhlp2 db      ' File specification (possibly wild) $'
filhlp3	db	' File spec (possibly wild) or confirm with carriage return$'
filmsg	db	' File specification with optional path name $'
filwmsg	db	' File specification (possibly wild) with optional path name $'
chkfil	db	0,'CHKDSK  COM'
chkflen	equ	$-chkfil

tophlp  db	cr,lf
	db	'BYE',tab,tab
	db	'CLOSE',tab,tab
	db      'CONNECT',tab,tab
	db	'DEFINE',tab,tab
	db	cr,lf
	db	'DELETE',tab,tab
	db	'DIRECTORY',tab
	db	'DO',tab,tab
        db      'EXIT',tab,tab
	db	cr,lf
	db	'FINISH',tab,tab
	db	'GET',tab,tab
        db      'HELP',tab,tab
	db	'LOCAL',tab,tab
	db	cr,lf
	db	'LOG',tab,tab
	db	'LOGOUT',tab,tab
	db	'PUSH',tab,tab
        db      'QUIT',tab,tab
	db	cr,lf
        db      'RECEIVE',tab,tab
	db	'REMOTE',tab,tab
	db	'RUN',tab,tab
        db      'SEND',tab,tab
	db	cr,lf
	db	'SERVER',tab,tab
        db      'SET',tab,tab
	db	'SHOW',tab,tab
	db	'SPACE',tab,tab
	db	cr,lf
        db      'STATUS',tab,tab
	db	'TAKE'
	db	'$'

lochlp	db	cr,lf,'DELETE file'
	db	cr,lf,'DIRECTORY [filespec]'
	db	cr,lf,'SPACE remaining on current disk'
	db	cr,lf,'RUN program'
	db	cr,lf,'PUSH to command interpreter'
	db	'$'

        ; COMND tables

yestab	db	2
	mkeyw	'NO',0
	mkeyw	'YES',1

comtab  db	27
	mkeyw	'BYE',bye
	mkeyw	'C',telnet
	mkeyw	'CLOSE',clscpt
	mkeyw	'CONNECT',telnet
	mkeyw	'DEFINE',dodef
	mkeyw	'DELETE',delete
	mkeyw	'DIRECTORY',direct
	mkeyw	'DO',docom
	mkeyw	'EXIT',exit
	mkeyw	'FINISH',finish
	mkeyw	'GET',get
	mkeyw	'HELP',help
	mkeyw	'LOCAL',lclcmd
	mkeyw	'LOG',setcpt
	mkeyw	'LOGOUT',logout
	mkeyw	'PUSH',dopush
	mkeyw	'QUIT',exit
	mkeyw	'RECEIVE',read
	mkeyw	'REMOTE',remote
	mkeyw	'RUN',run
	mkeyw	'SEND',send
	mkeyw	'SERVER',server
	mkeyw	'SET',setcom
	mkeyw	'SHOW',showcmd
	mkeyw	'SPACE',chkdsk
	mkeyw	'STATUS',status
	mkeyw	'TAKE',take

loctab	db	5
	mkeyw	'DELETE',delete
	mkeyw	'DIRECTORY',direct
	mkeyw	'PUSH',dopush
	mkeyw	'RUN',run
	mkeyw	'SPACE',chkdsk

shotab	db	2
	mkeyw	'KEY',shokey
	mkeyw	'MACRO',shomac

; Program storage.
 
oldstk  dw      ?               ; Storage for system stack.
oldsts  dw      ?               ; System stack segment.
ssave	dw	?		; Original SS when doing CHKDSK.
siz	dw	?		; Memory size.
in3ad	dd	0		; Original break interrupt addresses. [25]
curdsk	db	0		; Current disk. 
origd	db	0		; Original disk.
fildat	db	0		; Manipulate file data/time creation.
	db	0
taklev	db	0		; Take levels. [25t]
takadr	dw	takstr-(size takinfo) ; Pointer into structure. [25t]
temp	dw	0
temp1   dw      ?               ; Temporary storage.
temp2   dw      ?
temp3   dw      ?
temp4   dw      ?
psp	dw	?
divst	dw	0	
takstr	db	(size takinfo) * maxtak dup(?)
ininam	db	0,'MSKERMITINI'	; init file name, on default disk, 12 chars
ininm2	db	'MSKERMIT.INI',0 ; init file name for 2.0
nambuf	db	maxnam * namsiz dup (?)
cmdnam	db	namsiz dup (?)
exefcb	db	fcbsiz dup (?)
exefcb2	db	fcbsiz dup (?)
exearg	dw	?		; segment addr of environment (filled in below)
	dd	0		; ptr to cmd line (filled in below)
	dd	exefcb		; default fcb
	dd	exefcb2		; second default fcb
dircmd	db	' /c dir '
dirclen	equ	$-dircmd
dirnam	db	50h dup (?)
chkdcmd	db	'chkdsk.com'
chkdlen	equ	$-chkdcmd
dosnum	db	?		; dos version number
pthnam	db	'PATH='
pthlen	equ	$-pthnam
pthbuf	db	100 dup (?)	; buffer for path definition.
defpth	db	'\', 70 dup (?)	; buffer for default path
cmspnam	db	'COMSPEC='
cmsplen	equ	$-cmspnam
cmspbuf	db	'\command.com',0 ; default name
	db	30 dup (?)	; some additional space
tfile	db	100 dup (?)	; temp space for file names.
eexit	db	cr,'exit',cr
leexit	equ	$-eexit
swchar	db	'\'		; default switch character.
datas   ends                    ; End data segment
 
code	segment	public 
	public	takrd
start   proc  far
	extrn	cmblnk:near, locate:near, logout:near
	extrn	bye:near, telnet:near, finish:near, comnd:near
	extrn	read:near, remote:near, send:near, status:near, get:near
	extrn	dodisk:near, serrst:near, setcom:near
	extrn	clscpi:near, clscpt:near, getbaud:near
	extrn	dodef:near, setcpt:near, docom:near
	extrn	server:near, lclini:near, shokey:near, shomac:near
        assume  cs:code,ds:datas,ss:stack,es:nothing
 
        push ds                 ; Save system data area.
        sub ax,ax               ; Get a zero.
        push ax                 ; Put zero return addr on stack.

	mov ax,datas           ; Initialize DS.
        mov ds,ax
	sub ax,ax

        mov oldstk,sp           ; Save old stack pointer.

	mov ax,es:[2]		; In program segment prefix
	mov siz,ax		; Pick up memory size
	mov psp,es	

	mov ah,prstr
	mov dx,offset machnam	; print machine name
	int dos
        mov ah,prstr            ; Print the version header.
        mov dx,offset versio
        int dos

	mov ah,setdma		; Set disk transfer address.
	mov dx,offset buff
	int dos

	call getbaud		; Get the baud rate. [25]
	call dodisk		; See how many disk drives we have. [21a]
	call setint
	mov ah,gcurdsk		; Get current disk.
	int dos
	inc al			; We want 1 == A (not zero).
	mov curdsk,al
	mov origd,al		; Remember original disk we started on.
	mov ah,dosver
	int dos
	mov dosnum,al		; remember dos version
	cmp al,0
	je start1		; 1.1, keep going
	mov es,psp
	mov ax,es:[env]		; pick up environment address
	push ax
	call getpath		; get the path from the environment
	pop ax			; get environment back
	call getcsp		; get comspec from environment as well
start1:	call lclini		; do local initialization
	call gcmdlin		; read command line
	call rdinit		; read kermit init file
	call packlen		; Packet length in case do server comand.
; This is the main KERMIT loop.  It prompts for and gets the users commands.

kermit:	mov ax,ds
	mov es,ax		; make sure this addresses data segment
	mov dx,prmptr		; get prompt
	call prompt             ; Prompt the user.
	mov pack.state,0	; Clear the state.
	mov flags.cxzflg,0	; Reset each itme.
	mov ah,inichk		; Original or set checksum length.
	mov trans.chklen,ah	; Reset just in case.
        mov dx,offset comtab
        mov bx,offset tophlp
	mov comand.cmcr,1	; Allow bare CR's.
        mov ah,cmkey
        call comnd
         jmp kermt2
	mov comand.cmcr,0	; Not anymore.
        call bx                 ; Call the routine returned.
         jmp kermt3
	cmp flags.extflg,0	;  Check if the exit flag is set.
	jne krmend		;  If so jump to KRMEND.
	jmp kermit		; Do it again.
 
kermt2:	mov dx,offset ermes1	;  Give an error.
	jmp short kermt4

kermt3:	mov dx,offset ermes3	;  Give an error.
kermt4:	cmp flags.cxzflg,'C'	; some sort of abort?
	je kermit		; yes, don't print error message.
	mov ah,prstr
	int dos
	mov flags.droflg,0	; Reset drive override flag.
	mov flags.nmoflg,0	; Reset filename override flag.
	mov flags.getflg,0	; May as well do this one.
	mov flags.cmrflg,0	; This one too.
	jmp kermit
 
krmend: call serrst		; Just in case the port wasn't reset. [21c] 
	mov dl,origd		; Original disk drive.
	dec dl			; Want A == 0.
	mov ah,seldsk		; Reset original disk just in case.
	int dos
	mov sp,oldstk
	ret
 
START	ENDP

; This is the 'exit' command.  It leaves KERMIT and returns to DOS.
 
EXIT	PROC	NEAR
	mov ah,cmcfm
	call comnd		; Get a confirm.
	 jmp r
	test	flags.capflg,0FFH	; capturing?
	jz	exit1			; no, keep going
	mov	dx,offset tmsg5
	mov	ah,prstr
	int	dos
	call	clscpi
	 nop				; this skip returns...
	 nop
	 nop
exit1:
	mov flags.extflg,1	;  Set the exit flag.
	jmp rskp		; Then return to system.
EXIT	ENDP
 
 
; This is the 'help' command.  It gives a list of the commands.
 
HELP	PROC	NEAR
	mov ah,cmcfm
	call comnd		; Get a confirm.
	 jmp r
	mov ah,prstr		; Print a string to the console.
	mov dx,offset tophlp	; The address of the help message.
	int dos
	jmp rskp
HELP	ENDP
 
lclcmd	proc	near
	mov ah,cmkey
	mov dx,offset loctab
	mov bx,offset lochlp
	call comnd
	 jmp r
	call bx
	nop
	nop
	nop
	jmp rskp
lclcmd	endp

; Don't ignore ^C when in debug mode.
SETINT	PROC	NEAR
	push ds			; Don't forget this. [25]
	mov ax,ds
	mov es,ax		; So can access our data area.
	mov ax,0
	mov ds,ax		; Access low core.
	mov ax,ds:[23H * 4]	; Address for interrupt 23H.
	mov cx,ds:[23H * 4 +2]	; CS value for it.
	mov word ptr es:in3ad,ax ; Remember original values.
	mov word ptr es:in3ad+2,cx
	mov ax,cs
	mov ds,ax		; Access code are.
	mov dx,offset intbrk
	mov al,23H		; On ^C, goto above address.
	mov ah,25H
	int dos
	pop ds
	ret
SETINT	ENDP

; take commands from a file, but allow a path name
PTAKE	PROC	NEAR
	cmp taklev,maxtak		; Hit our limit?
	jl ptake1			; Continue if still OK.
	mov ah,prstr
	mov dx,offset erms30		; Complain.
	int dos 
	ret
ptake1:	mov di,takadr
	add di,size takinfo
	push di
	mov ah,cmtxt
	lea bx,[di].takbuf		; convenient place to parse name into
	mov dx,offset filmsg		; Help in case user types "?".
	call comnd
	 pop di
	 ret
	 nop
	pop di				; restore frame address
	push di				; keep it on stack.
	lea si,[di].takbuf		; get buffer back
	mov bl,ah			; length of thing parsed
	mov bh,0
	mov byte ptr [bx+si],0		; make it asciz
	mov ax,si			; point to name again
	call spath			; is it around?
	pop di				; need this back
	jc ptake2			; no, go complain
	mov dx,ax			; point to name from spath
	mov ah,open2			; 2.0 open call
	mov al,0			; open for reading
	int dos
	jnc ptake3			; open ok, keep going
ptake2:	mov ah,prstr
	mov dx,offset erms31
	int dos
	ret
ptake3:	inc taklev
	mov takadr,di
	mov word ptr [di].takfcb+1,ax	; save file descriptor
	mov byte ptr [di].takfcb,0feh	; mark as 2.0 file descriptor
	mov bx,ax			; need descriptor here
	mov ah,lseek
	mov al,2
	mov cx,0
	mov dx,cx			; seek 0 bytes from end
	int dos
	mov [di].takcnt,ax
	mov [di].takcnt+2,dx		; store length
	mov ah,lseek
	mov al,0
	mov cx,0
	mov dx,cx			; now seek back to beginning
	int dos
	cmp flags.takflg,0
	je ptake4
	mov ah,prstr
	mov dx,offset crlf
	int dos
ptake4:	call takrd		; Get a buffer full of data.
	jmp rskp
PTAKE	ENDP


; 	TAKE commands from a file.  [25t]

TAKE	PROC	NEAR
	cmp dosnum,0
	je take1
	jmp ptake			; use this for 2.0
take1:	cmp taklev,maxtak		; Hit our limit?
	jl take2				; Continue if still OK.
	mov ah,prstr
	mov dx,offset erms30		; Complain.
	int dos 
	ret
take2:	mov bx,takadr
	add bx,size takinfo
	push bx
	lea dx,[bx].takfcb
 	mov comand.cmcr,0		; Filename must be specified.
	mov ah,cmifi
	mov bx,offset filhlp1
	call comnd
	 pop bx
	 ret				; Make sure this is three bytes long.
	 nop
	pop bx
	mov byte ptr [bx].takfcb+32,0	; have to clear current record in fcb
	mov ah,openf
	lea dx,[bx].takfcb
	int dos
	cmp al,0FFH			; File not found?
	jne take3
	mov ah,prstr
	mov dx,offset erms31
	int dos
take3:	inc taklev
	mov takadr,bx
	mov ax,word ptr [bx+16].takfcb
	mov [bx].takcnt,ax
	mov ax,word ptr [bx+18].takfcb
	mov [bx].takcnt+2,ax		; copy size into takinfo
	cmp flags.takflg,0
	je take4
	mov ah,prstr
	mov dx,offset crlf
	int dos
take4:	call takrd			; Get a buffer full of data.
	jmp rskp
TAKE 	ENDP

TAKRD	PROC	NEAR
	push bx
	push cx	
	push dx
	mov bx,takadr
	cmp byte ptr [bx].takfcb,0feh	; is it a 2.0 file handle?
	jne takrd1			; no, handle differently
	push bx				; save frame address
	lea dx,[bx].takbuf		; buffer to read into
	mov cx,dmasiz			; # of bytes to read
	mov ah,readf2			; 2.0 read call
	mov bx,word ptr [bx].takfcb+1	; file handle is stored here
	int dos
	pop bx				; restore frame address
	jmp takrd2			; rejoin common exit

takrd1:	mov ah,setdma
	lea dx,[bx].takbuf
	int dos
	mov ah,readf
	lea dx,[bx].takfcb
	int dos
	mov ah,setdma
	lea dx,buff
	int dos
takrd2:	mov [bx].takchl,dmasiz
	lea ax,[bx].takbuf
	mov [bx].takptr,ax
	pop dx
	pop cx
	pop bx
	ret

TAKRD	ENDP

; copy the path into pthbuf
; enter with ax/ environment segment address
; works in 2.0 only.
getpath	proc	near
	push	es
	mov	bx,ds
	mov	es,bx			; address data segment
	mov	bx,offset pthnam	; thing to find
	mov	cx,pthlen		; length of it
	mov	dx,offset pthbuf	; place to put it
	mov	byte ptr pthbuf,0	; initialize to null...
	call	getenv			; get environment value
	pop	es
	ret				; and return
getpath	endp

; copy the comspec into cmspbuf
; enter with ax/ environment segment address
; works in 2.0 only.
getcsp	proc	near
	push	es
	mov	bx,ds
	mov	es,bx			; address data segment
	mov	bx,offset cmspnam	; thing to find
	mov	cx,cmsplen		; length of it
	mov	dx,offset cmspbuf	; place to put it
	call	getenv			; get environment value
	pop	es
	ret				; and return
getcsp	endp

; find a path variable.  Enter with ax/ environment segment,
; bx/ variable to find (incl =), cx/ length of variable name,
; dx/ address to store value at.
; The buffer given in dx is unchanged if the variable isn't found
getenv	proc	near
	push	ds
	push	es
	mov	es,ax			; address segment
	mov	di,0			; offset in segment
geten1:	cmp	es:byte ptr [di],0	; end?
	je	geten4			; yes, forget it
	push	cx			; save counter
	push	di			; and offset
	mov	si,bx
	repe	cmpsb			; is it the one?
	pop	di
	pop	cx			; restore these
	je	geten2			; found it, break loop
	push	cx			; preserve again
	mov	cx,0ffffh		; bogus length
	mov	al,0			; marker to look for
	repne	scasb			; search for it
	pop	cx			; restore length
	jmp	geten1			; loop thru rest of environment
geten2:	add	di,cx			; skip to definition
	mov	si,di			; this is source
	mov	di,dx			; destination as given
	mov	ax,ds
	mov	bx,es
	mov	ds,bx
	mov	es,ax			; exchange segment regs for copy
geten3:	lodsb				; get a byte
	stosb				; drop it off
	cmp	al,0			; end of string
	jne	geten3			; no, go on
geten4:	pop	es
	pop	ds			; restore registers
	ret				; and return
getenv	endp

; put kermit.ini onto take stack if it exists.  Just like
; the take command, except it doesn't read a filename.

rdinit	proc	near		; read kermit init file...
	mov al,dosnum		; get dos version
	or al,al
	jne rdini4		; post 2.0, use file handle instead...
	mov bx,takadr
	add bx,size takinfo	; bump take ptr, point to current take frame
	lea di,[bx].takfcb	; destination is fcb
	mov ax,ds
	mov es,ax		; destination segment = source segment
	mov si,offset ininam	; name of init file
	mov cx,12		; 8 char name + 3 char ext + 1 char drive...
	rep movsb		; copy it in
	mov byte ptr [bx].takfcb+32,0 ; have to clear current record in fcb
	mov ah,openf
	lea dx,[bx].takfcb
	int dos
	cmp al,0FFH		; File not found?
	jne rdini1		; no, keep going
	ret			; else just return, no init file
rdini1:	inc taklev		; bump take level
	mov takadr,bx		; save current take frame ptr
	mov ax,word ptr [bx+16].takfcb
	mov [bx].takcnt,ax
	mov ax,word ptr [bx+18].takfcb
	mov [bx].takcnt+2,ax	; copy size into takinfo
rdini2:	cmp flags.takflg,0
	je rdini3
	mov ah,prstr
	mov dx,offset crlf
	int dos
rdini3:	call takrd		; Get a buffer full of data.
	ret

rdini4:	mov ax,offset ininm2	; name to try
	push bx
	call spath		; can we find it?
	pop di
	jc rdini6		; no, forget it, go use it
	mov dx,ax		; point to name
	mov ah,open2		; 2.0 open function
	mov al,0		; for reading...
	int dos
	jc rdini6		; can't open, forget it

rdini5:	inc taklev		; bump take level
	add takadr,size takinfo
	mov di,takadr		; get current frame ptr
	mov word ptr [di].takfcb+1,ax	; save file handle
	mov byte ptr [di].takfcb,0feh	; mark as a handle
	mov bx,ax			; move file ptr
	mov ah,lseek
	mov al,2
	mov cx,0
	mov dx,0			; seek to end of file
	int dos
	mov [di].takcnt,ax		; copy file size
	mov [di].takcnt+2,dx		; into structure
	mov al,0
	mov ah,lseek
	mov cx,0
	mov dx,0
	int dos				; seek back to beginning
	jmp rdini2			; go rejoin common exit
rdini6:	ret				; no init file, just return
rdinit	endp

; get command line into a macro buffer.

gcmdlin	proc	near
	push	ds
	push	es
	cld
	mov	es,psp			; address psp
	mov	ch,0
	mov	cl,es:[cline]		; length of cmd line
	mov	di,cline+1		; point to actual line
	mov	al,' '
	jcxz	gcmdl3			; no command line, forget it.
	repe	scasb			; skip over spaces
	je	gcmdl3			; all spaces, forget it
	mov	si,di			; this is first non-space
	dec	si			; pre-incremented...
	inc	cx
	inc	taklev			; bump take level
	add	takadr,size takinfo	; address new take frame
	mov	bx,takadr
	mov	byte ptr [bx].takfcb,0ffh ; mark as a macro
	push	cx			; save length
	push	ds			; and segment
	lea	di,[bx].takbuf		; into take buffer
	mov	ax,ds
	mov	ds,psp
	mov	es,ax			; switch segments for copy
gcmdl1:	lodsb				; get a byte
	cmp	al,','			; comma?
	jne	gcmdl2			; no, keep going
	mov	al,cr			; convert to cr
gcmdl2:	stosb				; deposit it
	loop	gcmdl1			; copy whole cmd
	pop	ds			; restore segment
	mov	si,offset eexit		; something to tack onto end
	mov	cx,leexit		; length of it
	rep	movsb			; copy it in
	pop	cx			; restore len
	add	cx,leexit		; count wnat we added
	
	lea	ax,[bx].takbuf
	mov	[bx].takptr,ax		; init buffer ptr
	mov	[bx].takchl,cl		; chars remaining
	mov	[bx].takcnt,cx		; and all chars
	mov	[bx].takcnt+2,0		; clear high order
gcmdl3:	pop	es
	pop	ds
	ret
gcmdlin	endp

;	This routine prints the prompt and specifies the reparse address.
 
PROMPT	PROC  NEAR
	mov comand.cmprmp,dx	; save the prompt
	pop bx			; Get the return address.
	mov comand.cmrprs,bx	; Save as addr to go to on reparse.
	mov comand.cmostp,sp	; Save for later restoral.
	push bx			; Put it on the stack again.
	mov bx,offset comand.cmdbuf
	mov comand.cmcptr,bx	; Initialize the command pointer.
	mov comand.cmdptr,bx
	mov ah,0
	mov comand.cmaflg,ah	; Zero the flags.
	mov comand.cmccnt,ah
	mov comand.cmsflg,0FFH
	cmp flags.takflg,0	; look at take flag
	jne promp1		; supposed to echo, skip this check...
	cmp taklev,0		; inside a take file?
	je promp1		; no, keep going
	ret			; yes, return
promp1:	mov ah,prstr
	mov dx,offset crlf
	int dos
	mov ah,prstr		; Print the prompt.
	mov dx,comand.cmprmp
	int dos
	ret
PROMPT	ENDP
 
; Erase specified file(s).
DELETE	PROC	NEAR
 	mov comand.cmcr,0	; Filename must be specified.
	mov ah,cmifi		; Parse an input filespec.
	mov dx,offset fcb
	mov bx,offset filhlp2	; Text of help message.
	call comnd
	 jmp r			; Bad filename.
	mov ah,cmcfm		; Parse a confirm.
	call comnd
	 jmp r
	cld
	mov di,offset fcb+1
	mov al,'?'
	mov cx,11		; # of chars in a name
	repe scasb		; are they all ?'s?
	jne del1		; no, skip message
	mov dx,offset infms1
	call prompt
	mov ah,cmkey
	mov dx,offset yestab
	mov bx,0
	call comnd
	 jmp r
	push bx
	mov ah,cmcfm
	call comnd
	 pop bx
	 ret
	 nop
	pop bx
	cmp bx,0
	jne del1
	jmp rskp
del1:	mov dx,offset fcb
	mov ah,sfirst		; See if any files match this specification.
	int dos	
	cmp al,0FFH		; No matches?
	jne del2
	mov ah,prstr
	mov dx,offset erms32
	int dos
	jmp rskp
del2:	mov dx,offset fcb
	mov ah,delf		; Erase the file(s).
	int dos
	mov dx,offset infms8
	mov ah,prstr		; Say we did so.
	int dos
	jmp rskp
DELETE	ENDP	

CHKDSK	PROC	NEAR
	mov ah,cmcfm
	call comnd
	 jmp r
	cmp dosnum,0
	je chkds1			; yes, have to do it the hard way
	mov si,offset chkdcmd		; point to cmd
	mov cx,chkdlen			; and length
	jmp crun			; and go execute it nicely
chkds1:	push es
	mov ax,ds
	mov es,ax
	mov di,offset fcb
	mov si,offset chkfil
	mov cx,chkflen
	rep movsb
	mov dx,offset stk + 15		; End of stack plus roundoff.
	mov cl,4
	shr dx,cl			; Divide to get segment.
	add dx,seg stack		; Get past the stack.
	mov es,dx			; remember where segment is.
	mov ah,makseg			; Create new PSP.
	int dos
	mov ax,siz			; Update machine size.
	mov es:2,ax
	mov es: byte ptr [deffcb],0	; Blank default fcb.
	mov di,deffcb+1
	mov al,' '			; Blank out fcb.
	mov cx,fcbsiz
	rep stosb
	mov word ptr es:[terma],offset term	; Termination address.
	mov es:[terma+2],cs
	mov ah,openf
	mov dx,offset fcb
	int dos
	inc al
	jnz chkok
	mov dx,offset erms33
	mov ah,prstr
	int dos
	jmp chkend

chkok:	mov byte ptr fcb+32,0		; set current record field
	mov di,100h			; offset to copy into
lp:	mov dx,offset fcb
	mov ah,readf
	int dos
	push ax				; save status
	mov si,offset buff
	mov cx,dmasiz/2			; Word size of DMA
	rep movsw			; copy into new segment...
	pop ax
	cmp al,1			; End of file
	je dun
	cmp al,3			; Done here too
	jne lp
dun:	mov ssave,sp			; Save stack pointer.
	mov ax,es
	mov word ptr cs:[doit+2],ax 	; Set segment for CHKDSK.
	mov ds,ax
	mov ss,ax
	mov ax,0
	jmp cs: dword ptr [doit]	; Call CHKDSK.
term:	mov ax,seg datas		; Reset data area.
	mov ds,ax
	mov sp,ssave
	mov ax,seg stack
	mov ss,ax
	mov ah,setdma
	mov dx,offset buff
	int dos				; restore dma address!!
chkend:	pop es
	jmp rskp
doit	dd 100h
CHKDSK	ENDP

; Get directory listing.
DIRECT	PROC	NEAR
	mov ah,dosver		; See what level of DOS we're at.
	int dos
	cmp al,0		; Level 2.0 or above?
	jne dir4		; Yes - get directory the easy way.
	mov comand.cmcr,1	; Allow plain CR (so DIR == DIR *.*).
	mov ah,cmifi		; Get input file spec.
	mov dx,offset fcb	; Give the address for the FCB.
	mov bx,offset filhlp3
	call comnd
	 jmp r
	mov ah,cmcfm		; Parse a confirm.
	call comnd
	 jmp r
	mov comand.cmcr,0	; Reset this. 
	push es
	mov ax,ds
	mov es,ax
	mov temp1,0FFH
	mov di,offset nambuf
dir0:	call getfn		; Get a matching file name.
	cmp al,0FFH		; Retcode -- are we done?
	je dir1			; Yes, just leave.
	call dumpit		; Print it or dump to buffer.
	jmp dir0
dir1:	pop es
	jmp rskp

dir4:	mov si,offset cmspbuf	; command processor
	mov di,offset dirnam
dir5:	lodsb			; get a byte
	or al,al
	jz dir6			; stop on the null
	stosb			; otherwise copy it in
	jmp dir5		; and keep going
dir6:	mov si,offset dircmd	; add directory command to it
	mov cx,dirclen
	rep movsb
	mov ah,cmtxt		; parse with cmtxt so we can have paths...
	mov bx,di		; next available byte
	mov dx,offset filwmsg	; In case user wants help. 
	call comnd
	 jmp r
	mov cl,ah
	mov ch,0		; length of name
	sub di,offset dirnam	; compute # of bytes used
	add cx,di
	mov si,offset dirnam	; dir cmd
	jmp crun		; join run cmd from there.
DIRECT	ENDP

getfn:	cmp temp1,0FFH
	jne gtfn1
	mov ah,sfirst		; Any matches?
	mov dx,offset fcb
	int dos
	cmp al,0FFH		; Means no matches.
	je gtfn5
	call savfcb
	mov temp1,0
	jmp gtfn4
gtfn1:	cmp flags.wldflg,0FFH	; Wilcard seen?
	je gtfn2		; Yes, get next file.
	mov al,0FFH		; No, set retcode.
	ret
gtfn2:	call rstfcb
	mov ah,snext
	mov dx,offset fcb
	int dos
	cmp al,0		; Any more matches?
	je gtfn3		; Yes keep going.
	mov al,0FFH		; OK return code.
	ret
gtfn3:	call savfcb
gtfn4:	push di
	mov si,offset buff	; Data is here.
	mov di,offset fcb	; Copy to here.
	mov cx,37
	repne movsb
	pop di
	call nicnam		; Make name nice for printing.
	mov al,0
	ret
gtfn5:	mov ah,prstr		; Don't print if a server. 
	mov dx,offset erms32	; Say no matches.
	int dos
	mov al,0FFH		; Failure return code.
	ret

savfcb:	push di
	mov si,offset fcb	; Data is here.
	mov di,offset cpfcb	; Copy to here.
	mov cx,37
	repne movsb
	pop di
	ret

rstfcb:	push di
	mov si,offset cpfcb	; Data is here.
	mov di,offset fcb	; Copy to here.
	mov cx,37
	repne movsb
	pop di	
	ret

nicnam:	mov al,CR		; Add CRLF before print names
	stosb
	mov al,LF
	stosb
	mov cx,8
	mov si,offset fcb+1
	repne movsb		; Get the file name.
	mov al,' '
	stosb
	mov cx,3
	repne movsb
	mov al,tab
	stosb
	mov al,' '
	stosb
	mov ah,openf
	mov dx,offset fcb
	int dos	
	mov bx,offset fcb+18	; Get hi order word of file size.
	mov ax,[bx]
	mov dx,ax
	mov bx,offset fcb+16	; Get lo order word.
	mov ax,[bx]
	call nout2x		; Get it in decimal. 
	mov al,tab
	stosb
	mov al,' '
	stosb
	mov ah,0
	mov si,offset fcb+20
	lodsb
	mov fildat+1,al
	lodsb
	mov fildat,al		; Date field of fcb.
	mov cl,5
	shr fildat+1,cl
	and fildat,1
	mov cl,3
	shl fildat,cl
	mov al,fildat
	or al,fildat+1		; Get the month field.
	cmp al,9
	jg nic0
	push ax
	mov al,' '
	stosb
	pop ax
nic0:	call nout2		; Make it decimal.
	mov al,'-'
	stosb
	mov si,offset fcb+20	; Get date field.
	lodsb
	and al,1FH
	cmp al,10		; Only one digit?
	jge nic0x
	push ax
	mov al,'0'		; Make it two digits.
	stosb
	pop ax
nic0x:	call nout2		; Make it decimal.
	mov al,'-'
	stosb
	mov si,offset fcb+21	; Get the year field.
	lodsb
	shr al,1
	add al,80
	cmp al,100		; At the year 2000 or above?
	js nic0y		; No, just go on.
	sub al,100		; Go back to two digits.
nic0y:	cmp al,10		; Only one digit?
	jge nic0z
	push ax
	mov al,'0'		; Make it two digits.
	stosb
	pop ax
nic0z:	call nout2		; Make it decimal.
	mov al,tab
	stosb
	mov si,offset fcb+23	; Get time field of fcb.
	lodsb
	mov cl,3		; Get the hour field.
	shr al,cl
	mov tmp,'a'		; For AM.
	cmp al,12		; Before noon?
	jl nic1
	mov tmp,'p'		; It's PM.
	je nic1			; Don't change "12" to "0".
	sub al,12		; Use a 12 hr. clock.
nic1:	cmp al,0		; Just after midnight?
	jne nic1x
	add al,12		; Make it "12" instead of "0".
nic1x:	cmp al,10		; Pad with a space?
	jge nic2
	push ax
	mov al,' '
	stosb
	pop ax
nic2:	call nout2		; Make it decimal.
	mov al,':'		; Separate hours and minutes.
	stosb
	mov si,offset fcb+23	; Get the minutes field.
	lodsb
	and al,07
	mov cl,3
	shl al,cl
	mov ah,al
	mov si,offset fcb+22
	lodsb
	mov cl,5
	shr al,cl
	or al,ah
	mov ah,0
	cmp al,10		; Would there be a leading zero.
	jge nic3
	push ax
	mov al,'0'
	stosb
	pop ax
nic3:	call nout2		; Make it decimal.
	mov al,tmp		; Add 'a' (AM) or 'p' (PM).
	stosb
	mov ah,closf
	mov dx,offset fcb
	int dos
	ret

; For now, just print it.
dumpit:	mov al,'$'
	stosb
	mov ah,prstr
	mov dx,offset nambuf
	int dos
	mov di,offset nambuf
	ret

; push to an inferior command parser
dopush	proc	near
	cmp	dosnum,0		; < 2.0 ?
	jne	dopus1			; no, go on
	mov	dx,offset erms34
	mov	ah,prstr
	int	dos
	jmp	rskp
dopus1:	mov	ah,cmcfm
	call	comnd
	 jmp	r
	mov	si,offset cmspbuf	; name of parser
	push	si			; save beginning
	sub	cx,cx			; initial length
dopus2:	lodsb
	inc	cx			; count this
	or	al,al			; at end?
	jnz	dopus2			; no, keep going
	pop	si			; restore cmd
	dec	cx			; this is incremented one over
	jmp	short crun		; go run it
dopush	endp

; crun - run an arbitrary program.  Enter with si/address of whole
; cmd, cx/length of cmd.
CRUN	proc	near
	push cx			; save length of cmd
	mov ax,ds
	mov es,ax		; address dest segment
	mov di,offset nambuf
	rep movsb		; copy command so we can mess with it
	pop cx
	mov si,offset nambuf	; point to command
	jmp short run3		; and join run code
CRUN	ENDP

RUN	PROC	NEAR
	cmp dosnum,0
	jne run1
	mov ah,prstr
	mov dx,offset erms34	; Complain.
	int dos
	jmp rskp
run1:	mov ah,cmtxt		; Get program name.
	mov bx,offset nambuf	; Convenient buffer.
	mov dx,offset filmsg	; In case user wants help.
	call comnd
	 nop
	 nop
	 nop
	cmp ah,0
	jne run2
	mov ah,prstr
	mov dx,offset erms35
	int dos
	jmp rskp
run2:	mov cl,ah
	mov ch,0
	mov si,offset nambuf

; alternate entry if cmd is already known.  Source cmd ptr in si
; is trashed.
run3:	mov bx,cx
	mov byte ptr [si+bx],cr	; end string with a cr for dos.
	mov di,offset cmdnam
	mov ax,ds
	mov es,ax
run4:	lodsb
	cmp al,' '
	jne run5
	dec si			; back up over space
	jmp short run6		; and exit loop
run5:	stosb
	loop run4
run6:	mov byte ptr [di],0	; terminate string
	dec si			; point back a byte into argument
	mov [si],cl		; put length of argument here
	mov exearg+2,si		; pointer to argument string
	mov exearg+4,ds		; segment of same
	inc si			; pass length over
	mov al,1		; scan leading separators
	mov di,offset exefcb	; parse into this fcb
	mov ah,prsfcb
	int dos			; go parse the fcb
	mov al,1		; scan leading separators
	mov di,offset exefcb2	; second fcb to fill
	mov ah,prsfcb
	int dos			; parse the fcb
	mov es,psp		; point to psp again
	mov ax,es:[env]		; get environment ptr
	mov exearg,ax		; put into argument block
	mov bx,offset stk + 15	; end of pgm
	mov cl,4
	shr bx,cl		; compute # of paragraphs in last segment
	mov ax,seg stack	; end of kermit
	sub ax,psp		; minus beginning...
	add bx,ax		; # of paragraphs occupied
	mov ah,setblk
	int dos
	jc run7			; nope...
	mov ax,ds
	mov es,ax		; put es segment back
	mov ax,offset cmdnam	; point to cmd name again
	call spath		; look for it
	jc run8			; not found, go complain
	mov dx,ax		; point to command name
	mov al,0		; load and execute...
	mov ah,exec
	mov bx,offset exearg	; and to arguments
	mov ssave,sp		; save stack ptr
	int dos			; go run the program
	mov ax,seg datas
	mov ds,ax		; reset data segment
	mov ax,seg stack
	mov ss,ax		; and stack segment
	mov sp,ssave		; restore stack ptr
	mov ah,setdma
	mov dx,offset buff
	pushf			; save flags
	int dos			; restore dma address!!
	popf			; recover flags
	jc run8			; error, handle.
	jmp rskp		; ok, return
run7:	mov ah,prstr
	mov dx,offset erms36
	int dos
	jmp rskp
run8:	mov ah,prstr
	mov dx,offset erms37
	int dos
	jmp rskp
RUN	ENDP

; the show command
showcmd	proc	near
	mov	ah,cmkey
	mov	dx,offset shotab
	xor	bx,bx			; no canned help
	call	comnd
	 jmp	r
	call	bx			; call the handler
	 jmp	r
	jmp	rskp			; and return
showcmd	endp

intbrk:	cmp flags.debug,1	; Debug mode?
	je intb1		; Yes, then don't ignore the ^C.
	push ax
	push ds
	mov ax,seg datas
	mov ds,ax
	mov flags.cxzflg,'C'	; Say we saw a ^C.
	mov pack.state,'A'	; Set the state to abort.
	pop ds
	pop ax
	iret
intb1:	jmp in3ad		; Original break interrupt address.

; Set the maximum data packet size. [21b]

PACKLEN	PROC	NEAR
	mov ah,trans.spsiz	; Maximum send packet size. 
	sub ah,4		; Size minus control info. 
	sub ah,trans.chklen	; And minus checksum chars.
	sub ah,2		; Leave room at end: 2 for possible #X.
	cmp trans.ebquot,'N'	; Doing 8-bit quoting?
	je pack0		; Nope so we've got our size.
	cmp trans.ebquot,'Y'
	je pack0		; Not doing it in this case either.
	sub ah,1		; Another 1 for 8th-bit quoting. 
pack0:	mov trans.maxdat,ah	; Save max length for data field.
	ret
PACKLEN	ENDP

NOUT2 	PROC	NEAR
	push ax
	push dx
	mov temp,10		; Divide quotient by 10.
	cwd			; Convert word to doubleword.
	div temp		; AX <-- Quo, DX <-- Rem.
	cmp ax,0		; Are we done?	
	jz nout0		; Yes.
	call nout2		; If not, then recurse.
nout0:	add dl,'0'		; Make it printable.
	mov temp,ax
	mov al,dl
	stosb
	mov ax,temp
	pop dx
	pop ax
	ret			; We're done. [21c]
NOUT2	ENDP

NOUT2X 	PROC	NEAR
	push ax
	push dx
	push cx
	mov temp,10		; Divide quotient by 10.
	div temp		; AX <-- Quo, DX <-- Rem.
	mov cx,dx		; Remember the remainder.
	cmp ax,0		; Are we done?	
	jz nout0x		; Yes.
	mov dx,0
	call nout2		; If not, then recurse.
nout0x:	add cl,'0'		; Make it printable.
	mov temp,ax
	mov al,cl
	stosb
	mov ax,temp
	pop cx
	pop dx
	pop ax
	ret			; We're done. [21c]
NOUT2X	ENDP

SPATH	proc	near
; enter with ax/ ptr to file name.  Searches path for given file,
; returns with ax/ ptr to whole name, or carry on if file isn't
; to be found.
	push	es
	mov	bx,ds
	mov	es,bx			; address data segment
	mov	bx,ax			; convenient place to keep this
	mov	si,ax
	mov	di,offset tfile		; place to copy to
	mov	dl,0			; no '\' seen yet
	mov	ah,swchar		; get switch character
spath1:	lodsb
	stosb
	cmp	al,ah			; contain path characters?
	jne	spath2			; no, keep going
	mov	dl,1			; remember we've seen them
spath2:	or	al,al
	jnz	spath1			; copy name in
	or	dl,dl			; look at flag
	jz	spath3			; no path embedded, keep going
	pop	es
	mov	ax,offset tfile		; else...
	call	isfile
	mov	ax,offset tfile		; point to right thing...
	ret				; let isfile decide and return
; no path, keep going
spath3:	mov	si,offset pthbuf	; path definition
	cmp	byte ptr [si],0		; empty path?
	jne	spath4			; no, keep going
	mov	ah,gcd			; get current dir
	mov	dl,0			; for default drive
	mov	si,offset defpth+1	; place to put it
	int	dos
	mov	si,offset defpth	; point to the path
spath4:	cmp	byte ptr [si],0		; null, exit loop
	je	spath9
	mov	di,offset tfile		; place to put name
spath5:	lodsb				; get a byte
	cmp	al,';'			; end of this part?
	je	spath7			; yes, break loop
	cmp	al,0			; maybe end of string?
	jne	spath6			; no, keep going
	dec	si			; back up over it
	jmp	short spath7		; and break loop
spath6:	stosb				; else stick in dest string
	jmp	spath5			; and continue
spath7:	push	si			; save this ptr
	mov	si,bx			; this is user's file name
	mov	al,swchar		; get switch character.
	cmp	byte ptr [di-1],al	; does it end with switch char?
	je	spath8			; yes, don't put one in
	stosb				; else add one
spath8:	lodsb
	stosb
	or	al,al
	jnz	spath8			; copy rest of name
	pop	si			; restore pos in path def
	mov	ax,offset tfile
	call	isfile			; is it a file?
	jc	spath4			; no, keep looking
	mov	ax,offset tfile
	pop	es
	ret				; return success (carry off)
spath9:	pop	es			; restore this
	mov	ax,bx			; not found yet, get original path
	call	isfile			; does it exist?
	ret				; return whatever isfile says.
spath	endp


isfile	proc	near
; returns carry off if the file pointed to by ax exists
	mov	dx,ax			; copy ptr
	mov	al,0			; don't change anything
	mov	ah,chmod
	int	dos
	ret				; dos sets carry
isfile	endp

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

RSKP	ENDP
 
; Jumping here is the same as a ret.
 
R	PROC	NEAR
	ret
R	ENDP
 
code 	ends			; End of code section.
	end	start
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 mskerm.asm
	/bin/echo -n '	'; /bin/ls -ld mskerm.asm
fi
/bin/echo 'Extracting mskermit.ini'
sed 's/^X//' <<'//go.sysin dd *' >mskermit.ini
; Make shift-comma send a left angle bracket
set key scan 556
<
; Shift-period sends a right angle bracket
set key scan 558
>
; Accent grave is where ESC is supposed to be
set key scan 96
\33
; Put accent grave on the ESC function key
set key scan 27
`
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 mskermit.ini
	/bin/echo -n '	'; /bin/ls -ld mskermit.ini
fi
/bin/echo 'Extracting mskermit.msg'
sed 's/^X//' <<'//go.sysin dd *' >mskermit.msg
This issue of the Info-Kermit Digest is devoted to the long-heralded (and
overdue) announcement of version 2 of Kermit for MS-DOS systems (Kermit
is Columbia University's file transfer protocol for use over
telecommunication lines, and it runs on a wide variety of systems).  We
announced our intention to provide this new release back in January, and
have been working on it ever since.  The previous release was 1.20, 28
November 1983.

The new version is called "Kermit-MS" rather than "Kermit-86" and the
version number is 2.26.  It is available for several systems:

	System                      DOS Versions
	------                      ------------
	IBM PC, PPC, and XT         1.1, 2.0 & above
	DEC Rainbow 100 and 100+    2.05 & above
	HP-150                      2.0
	Wang PC                     2.01
	Others (Generic DOS)        1.1, 2.0 & above

Versions for the IBM PCjr and Heath/Zenith 100 are soon to be added
(version 1.20 already run on these machines).  If your MS-DOS system is
not on this list, you are invited to add support for it by supplying the
appropriate system- and device-dependent modules (described below).

The IBM version has been tested on IBM PCs with the old and new
motherboards and ROMs, as well as on the XT and Portable PC, on hard
disks, floppy disks, and RAM disks, and on color and monochrome monitors.
It has NOT been tested on the Compaq, Columbia, or other "PC compatible"
product; there is some chance that it might not work on the compatibles
even when the previous release (1.20) did, because of greater dependence
on the display hardware.

Version 2 of MS-DOS Kermit has been tested successfully up to 9600 baud on
the IBM, DEC, HP, and Wang micros, in communication with full duplex
systems like the DEC-20 and VAX, and half duplex systems like IBM
mainframes.  Kermit-MS requires about 80K RAM, and certain functions like
PUSH and RUN will need additional memory.  Thus, for DOS plus Kermit, you
will need a machine with at least 128K.  Version 1.20 can run on a 64K
machine.

Version 1.20 will remain available indefinitely because it has proven
quite stable, runs on a variety of PC-compatible systems, and is
considerably smaller than version 2.

Here is a summary of the changes:

* Program organization:

The program has been broken up into separate source files, assembled
separately, and linked together.  The modules are:

System/Device Independent:

    MSKERM.ASM - Main program
    MSSEND.ASM - File sender
    MSRECV.ASM - File receiver
    MSSERV.ASM - Server operation
    MSFILE.ASM - File i/o
    MSCMD.ASM  - Command parser
    MSTERM.ASM - CONNECT command
    MSCOMM.ASM - Communications port buffering & flow control
    MSSET.ASM  - SET, SHOW, and STATUS commands
    MSDEFS.H   - Data structure definitions and equates

System/Device Dependent:

    MSXxxx.ASM - System-dependent code for system xxx
    MSYxxx.ASM - System-dependent screen and keyboard code
    MSZxxx.ASM - Modem control (modem-dependent)

    MSXSYS.DOC - Description of system-dependent modules

The modular organization allows easier modification of the program,
quicker transfer of modified portions from system to system.  The modules
are designed to be well-defined and self-contained, such that they can
be easily replaced.  For instance, someone who prefers windows and mice
to typing commands could replace the command parsing module without having
to worry about the effect on the other modules.

* Kermit Protocol Improvements:

Kermit-MS now supports:

X. 8th-bit prefixing for passing binary data through 7-bit communication links
X. 12-bit checksums and 16-bit CRCs as alternate block check types
X. Compression of repeated bytes
X. Server operation
X. Advanced commands for servers, including: 

    REMOTE DELETE
    REMOTE DIRECTORY
    REMOTE HELP
    REMOTE HOST
    REMOTE SPACE
    REMOTE TYPE

These advanced protocol features can be used in conjunction with other
advanced Kermit implementations, including itself, as well as the current
Kermits for the DECsystem-10, DECSYSTEM-20, VAX/VMS, PDP-11 (RSX, RSTS, RT),
DEC Pro-350, and others, and soon to include IBM VM/CMS and UNIX.

* Local command execution:

The following new commands provide access to DOS functions from within the
Kermit-MS program:

    DELETE
    DIRECTORY
    SET DEFAULT DISK
    PUSH (to DOS)
    SET DESTINATION (device - disk or printer)
    SPACE (runs CHKDSK)
    RUN (a program)

* Command parsing:

The command parser has been improved in many areas.  For instance, "?" now
works much better than before (though still not perfectly).  ESC now
provides completion not only in keywords, but also in filenames.  CTRL-W
deletes the previous "word" on the command line.  CTRL-C always returns to
the Kermit-MS> prompt.

There is a command macro facility; DEFINE lets you build macros by
combining Kermit-MS commands, DO executes them, SHOW displays them.

DOS command line arguments are accepted, and may be strung together
separated by commas, e.g. "kermit set baud 9600, set timer on, connect"

Kermit-MS now reads an initialization file, MSKERMIT.INI, and can process
(nested) command files with a TAKE command.

* Terminal emulation:

On IBM micros, the speed of Heath-19 terminal emulation has been improved
by using direct screen memory access.  Functions like insert and delete
character now execute very rapidly.  Heath-19 emulation functions, such as
reverse index, missing from earlier releases are now supplied.  H19
emulation may be disabled to allow the use of other console drivers, like
ANSI.SYS, in conjunction with Kermit-MS.

On systems with 25 lines, the 25th line is an inverse video mode line,
displaying current settings, which may be kept or turned off.  On the IBM
and DEC systems, there are pop-up help and status screens, and the screen
is saved and restored between remote/local context switches.

The terminal session can be logged to disk to provide unguarded capture of
remote files or session typescripts.

On the IBM, DEC, and HP systems, the screen can be rolled back several
pages, on a per-line or per-screen basis.

On most of the systems, print-screen (screen dump) and CTRL-print-screen
(toggle printing on/off) work as they do in DOS.

On the IBM and DEC systems, a key redefinition facility is available to
allow the layout of the keyboard to be altered to suit individual tastes,
to set up keypads or function keys for specific applications, or to
construct "keystroke macros".  On IBM micros, the ALT key can be set up
for use as a META key for use with EMACS-like editors.

All versions of Kermit-MS except the generic DOS version are capable of
transmitting the BREAK signal.

The functions that are missing from the Wang and/or HP micros -- key
redefinition, pop-up menus, screen rollback, screen print -- were omitted
due to lack of information about how to get at the scan codes, screen
memory, printer interrupts, etc, and may be added at a later time.
Meanwhile, anyone out there who has the information and feels inclined to
add missing features is invited to do so.

* Communication options:

The port characteristics are left alone when Kermit-MS starts (in the
previous release, Kermit-MS always set the baud rate).  The program allows
settings for speed, duplex, flow control, handshake, and parity on a
per-port basis, to allow convenient switching between ports.

* File Transfer:

You can now supply new names for files in SEND and GET commands.

A timeout facility has been added to allow automatic recovery from
deadlocks when communicating with systems (like IBM mainframes) that can't
time out.

The file transfer display has been reformatted, and includes more useful
information, including a percentage for outbound files.  The various
counts are updated more reliably.

Several options are available for interrupting file transfer, including
^X (cancel current file), ^Z (cancel entire batch), ^E (user-generated
"error"), ^C (return immediately to command level), CR (simulate a timeout).
The options are displayed during file transfer.

There is a new end-of-file option to allow selection of DOS-style (believe
the DOS byte count) or CP/M-style (file ends at first CTRL-Z) EOF detection.

* Remote operation:

Kermit-MS may be run from the back port in either interactive or server mode.
This allows micro-to-micro file transfer without requiring an operator on
both ends.

* New Bootstrapping Procedure:

The Kermit .EXE files for the various systems are now encoded using a
printable 4-for-3 encoding, with compression of repeated 0 bytes.  The
result tends to be smaller than the original .EXE file.  A new set of
bootstrapping programs has been provided:

MSMKBOO.C      Encode.  Can be used on any binary file.   Written in C.
MSBOOT.FOR     Send the encoded file from the mainframe.  Fortran.
MSPCTRAN.BAS   Decode the encoded file on the micro.      MS Basic.
MSPCBOOT.BAS   Receive on the micro, decode on the fly.   MS Basic.

* Documentation:

There's an entirely new manual, available now as a separate document,
soon to be incorporated into the Kermit User Guide.  It describes
operation of the program in detail, along with the new bootstrapping
procedure.

* How To Get It:

Kermit is available for a wide variety of systems -- micros, minis, and
mainframes.  It is distributed by Columbia University via network or on
magnetic tape.  For further information about Kermit, send network mail to
INFO-KERMIT-REQUEST@COLUMBIA-20, or write to the Kermit Distribution
address below.  To be added to the Info-Kermit network mailing list, mail
to INFO-KERMIT-REQUEST@COLUMBIA-20.

The new MS-DOS Kermit files are available from COLUMBIA-20 via anonymous
FTP after 6pm daily (ARPANET), though KERMSRV at CUVMA on BITNET (BITNET
users should type "SMSG RSCS MSG CUVMA KERMSRV HELP" for information about
the Columbia Kermit file server), and on all the Columbia DEC-20 systems
in the KERMIT area.  The file names all begin with "MS" (on BITNET, omit
the "KER:" prefix).

The executable programs have the suffix .EXE and are in 8-bit binary
format.  The corresponding 7-bit ASCII encoded files have the suffix .BOO.
The system-specific programs are available in both .EXE and .BOO formats.

    KER:MSIBMPC -- IBM PC, XT
    KER:MSIBMJR -- IBM PCjr (not yet availble)
    KER:MSRB100 -- DEC Rainbow 100, 100+
    KER:MSHP150 -- Hewlett-Packard 150
    KER:MSHZ100 -- Heath/Zenith 100 (not yet available)
    KER:MSWANG  -- Wang PC
    KER:MSGENER -- Generic DOS

KER:MS*.ASM, KER:MS*.H are the assembler source files.
KER:MSBUILD.HLP tells how to build the program.

KER:MSKERMIT.DOC is the new MS-DOS section for the Kermit User Guide.
KER:MSKERMIT.MSS is the Scribe source for the .DOC file.

Those without network access may write to the following address for
details of how to order a complete Kermit distribution on 9-track
magnetic tape:

    KERMIT Distribution
    Columbia University Center for Computing Activities
    612 West 115th Street
    New York, NY  10025

Version 2 of MS-DOS Kermit will be submitted to PC-SIG so that it can be
ordered on IBM PC floppy disks.  Inquiries should be directed to

    PC Software Interest Group
    1556 Halford Avenue, Suite #130
    Santa Clara, CA  95051
    Phone 408-730-9291

Be sure to wait until they have version 2, because they are presently
distributing version 1 on their disks numbers 41 and 42.  It may take
some time for them to update their distribution.

* Credit:

The bulk of the work was done by Daphne Tzoar and Jeff Damens of the
Columbia University Center for Computing Activities.  Many ideas (and
"existence proofs") were contributed by Herm Fischer of Litton Data
Systems -- key redefinitions, remote and server operation, etc, but those
who have been using Herm's modified 1.20 will find that some of the
features he added have been done differently in this release.  8th-bit
quoting was originally added by Leslie Spira and her staff at The Source
Telecomputing to allow Kermit to transfer binary files over Telenet.  The
new bootstrapping procedure and the new file transfer display were done by
Bill Catchings of Columbia.  Filename completion came from Kimmo Laaksonen
at the Helsinki University of Technology.  Some corporate support and
encouragement was provided by Digital Equipment Corporation, Wang
Laboratories, and IBM.

* Disclaimer:

Although we have been using the new version on several different kinds
of systems for a good while and have done extensive testing, some bugs
may have slipped through.  Please hang on to your old release (1.20),
and don't hesitate to report any problems to Info-Kermit@COLUMBIA-20.
Suggestions and contributions are also welcome.
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 mskermit.msg
	/bin/echo -n '	'; /bin/ls -ld mskermit.msg
fi
/bin/echo 'Extracting msmeta.ini'
sed 's/^X//' <<'//go.sysin dd *' >msmeta.ini
; TAKE'ing this file will make the ALT key function like a META key
; on the IBM PC.
set key scan 2064
\321
set key scan 2065
\327
set key scan 2066
\305
set key scan 2067
\322
set key scan 2068
\324
set key scan 2069
\331
set key scan 2070
\325
set key scan 2071
\311
set key scan 2072
\317
set key scan 2073
\320
set key scan 2078
\301
set key scan 2079
\323
set key scan 2080
\304
set key scan 2081
\306
set key scan 2082
\307
set key scan 2083
\310
set key scan 2084
\312
set key scan 2086
\313
set key scan 2087
\314
set key scan 2168
\261
set key scan 2169
\262
set key scan 2170
\263
set key scan 2171
\264
set key scan 2172
\265
set key scan 2173
\266
set key scan 2174
\267
set key scan 2175
\270
set key scan 2176
\271
set key scan 2177
\272
set key scan 2092
\332
set key scan 2093
\330
set key scan 2094
\303
set key scan 2095
\326
set key scan 2096
\302
set key scan 2097
\316
set key scan 2098
\317//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msmeta.ini
	/bin/echo -n '	'; /bin/ls -ld msmeta.ini
fi
X/bin/echo 'Extracting msrbemacs.ini'
sed 's/^X//' <<'//go.sysin dd *' >msrbemacs.ini
; EMACS function key setup for Kermit-MS/Rainbow
;
; C-@ (set mark) on SELECT
set key select
\00
;
; M-h (select region) on CTRL-SELECT
set key scan 1313
\33h
;
; C-U 12 C-X C-I (rigidly indent region 12 spaces) on TAB
set key scan 9
\25\61\62\30\11
; C-X C-I (rigidly indent region) on SHIFT-TAB
set key scan 521
\30\11
;
; C-S (forward search) on FIND
set key find
\23
;
; C-R (reverse search) on CTRL-FIND
set key scan 1307
\22
;
; M-D (delete word) on REMOVE
set key remove
\33d
;
; M-K (delete sentence) on CTRL-REMOVE
set key scan 1311
\33k
;
; C-P (up line) on uparrow
set key scan 295
\20
;
; M-[ (up paragraph) on CTRL-uparrow
set key scan 1319
\33[
;
; C-X [ (up page) on SHIFT-uparrow
set key scan 807
\30[
;
; M-< (top of file) on CTRL-SHIFT-uparrow
set key scan 1831
\33<
;
; C-B (back character) on leftarrow
set key scan 301
\02
;
; C-A (beginning of line) on CTRL-leftarrow
set key scan 1325
\01
;
; M-A (back sentence) on SHIFT-leftarrow
set key scan 813
\33a
;
; C-N (next line) on downarrow
set key scan 297
\16
; M-] (down paragraph) on CTRL-downarrow
set key scan 1321
\33]
;
; C-X ] (down page) on SHIFT-downarrow
set key scan 809
\30]
;
; M-> (end of file) on CTRL-SHIFT-downarrow
set key scan 1833
\30>
;
; C-F (forward character) on rightarrow
set key scan 299
\06
;
; C-E (end of line) on CTRL-rightarrow
set key scan 1323
\05
;
; M-E (end of sentence) on SHIFT-rightarrow
set key scan 811
\33e
;
; C-X E (do keyboard macro) on DO
set key scan 257
\30e
;
; C-U C-X E (do keyboard macro 4x) on CTRL-DO
set key scan 1281
\25\30e
;
; C-U 8 C-X E (do keyboard macro 8x) on SHIFT-DO
set key scan 769
\25\70\30e
;
; C-U C-U C-X E (do keyboard macro 16x) on CTRL-SHIFT-DO
set key scan 1793
\25\25\30e
;
; ^_ on HELP
set key scan 256
\37
;
; C-X C-Z on EXIT
set key scan 271
\30\32
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msrbemacs.ini
	/bin/echo -n '	'; /bin/ls -ld msrbemacs.ini
fi
X/bin/echo 'Extracting msrecv.asm'
sed 's/^X//' <<'//go.sysin dd *' >msrecv.asm
	public	read12, read2, rin21, rfile3, read, updrtr, nak, rrinit
	include msdefs.h	

datas 	segment	public 'datas'
	extrn	fcb:byte, data:byte, bufpnt:word, chrcnt:word, curchk:byte
	extrn	comand:byte, flags:byte, pack:byte, trans:byte

ermes7  db      '?Unable to receive initiate$'
ermes8  db      '?Unable to receive file name$'
ermes9  db      '?Unable to receive end of file$'
erms10  db      '?Unable to receive data$'
infms1  db	cr,'           Receiving: In progress$'
infms3  db      'Completed$'
infms4  db      'Failed$'
infms6  db      'Interrupted$'
remmsg1	db	'Kermit-MS: Invalid filename'
filhlp2 db      ' Confirm with carriage return or specify name '
	db	' to use for incoming file $'
ender	db	bell,bell,'$'
crlf	db	cr,lf,'$'
temp	dw	0
datas	ends

code	segment	public
	extrn	gofil:near, outbuf:near, fixfcb:near, comnd:near
	extrn	spack:near, rpack:near, serini:near, serrst:near
	extrn	spar:near, rpar:near, init:near, init1:near, cxmsg:near
	extrn	error:near, ptchr:near, erpos:near, rtpos:near
	extrn	stpos:near, rprpos:near, nppos:near, nout:near
	extrn	dodec:near, doenc:near, errpack:near
	extrn	send11:near, clrmod:near
	assume  cs:code, ds:datas

 
; Update retry count and fall through to send a NAK.
nak0:	call updrtr		; Update retry count.

nak:	mov ax,pack.pktnum     ; Get the packet number we're waiting for.
        mov pack.argblk,ax
        mov pack.argbk1,0
	mov cx,0		; No data, but this may change.
	call doenc		; So call encode.
        mov ah,'N'              ; NAK that packet.
        call spack
	 jmp abort
	 nop			; So 'jmp rskp' in SPACK comes here. [19a]
        ret                     ; Go around again.

updrtr:	cmp pack.state,'A'	; Supposed to abort?
	je upd0			; Yes, don't bother with retry count.
	inc pack.numrtr		; Increment the number of retries.
	cmp flags.xflg,1	; Writing to screen?
	je upd0
	call rtpos		; Position cursor.
	mov ax,pack.numrtr
	call nout		; Write the number of retries.
upd0:	ret

;       Abort
ABORT   PROC    NEAR
        mov pack.state,'A'      ; Otherwise abort.
        ret
ABORT   ENDP

; init variables for read...
rrinit	proc	near
	mov pack.numpkt,0	; Set the number of packets to zero.
	mov pack.numrtr,0	; Set the number of retries to zero.
	mov pack.pktnum,0	; Set the packet number to zero.
	mov pack.numtry,0	; Set the number of tries to zero.
	ret
rrinit	endp

;	RECEIVE command  --  Some code moved to the GET routine. [21a] 
 
READ	PROC	NEAR		
	mov comand.cmrflg,1	; Say we're receiving a file. [21a start]
	mov comand.cmcr,1	; Allow bare CR after RECEIVE.
	mov flags.droflg,0	; Override default drive flag.
	mov flags.nmoflg,0	; Override file name from other host?
	mov dx,offset fcb	; Put filename here. 
	mov bx,offset filhlp2	; Text of help message.
	mov ah,cmifi		; Read in the filename.
	call comnd		
	 jmp r				
	mov comand.cmrflg,0	; Reset flag.
	mov comand.cmcr,0
	mov flags.wldflg,0	; Just in case
	mov ah,cmcfm		; Get a confirm.
	call comnd
	 jmp r
read1:	cmp flags.remflg,0	; remote mode?
	jne read12		; yes, no printing
	call init
read12:	mov flags.cxzflg,0	; Reset ^X/^Z flag. [20c] 
	call rrinit		; init variables for read
	call serini		; Initialize serial port. [14]
	cmp flags.remflg,0	; in remote mode?
	jne read12a		; yes, no printing
	call init1		; Clear the line and initialize the buffers.
	call stpos
	mov ah,prstr		; Be informative.
	mov dx,offset infms1
	int dos
	call rtpos		; Position cursor.
	mov ax,pack.numrtr
	call nout		; Write the number of retries.
read12a:mov pack.state,'R'	; Set the state to receive initiate.
read2: 	cmp flags.xflg,1	; Are we receiving to the screen. [21c]
	je read21		; Skip the screen stuff. [21c]
	cmp flags.remflg,0	; maybe remote mode?
	jne read21		; yup, skip the screen stuff
	call nppos		; Position cursor for number of packets msg.
	mov ax,pack.numpkt
	call nout		; Write the number of packets.
read21: mov ah,pack.state	; Get the state. [21c]
	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
	call serrst		; Reset serial port. [14]
	cmp flags.xflg,0	; Did we write to the screen? [21c]
	je read51		; No so print status. [21c]
	mov flags.xflg,0	; Reset it. [21c]
	jmp rskp		; Yes, so just return. [21c]	
read51:	cmp flags.remflg,0	; remote mode?
	jne read51a		; yes, keep going
	call stpos		; Position cursor. [21c]
	mov ah,prstr
	mov dx,offset infms3	; Plus a little cuteness.
	cmp flags.cxzflg,0	; Completed or interrupted? [20c]
	je read13		; Ended normally. [20c]
	mov dx,offset infms6	; Say was interrupted. [20c]
read13: int dos
	cmp flags.belflg,0	; Bell desired?  [17a]
	je readnb		; No.  [17a]
	mov dx,offset ender	; Ring them bells.    [4]
	int dos			; [4]
readnb:	call clrmod		; clear 25th line
	call rprpos		; Put prompt here.
read51a:jmp rskp
read6: 	call serrst		; Reset serial port. [14]
	cmp flags.xflg,0	; Did we write out to screen? [21c]
	je read61		; No so print status. [21c]
	mov flags.xflg,0	; Reset it. [21c]
	jmp rskp		; Print onto screen. [21c]
read61:	cmp flags.remflg,0	; remote mode?
	jne read7a		; yes, no printing.
	call stpos		; Position cursor.  [21c]
	mov ah,prstr
	mov dx,offset infms4	; Plus a little cuteness.
	int dos
	cmp flags.belflg,0	; Bell desired?  [17a]
	je read7		; No.  [17a]
	mov dx,offset ender	; Ring them bells.   [4]
	int dos			;  [4]
read7:	call clrmod		; clear mode line
	call rprpos		; Put prompt here.
read7a:	jmp rskp
READ	ENDP
 
 
;	Receive routines
 
;	Receive init
 
RINIT	PROC	NEAR
	mov ah,pack.numtry	; Get the number of tries.
	cmp ah,imxtry		; Have we reached the maximum number of tries?
	jl rinit2
	call erpos		; Position cursor.
	mov dx,offset ermes7
	mov ah,prstr
	int dos			; Print an error message.
	mov bx,dx
	call errpack		; Send error packet just in case.
	jmp abort		; Change the state to abort.
rinit2: inc ah			; Increment it.
	mov pack.numtry,ah	; Save the updated number of tries.
	mov ax,pack.argbk2	; get packet type if here from get
	cmp flags.getflg,1	; Have we already read in the packet? [21a] 
	je rin21a		; Yes, so don't call RPACK. [21a]
	mov ah,trans.chklen
	mov curchk,ah		; Save checksum length we want to use.
	mov trans.chklen,1	; Use 1 char for init packet.
	call rpack		; Get a packet.
	 jmp rin22		; Trashed packet: nak, retry.
	push ax
	mov ah,curchk
	mov trans.chklen,ah	; Reset to desired value.
	pop ax
rin21a:	cmp ah,'S'		; Is it a send initiate packet?
	jne rinit3		; If not see if its an error.
rin21:	mov flags.getflg,0	; Reset flag. [21a]
	mov ah,pack.numtry	; Get the number of tries.
	mov pack.oldtry,ah	; Save it.
	mov pack.numtry,0	; Reset the number of tries.
	mov ax,pack.argblk	; Returned packet number.  (Synchronize them.)
	inc ax			; Increment it.
	and ax,3FH		; Turn off the two high order bits.
	mov pack.pktnum,ax	; Save modulo 64 of the number.
	mov bx,pack.numpkt
	inc bx			; Increment the number of packets.
	mov pack.numpkt,bx
	mov ax,pack.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 pack.argbk1,ax	; Store the returned number of arguments.
	mov ah,trans.chklen	; Checksum length we'll use.
	mov curchk,ah		; Save it.
	mov trans.chklen,1	; Use 1 char for init packet.
	mov ah,'Y'		; Acknowledge packet.
	call spack		; Send the packet.
	 jmp abort
	mov ah,curchk		; Checksum length we'll use.
	mov trans.chklen,ah	; Reset to desired value.
	mov ah,'F'		; Set the state to file send.
	mov pack.state,ah
	ret
rin22:	mov ah,curchk
	mov trans.chklen,ah	; Reset to desired value.
	jmp nak0		; Try again.
rinit3: cmp ah,'E'		; Is it an error packet?
	jne rinit4
	call error
rinit4:	jmp abort
RINIT	ENDP
 

;	Receive file
 
RFILE	PROC	NEAR
	cmp pack.numtry,maxtry	; Have we reached the maximum number of tries?
	jl rfile1
	call erpos		; Position cursor.
	mov dx,offset ermes8
	mov ah,prstr
	int dos			; Print an error message.
	mov bx,dx
	call errpack		; Send error packet just in case.
	jmp abort		; Change the state to abort.
rfile1: inc pack.numtry		; Save the updated number of tries.
	call rpack		; Get a packet.
	 jmp nak0		;  Trashed packet: nak, retry.
	cmp ah,'S'		; Is it a send initiate packet?
	je rfil10
	call dodec		; Decode all incoming packets.
	jmp rfile2		;  No, try next type.
rfil10:	cmp pack.oldtry,imxtry	; Have we reached the maximum number of tries?
	jl rfil12		; If not proceed.
	call erpos		; Position cursor.
	mov dx,offset ermes7
	mov ah,prstr
	int dos			; Print an error message.
	mov bx,dx
	call errpack		; Send error packet just in case.
	jmp abort		; Change the state to abort.
rfil12: inc pack.oldtry		; Save the updated number of tries.
	mov ax,pack.pktnum	; Get the present packet number.
	cmp ax,0		; Had we wrapped around? [18 start]
	jne rfilx 
	mov ax,64
rfilx:  dec ax			; Decrement.  [18 end -- new label]
	cmp ax,pack.argblk	; Is the packet's number one less than now?
	je rfil13
	jmp nak0		; No, NAK and try again.
rfil13: call updrtr		; Update retry count.
	mov pack.numtry,0	; Reset the 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 pack.argbk1,ax	; Save the number of arguments.
	mov ah,'Y'		; Acknowledge packet.
	call spack		; Send the packet.
	 jmp abort
	ret
rfile2: cmp ah,'Z'		; Is it an EOF packet?
	jne rfile3		;  No, try next type.
	cmp pack.oldtry,maxtry	; Have we reached the maximum number of tries?
	jl rfil21		; If not proceed.
	call erpos		; Position cursor.
	mov dx,offset ermes9
	mov ah,prstr
	int dos			; Print an error message.
	mov bx,dx
	call errpack		; Send error packet just in case.
	jmp abort		; Change the state to abort.
rfil21: inc pack.oldtry		; Increment it.
	mov ax,pack.pktnum	; Get the present packet number.
	cmp ax,0		; Had we wrapped around? [18 start]
	jne rfily
	mov ax,64
rfily:  dec ax			; Decrement.  [18 end -- new label]
	cmp ax,pack.argblk	; Is the packet's number one less than now?
	je rfil24
	jmp nak0		; No, NAK and try again.
rfil24: call updrtr		; Update retry count.
	mov pack.numtry,0
	mov pack.argbk1,0	; No data.  (The packet number is in argblk.)
	mov cx,0
	call doenc
	mov ah,'Y'		; Acknowledge packet.
	call spack		; Send the packet.
	 jmp abort
	ret
rfile3: cmp ah,'F'		; Start of file?
	je rfil31		; Yes. [21c]
	cmp ah,'X'		; Text header packet? [21c]
	jne rfile4		; Neither one. 
rfil31: mov ax,pack.argblk	; Get the packet number. [21c]
	cmp ax,pack.pktnum	; Is it the right packet number?
	je rfil32
	jmp nak			; No, NAK it and try again.
rfil32: inc ax			; Increment the packet number.
	and ax,3FH		; Turn off the two high order bits.
	mov pack.pktnum,ax	; Save modulo 64 of the number.
	inc pack.numpkt		; Increment the number of packets.
	call gofil		; Get a file to write to.
	 jmp abort
	call init1		; Initialize all the buffers.
	mov ah,pack.numtry	; Get the number of tries.
	mov pack.oldtry,ah	; Save it.
	mov pack.numtry,0	; Reset the number of tries.
	mov pack.argbk1,0	; No data.  (The packet number is in argblk.)
	mov cx,0
	call doenc
	mov ah,'Y'		; Acknowledge packet.
	call spack		; Send the packet.
	 jmp abort
	mov pack.state,'D'	; Set the state to data receive.
	ret
rfile4: cmp ah,'B'		; End of transmission.
	jne rfile5
	mov ax,pack.pktnum
	cmp ax,pack.argblk	; Do we match?
	je rfil41
	jmp nak			; No, NAK it and try again.
rfil41: mov pack.argbk1,0	; No data.  (Packet number already in argblk).
	mov cx,0
	call doenc
	mov ah,'Y'		; Acknowledge packet.
	call spack		; Send the packet.
	 jmp abort
	mov pack.state,'C'	; Set the state to complete.
	ret
rfile5: cmp ah,'E'		; Is it an error packet.
	jne rfile6
	call error
rfile6: jmp abort
RFILE	ENDP
 
 
;	Receive data
 
RDATA	PROC	NEAR
	cmp pack.numtry,maxtry	; Get the number of tries.
	jl rdata1
	call erpos		; Position cursor.
	mov dx,offset erms10
	mov ah,prstr
	int dos			; Print an error message.
	mov bx,dx
	call errpack		; Send error packet just in case.
	jmp abort		; Change the state to abort.
rdata1: inc pack.numtry		; Save the updated number of tries.
	call rpack		; Get a packet.
	 jmp nak0		;  Trashed packet: nak, retry.
	cmp ah,'D'		; Is it a data packet?
	je rdat11
	call dodec		; Decode data.
	jmp rdata2		;  No, try next type.
rdat11: mov ax,pack.pktnum	; Get the present packet number.
	cmp ax,pack.argblk	; Is the packet's number correct?
	jz rdat14
	cmp pack.oldtry,maxtry	; Have we reached the maximum number of tries?
	jl rdat12		; If not proceed.
	call erpos		; Position cursor.
	mov dx,offset erms10
	mov ah,prstr
	int dos			; Print an error message.
	mov bx,dx
	call errpack		; Send error packet just in case.
	jmp abort		; Change the state to abort.
rdat12: inc pack.oldtry		; Save the updated number of tries.
	mov ax,pack.pktnum
	cmp ax,0		; Had we wrapped around? [18 start]
	jne rdatx
	mov ax,64
rdatx:	dec ax			; [14] [18 end -- new label]
	cmp ax,pack.argblk	; Is the packet's number one less than now?
	je rdat13
	jmp nak0		; No, NAK it and try again.
rdat13: call updrtr		; Update retry count.
	mov pack.numtry,0	; Reset number of tries.
	mov pack.argbk1,0	; No data.  (The packet number is in argblk.)
	mov cx,0
	call doenc
	mov ah,'Y'		; Acknowledge packet.
	call spack		; Send the packet.
	 jmp abort
	ret
rdat14: inc ax			; Increment the packet number.
	and ax,3FH		; Turn off the two high order bits.
	mov pack.pktnum,ax	; Save modulo 64 of the number.
	inc pack.numpkt		; Increment the number of packets.
	mov ah,pack.numtry	; Get the number of tries.
	mov pack.oldtry,ah	; Save it.
	mov ax,pack.argbk1	; Get the length of the data.
	cmp flags.cxzflg,0	; Has the user typed a ^X or ^Z? [20c]
	je rdt14x		; No, write out the data.
	cmp flags.abfflg,1	; Discard incomplete files?
	je rdat15		; If yes don't write data out to file. [20c]
rdt14x:	mov bx,offset data	; Where the data is. [25]
	call ptchr
	 jmp abort		;  Unable to write out chars; abort.
rdat15: mov pack.numtry,0	; Reset the number of tries.
	mov pack.argbk1,0	; No data.  (Packet number still in argblk.)
	mov cx,0
	cmp flags.cxzflg,0	; Interrupt file transfer? [20c]
	je rdat16		; Nope. [20c] 
	mov bx,offset data	; Send data in ACK in case remote... [20c] 
	mov ah,flags.cxzflg	; ... knows about ^X/^Z. [20c]
	mov [bx],ah		; Put data into the packet. [20c]
	mov pack.argbk1,1	; Set data size to 1. [20c]
	mov cx,1
rdat16: call doenc
	mov ah,'Y'		; Acknowledge packet.
	call spack		; Send the packet.
	 jmp abort
	ret
rdata2: cmp ah,'F'		; Start of file?
	je rdat20		; Yup. [21c]
	cmp ah,'X'		; Text header packet? [21c]
	jne rdata3		;  No, try next type.
rdat20: cmp pack.oldtry,maxtry	; Reached the max number of tries? [21c]
	jl rdat21		; If not proceed.
	call erpos		; Position cursor.
	mov dx,offset ermes8
	mov ah,prstr
	int dos			; Print an error message.
	mov bx,dx
	call errpack		; Send error packet just in case.
	jmp abort		; Change the state to abort.
rdat21: inc pack.oldtry		; Save the updated number of tries.
	mov ax,pack.pktnum
	cmp ax,0		; Had we wrapped around? [18 start]
	jne rdaty
	mov ax,64
rdaty:	dec ax			; [14 Omitted accidentally - D.T.] [18 end]
	cmp ax,pack.argblk	; Is the packet's number one less than now?
	je rdat22
	jmp nak0		; No, NAK it and try again.
rdat22: call updrtr		; Update retry count.
	mov pack.numtry,0	; Reset number of tries.
	mov pack.argbk1,0	; No data.  (The packet number is in argblk.)
	mov cx,0
	call doenc
	mov ah,'Y'		; Acknowledge packet.
	call spack		; Send the packet.
	 jmp abort
	ret
rdata3: cmp ah,'Z'		; Is it a EOF packet?
	je rdat3x		; [13]
	jmp rdata4		; Try and see if its an error. [13]
rdat3x: mov ax,pack.pktnum	; Get the present packet number. [13]
	cmp ax,pack.argblk	; Is the packet's number correct?
	je rdat32
	jmp nak0		; No, NAK it and try again.
rdat32: inc ax			; Increment the packet number.
	and ax,3FH		; Turn off the two high order bits.
	mov pack.pktnum,ax	; Save modulo 64 of the number.
	inc pack.numpkt
	cmp flags.cxzflg,0	; Do we want to discard the file? [20c]
	jne rdt32x		; Yes. [20c]
	cmp pack.argbk1,1	; One piece of data? [20c]
	jne rdat33		; Nope - finish writing out file? [20c]
	mov bx,offset data	; Get data area. [20c]
	mov ah,[bx]		; Get the data. [20c]
	cmp ah,'D'		; "D" for discard? [20c]
	jne rdat33		; Nope - write out file. [20c]
rdt32x:	cmp flags.abfflg,0	; Keep incomplete files?
	je rdat33		; Yes, go write it out.
	mov ah,closf		; First, close the file.
	mov dx,offset fcb	; Give the file parameters. [20c]
	int dos			; Kill it, ignore errors. [20c]
	mov ah,delf		; Delete the file if opened. [20c]
	int dos
	cmp flags.cxzflg,'X'	; Kill one file or all? [20c]
	jne rdat36		; No so leave flag alone. [20c]
	call cxmsg		; Clear msg about interrupt. [20c]
	mov flags.cxzflg,0	; Reset - ^X only kills one file. [20c]
	jmp rdat36
rdat33: mov bx,bufpnt		; Get the dma pointer.
	mov ax,80H
	sub ax,chrcnt		; Get the number of chars left in the DMA.
	cmp flags.eofcz,0	; should we write a ^Z?
	jz rdat35		; no, keep going
	cmp flags.xflg,0	; writing to a file?
	jne rdat35		; no, skip ^Z
	cmp ax,80H		;   [13 start]
	jne rdat34
	call outbuf		; Write out buffer if no room for ^Z.
	 jmp abort
	mov ax,0		;   [13 end]
	inc chrcnt		; Increment size by one (not two). [21b]
rdat34: mov cl,'Z'-100O		; Put in a ^Z for EOF.
	mov [bx],cl		; Add it. [21c]
	inc ax
	dec chrcnt
rdat35:	mov cx,chrcnt
	mov temp,cx
	call outbuf		; Output the last buffer.
	 jmp abort		; Give up if the disk is full.
	mov ax,temp		; Prepare for the function call.
	call fixfcb
	mov ah,closf		; Close up the file.
	mov dx,offset fcb
	int dos
rdat36:	cmp flags.destflg,1	; Writing to disk?
	je rdat37		; Yes, skip next part.
	cmp flags.xflg,1	; Writing to screen?
	je rdat37		; Yes, skip this part.
	mov dl,ff		; Send a form feed.
	mov ah,lstout		; Write out to first printer.
	int dos
rdat37:	mov ah,pack.numtry	; Get the number of tries.
	mov pack.oldtry,ah	; Save it.
	mov pack.numtry,0	; Reset the number of tries.
	mov pack.argbk1,0	; No data.  (The packet number is in argblk.)
	mov cx,0
	call doenc
	mov ah,'Y'		; Acknowledge packet.
	call spack		; Send the packet.
	 jmp abort
	mov pack.state,'F'
	ret
rdata4: cmp ah,'E'			; Is it an error packet.
	jne rdata5
	call error
rdata5: jmp abort
RDATA	ENDP

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

R	PROC	NEAR
	ret
R	ENDP

code	ends 
	end
 
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msmeta.ini
	/bin/echo -n '	'; /bin/ls -ld msmeta.ini
fi
/bin/echo 'Extracting msrecv.asm'
sed 's/^X//' <<'//go.sysin dd *' >msrecv.asm
	public	read12, read2, rin21, rfile3, read, updrtr, nak, rrinit
	include msdefs.h	

datas 	segment	public 'datas'
	extrn	fcb:byte, data:byte, bufpnt:word, chrcnt:word, curchk:byte
	extrn	comand:byte, flags:byte, pack:byte, trans:byte

ermes7  db      '?Unable to receive initiate$'
ermes8  db      '?Unable to receive file name$'
ermes9  db      '?Unable to receive end of file$'
erms10  db      '?Unable to receive data$'
infms1  db	cr,'           Receiving: In progress$'
infms3  db      'Completed$'
infms4  db      'Failed$'
infms6  db      'Interrupted$'
remmsg1	db	'Kermit-MS: Invalid filename'
filhlp2 db      ' Confirm with carriage return or specify name '
	db	' to use for incoming file $'
ender	db	bell,bell,'$'
crlf	db	cr,lf,'$'
temp	dw	0
datas	ends

code	segment	public
	extrn	gofil:near, outbuf:near, fixfcb:near, comnd:near
	extrn	spack:near, rpack:near, serini:near, serrst:near
	extrn	spar:near, rpar:near, init:near, init1:near, cxmsg:near
	extrn	error:near, ptchr:near, erpos:near, rtpos:near
	extrn	stpos:near, rprpos:near, nppos:near, nout:near
	extrn	dodec:near, doenc:near, errpack:near
	extrn	send11:near, clrmod:near
	assume  cs:code, ds:datas

 
; Update retry count and fall through to send a NAK.
nak0:	call updrtr		; Update retry count.

nak:	mov ax,pack.pktnum     ; Get the packet number we're waiting for.
        mov pack.argblk,ax
        mov pack.argbk1,0
	mov cx,0		; No data, but this may change.
	call doenc		; So call encode.
        mov ah,'N'              ; NAK that packet.
        call spack
	 jmp abort
	 nop			; So 'jmp rskp' in SPACK comes here. [19a]
        ret                     ; Go around again.

updrtr:	cmp pack.state,'A'	; Supposed to abort?
	je upd0			; Yes, don't bother with retry count.
	inc pack.numrtr		; Increment the number of retries.
	cmp flags.xflg,1	; Writing to screen?
	je upd0
	call rtpos		; Position cursor.
	mov ax,pack.numrtr
	call nout		; Write the number of retries.
upd0:	ret

;       Abort
ABORT   PROC    NEAR
        mov pack.state,'A'      ; Otherwise abort.
        ret
ABORT   ENDP

; init variables for read...
rrinit	proc	near
	mov pack.numpkt,0	; Set the number of packets to zero.
	mov pack.numrtr,0	; Set the number of retries to zero.
	mov pack.pktnum,0	; Set the packet number to zero.
	mov pack.numtry,0	; Set the number of tries to zero.
	ret
rrinit	endp

;	RECEIVE command  --  Some code moved to the GET routine. [21a] 
 
READ	PROC	NEAR		
	mov comand.cmrflg,1	; Say we're receiving a file. [21a start]
	mov comand.cmcr,1	; Allow bare CR after RECEIVE.
	mov flags.droflg,0	; Override default drive flag.
	mov flags.nmoflg,0	; Override file name from other host?
	mov dx,offset fcb	; Put filename here. 
	mov bx,offset filhlp2	; Text of help message.
	mov ah,cmifi		; Read in the filename.
	call comnd		
	 jmp r				
	mov comand.cmrflg,0	; Reset flag.
	mov comand.cmcr,0
	mov flags.wldflg,0	; Just in case
	mov ah,cmcfm		; Get a confirm.
	call comnd
	 jmp r
read1:	cmp flags.remflg,0	; remote mode?
	jne read12		; yes, no printing
	call init
read12:	mov flags.cxzflg,0	; Reset ^X/^Z flag. [20c] 
	call rrinit		; init variables for read
	call serini		; Initialize serial port. [14]
	cmp flags.remflg,0	; in remote mode?
	jne read12a		; yes, no printing
	call init1		; Clear the line and initialize the buffers.
	call stpos
	mov ah,prstr		; Be informative.
	mov dx,offset infms1
	int dos
	call rtpos		; Position cursor.
	mov ax,pack.numrtr
	call nout		; Write the number of retries.
read12a:mov pack.state,'R'	; Set the state to receive initiate.
read2: 	cmp flags.xflg,1	; Are we receiving to the screen. [21c]
	je read21		; Skip the screen stuff. [21c]
	cmp flags.remflg,0	; maybe remote mode?
	jne read21		; yup, skip the screen stuff
	call nppos		; Position cursor for number of packets msg.
	mov ax,pack.numpkt
	call nout		; Write the number of packets.
read21: mov ah,pack.state	; Get the state. [21c]
	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
	call serrst		; Reset serial port. [14]
	cmp flags.xflg,0	; Did we write to the screen? [21c]
	je read51		; No so print status. [21c]
	mov flags.xflg,0	; Reset it. [21c]
	jmp rskp		; Yes, so just return. [21c]	
read51:	cmp flags.remflg,0	; remote mode?
	jne read51a		; yes, keep going
	call stpos		; Position cursor. [21c]
	mov ah,prstr
	mov dx,offset infms3	; Plus a little cuteness.
	cmp flags.cxzflg,0	; Completed or interrupted? [20c]
	je read13		; Ended normally. [20c]
	mov dx,offset infms6	; Say was interrupted. [20c]
read13: int dos
	cmp flags.belflg,0	; Bell desired?  [17a]
	je readnb		; No.  [17a]
	mov dx,offset ender	; Ring them bells.    [4]
	int dos			; [4]
readnb:	call clrmod		; clear 25th line
	call rprpos		; Put prompt here.
read51a:jmp rskp
read6: 	call serrst		; Reset serial port. [14]
	cmp flags.xflg,0	; Did we write out to screen? [21c]
	je read61		; No so print status. [21c]
	mov flags.xflg,0	; Reset it. [21c]
	jmp rskp		; Print onto screen. [21c]
read61:	cmp flags.remflg,0	; remote mode?
	jne read7a		; yes, no printing.
	call stpos		; Position cursor.  [21c]
	mov ah,prstr
	mov dx,offset infms4	; Plus a little cuteness.
	int dos
	cmp flags.belflg,0	; Bell desired?  [17a]
	je read7		; No.  [17a]
	mov dx,offset ender	; Ring them bells.   [4]
	int dos			;  [4]
read7:	call clrmod		; clear mode line
	call rprpos		; Put prompt here.
read7a:	jmp rskp
READ	ENDP
 
 
;	Receive routines
 
;	Receive init
 
RINIT	PROC	NEAR
	mov ah,pack.numtry	; Get the number of tries.
	cmp ah,imxtry		; Have we reached the maximum number of tries?
	jl rinit2
	call erpos		; Position cursor.
	mov dx,offset ermes7
	mov ah,prstr
	int dos			; Print an error message.
	mov bx,dx
	call errpack		; Send error packet just in case.
	jmp abort		; Change the state to abort.
rinit2: inc ah			; Increment it.
	mov pack.numtry,ah	; Save the updated number of tries.
	mov ax,pack.argbk2	; get packet type if here from get
	cmp flags.getflg,1	; Have we already read in the packet? [21a] 
	je rin21a		; Yes, so don't call RPACK. [21a]
	mov ah,trans.chklen
	mov curchk,ah		; Save checksum length we want to use.
	mov trans.chklen,1	; Use 1 char for init packet.
	call rpack		; Get a packet.
	 jmp rin22		; Trashed packet: nak, retry.
	push ax
	mov ah,curchk
	mov trans.chklen,ah	; Reset to desired value.
	pop ax
rin21a:	cmp ah,'S'		; Is it a send initiate packet?
	jne rinit3		; If not see if its an error.
rin21:	mov flags.getflg,0	; Reset flag. [21a]
	mov ah,pack.numtry	; Get the number of tries.
	mov pack.oldtry,ah	; Save it.
	mov pack.numtry,0	; Reset the number of tries.
	mov ax,pack.argblk	; Returned packet number.  (Synchronize them.)
	inc ax			; Increment it.
	and ax,3FH		; Turn off the two high order bits.
	mov pack.pktnum,ax	; Save modulo 64 of the number.
	mov bx,pack.numpkt
	inc bx			; Increment the number of packets.
	mov pack.numpkt,bx
	mov ax,pack.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 pack.argbk1,ax	; Store the returned number of arguments.
	mov ah,trans.chklen	; Checksum length we'll use.
	mov curchk,ah		; Save it.
	mov trans.chklen,1	; Use 1 char for init packet.
	mov ah,'Y'		; Acknowledge packet.
	call spack		; Send the packet.
	 jmp abort
	mov ah,curchk		; Checksum length we'll use.
	mov trans.chklen,ah	; Reset to desired value.
	mov ah,'F'		; Set the state to file send.
	mov pack.state,ah
	ret
rin22:	mov ah,curchk
	mov trans.chklen,ah	; Reset to desired value.
	jmp nak0		; Try again.
rinit3: cmp ah,'E'		; Is it an error packet?
	jne rinit4
	call error
rinit4:	jmp abort
RINIT	ENDP
 

;	Receive file
 
RFILE	PROC	NEAR
	cmp pack.numtry,maxtry	; Have we reached the maximum number of tries?
	jl rfile1
	call erpos		; Position cursor.
	mov dx,offset ermes8
	mov ah,prstr
	int dos			; Print an error message.
	mov bx,dx
	call errpack		; Send error packet just in case.
	jmp abort		; Change the state to abort.
rfile1: inc pack.numtry		; Save the updated number of tries.
	call rpack		; Get a packet.
	 jmp nak0		;  Trashed packet: nak, retry.
	cmp ah,'S'		; Is it a send initiate packet?
	je rfil10
	call dodec		; Decode all incoming packets.
	jmp rfile2		;  No, try next type.
rfil10:	cmp pack.oldtry,imxtry	; Have we reached the maximum number of tries?
	jl rfil12		; If not proceed.
	call erpos		; Position cursor.
	mov dx,offset ermes7
	mov ah,prstr
	int dos			; Print an error message.
	mov bx,dx
	call errpack		; Send error packet just in case.
	jmp abort		; Change the state to abort.
rfil12: inc pack.oldtry		; Save the updated number of tries.
	mov ax,pack.pktnum	; Get the present packet number.
	cmp ax,0		; Had we wrapped around? [18 start]
	jne rfilx 
	mov ax,64
rfilx:  dec ax			; Decrement.  [18 end -- new label]
	cmp ax,pack.argblk	; Is the packet's number one less than now?
	je rfil13
	jmp nak0		; No, NAK and try again.
rfil13: call updrtr		; Update retry count.
	mov pack.numtry,0	; Reset the 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 pack.argbk1,ax	; Save the number of arguments.
	mov ah,'Y'		; Acknowledge packet.
	call spack		; Send the packet.
	 jmp abort
	ret
rfile2: cmp ah,'Z'		; Is it an EOF packet?
	jne rfile3		;  No, try next type.
	cmp pack.oldtry,maxtry	; Have we reached the maximum number of tries?
	jl rfil21		; If not proceed.
	call erpos		; Position cursor.
	mov dx,offset ermes9
	mov ah,prstr
	int dos			; Print an error message.
	mov bx,dx
	call errpack		; Send error packet just in case.
	jmp abort		; Change the state to abort.
rfil21: inc pack.oldtry		; Increment it.
	mov ax,pack.pktnum	; Get the present packet number.
	cmp ax,0		; Had we wrapped around? [18 start]
	jne rfily
	mov ax,64
rfily:  dec ax			; Decrement.  [18 end -- new label]
	cmp ax,pack.argblk	; Is the packet's number one less than now?
	je rfil24
	jmp nak0		; No, NAK and try again.
rfil24: call updrtr		; Update retry count.
	mov pack.numtry,0
	mov pack.argbk1,0	; No data.  (The packet number is in argblk.)
	mov cx,0
	call doenc
	mov ah,'Y'		; Acknowledge packet.
	call spack		; Send the packet.
	 jmp abort
	ret
rfile3: cmp ah,'F'		; Start of file?
	je rfil31		; Yes. [21c]
	cmp ah,'X'		; Text header packet? [21c]
	jne rfile4		; Neither one. 
rfil31: mov ax,pack.argblk	; Get the packet number. [21c]
	cmp ax,pack.pktnum	; Is it the right packet number?
	je rfil32
	jmp nak			; No, NAK it and try again.
rfil32: inc ax			; Increment the packet number.
	and ax,3FH		; Turn off the two high order bits.
	mov pack.pktnum,ax	; Save modulo 64 of the number.
	inc pack.numpkt		; Increment the number of packets.
	call gofil		; Get a file to write to.
	 jmp abort
	call init1		; Initialize all the buffers.
	mov ah,pack.numtry	; Get the number of tries.
	mov pack.oldtry,ah	; Save it.
	mov pack.numtry,0	; Reset the number of tries.
	mov pack.argbk1,0	; No data.  (The packet number is in argblk.)
	mov cx,0
	call doenc
	mov ah,'Y'		; Acknowledge packet.
	call spack		; Send the packet.
	 jmp abort
	mov pack.state,'D'	; Set the state to data receive.
	ret
rfile4: cmp ah,'B'		; End of transmission.
	jne rfile5
	mov ax,pack.pktnum
	cmp ax,pack.argblk	; Do we match?
	je rfil41
	jmp nak			; No, NAK it and try again.
rfil41: mov pack.argbk1,0	; No data.  (Packet number already in argblk).
	mov cx,0
	call doenc
	mov ah,'Y'		; Acknowledge packet.
	call spack		; Send the packet.
	 jmp abort
	mov pack.state,'C'	; Set the state to complete.
	ret
rfile5: cmp ah,'E'		; Is it an error packet.
	jne rfile6
	call error
rfile6: jmp abort
RFILE	ENDP
 
 
;	Receive data
 
RDATA	PROC	NEAR
	cmp pack.numtry,maxtry	; Get the number of tries.
	jl rdata1
	call erpos		; Position cursor.
	mov dx,offset erms10
	mov ah,prstr
	int dos			; Print an error message.
	mov bx,dx
	call errpack		; Send error packet just in case.
	jmp abort		; Change the state to abort.
rdata1: inc pack.numtry		; Save the updated number of tries.
	call rpack		; Get a packet.
	 jmp nak0		;  Trashed packet: nak, retry.
	cmp ah,'D'		; Is it a data packet?
	je rdat11
	call dodec		; Decode data.
	jmp rdata2		;  No, try next type.
rdat11: mov ax,pack.pktnum	; Get the present packet number.
	cmp ax,pack.argblk	; Is the packet's number correct?
	jz rdat14
	cmp pack.oldtry,maxtry	; Have we reached the maximum number of tries?
	jl rdat12		; If not proceed.
	call erpos		; Position cursor.
	mov dx,offset erms10
	mov ah,prstr
	int dos			; Print an error message.
	mov bx,dx
	call errpack		; Send error packet just in case.
	jmp abort		; Change the state to abort.
rdat12: inc pack.oldtry		; Save the updated number of tries.
	mov ax,pack.pktnum
	cmp ax,0		; Had we wrapped around? [18 start]
	jne rdatx
	mov ax,64
rdatx:	dec ax			; [14] [18 end -- new label]
	cmp ax,pack.argblk	; Is the packet's number one less than now?
	je rdat13
	jmp nak0		; No, NAK it and try again.
rdat13: call updrtr		; Update retry count.
	mov pack.numtry,0	; Reset number of tries.
	mov pack.argbk1,0	; No data.  (The packet number is in argblk.)
	mov cx,0
	call doenc
	mov ah,'Y'		; Acknowledge packet.
	call spack		; Send the packet.
	 jmp abort
	ret
rdat14: inc ax			; Increment the packet number.
	and ax,3FH		; Turn off the two high order bits.
	mov pack.pktnum,ax	; Save modulo 64 of the number.
	inc pack.numpkt		; Increment the number of packets.
	mov ah,pack.numtry	; Get the number of tries.
	mov pack.oldtry,ah	; Save it.
	mov ax,pack.argbk1	; Get the length of the data.
	cmp flags.cxzflg,0	; Has the user typed a ^X or ^Z? [20c]
	je rdt14x		; No, write out the data.
	cmp flags.abfflg,1	; Discard incomplete files?
	je rdat15		; If yes don't write data out to file. [20c]
rdt14x:	mov bx,offset data	; Where the data is. [25]
	call ptchr
	 jmp abort		;  Unable to write out chars; abort.
rdat15: mov pack.numtry,0	; Reset the number of tries.
	mov pack.argbk1,0	; No data.  (Packet number still in argblk.)
	mov cx,0
	cmp flags.cxzflg,0	; Interrupt file transfer? [20c]
	je rdat16		; Nope. [20c] 
	mov bx,offset data	; Send data in ACK in case remote... [20c] 
	mov ah,flags.cxzflg	; ... knows about ^X/^Z. [20c]
	mov [bx],ah		; Put data into the packet. [20c]
	mov pack.argbk1,1	; Set data size to 1. [20c]
	mov cx,1
rdat16: call doenc
	mov ah,'Y'		; Acknowledge packet.
	call spack		; Send the packet.
	 jmp abort
	ret
rdata2: cmp ah,'F'		; Start of file?
	je rdat20		; Yup. [21c]
	cmp ah,'X'		; Text header packet? [21c]
	jne rdata3		;  No, try next type.
rdat20: cmp pack.oldtry,maxtry	; Reached the max number of tries? [21c]
	jl rdat21		; If not proceed.
	call erpos		; Position cursor.
	mov dx,offset ermes8
	mov ah,prstr
	int dos			; Print an error message.
	mov bx,dx
	call errpack		; Send error packet just in case.
	jmp abort		; Change the state to abort.
rdat21: inc pack.oldtry		; Save the updated number of tries.
	mov ax,pack.pktnum
	cmp ax,0		; Had we wrapped around? [18 start]
	jne rdaty
	mov ax,64
rdaty:	dec ax			; [14 Omitted accidentally - D.T.] [18 end]
	cmp ax,pack.argblk	; Is the packet's number one less than now?
	je rdat22
	jmp nak0		; No, NAK it and try again.
rdat22: call updrtr		; Update retry count.
	mov pack.numtry,0	; Reset number of tries.
	mov pack.argbk1,0	; No data.  (The packet number is in argblk.)
	mov cx,0
	call doenc
	mov ah,'Y'		; Acknowledge packet.
	call spack		; Send the packet.
	 jmp abort
	ret
rdata3: cmp ah,'Z'		; Is it a EOF packet?
	je rdat3x		; [13]
	jmp rdata4		; Try and see if its an error. [13]
rdat3x: mov ax,pack.pktnum	; Get the present packet number. [13]
	cmp ax,pack.argblk	; Is the packet's number correct?
	je rdat32
	jmp nak0		; No, NAK it and try again.
rdat32: inc ax			; Increment the packet number.
	and ax,3FH		; Turn off the two high order bits.
	mov pack.pktnum,ax	; Save modulo 64 of the number.
	inc pack.numpkt
	cmp flags.cxzflg,0	; Do we want to discard the file? [20c]
	jne rdt32x		; Yes. [20c]
	cmp pack.argbk1,1	; One piece of data? [20c]
	jne rdat33		; Nope - finish writing out file? [20c]
	mov bx,offset data	; Get data area. [20c]
	mov ah,[bx]		; Get the data. [20c]
	cmp ah,'D'		; "D" for discard? [20c]
	jne rdat33		; Nope - write out file. [20c]
rdt32x:	cmp flags.abfflg,0	; Keep incomplete files?
	je rdat33		; Yes, go write it out.
	mov ah,closf		; First, close the file.
	mov dx,offset fcb	; Give the file parameters. [20c]
	int dos			; Kill it, ignore errors. [20c]
	mov ah,delf		; Delete the file if opened. [20c]
	int dos
	cmp flags.cxzflg,'X'	; Kill one file or all? [20c]
	jne rdat36		; No so leave flag alone. [20c]
	call cxmsg		; Clear msg about interrupt. [20c]
	mov flags.cxzflg,0	; Reset - ^X only kills one file. [20c]
	jmp rdat36
rdat33: mov bx,bufpnt		; Get the dma pointer.
	mov ax,80H
	sub ax,chrcnt		; Get the number of chars left in the DMA.
	cmp flags.eofcz,0	; should we write a ^Z?
	jz rdat35		; no, keep going
	cmp flags.xflg,0	; writing to a file?
	jne rdat35		; no, skip ^Z
	cmp ax,80H		;   [13 start]
	jne rdat34
	call outbuf		; Write out buffer if no room for ^Z.
	 jmp abort
	mov ax,0		;   [13 end]
	inc chrcnt		; Increment size by one (not two). [21b]
rdat34: mov cl,'Z'-100O		; Put in a ^Z for EOF.
	mov [bx],cl		; Add it. [21c]
	inc ax
	dec chrcnt
rdat35:	mov cx,chrcnt
	mov temp,cx
	call outbuf		; Output the last buffer.
	 jmp abort		; Give up if the disk is full.
	mov ax,temp		; Prepare for the function call.
	call fixfcb
	mov ah,closf		; Close up the file.
	mov dx,offset fcb
	int dos
rdat36:	cmp flags.destflg,1	; Writing to disk?
	je rdat37		; Yes, skip next part.
	cmp flags.xflg,1	; Writing to screen?
	je rdat37		; Yes, skip this part.
	mov dl,ff		; Send a form feed.
	mov ah,lstout		; Write out to first printer.
	int dos
rdat37:	mov ah,pack.numtry	; Get the number of tries.
	mov pack.oldtry,ah	; Save it.
	mov pack.numtry,0	; Reset the number of tries.
	mov pack.argbk1,0	; No data.  (The packet number is in argblk.)
	mov cx,0
	call doenc
	mov ah,'Y'		; Acknowledge packet.
	call spack		; Send the packet.
	 jmp abort
	mov pack.state,'F'
	ret
rdata4: cmp ah,'E'			; Is it an error packet.
	jne rdata5
	call error
rdata5: jmp abort
RDATA	ENDP

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

R	PROC	NEAR
	ret
R	ENDP

code	ends 
	end
 
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msrecv.asm
	/bin/echo -n '	'; /bin/ls -ld msrecv.asm
fi

knutson@ut-ngp.UUCP (Jim Knutson) (10/05/84)

: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting mssend.asm'
sed 's/^X//' <<'//go.sysin dd *' >mssend.asm
	public	spar, rpar, error, error1, nout, send, flags, trans, pack
	public	dodec, doenc, curchk, inichk, packlen, send11
	include msdefs.h	

spmin	equ	20		; Minimum packet size.
spmax	equ	94		; Maximum packet size.

datas 	segment	public 'datas'
	extrn	buff:byte, data:byte, fcb:byte, cpfcb:byte, filbuf:byte
	extrn	decbuf:byte, chrcnt:word, bufpnt:word, comand:byte
	extrn	rptq:byte, origr:byte, rptct:byte, rptval:byte

flags	flginfo	<>
trans	trinfo	<>
pack	pktinfo <>
crlf	db	cr,lf,'$'
ender	db	bell,bell,'$' 			;  [4]
erms14  db	'?Unable to receive an acknowledgment from the host$'
erms15  db	'?Unable to find file$'
erms20	db	'Unable to send init packet$'
erms21	db	'Unable to send file header$'
erms22	db	'Unable to send data$'
erms23	db	'Unable to send end-of-file packet$'
erms24	db	'Unable to send break packet$'
infms2  db	cr,'             Sending: In progress$'
infms3  db	'Completed$'
infms4  db	'Failed$'
infms6  db	'Interrupted$'
infms7	db	cr,' Percent transferred: 100%$'
remmsg1	db	'Kermit-MS: File not found$'
filhlp  db      ' Input file spec (possibly wild) $'
filmsg	db	' File name to use on target system or confirm with'
	db	' a carriage return $'

curchk	db	0		; Use to store checksum length.
inichk	db	1		; Original or set checksum length.
chrptr  dw	?		; Position in character buffer.
fcbpt	dw	?		; Position in FCB.
datptr  dw	?		; Position in packet data buffer.
siz	dw	?		; Size of data from gtchr.
temp	dw	0
temp4	dw	0
sendas	dw	50 dup(0)	; Buffer for file name.
difnam	db	0		; Send under different name?
difsiz	db	0		; Size of new file name.
asmsg	db	'  as  $'
datas	ends

code	segment	public
	extrn serini:near, serrst:near, comnd:near, init:near
	extrn spack:near, rpack:near, gtnfil:near, gtchr:near
	extrn getfil:near, clrfln:near, nppos:near, rprpos:near
	extrn erpos:near, rtpos:near, cxmsg:near, stpos:near
	extrn encode:near, nulref:near, decode:near, nulr:near
	extrn errpack:near, updrtr:near, clrmod:near, fcbcpy:near
	extrn perpos:near
	assume	cs:code,ds:datas

;	This routine sets up the data for init packet (either the
;	Send_init or ACK packet).
 
RPAR	PROC	NEAR
	mov ah,trans.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,trans.rtime	; Get the receive packet time out.
	add ah,' '		; Add a space.
	mov 1[bx],ah		; Put it in the packet.
	mov ah,trans.rpad	; Get the number of padding chars.
	add ah,' '
	mov 2[bx],ah		; Put it in the packet.
	mov ah,trans.rpadch	; Get the padding char.
	add ah,100O		; Uncontrol it.
	and ah,7FH
	mov 3[bx],ah		; Put it in the packet.
	mov ah,trans.reol	; Get the EOL char.
	add ah,' '
	mov 4[bx],ah		; Put it in the packet.
	mov ah,trans.rquote	; Get the quote char.
	mov 5[bx],ah		; Put it in the packet.
	mov ah,trans.ebquot	; Get 8-bit quote char. [21b]
	mov 6[bx],ah		; Add it to the packet. [21b] 
	mov ah,trans.chklen	; Length of checksum.
	add ah,48		; Make into a real digit.
	mov 7[bx],ah
	mov ah,rptq		; Repeat quote char.
	cmp ah,0		; Null means no.
	jne rpar0
	mov ah,' '		; Send a blank instead.
rpar0:	mov 8[bx],ah
	mov ah,09H		; Nine pieces of data.
	ret
RPAR	ENDP
 
;	This routine reads in all the send_init packet information.
 
SPAR	PROC	NEAR
	cmp ax,1
	jge sparx
	mov ah,dspsiz		; Data not supplied by host, use default.
	jmp sparx2
sparx:	mov temp4,ax		; Save the number of arguments.
	mov ah,trans.spsiz
	cmp ah,dspsiz		; Is current value the default?
	jne sparx2		; No, assume changed by user.
	mov ah,[bx]		; Get the max packet size.
	sub ah,' '		; Subtract a space.
	cmp ah,spmin		; Can't be below the minimum.
	jge sparx1
	mov ah,spmin
	jmp sparx2
sparx1:	cmp ah,spmax		; Or above the maximum.
	jle sparx2
	mov ah,spmax
sparx2:	mov trans.spsiz,ah	; Save it.
	mov ax,temp4
	cmp al,2		; Fewer than two pieces?
	jge spar0
	mov ah,dstime		; Data not supplied by host, use default.
	jmp spar02
spar0: 	mov ah,trans.stime
	cmp ah,dstime		; Is current value the default?
	jne spar02		; No, assume changed by user.
	mov ah,1[bx]		; Get the timeout value.
	sub ah,' '		; Subtract a space.
	cmp ah,0
	ja spar01		; Must be non-negative.
	mov ah,0
spar01:	cmp ah,trans.rtime	; Same as other side's timeout.
	jne spar02
	add ah,5		; If so, make it a little different.
spar02:	mov trans.stime,ah	; Save it.
	mov ax,temp4
	cmp al,3		; Fewer than three pieces?
	jge spar1
	mov ah,dspad		; Data not supplied by host, use default.
	jmp spar11
spar1:	mov ah,trans.spad
	cmp ah,dspad		; Is current value the default?
	jne spar11		; No, assume changed by user.
	mov ah,2[bx]		; Get the number of padding chars.
	sub ah,' '
	cmp ah,0
	ja spar11		; Must be non-negative.
	mov ah,0
spar11:	mov trans.spad,ah
	mov ax,temp4
	cmp al,4		; Fewer than four pieces?
	jge spar2
	mov ah,dspadc		; Data not supplied by host, use default.
	jmp spar21
spar2:	mov ah,trans.spadch
	cmp ah,dspadc		; Is current value the default?
	jne spar21		; No, assume changed by user.
	mov ah,3[bx]		; Get the padding char.
	add ah,100O		; Re-controlify it.
	and ah,7FH
	cmp ah,del		; Delete?
	je spar21		; Yes, then it's OK.
	cmp ah,0
	jge spar20
	mov ah,0		; Below zero is no good.
	jmp spar21		; Use zero (null).
spar20:	cmp ah,31		; Is it a control char?
	jle spar21		; Yes, then OK.
	mov ah,0		; No, use null.
spar21:	mov trans.spadch,ah
	mov ax,temp4
	cmp al,5		; Fewer than five pieces?
	jge spar3
	mov ah,dseol		; Data not supplied by host, use default.
	jmp spar31
spar3:  mov ah,trans.seol
	cmp ah,dseol		; Is current value the default?
	jne spar31		; No, assume changed by user.
	mov ah,4[bx]		; Get the EOL char.
	sub ah,' '
	cmp ah,0
	jge spar30		; Cannot be negative.
	mov ah,cr		; If it is, use default of carriage return.
	jmp spar31
spar30:	cmp ah,31		; Is it a control char?
	jle spar31		; Yes, then use it.
	mov ah,cr		; Else, use the default.
spar31:	mov trans.seol,ah
	mov ax,temp4
	cmp al,6		; Fewer than six pieces?
	jge spar4
	mov ah,dsquot		; Data not supplied by host, use default.
	jmp spar41
spar4:	mov ah,trans.squote
	cmp ah,dsquot		; Is current value the default?
	jne spar41		; No, assume changed by user.
	mov ah,5[bx]		; Get the quote char.
	cmp ah,' '		; Less than a space?
	jge spar40
	mov ah,dsquot		; Yes, use default.
	jmp spar41
spar40:	cmp ah,'~'		; Must also be less then a tilde.
	jle spar41
	mov ah,dsquot		; Else, use default.
spar41:	mov trans.squote,ah
	cmp al,7		; Fewer than seven pieces? [21b begin]
	jge spar5
	mov trans.ebquot,'Y'	; Data not supplied by host, use default.
	jmp spar51
spar5:	mov ah,6[bx]		; Get other sides 8-bit quote request.
	call doquo		; And set quote char.  [21b end]
spar51:	cmp al,8		; Fewer than eight pieces?
	jge spar6
	mov trans.chklen,1
	jmp spar61
spar6:	mov ah,inichk
	mov trans.chklen,ah	; Checksum length we really want to use.
	mov ah,7[bx]		; Get other sides checksum length.
	call dochk		; Determine what size to use.
spar61:	cmp al,9		; Fewer than nine pieces?
	jge spar7
	mov rptq,0
	ret
spar7:	mov ah,8[bx]		; Get other sides repeat count prefix.
	mov ch,drpt
	mov rptq,0
	call dorpt
	ret
SPAR	ENDP
 
; Set 8-bit quote character based on my capabilities and the other
; Kermit's request.   [21b]

DOQUO	PROC	NEAR
	cmp trans.ebquot,'N'	; Can I do 8-bit quoting at all?
	je dq3			; No - so forget it.
	cmp trans.ebquot,'Y'	; Can I do it if requested?
	jne dq0			; No - it's a must that I do it.
	mov trans.ebquot,ah	; Do whatever he wants.
	jmp dq1
dq0:	cmp ah,'Y'		; I need quoting - can he do it?
	je dq1			; Yes - then all is settled.
	cmp ah,'N'		; No - then don't quote.
	je dq3
	cmp ah,trans.ebquot	; Both need quoting - chars must match.
	jne dq3
dq1:	mov ah,trans.ebquot
	cmp ah,'Y'		; If Y or N, don't validate prefix.
	je dq2
	cmp ah,'N'
	je dq2
	call prechk		; Is it in range 33-62, 96-126?
	 mov ah,'Y'		; Failed, don't do quoting.
	 nop
	cmp ah,trans.rquote	; Same prefix?
	je dq3			; Not allowed, so don't do quoting. 
	cmp ah,trans.squote	; Same prefix here?
	je dq3			; This is illegal too.
	mov trans.ebquot,ah	; Remember what we decided on.
dq2:	ret
dq3:	mov trans.ebquot,'N'	; Quoting will not be done.
	ret
DOQUO	ENDP
 
; Check if prefix in AH is in the proper range: 33-62, 96-126. 
; RSKP if so else RETURN.
prechk:	cmp ah,33
	jge prec0		; It's above 33.
	ret
prec0:	cmp ah,62
	jg prec1
	jmp rskp		; And below 62.  OK.
prec1:	cmp ah,96
	jge prec2		; It's above 96.
	ret
prec2:	cmp ah,126
	jg prec3
	jmp rskp		; And below 126.  OK.
prec3:	ret

; Set checksum length. 
dochk:	cmp ah,'1'		; Must be 1, 2, or 3.
	jl doc1
	cmp ah,'3'
	jle doc2
doc1:	mov ah,'1'
doc2:	sub ah,48		; Don't want it printable.
	cmp ah,trans.chklen	; Do we want the same thing?
	je dochk0		; Yes, then we're done.
	mov trans.chklen,1	; No, use single character checksum.
dochk0:	ret			; Just return for now.

; Set repeat count quote character.  The one used must be different than
; the control and eight-bit quote characters.  Also, both sides must 
; use the same character.
dorpt:	call prechk		; Is it in the valid range?
	 mov ah,0		; No, don't use their value. 
	 nop
	cmp ah,trans.squote	; Same as the control quote char?
	je dorpt0		; Yes, that's illegal, no repeats.
	cmp ah,trans.rquote	; How about this one?
	je dorpt0		; No good.
	cmp ah,trans.ebquot	; Same as eight bit quote char?
	je dorpt0		; Yes, that's illegal too, no repeats.
	cmp ah,ch		; Are we planning to use the same char?
	jne dorpt0		; No, that's no good either.
	mov rptq,ch		; Use repeat quote char now.
dorpt0:	ret

;	Send command
 
SEND	PROC	NEAR
	mov comand.cmcr,0	; Filename must be specified.
	mov difnam,0		; Assume we'll use original filename.
	mov flags.wldflg,0	; Re-initialize every time.
	mov ah,cmifi		; Parse an input file spec.
	mov dx,offset fcb	; Give the address for the FCB.
	mov bx,offset filhlp	; Text of help message.
	call comnd
	 jmp r			;  Give up on bad parse.
	cmp flags.wldflg,0FFH	; Any wildcards seen?
	je send1		; Yes, get a confirm.
	mov bx,offset sendas	; See if want to send file under dif name.
	mov dx,offset filmsg	; In case user needs help.
	mov ah,cmtxt
	call comnd
	 jmp r
	cmp ah,0		; Different name supplied?
	je send11		; No - keep as it.
	mov difnam,1		; Yes - send different filename.
	mov difsiz,ah		; Remember length of new name.
	jmp send11
send1:  mov ah,cmcfm
	call comnd		; Get a confirm.
	 jmp r			;  Didn't get a confirm.
send11: mov flags.droflg,0	; Reset flags from fn parsing. [21a]
	mov flags.nmoflg,0	; Reset flags from fn parsing. [21a]
	mov ah,sfirst		; Get the first file.
	mov dx,offset fcb
	int dos
	cmp al,0FFH		; Any found?
	jne send12
	cmp pack.state,'R'	; was this from a remote GET?
	jne sen11a		; no, print error and continue
	mov bx,offset remmsg1	; else get error message
	call errpack		; go complain
	jmp abort		; and abort this
sen11a:	mov ah,prstr
	mov dx,offset crlf
	int dos
	mov ah,prstr
	mov dx,offset erms15
	int dos
	ret
send12: cmp flags.wldflg,0	; Any wildcards.      [7 start]
	je send16		; Nope, so no problem.
	mov bx,offset fcb	; Remember what FCB looked like.
	mov di,offset cpfcb
	mov cl,37		; Size of FCB.
	call fcbcpy
	mov di,offset fcb+1	; Copy filename	from DTA to FCB.
	mov bx,offset buff+1
	mov cl,11
	call fcbcpy					; [7 end]
send16:	call serini		; Initialize serial port. [14]
	mov pack.pktnum,0	; Set the packet number to zero.
	mov pack.numtry,0	; Set the number of tries to zero.
	mov pack.numpkt,0 	; Set the number of packets to zero.
	mov pack.numrtr,0	; Set the number of retries to zero.
	mov pack.state,'S'	; Set the state to receive initiate.
	cmp flags.remflg,0	; remote mode?
	jne send2a		; yes, continue below.
	call init		; Clear the line and initialize the buffers.
	call rtpos		; Position cursor.
	mov ax,0
	call nout		; Write the number of retries.
	call stpos		; Print status of file transfer.
	mov ah,prstr		; Be informative.
	mov dx,offset infms2
	int dos
send2:	cmp flags.remflg,0	; remote mode?
	jne send2a		; yes, skip printing
	call nppos		; Number of packets sent.
	mov ax,pack.numpkt
	call nout		; Write the packet number.
send2a:	cmp pack.state,'D'	; Are we in the data send state?
	jne send3
	call sdata
	jmp send2
send3:  cmp pack.state,'F'	; Are we in the file send state?
	jne send4
	call sfile		; Call send file.
	jmp send2
send4:  cmp pack.state,'Z'	; Are we in the EOF state?
	jne send5
	call seof
	jmp send2
send5:  cmp pack.state,'S'	; Are we in the send initiate state?
	jne send6
	call sinit
	jmp send2
send6:  cmp pack.state,'B'	; Are we in the eot state?
	jne send7
	call seot
	jmp send2
send7:  cmp pack.state,'C'	; Are we in the send complete state?
	jne send8
	call serrst		; Reset serial port.  [14]
	cmp flags.remflg,0	; remote mode?
	jne send7a		; yes, no printing.
	cmp flags.cxzflg,0	; completed normally?
	jne send7b		; no, don't bother with this
	call perpos
	mov ah,prstr
	mov dx,offset infms7
	int dos
send7b:	call stpos
	mov ah,prstr
	mov dx,offset infms3	; Plus a little cuteness.
	cmp flags.cxzflg,0	; Completed or interrupted?
	je snd71		; Ended normally.
	mov dx,offset infms6	; Say was interrupted.
snd71:  int dos			; New label. 
	cmp flags.belflg,0	; Bell desired? [17a]
	je sendnb		; [17a]
	mov dx,offset ender	; Ring them bells.   [4]
	int dos
sendnb:	call clrmod
	call rprpos
send7a:	jmp rskp
send8: 	call serrst		; Reset serial port.  [14]
	cmp flags.remflg,0	; remote mode?
	jne send9a		; no, no printing.
	call stpos
	mov ah,prstr
	mov dx,offset infms4	; Plus a little cuteness.
	int dos
	cmp flags.belflg,0	; Bell desired?  [17a]
	je send9		; No.  [17a]
	mov dx,offset ender	; Ring them bells.   [4]
	int dos			;  [4]
send9:	call clrmod
	call rprpos
send9a:	jmp rskp
SEND	ENDP
 
 
;	Send routines
 
;	Send initiate
 

SINIT	PROC	NEAR
	cmp pack.numtry,imxtry	; Have we reached the maximum number of tries?
	jl sinit2
	call erpos
	mov dx,offset erms14
	mov ah,prstr
	int dos			; Print an error message.
	mov bx,offset erms20
	call errpack		; Send error packet just in case.
	jmp abort		; Change the state to abort.
sinit2: inc pack.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 pack.argbk1,ax	; Save the number of arguments.
	mov ax,pack.numpkt	; Get the packet number.
	mov pack.argblk,ax
	mov ah,trans.chklen
	mov curchk,ah		; Store checksum length we want to use.
	mov trans.chklen,1	; Send init checksum is always 1 char.
	mov ah,'S'		; Send initiate packet.
	call spack		; Send the packet.
	 jmp abort
	call rpack		; Get a packet.
	 jmp sini23		; Trashed packet don't change state, retry.
	push ax
	mov ah,curchk
	mov trans.chklen,ah	; Checksum length we want to use.
	pop ax
	cmp ah,'Y'		; ACK?
	jne sinit3		; If not try next.
	mov ax,pack.pktnum	; Get the packet number.
	cmp ax,pack.argblk	; Is it the right packet number?
	je sini22
	ret			; If not try again.
sini22: inc ax			; Increment the packet number.
	and ax,3FH		; Turn off the two high order bits.
	mov pack.pktnum,ax	; Save modulo 64 of the number.
	inc pack.numpkt		; Increment the number of packets.
	mov ax,pack.argbk1	; Get the number of pieces of data.
	mov bx,offset data	; Pointer to the data.
	call spar		; Read in the data.
	call packlen		; Get max send packet size. [21b]
	mov ah,pack.numtry	; Get the number of tries.
	mov pack.oldtry,ah	; Save it.
	mov pack.numtry,0	; Reset the number of tries.
	mov pack.state,'F'	; Set the state to file send.
	call getfil		; Open the file.
	 jmp abort		;  Something is wrong, die.
	ret
sini23:	mov ah,curchk		; Restore desired checksum length.
	mov trans.chklen,ah
	call updrtr		; Update retry counter.
	ret			; And retry.
sinit3: cmp ah,'N'		; NAK?
	jne sinit4		; If not see if its an error.
	call rtpos		; Position cursor.
	inc pack.numrtr		; Increment the number of retries
	mov ax,pack.numrtr
	call nout		; Write the number of retries.
	ret
sinit4: cmp ah,'E'		; Is it an error packet.
	jne sinit5
	call error
sinit5: jmp abort
SINIT	ENDP
 


;	Send file header
 
SFILE	PROC	NEAR
	cmp pack.numtry,maxtry	; Have we reached the maximum number of tries?
	jl sfile1
	call erpos
	mov dx,offset erms14
	mov ah,prstr
	int dos			; Print an error message.
	mov bx,offset erms21
	call errpack		; Send error packet just in case.
	jmp abort		; Change the state to abort.
sfile1: inc pack.numtry		; Increment it.
	mov flags.cxzflg,0	; Clear ^X,^Z flag. 
	mov datptr,offset data  ; Get a pointer to our data block.
	mov bx,offset fcb+1		; Pointer to file name in FCB.
	mov fcbpt,bx		; Save position in FCB.
	mov cl,0		; Counter for chars in file name.
	mov ch,0		; Counter for number of chars in FCB.
sfil11:	cmp ch,8H		; Ninth char?
	jne sfil12
	mov ah,'.'
	mov bx,datptr
	mov [bx],ah		; Put dot in data packet.	
	inc bx
	mov datptr,bx		; Save new position in data packet.
	inc cl
sfil12:	inc ch
	cmp ch,0CH		; Twelve?
	jns sfil13
	mov bx,fcbpt
	mov ah,[bx]		; Get char of filename.
	inc bx
	mov fcbpt,bx		; Save position in FCB.
	cmp ah,'!'		; Is it a good char?
	jl sfil11		; If not, get the next.
	mov bx,datptr
	mov [bx],ah		; Put char in data buffer.
	inc cl			; Increment counter.
	inc bx
	mov datptr,bx		; Save new position. 
	jmp sfil11		; Get another char.
sfil13: mov ch,0
	cmp flags.remflg,0	; remote mode?
	jne sfil13a		; yes, no printing.
	push cx			; Don't forget the size.
	mov bx,datptr
	mov ah,'$'
	mov [bx],ah		; Put dollar sign for printing.
	call clrfln
	mov ah,prstr
	mov dx,offset data	; Print file name.
	int dos
	pop cx
sfil13a:cmp difnam,0		; Sending file under different name.
	je sfl13x		; No, so don't give new name.
	call newfn
sfl13x:	call doenc		; Do encoding.
	mov ax,pack.pktnum	; Get the packet number.
	mov pack.argblk,ax
	mov ah,'F'		; File header packet.
	call spack		; Send the packet.
	 jmp abort
	call rpack		; Get a packet.
	 jmp tryagn		; Trashed packet don't change state, retry.
	call dodec		; Do all decoding.
	cmp ah,'Y'		; ACK?
	jne sfile2		; If not try next.
	mov ax,pack.pktnum	; Get the packet number.
	cmp ax,pack.argblk
	je sfil14
	ret			; If not hold out for the right one.
sfil14: inc ax			; Increment the packet number.
	and ax,3FH		; Turn off the two high order bits.
	mov pack.pktnum,ax	; Save modulo 64 of the number.
	inc pack.numpkt		; Increment the number of packets.
	mov ah,pack.numtry	; Get the number of tries.
	mov pack.oldtry,ah	; Save it.
	mov pack.numtry,0	; Reset the number of tries.

sfil15: mov ah,0		; Get a zero.
	mov bx,offset fcb
	add bx,20H
	mov [bx],ah		; Set the record number to zero.
;	mov flags.eoflag,ah	; Indicate not EOF.  (Done in GETFIL).
	mov ah,0FFH
	mov flags.filflg,ah	; Indicate file buffer empty.
	call gtchr
	 jmp sfil16		; Error go see if its EOF.
	 nop
	jmp sfil17		; Got the chars, proceed.
sfil16: cmp ah,0FFH		; Is it EOF?
	je sfl161
	jmp abort		; If not give up.
sfl161: mov ah,'Z'		; Set the state to EOF.
	mov pack.state,ah
	ret
sfil17: mov siz,ax

	mov pack.state,'D'	; Set the state to data send.
	ret
sfile2: cmp ah,'N'		; NAK?
	jne sfile3		; Try if error packet.
	call rtpos		; Position cursor.
	inc pack.numrtr		; Increment the number of retries
	mov ax,pack.numrtr
	call nout		; Write the number of retries.
	mov ax,pack.pktnum	; Get the present packet number.
	inc ax			; Increment.
	and ax,03FH		; Account for wraparound.  [18]
	cmp ax,pack.argblk	; Is the packet's number one more than now?
	jz sfil14		; Just as good as a ACK; go to the ACK code.
	ret			; If not go try again.
sfile3: cmp ah,'E'		; Is it an error packet.
	jne sfile4
	call error
sfile4: jmp abort
SFILE	ENDP
 
 
;	Send data
 
SDATA	PROC	NEAR
	cmp flags.cxzflg,0	; Have we seen ^X or ^Z?
	je sdata2		; Nope, just continue.
	cmp flags.cxzflg,'C'	; Stop it all? [25]
	jne sdata1		; It was a ^X or ^Z.
	mov pack.state,'A'	; It was a ^C -- abort [25]
	ret
sdata1:	mov pack.state,'Z'	; Else, abort sending the file.
	ret
sdata2: cmp pack.numtry,maxtry	; Have we reached the maximum number of tries?
	jl sdata3
	call erpos
	mov dx,offset erms14
	mov ah,prstr
	int dos			; Print an error message.
	mov bx,offset erms22
	call errpack		; Send error packet just in case.
	jmp abort		; Change the state to abort.
sdata3: inc pack.numtry		; Increment it.
	mov datptr,offset data  ; Get a pointer to our data block.
	mov chrptr,offset filbuf ; Pointer to chars to be sent.
	mov cx,siz		; number to transfer
	mov si,chrptr		; source of characters
	mov di,datptr		; destination
	cmp flags.eofcz,0	; stopping on ctl-z's?
	jz sdata6		; no, do blind copy
sdata4:	lodsb			; get a byte
	cmp al,'Z'-40H		; is it a ctl-z?
	je sdata5		; yes, break loop
	stosb			; else copy it
	loop sdata4		; and keep going
sdata5:	mov ax,siz		; size to send
	sub ax,cx		; minus actually sent...
	jmp short sdata7
sdata6:	rep movsb		; just copy data
	mov ax,siz		; this is how many were moved
sdata7:	mov pack.argbk1,ax
	mov ax,pack.pktnum	; Get the packet number.
	mov pack.argblk,ax
	mov ah,'D'		; Data packet.
	call spack		; Send the packet.
	 jmp tryagn		; if can't send it, retry before giving up
	call rpack		; Get a packet.
	 jmp tryagn		; Trashed packet don't change state, retry.
	call dodec		; Do all decoding.
	cmp ah,'Y'		; ACK?
	jne sdat14		; If not try next.
	mov ax,pack.pktnum	; Get the packet number.
	cmp ax,pack.argblk	; Is it the right packet number?
	jz sdata8
	ret			; If not hold out for the right one.
sdata8: inc ax			; Increment the packet number.
	and ax,3FH		; Turn off the two high order bits.
	mov pack.pktnum,ax	; Save modulo 64 of the number.
	inc pack.numpkt		; Increment the number of packets.
	mov ah,pack.numtry	; Get the number of tries.
	mov pack.oldtry,ah	; Save it.
	mov pack.numtry,0	; Reset the number of tries.
	cmp pack.argbk1,1	; Does the ACK contain data?
	jne sdat11		; Nope, so continue.
	mov bx,offset data	; If yes, check the data field.
	mov ah,[bx]		; Pick it up.
	cmp ah,'X'		; Other side requests ^X?
	jne sdata9		; Nope.
	jmp sdat10		; And leave.
sdata9: cmp ah,'Z'		; Other side requests ^Z?
	jne sdat11		; Nope.
sdat10:	mov flags.cxzflg,ah	; Yes remember it.
	mov pack.state,'Z'	; Abort sending file(s).
	ret
sdat11: call gtchr
	 jmp sdat12		; Error go see if its EOF.
	mov siz,ax		; Save the size of the data gotten.
	ret

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

sdat13: mov pack.state,'Z'	; Set the state to EOF.
	ret
sdat14: cmp ah,'N'		; NAK?
	jne sdat15		; See if is an error packet.
	call rtpos		; Position cursor.
	inc pack.numrtr		; Increment the number of retries
	mov ax,pack.numrtr
	call nout		; Write the number of retries.
	mov ax,pack.pktnum	; Get the present packet number.
	inc ax			; Increment.
	and ax,03FH		; Account for wraparound.  [18]
	cmp ax,pack.argblk	; Is the packet's number one more than now?
	jz sdata8		; Just as good as ACK; goto ACK code.
	ret			; If not go try again.
sdat15: cmp ah,'E'		; Is it an error packet.
	jne sdat16
	call error
sdat16: jmp abort
SDATA	ENDP
 
 
;	Send EOF
 
SEOF	PROC	NEAR
	cmp pack.numtry,maxtry	; Have we reached the maximum number of tries?
	jl seof1
	call erpos		; Position cursor.
	mov dx,offset erms14
	mov ah,prstr
	int dos			; Print an error message.
	mov bx,offset erms23
	call errpack		; Send error packet just in case.
	jmp abort		; Change the state to abort.
seof1:  inc pack.numtry		; Increment it.
	mov ax,pack.pktnum	; Get the packet number.
	mov pack.argblk,ax
	mov pack.argbk1,0	; No data.
	cmp flags.cxzflg,0	; Seen a ^X or ^Z?
	je seof11		; Nope, send normal EOF packet.
	mov bx,offset data	; Get data area of packet.
	mov ah,'D'		; Use "D" for discard.
	mov [bx],ah		; And add it to the packet.
	mov pack.argbk1,1	; Set data size to 1.
seof11:	mov cx,pack.argbk1	; Put size in CX.
	call doenc		; Encode the packet.
	mov ah,'Z'		; EOF packet.
	call spack		; Send the packet.
	 jmp abort
	call rpack		; Get a packet.
	 jmp tryagn		;  Trashed packet don't change state, retry.
	call dodec		; Do decoding.
	cmp ah,'Y'		; ACK?
	jne seof2		; If not try next.
	mov ax,pack.pktnum	; Get the packet number.
	cmp ax,pack.argblk	; Is it the right packet number?
	jz seof12
	ret			; If not hold out for the right one.
seof12: inc ax			; Increment the packet number.
	and ax,3FH		; Turn off the two high order bits.
	mov pack.pktnum,ax	; Save modulo 64 of the number.
	inc pack.numpkt		; Increment the number of packets.
	mov ah,pack.numtry	; Get the number of tries.
	mov pack.oldtry,ah	; Save it.
	mov pack.numtry,0	; Reset the number of tries.
	mov ah,closf		; Close the file.
	mov dx,offset fcb
	int dos
	call gtnfil		; Get the next file.
	 jmp seof13		;  No more.
	mov pack.state,'F'	; Set the state to file send.
	cmp flags.cxzflg,'X'	; Control-X seen?
	jne seof14
	call cxmsg		; Clear out the interrupt msg.
seof14:	mov flags.cxzflg,0	; Reset the flag.
	ret
seof13: mov pack.state,'B'	; Set the state to EOT.
	ret
seof2:  cmp ah,'N'		; NAK?
	jne seof3		; Try and see if its an error packet.
	call rtpos		; Position cursor.
	inc pack.numrtr		; Increment the number of retries
	mov ax,pack.numrtr
	call nout		; Write the number of retries.
	mov ax,pack.pktnum	; Get the present packet number.
	inc ax			; Increment.
	and ax,03FH		; Account for wraparound.  [18]
	cmp ax,pack.argblk	; Is the packet's number one more than now?
	jz seof12		; Just as good as a ACK; go to the ACK code.
	ret			; If not go try again.
seof3:  cmp ah,'E'		; Is it an error packet?
	jne seof4
	call error
seof4:  jmp abort
SEOF	ENDP
 
 
;	Send EOT
 
SEOT	PROC	NEAR
	cmp pack.numtry,maxtry	; Have we reached the maximum number of tries?
	jl seot1
	call erpos	       ; Position cursor.
	mov dx,offset erms14
	mov ah,prstr
	int dos			; Print an error message.
	mov bx,offset erms24
	call errpack		; Send error packet just in case.
	jmp abort		; Change the state to abort.
seot1:  inc pack.numtry		; Increment it.
	mov ax,pack.pktnum	; Get the packet number.
	mov pack.argblk,ax
	mov pack.argbk1,0	; No data.
	mov cx,pack.argbk1
	call doenc		; Encode packet.
	mov ah,'B'		; EOF packet.
	call spack		; Send the packet.
	 jmp abort
	call rpack		; Get a packet.
	 jmp tryagn		; Trashed packet don't change state, retry.
	call dodec		; Decode packet.
	cmp ah,'Y'		; ACK?
	jne seot2		; If not try next.
	mov ax,pack.pktnum	; Get the packet number.
	cmp ax,pack.argblk	; Is it the right packet number?
	jz seot12
	ret			; If not hold out for the right one.
seot12: inc ax			; Increment the packet number.
	and ax,3FH		; Turn off the two high order bits.
	mov pack.pktnum,ax	; Save modulo 64 of the number.
	inc pack.numpkt		; Increment the number of packets.
	mov ah,pack.numtry	; Get the number of tries.
	mov pack.oldtry,ah	; Save it.
	mov pack.numtry,0	; Reset the number of tries.
	mov pack.state,'C'	; Set the state to file send.
	ret
seot2:  cmp ah,'N'		; NAK?
	jne seot3		; Is it error.
	call rtpos		; Position cursor.
	inc pack.numrtr		; Increment the number of retries
	mov ax,pack.numrtr
	call nout		; Write the number of retries.
	mov ax,pack.pktnum	; Get the present packet number.
	inc ax			; Increment.
	and ax,03FH		; Account for wraparound.  [18]
	cmp ax,pack.argblk	; Is the packet's number one more than now?
	jz seot12		; Just as good as a ACK; go to the ACK code.
	ret			; If not go try again.
seot3:  cmp ah,'E'		; Is it an error packet.
	jne seot4
	call error
seot4:  jmp abort
SEOT	ENDP
 
tryagn:	call updrtr
	ret

newfn:	mov ah,prstr
	mov dx,offset asmsg
	int dos
	mov ah,dconio
	mov si,offset sendas	; Buffer where the name is.
	mov di,offset data
	mov ch,0
	mov cl,difsiz		; Length of name.
newf0:	lodsb			; Get a char.
	cmp al,61H
	jb newf1		; Leave alone if less than 'a'?
	cmp al,7AH
	ja newf1		; Leave alone if over 'z'.
	sub al,20H		; Uppercase the letters.
newf1:	stosb
	mov dl,al
	cmp flags.remflg,0	; should we print?
	jne newf2		; no, we're in remote mode.
	int dos			; Print them.
newf2:	loop newf0
	mov ch,0
	mov cl,difsiz		; Reset the length field.
	ret

; Do encoding.  Expectx CX to be the data size.
doenc:	jcxz doen0
	mov chrcnt,cx		; Number of chars in filename.
	mov bx,offset data	; Source of data.
	mov bufpnt,bx
	mov bx,offset nulref	; Null routine for refilling buffer.
	mov ah,rptq
	mov origr,ah		; Save repeat prefix here.
	mov rptct,1		; Number of times char is repeated.
	mov rptval,0		; Value of repeated char.
	call encode		; Make a packet with size in AX.
	 nop
	 nop
	 nop
	mov pack.argbk1,ax	; Save number of char in filename.
	mov cx,ax
	call movpak		; Move to data part of packet.
doen0:	ret

; CX is set before this is called.
movpak:	push es
	mov ax,ds
	mov es,ax
	mov si,offset filbuf	; Move from here
	mov di,offset data	; to here
	repne movsb
	pop es
	ret

; Do decoding.
dodec:	cmp pack.argbk1,0
	je dodc0
	push ax			; Save packet size.
	mov cx,pack.argbk1	; Size of data.
	mov bx,offset data	; Address of data.
	mov ax,offset nulr	; Routine to dump buffer (null routine).
	mov bufpnt,offset decbuf  ; Where to put output.
	mov chrcnt,80H		; Buffer size.
	call decode
	 nop     
	 nop     
	 nop     
	call decmov		; Move decoded data back to "data" buffer.
 	pop ax
dodc0:	ret

; Move decoded data from decode buffer back to "data". 
decmov:	push si
	push di
	push es
	mov ax,ds
	mov es,ax
	mov cx,bufpnt		; Last char we added.
	sub cx,offset decbuf	; Get actual number of characters.
	mov pack.argbk1,cx	; Remember size of real data.
	lea si,decbuf		; Data is here.
	lea di,data		; Move to here.
	repne movsb		; Copy the data.
	pop es
	pop di
	pop si
	ret

;	Abort
 
ABORT	PROC	NEAR
	mov pack.state,'A'	; Otherwise abort.
	ret
ABORT	ENDP

; 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.  [8]
 
ERROR	PROC	NEAR
	mov pack.state,'A'	; Set the state to abort.
	call erpos		; Position the cursor.
	jmp error2
error1:	mov ah,prstr
	mov dx,offset crlf
	int dos
error2: mov bx,pack.argbk1	; Get the length of the data.
	add bx,offset data	; Get to the end of the string.
	mov ah,'$'		; Put a dollar sign at the end.
	mov [bx],ah
	mov ah,prstr		; Print the error message.
	mov dx,offset data
	int dos
	ret
ERROR	ENDP
 
; Set the maximum data packet size. [21b]

PACKLEN	PROC	NEAR
	mov ah,trans.spsiz	; Maximum send packet size. 
	sub ah,4		; Size minus control info. 
	sub ah,trans.chklen	; And minus checksum chars.
	sub ah,2		; Leave room at end: 2 for possible #X.
	cmp trans.ebquot,'N'	; Doing 8-bit quoting?
	je pack0		; Nope so we've got our size.
	cmp trans.ebquot,'Y'
	je pack0		; Not doing it in this case either.
	sub ah,1		; Another 1 for 8th-bit quoting. 
pack0:	cmp rptq,0		; Doing repeat character quoting?
	je pack1		; Nope, so that's all for now.
	sub ah,2		; Another 2 for repeat prefix.
pack1:	mov trans.maxdat,ah	; Save max length for data field.
	ret
PACKLEN	ENDP

 ; Print the number in AX on the screen in decimal rather that hex. [19a]

NOUT 	PROC	NEAR
	cmp flags.xflg,1	; Writing to screen? [21c]
	je nout1		; Yes, just leave. [21c]
	push ax
	push dx
	mov temp,10		; Divide quotient by 10.
;	cwd			; Convert word to doubleword.
	mov dx,0		; High order word should be zero.
	div temp		; AX <-- Quo, DX <-- Rem.
	cmp ax,0		; Are we done?	
	jz nout0		; Yes.
	call nout		; If not, then recurse.
nout0:	add dl,'0'		; Make it printable.
	mov temp,ax
	mov ah,conout
	int dos	
	mov ax,temp
	pop dx
	pop ax
nout1:	ret			; We're done. [21c]
NOUT	ENDP

; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.
 
RSKP	PROC	NEAR
	pop bp
	add bp,3
	push bp
	ret
RSKP	ENDP
 
; Jumping here is the same as a ret.
 
R	PROC	NEAR
	ret
R	ENDP

code	ends 
	end
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 mssend.asm
	/bin/echo -n '	'; /bin/ls -ld mssend.asm
fi
/bin/echo 'Extracting msserv.asm'
sed 's/^X//' <<'//go.sysin dd *' >msserv.asm
	public	logout, bye, finish, remote, get, server
	include	msdefs.h

datas	segment	public 'datas'
	extrn	data:byte, flags:byte, trans:byte, pack:byte, curchk:byte
	extrn	fcb:byte

remcmd	db	0		; Remote command to be executed. [21c]
rempac	db	0		; Packet type: C (host) or G (generic). [21c] 

cmer05	db	cr,lf,'?Filename must be specified$'	 ; [21a]
ermes7  db      '?Unable to receive initiate$'
erms18	db	cr,lf,'?Unable to tell host that session is finished$'
erms19	db	cr,lf,'?Unable to tell host to logout$'
erms21  db      cr,lf,'?Unable to tell host to execute command$' ; [21c]
infms1	db	'Entering server mode',cr,lf,'$'
remms1	db	'Kermit-MS: Unknown server command$'
remms2	db	'Kermit-MS: Illegal file name$'
remms3	db	'Kermit-MS: Unknown generic command$'
pass	db	lf,cr,' Password: $' 	; When change directory. [21c]
crlf    db      cr,lf,'$'
tmp	db	?,'$'
temp	dw	0
oloc	dw	0		; Original buffer location. [21c]
osiz	dw	0		; Original buffer size. [21c]
inpbuf	dw	0		; Pointer to input buffer. [21c]
cnt	dw	0
delinp	db	BS,BS,BS,'   ',BS,BS,BS,'$'	; When DEL key is used. [21d]
clrspc  db      ' ',10O,'$'             ; Clear space.

srvchr	db	'SRGIE'		; server cmd characters
srvfln	equ	$-srvchr	; length of tbl
srvfun	dw	srvsnd,srvrcv,srvgen,srvini,serv1

remhlp	db	cr,lf,'CWD connect to a directory'	; [21c start]
	db	cr,lf,'DELETE a file'
	db	cr,lf,'DIRECTORY listing'
	db	cr,lf,'HELP'
	db	cr,lf,'HOST command'
	db	cr,lf,'SPACE in a directory'
	db	cr,lf,'TYPE a file$'			; [21c end]

remtab	db	07H		; Seven entries. [21c start]
	mkeyw	'CWD',remcwd
	mkeyw	'DELETE',remdel
	mkeyw	'DIRECTORY',remdir
	mkeyw	'HELP',remhel
	mkeyw	'HOST',remhos
	mkeyw	'SPACE',remdis
	mkeyw	'TYPE',remtyp		; [21c end]

remfnm	db	' Remote Source File: $'
lclfnm	db	' Local Destination File: $'
filhlp	db	' File name to receive as$'
filmsg	db	' Remote file specification or confirm with carriage return $'
frem	db	' Name of file on remote system $'
genmsg	db	' Enter text to be sent to remote server $'
rdbuf	db	80H DUP(?)
datas	ends

code	segment	public
	extrn comnd:near, serrst:near, spack:near, rpack5:near, init:near
	extrn read12:near, serini:near, read2:near, rpar:near, spar:near
	extrn rin21:near, rfile3:near, error1:near, clrfln:near
	extrn dodel:near, clearl:near, dodec: near, doenc:near
	extrn packlen:near, send11:near, errpack:near, init1:near
	extrn rpack:near,nak:near, rrinit:near, cmblnk:near
	extrn error:near, erpos:near, rprpos:near, clrmod:near
	extrn prompt:near
	assume	cs:code,ds:datas

; LOGOUT - tell remote KERSRV to logout.

LOGOUT	PROC	NEAR
	mov ah,cmcfm
	call comnd		; Get a confirm.
	 jmp r
	call logo
	 jmp rskp		; Go get another command whether we ....
	jmp rskp		; .... succeed or fail.
LOGOUT	ENDP

LOGO	PROC	NEAR
	mov pack.numtry,0	; Initialize count.
	mov pack.numrtr,0	; No retries yet.
	call serini		; Initialize port.  [14]
	mov ah,trans.chklen	; Don't forget the checksum length.
	mov curchk,ah
	mov trans.chklen,1	; Use one char for server functions.
logo1:	cmp pack.state,'A'	; Did user type a ^C?
	je logo2x		; Yes just leave.
	mov ah,pack.numtry
	cmp ah,maxtry		; Too many times?
	js logo3		; No, try it.
logo2:	mov ah,prstr
	mov dx,offset erms19
	int dos
logo2x:	call serrst		; Reset port.  [14]
	mov ah,curchk
	mov trans.chklen,ah	; Restore value.
	ret
logo3:	inc pack.numtry		; Increment number of tries.
	mov pack.argblk,0	; Packet number zero.
	mov pack.argbk1,1	; One piece of data.
	mov bx,offset data
	mov ah,'L'
	mov [bx],ah		; Logout the remote host.
	mov cx,1		; One piece of data.
	call doenc		; Do encoding.
	mov ah,'G'		; Generic command packet.
	call spack
	 jmp logo2		; Tell user and die.
	 nop
	call rpack5		; Get ACK (w/o screen msgs.)
	 jmp logo1		; Go try again.
	 nop
	push ax
	call dodec		; Decode packet.
	mov ah,curchk
	mov trans.chklen,ah	; Restore value.
	pop ax
	cmp ah,'Y'		; ACK?
	jne logo4
	call serrst		; Reset port.  [14]
	jmp rskp
logo4:	cmp ah,'E'		; Error packet?	
	jnz logo1		; Try sending the packet again.
	call error1
	call serrst		; Reset port.  [14]
	ret
LOGO	ENDP

; FINISH - tell remote KERSRV to exit.

FINISH	PROC	NEAR
	mov ah,cmcfm		; Parse a confirm.
	call comnd
	 jmp r
	mov pack.numtry,0	; Initialize count.
	mov pack.numrtr,0	; No retries yet.
	call serini		; Initialize port.  [14]
	mov ah,trans.chklen	; Don't forget the checksum length.
	mov curchk,ah
	mov trans.chklen,1	; Use one char for server functions.
fin1:	cmp pack.state,'A'	; ^C typed?
	je fin2x
	mov ah,pack.numtry
	cmp ah,maxtry		; Too many times?
	js fin3			; Nope, try it.
fin2:	mov ah,prstr
	mov dx,offset erms18
	int dos
fin2x:	call serrst		; Reset port.  [14]
	mov ah,curchk
	mov trans.chklen,ah	; Restore value.
	jmp rskp		; Go home.
fin3:	inc pack.numtry		; Increment number of tries.
	mov pack.argblk,0	; Packet number zero.
	mov pack.argbk1,1	; One piece of data.
	mov bx,offset data
	mov ah,'F'
	mov [bx],ah		; Finish running Kermit.
	mov cx,1		; One piece of data.
	call doenc		; Do encoding.
	mov ah,'G'		; Generic command packet.
	call spack
	 jmp fin2		; Tell user and die.
	 nop
	call rpack5		; Get ACK (w/o screen stuff).
	 jmp fin1		; Go try again.
	 nop
	push ax
	call dodec		; Decode data.
	mov ah,curchk
	mov trans.chklen,ah	; Restore value.
	pop ax
	cmp ah,'Y'		; Got an ACK?
	jnz fin4
	call serrst		; Reset port. [14]
	jmp rskp		; Yes, then we're done.
fin4:	cmp ah,'E'		; Error packet?
	jnz fin1		; Try sending it again.
	call error1
	call serrst		; Reset port.  [14]
	jmp rskp
FINISH	ENDP

; BYE command - tell remote KERSRV to logout & exits to DOS.  

BYE	PROC	NEAR
	mov ah,cmcfm		; Parse a confirm.
	call comnd
	 jmp r
	call logo		; Tell the mainframe to logout.
 	 jmp rskp		; Failed - don't exit.
	mov flags.extflg,1	; Set exit flag.
	jmp rskp					; [8 end]
BYE	ENDP

; Tell remote server to send the specified file(s).

get	PROC	NEAR
	mov flags.droflg,0	; Reset flags from fn parsing.
	mov flags.nmoflg,0	; Reset flags from fn parsing.
	mov flags.cxzflg,0	; no ctl-c typed yet...
	mov bx,offset data	; Where to put text.  [8 start]
	mov dx,offset filmsg	; In case user needs help.
	mov ah,cmtxt
        call comnd              ; Get text or confirm.
         jmp r			; Fail. 
	cmp ah,0		; Read in any chars?
	jne get4		; Yes, then OK.
; empty line, ask for file names
get1:	mov dx,offset remfnm	; ask for remote first
	call prompt
	mov bx,offset data
	mov dx,offset frem
	mov ah,cmtxt
	call comnd		; get a line of text
	 jmp r
	cmp flags.cxzflg,'C'	; ctl-C typed?
	jne get2		; no, continue
	jmp rskp
get2:	cmp ah,0
	je get1		; ignore empty lines
	mov bl,ah
	mov bh,0
	mov byte ptr data[bx],'$' ; terminate name for printing
	mov pack.argbk1,bx	; remember length here
	mov dx,offset lclfnm
	call prompt
	mov ah,cmifi
	mov bx,offset filhlp
	mov dx,offset fcb
	call comnd
	 jmp r
	mov ah,cmcfm
	call comnd
	 jmp r
	cmp flags.cxzflg,'C'	; control-C typed?
	jne get3		; no, keep going
	jmp rskp
get3:	mov flags.nmoflg,1	; remember changed name
	jmp short get5
get4:	mov al,ah
	mov ah,0
	mov pack.argbk1,ax	; Remember number of chars we read.
	mov byte ptr [bx],'$'	; use for printing.
get5:	cmp flags.remflg,0	; remote mode?
	jne get6		; yes, don't print anything
	call init		; Clear line and initialize buffers.
	call clrfln		; Prepare to print filename.
	mov ah,prstr
	mov dx,offset data	; Print file name.
	int dos
get6:	call init1		; init buffers
	mov pack.numtry,0	; Initialize count.
	mov pack.numrtr,0	; No retries yet.
	mov pack.state,'R'	; this is what state will soon be...
	call serini		; Initialize port. 
	mov cx,pack.argbk1	; Data size.
	call doenc		; Encode data.
	mov ah,trans.chklen	; Don't forget the checksum length.
	mov curchk,ah
	mov trans.chklen,1	; Use one char for server functions.
get7:	cmp pack.state,'A'	; Did user type a ^C?
	je get9			; Yes - just return to main loop.
	mov ah,pack.numtry
	cmp ah,maxtry		; Too many times?
	jbe get10		; Nope, try it.
get8:	cmp flags.remflg,0	; remote mode?
	jne get9		; yes, no printing
	call erpos
	mov ah,prstr
	mov dx,offset ermes7	; Can't get init packet. 
	int dos
get9:	call serrst		; Reset port. 
	mov ah,curchk
	mov trans.chklen,ah	; Restore value.
	jmp rskp		; Go home.
get10:	inc pack.numtry		; Increment number of tries.
	mov pack.argblk,0	; Start at packet zero.
	mov ah,'R'		; Receive init packet.
	call spack		; Send the packet.
	 jmp get8		; Tell user we can't do it.
	 nop
	call rpack5		; Get ACK (w/o screen stuff).
	 jmp get7		; Got a NAK - try again.
	 nop
	push ax
	mov ah,curchk
	mov trans.chklen,ah	; Restore value.
	pop ax
	mov pack.argbk2,ax	; this is where rinit wants pkt type if getting
	mov flags.getflg,1	; "Get" as vs "Receive".
	jmp read12		; go join read code
get11:	mov ah,prstr		; Complain if no filename. 
	mov dx,offset cmer05
	int dos
	jmp rskp
GET	ENDP

; server command

server	proc	near
	mov	ah,cmcfm
	call	comnd
	 jmp	r
	push	es
	mov	ax,ds
	mov	es,ax		; address data segment
	mov	al,flags.remflg	; get remote flag
	push	ax		; preserve for later
	mov	flags.remflg,1	; set remote if server
	call	cmblnk		; clear screen
	mov	ah,prstr
	mov	dx,offset infms1
	int	dos
; should reset to default parms here...
; should increase timeout interval
serv1:	call	serini		; init serial line (send & recv reset it)
	mov	trans.chklen,1	; checksum len = 1
	mov	pack.pktnum,0	; pack number resets to 0
	mov	pack.numtry,0	; no retries yet.
	call	rpack		; get a packet
	 jmp	short serv2	; no good, nak and continue
	 nop
	jmp	short serv3	; try to figure this out
serv2:	cmp	flags.cxzflg,'C' ; ctl-C?
	je	serv5		; yes, stop this.
	call	nak		; nak the packet
	jmp	serv1		; and keep readiserv2 packets

serv3:	mov	di,offset srvchr ; server characters
	mov	cx,srvfln	; length of striserv2
	mov	al,ah		; packet type
	repne	scasb		; hunt for it
	je	serv4		; we know this one, go handle it
	mov	bx,offset remms1 ; else give a message
	call	errpack		; back to local kermit
	jmp	serv1		; and keep lookiserv2 for a cmd
serv4:	sub	di,offset srvchr+1 ; find offset, +1 for pre-increment
	shl	di,1		; convert to word index.
	call	srvfun[di]	; call the appropriate handler
	 jmp	serv5		; someone wanted to exit...
; should we reset serial line?
	jmp	serv1		; else keep goiserv2 for more cmds.

serv5:
;** restore timer values
	pop	ax		; get this off stack
	mov	flags.remflg,al	; restore old flag
	call	serrst		; reset serial handler
	pop	es		; restore register
	jmp	rskp		; and return
server	endp

; server commands.

; srvsnd - receives a file that the local kermit is sending.
srvsnd	proc	near
	mov	bx,offset data
	call	spar		; parse the send-init packet
	call	packlen		; figure max packet
	mov	bx,offset data
	call	rpar		; make answer for them
	mov	al,ah		; length of packet
	mov	ah,0
	mov	pack.argbk1,ax	; store length for spack
	mov	ah,'Y'		; ack
	call	spack		; answer them
	 jmp	rskp		; can't answer, forget this
	call	rrinit		; init variables for init
	inc	pack.pktnum	; count the send-init packet.
	mov	pack.state,'F'	; expecting file name about now
	call	read2		; and join read code
	 nop
	 nop
	 nop			; ignore errors
	jmp	rskp		; and return for more
srvsnd	endp

; srvrcv - send a file that they're receiving.
srvrcv	proc	near
	mov	si,offset data	; this should be filename
	mov	di,offset fcb	; this is where filename goes
	mov	al,1		; skip leading separators
	mov	ah,prsfcb	; parse an fcb
	int	dos		; let dos do the work
	cmp	al,0ffh		; invalid?
	jne	srvrc1		; no, keep going
	mov	bx,offset remms2 ; complain
	call	errpack		; that we can't find it
	jmp	rskp		; and return
srvrc1:	mov	pack.state,'R'	; remember state.
	call	send11		; this should send it
	 jmp	rskp
	jmp	rskp		; return in any case
srvrcv	endp

; srvgen - generic server commands.
; We only support Logout and Finish right now.
srvgen	proc	near
	mov	al,data		; get 1st packet char
	cmp	al,'F'		; maybe finish?
	je	srvge1		; yup, handle
	cmp	al,'L'		; logout?
	jne	srvge2		; no.
srvge1:	mov	pack.argbk1,0	; 0-length data
	mov	ah,'Y'
	call	spack		; ack it
	 nop
	 nop
	 nop			; *** ignore error?
	ret			; and return to signal exit.
srvge2:	mov	bx,offset remms3
	call	errpack
	jmp	rskp
srvgen	endp

; srvini - init parms based on init packet
srvini	proc	near
	mov	bx,offset data
	call	spar		; parse info
	call	packlen		; this should really be part of spar, but...
	mov	bx,offset data
	call	rpar		; get receive info
	mov	al,ah
	mov	ah,0
	mov	pack.argbk1,ax	; set size of return info
	mov	ah,'Y'
	call	spack		; send the packet off
	 jmp	rskp
	jmp	rskp		; and go succeed
srvini	endp

;       This is the REMOTE command. [21c]

REMOTE	PROC	NEAR
	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 appropriate routine.
	 jmp r			; Command failed.
	jmp rskp
REMOTE	ENDP

; REMDIS - Get disk usage on remote system. [21c]

REMDIS	PROC	NEAR
	mov remcmd,'U'		; Disk usage command.
	mov rempac,'G'		; Packet type = generic.
	jmp genric		; Execute generic Kermit command.
REMDIS	ENDP


; REMHEL - Get help about remote commands. [21c] 

REMHEL	PROC	NEAR
	mov remcmd,'H'		; Help......
	mov rempac,'G'		; Packet type = generic.
	jmp genric		; Execute generic Kermit command.
REMHEL	ENDP

; REMTYP - Print a remote file. [21c]

REMTYP	PROC	NEAR
	mov remcmd,'T'		; Type the file.
	mov rempac,'G'		; Packet type = generic.
	jmp genric
REMTYP	ENDP

; REMHOS - Execute a remote host command. [21c]

REMHOS	PROC	NEAR
	mov remcmd,' '		; Don't need one.
	mov rempac,'C'		; Packet type = remote command.
	jmp genric
REMHOS	ENDP

; REMDIR - Do a directory. [21c]

REMDIR	PROC	NEAR
	mov remcmd,'D'
	mov rempac,'G'		; Packet type = generic.
	jmp genric
REMDIR	ENDP

; REMDEL - Delete a remote file. [21c]

REMDEL	PROC	NEAR
 	mov remcmd,'E'
	mov rempac,'G'		; Packet type = generic.
	jmp genric
REMDEL	ENDP

; REMCWD - Change remote working directory.  [21c]

REMCWD	PROC	NEAR
	mov remcmd,'C'
	mov rempac,'G'		; Packet type = generic.
	jmp genric
REMCWD	ENDP

; GENRIC - Send a generic command to a remote Kermit server. [21c]

GENRIC	PROC	NEAR
	mov bx,offset rdbuf	; Where to put the text.
	cmp rempac,'C'		; Remote host command? 
	je genra		; Yes, leave as is. 
	add bx,2		; Leave room for type and size.
genra:	mov ah,cmtxt		; Parse arbitrary text up to a CR.
	mov dx,offset genmsg	; In case they want text.
	call comnd
	 jmp r
	mov al,ah		; Don't forget the size.
	mov ah,0
	mov cnt,ax		; Save it here.
	cmp rempac,'C'		; Remote host command? 
	jne genrb		; No, skip this part. 
	call ipack
	 jmp genr2
	mov pack.numtry,0
	mov ah,trans.chklen
	mov curchk,ah		; Save desired checksum length.
	mov trans.chklen,1	; Use 1 char for server functions.
	mov pack.numrtr,0	; No retries yet.
	jmp genr1		; Send the packet.
genrb:	mov ax,cnt
	cmp ax,0		; Any data?
	je genr0		; Nope.
	mov ah,al		; Don't overwrite the real count value.
	add ah,32		; Do the char function.
	mov temp,bx		; Remember where we are.
	mov bx,offset rdbuf+1	; Size of remote command.
	mov [bx],ah
	mov ah,0
	inc al			; For the size field.
	cmp remcmd,'C'		; Change working directory?
	jne genr0		; No, so don't ask for password.
	mov cnt,ax		; Save here for a bit.
	mov ah,prstr
	mov dx,offset pass	; Send along an optional password. 
	int dos
	mov bx,temp		; Where to put the password.
	push bx			; Is safe since subroutine never fails.
	inc bx			; Leave room for count field.
	call input		; Read in the password.
	mov temp,bx		; Remember end of data pointer.
	pop bx			; Where to put the size.
	cmp ah,0		; No password given?
	jne genrc
	mov ax,cnt
	jmp genr0		; Then that's it.
genrc:	mov al,ah
	add ah,32		; Make it printable.
	mov [bx],ah		; Tell remote host the size.
	mov ah,0
	push ax			; Remember the count.
	call clearl		; Clear to end-of-line.
	pop ax
	inc al			; For second count value.
	add ax,cnt		; Total for both fields of input.
genr0:	inc al			; For the char representing the command.
	mov pack.argbk1,ax	; Set the size.
	mov cnt,ax		; And remember it. 
	mov pack.numtry,0	; Initialize count
	mov bx,offset rdbuf	; Start of data buffer.	
	mov ah,remcmd		; Command subtype.
	mov [bx],ah
	call ipack		; Send init parameters.
	 jmp genr2
	 nop			; Make it 3 bytes long.
	mov ah,trans.chklen
	mov curchk,ah		; Save desired checksum length.
	mov trans.chklen,1	; Use 1 char for server functions.
	mov pack.numrtr,0	; No retries yet.
genr1:	cmp pack.state,'A'	; Did the user type a ^C?
	je genr2x
	mov ah,pack.numtry
	cmp ah,maxtry		; Too many tries?
	js genr3		; Nope, keep trying.
genr2:	mov ah,prstr
	mov dx,offset erms21	; Print error msg and fail.
	int dos
genr2x:	call serrst		; Reset the port.
	mov ah,curchk
	mov trans.chklen,ah	; Restore.
	jmp rskp
genr3:	push es			; Prepare to put string into packet. 
	mov ax,ds
	mov es,ax
	mov si,offset rdbuf	; Move from here
	mov di,offset data	; to here.
	mov cx,cnt		; Move this many characters.
	rep movsb		; Perform the string move.
	pop es
	mov ax,cnt
	mov pack.argbk1,ax	; How much data to send.
	mov cx,ax		; Size of data.
	call doenc		; Encode it.
	inc pack.numtry		; Increment number of trials.
        mov pack.argblk,0       ; Packet number 0.
	mov ah,rempac		; Packet type.
	call spack		; Send the packet.
	 jmp genr2		; Tell user we can't do it.
	 nop
	call rpack5		; Get ACK (w/o screen stuff)
	 jmp genr1		; Got a NAK - try again.
	 nop
	push ax
	mov ah,curchk
	mov trans.chklen,ah	; Restore.
	pop ax
	cmp ah,'Y'		; Is all OK?
	jne genr4
	cmp pack.argbk1,0	; Any data in the ACK?
	je genr31		; Nope - just return. 
	call dodec		; Decode data.
	mov ah,prstr
	mov dx,offset crlf	; First go to a new line.
	int dos	
	mov di,offset data	; Where the reply is.
	mov cx,pack.argbk1	; How much data we have. 
	call prtscr		; Print it on the screen.
genr31:	jmp rskp		; And we're done. 
genr4:	cmp ah,'X'		; Text packet?
	je genr5
	cmp ah,'S'		; Handling this like a file?
	jne genr6
	mov pack.state,'R'	; Set the state.
	mov bx,offset rin21	; Where to go to.
	jmp genr51		; Continue.
genr5:	mov pack.state,'F'
	call dodec		; Decode data.
	mov bx,offset rfile3	; Jump to here.
genr51:	mov tmp,ah		; Save packet type.
	mov flags.xflg,1	; Remember we saw an "X" packet.
	mov pack.numtry,0
	mov pack.numrtr,0
	mov pack.numpkt,0
	mov pack.pktnum,0
	mov flags.cxzflg,0
	mov ah,tmp		; Packet type.
	call bx			; Handle it almost like filename.
	call read2		; Receive the rest.
	 jmp r			; Oops, we failed.
	jmp rskp		; Done OK.
genr6:	cmp ah,'E'		; Error packet?
	je genr6x		
	jmp genr1		; Try again.
genr6x: call dodec		; Decode data.
	call error1		; Print the error messge.
	call serrst
	jmp rskp		; And return.
GENRIC	ENDP

; Send "I" packet with transmission parameters. [21c]

IPACK	PROC	NEAR
	mov ah,trans.chklen
	mov curchk,ah		; Initialize.
	call serini
	mov pack.pktnum,0	; Use packet number 0.
	mov pack.numtry,0	; Number of retries.
ipk0:   cmp pack.state,'A'	; Did user type a ^C?
	je ipk0x
	cmp pack.numtry,imxtry  ; Reached our limit?
        jl ipk1
ipk0x:	ret			; Yes, so we fail. 
ipk1:   inc pack.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 pack.argbk1,ax      ; Save the number of arguments.
        mov pack.argblk,0	; Use packet number 0.
	mov ah,trans.chklen
	mov curchk,ah		; Save real value.
	mov trans.chklen,1	; One char for server function.
        mov ah,'I'              ; "I" packet.
        call spack              ; Send the packet.
	 jmp ipk4
	 nop
        call rpack5             ; Get a packet.
         jmp ipk4               ; Try again.
	 nop
	push ax
	mov ah,curchk
	mov trans.chklen,ah	; Reset.
	pop ax
        cmp ah,'Y'              ; ACK?
        jne ipk3                ; If not try next.
        mov ax,pack.pktnum      ; Get the packet number.
        cmp ax,pack.argblk      ; Is it the right packet number?
        je ipk2
         jmp ipk0               ; If not try again.
ipk2:   mov ax,pack.argbk1      ; Get the number of pieces of data.
        mov bx,offset data      ; Pointer to the data.
        call spar               ; Read in the data.
	mov ah,trans.chklen
	mov curchk,ah		; This is what we decided on.
	call packlen		; Get max send packet size. [21b] 
        mov pack.numtry,0       ; Reset the number of tries.
        jmp rskp
ipk3:   cmp ah,'N'              ; NAK?
        je ipk0                 ; Yes, try again.
	cmp ah,'E'              ; Is it an error packet.
	je ipk3x
        jmp ipk0		; Trashed data. 
ipk3x:	jmp rskp		; Other side doesn't know about "I" packet.
ipk4:	mov ah,curchk
	mov trans.chklen,ah	; Reset.	
	jmp ipk0		; Keep trying.
IPACK	ENDP

; Returns in AH the count of characters read in.
;	  in BX the updated pointer to the input buffer.

INPUT	PROC	NEAR
	mov cl,0		; Keep a count.	
	mov inpbuf,bx		; Where to put data. 
input0:	mov ah,conin		; Read in a char.
	int dos
	cmp al,CR		; Done with input?
	jne input1
	mov ah,cl		; Return count in AH.
	jmp r
input1:	cmp al,BS		; Backspace?
	je inpt11		; 
	cmp al,DEL		; Or delete?
	jne input3
	call dodel		; Erase weird character.
inpt11:	dec cl			; Don't include in char count. 
	cmp cl,0		; Backspaced too much? 
	jns input2		; No, is OK.
	push bx
	call clearl
	pop bx	
	mov ah,conout
	mov dl,bell
	int dos
	mov cl,0
	jmp input0
input2:	dec bx			; 'Remove' from buffer.
	mov ah,prstr	
	mov dx,offset clrspc
	int dos
	jmp input0		; Go get more.
input3:	cmp al,'U'-64		; Control-U?
	jne input4
	mov ah,prstr
	mov dx,offset pass+1
	int dos	
	push bx
	push cx
	call clearl		; Blank out the line. 
	pop cx
	pop bx
	mov cl,0		; Reset count to zero.
	mov bx,inpbuf		; Start at head of buffer.
	jmp input0
input4:	cmp al,0		; Two character sequence?
	jne input5
	mov ah,conin
	int dos			; Get second char.
	cmp al,83		; Delete key?
	je inpt40		; Yup. 
	cmp al,75		; Backarrow key?
	je inpt40
	call dodel		; Erase weird character.
	jmp input0		; And go on computing.
inpt40:	mov ah,prstr
	mov dx,offset delinp	; Erase weird character. 
	int dos
	jmp inpt11		; Remove the offending char.
input5: mov [bx],al		; Add char to buffer.
	inc cl			; Include in count.
	inc bx
	jmp input0
INPUT	ENDP

; Print data onto the screen.  If text has no "$" in it, just print
; it.  Else, do special output for the "$".  
; Routine expects: DI = Start of buffer we are to print.
;                  CX = Number of characters to print.   [21c]

PRTSCR	PROC	NEAR
	mov al,'$'		; This is what we're looking for.
	mov oloc,di		; Remember original buffer address. 
	mov osiz,cx		; And original size. 
	push es
	mov bx,ds
	mov es,bx		; Have ES point to data area.
prts0:	repnz scasb		; Search for "$" in the buffer.
	cmp cx,0		; Found one?
	je prts1		; No, do a regular DOS call.
	mov ah,prstr
	mov dx,oloc		; Print up to the "$". 
	int dos
	mov ah,dconio
	mov dl,'$'
	int dos			; Print the "$"
	mov oloc,di		; New starting location.
	mov osiz,cx		; New size.
	jmp prts0
prts1:	mov bx,oloc		; The buffer location.
	add bx,osiz		; Point past the data.
	mov [bx],al		; Add "$" for printing.
	mov ah,prstr
	mov dx,oloc
	int dos
	pop es
	ret
PRTSCR	ENDP

; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.
 
RSKP    PROC    NEAR
	pop bp
	add bp,3
	push bp
        ret
RSKP    ENDP
 
; Jumping here is the same as a ret.
 
R       PROC    NEAR
        ret
R       ENDP

code	ends
	end
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msserv.asm
	/bin/echo -n '	'; /bin/ls -ld msserv.asm
fi
/bin/echo 'Extracting msset.asm'
sed 's/^X//' <<'//go.sysin dd *' >msset.asm
	public setcom, status, stat0, baudprt, escprt, prmptr, dodef
	public setcpt, docom, shomac, atoi
	include msdefs.h

setextra  equ	100
macmax	equ	20			; max # of macros

datas 	segment	public 'datas'
	extrn	comand:byte, flags:byte, trans:byte, cptfcb:byte, takadr:word
	extrn	taklev:byte, inichk:byte, portval:word, curdsk:byte
	extrn	setktab:byte, setkhlp:byte

kerm    db      'Kermit-MS>$'
crlf	db      cr,lf,'$'
crlfsp	db	cr,lf,' '	; crlf space
	db	'$'
eqs	db	' = $'
ermes1	db	cr,lf,'?Too many macros$'
ermes2	db	cr,lf,'?No room in table for macro$'
ermes3  db      cr,lf,'?Not confirmed$'
ermes4	db	cr,lf,'?No room in take stack to expand macro$'
ermes5	db	cr,lf,'?Not implemented$'
erms23	db	cr,lf,'?0 or null scan code not allowed$' ;[jd] 
erms24	db	cr,lf,'?Capture file already open (use close command)$' ;[jd]
filhlp	db	' Input file specification for session logging$'
macmsg	db	' Specify macro name followed by body of macro $'
shmmsg	db	' Confirm with carriage return $'
prmmsg	db	' Enter new prompt string $'
sk1msg	db	' Decimal scan code for key $'
sk2msg	db	' Redefinition string for key $'
prterr	db	'?Unrecognized value$'
unrec	db	'Baud rate is unknown$'
defpmp	db	'Definition string: $'
esctl	db	'Control-$'         ; [6]
nonmsg	db	'none$'
delmsg	db	'delete$'
onmsg	db	'On'
offmsg	db	'Off'
tmp	db	?,'$'
sum	db	0
min	db	0
max	db	0
desta	dw	0
numerr	dw	0
numhlp	dw	0
stflg	db	0		; Says if setting SEND or RECEIVE parameter.
srtmp	db	0
savsp	dw	0
temp	dw	0
temp1   dw      ?               ; Temporary storage.
temp2   dw      ?               ; Temporary storage.

locst   db      'Local echo $'
belon	db	'Ring bell after transfer$'
beloff	db	'No bell after transfer$'
vtemst  db	'HEATH-19 emulation $'
cm1st	db	'Communications port: 1$'
cm2st	db	'Communications port: 2$'
capmsg	db	'Session logging $'
eofmsg	db	'EOF mode: $'
flost	db	'No flow control used$'
floxmsg	db	'Flow control: XON/XOFF $'
handst	db	'Handshake used: $'
destst	db	'File destination: $'
diskst	db	'Default disk: $'
blokst	db	'Block check used: $'
ebyst	db	'8-bit quoting done only on request$'
ebvst	db	'8-bit quoting will be done with: $'
sqcst	db	'Send cntrl char prefix: $'
rqcst	db	'Receive cntrl char prefix: $'
debon	db	'Debug mode $'
flwon	db	'Warning $'
parmsg	db	'Parity $'
abfdst	db	'Discard incomplete file$'
abfkst	db	'Keep incomplete file$'
eolst	db	'End-of-line character: $'
ssohst	db	'Send start-of-packet char: $'
rsohst	db	'Receive start-of-packet char: $'
stimst	db	'Send timeout (seconds): $'
rtimst	db	'Receive timeout (seconds): $'
spakst	db	'Send packet size: $'
rpakst	db	'Receive packet size: $'
snpdst	db	'# of send pad chars: $'
rnpdst	db	'# of receive pad chars: $'
timmsg	db	'Timer $'
escmes  db      'Escape character: $'
b03st	db	'Baud rate is 300$'
b12st	db	'Baud rate is 1200$'
b18st	db	'Baud rate is 1800$'
b24st	db	'Baud rate is 2400$'
b48st	db	'Baud rate is 4800$'
b96st	db	'Baud rate is 9600$'
b04st	db	'Baud rate is 45.5$'
b05st	db	'Baud rate is 50$'
b07st	db	'Baud rate is 75$'
b11st	db	'Baud rate is 110$'
b13st	db	'Baud rate is 134.5$'
b15st	db	'Baud rate is 150$'
b06st	db	'Baud rate is 600$'
b20st	db	'Baud rate is 2000$'
b19st	db	'Baud rate is 19200$'
b38st	db	'Baud rate is 38400$'

eolhlp	db	cr,lf,'Decimal number between 0 and 31$'
eolerr	db	cr,lf,'Illegal end-of-line character$'
timerr	db	cr,lf,'Illegal timeout value$' 
timhlp	db	cr,lf,'Decimal number between 0 and 94$'
soherr	db	cr,lf,'Illegal start-of-packet character$'
quohlp	db	cr,lf,'Decimal number between 33 and 126$'
quoerr	db	cr,lf,'Illegal control character prefix$'
pakerr	db	cr,lf,'Illegal packet length$'
pakhlp	db	cr,lf,'Decimal number between 20 and 94$'
npderr	db	cr,lf,'Illegal number of pad characters$'
npdhlp	db	cr,lf,'Decimal number between 0 and 99$'
paderr	db	cr,lf,'Illegal pad character$'
padhlp	db	cr,lf,'Decimal number between 0 and 31 or 127$'
eschlp  db      cr,lf,'Enter literal value (ex: Cntrl ])  $'
desterr	db	cr,lf,'Illegal destination device$'
dskhlp	db	cr,lf,'Default disk drive to use, such as A:$'
dskerr	db	cr,lf,'Invalid drive specification$' 

sethlp	db      cr,lf,'BAUD rate'	
	db	cr,lf,'BELL'
	db	cr,lf,'BLOCK-CHECK-TYPE'
	db	cr,lf,'DEBUG'
	db	cr,lf,'DEFAULT-DISK'
	db	cr,lf,'DESTINATION'
	db	cr,lf,'END-OF-LINE character'
	db	cr,lf,'EOF CTRL-Z or NOCTRL-Z'
	db      cr,lf,'ESCAPE character change'
	db	cr,lf,'FLOW-CONTROL'
	db	cr,lf,'HANDSHAKE'
        db      cr,lf,'HEATH-19'
	db	cr,lf,'INCOMPLETE file'
	db	cr,lf,'KEY' 
        db      cr,lf,'LOCAL-ECHO echoing (half-duplex)'
	db	cr,lf,'PARITY type'
	db	cr,lf,'PORT for communication'
	db	cr,lf,'PROMPT'
	db	cr,lf,'RECEIVE parameter'
	db	cr,lf,'REMOTE on/off'
	db	cr,lf,'SEND parameter'
	db	cr,lf,'TAKE-ECHO' 
	db	cr,lf,'TIMER'
        db      cr,lf,'WARNING'
	db	'$'

settab  db      24
	mkeyw	'BAUD',baudst
	mkeyw	'BELL',bellst
	mkeyw	'BLOCK-CHECK-TYPE',blkset
	mkeyw	'DEBUG',debst
	mkeyw	'DEFAULT-DISK',dskset
	mkeyw	'DESTINATION',desset
	mkeyw	'END-OF-LINE',eolset
	mkeyw	'EOF',seteof
	mkeyw	'ESCAPE',escape
	mkeyw	'FLOW-CONTROL',floset
	mkeyw	'HANDSHAKE',hndset
	mkeyw	'HEATH19-EMULATION',vt52em
	mkeyw	'INCOMPLETE',abfset
	mkeyw	'KEY',setkey
	mkeyw	'LOCAL-ECHO',lcal
	mkeyw	'PARITY',setpar
	mkeyw	'PORT',comset
	mkeyw	'PROMPT',promset
	mkeyw	'RECEIVE',recset
	mkeyw	'REMOTE',remset
	mkeyw	'SEND',sendset
	mkeyw	'TAKE-ECHO',takset
	mkeyw	'TIMER',timset
	mkeyw	'WARNING',filwar

 
seoftab	db	2
	mkeyw	'CTRL-Z',1
	mkeyw	'NOCTRL-Z',0

stsrtb	db	06		; Number of options.
	mkeyw	'PACKET-LENGTH',srpack
	mkeyw	'PADCHAR',srpad
	mkeyw	'PADDING',srnpd
	mkeyw	'QUOTE',srquo
	mkeyw	'START-OF-PACKET',srsoh
	mkeyw	'TIMEOUT',srtim

ontab   db      02H             ; Two entries.
	mkeyw	'OFF',00H
	mkeyw	'ON',01H

destab	db	02H		; Two choices.
	mkeyw	'DISK',01H
	mkeyw	'PRINTER',00H

; What type of block check to use.
blktab	db	03H
	mkeyw	'1-CHARACTER-CHECKSUM',1
	mkeyw	'2-CHARACTER-CHECKSUM',2
	mkeyw	'3-CHARACTER-CRC-CCITT',3

; If abort when receiving files, can keep what we have or discard. [20d]

abftab	db	02H		; Only two options. 
	mkeyw	'DISCARD',01H
	mkeyw	'KEEP',00H

partab	db	05H		; Five entries.			[10 start]
	mkeyw	'EVEN',PAREVN
	mkeyw	'MARK',PARMRK
	mkeyw	'NONE',PARNON
	mkeyw	'ODD',PARODD
	mkeyw	'SPACE',PARSPC

flotab	db	2
	mkeyw	'NONE',flonon
	mkeyw	'XON/XOFF',floxon

hndtab	db	7
	mkeyw	'BELL',bell
	mkeyw	'CR',cr
	mkeyw	'ESC',esc
	mkeyw	'LF',lf
	mkeyw	'NONE',0
	mkeyw	'XOFF',xoff
	mkeyw	'XON',xon

BStab	db	02H			;Two entries [19c start]
	mkeyw	'BACKSPACE',00H
	mkeyw	'DELETE',01H

bdtab	db	010H		; 16 entries
	mkeyw	'110',b0110
	mkeyw	'1200',b1200
	mkeyw	'134.5',b01345
	mkeyw	'150',b0150
	mkeyw	'1800',b1800
	mkeyw	'19200',b19200
	mkeyw	'2000',b2000
	mkeyw	'2400',b2400
	mkeyw	'300',b0300
	mkeyw	'38400',b38400
	mkeyw	'45.5',b00455
	mkeyw	'4800',b4800
	mkeyw	'50',b0050
	mkeyw	'600',b0600
	mkeyw	'75',b0075
	mkeyw	'9600',b9600

ten	dw	10			; multiplier for setatoi
rdbuf	db	80H DUP(?)
prm	db	30 dup(0)		; Buffer for new prompt.
prmptr	dw	kerm			; pointer to prompt
defkw	db	100 dup (?)
macnum	dw	0			; one macro yet
mactab	dw	ibmmac			; default ibm mac is macro 0
	dw	macmax dup (?)		; empty macro table
defptr	dw	macbuf
macbuf	db	macmax*100 dup (?)	; buffer for macro defs
rmlft	db	setextra		; space left in set table
mcctab	db	1			; macro cmd table, one initially
	mkeyw	'IBM',0			; macro # 0
	db	setextra dup (?)	; room for more.

ibmmac	db	imlen-1
	db	'set timer on',cr,'set parity mark',cr
	db	'set local-echo on',cr,'set handshake xon',cr
	db	'set flow none',cr
imlen	equ	$-ibmmac

; structure for status information
stent	struc
sttyp	dw	?		; type (actually routine to call)
msg	dw	?		; message to print
val2	dw	?		; needed value: another message, or tbl addr
tstcel	dw	?		; address of cell to test, in data segment
basval	dw	0		; base value, if non-zero
stent	ends

sttab	stent	<onoff,vtemst,,flags.vtflg>
	stent	<onoff,locst,,ecoflg,portval>
	stent	<baudprt>
	stent	<srchkw,parmsg,partab,parflg,portval>
	stent	<onechr,escmes,,trans.escchr>
	stent	<onoff,capmsg,,flags.capflg>
	stent	<msg2,flost,floxmsg,floflg,portval>
	stent	<prhnd>
	stent	<srchkw,destst,destab,flags.destflg>
	stent	<drnum,diskst,,curdsk>
	stent	<onoff,flwon,,flags.flwflg>
	stent	<msg2,beloff,belon,flags.belflg>
	stent	<msg2,abfkst,abfdst,flags.abfflg>
	stent	<srchkw,eofmsg,seoftab,flags.eofcz>
	stent	<onechr,sqcst,,trans.rquote>
	stent	<onechr,rqcst,,trans.squote>
	stent	<onechr,rsohst,,trans.rsoh>
	stent	<onechr,ssohst,,trans.ssoh>
	stent	<stnum,rtimst,,trans.rtime>
	stent	<stnum,stimst,,trans.stime>
	stent	<stnum,rpakst,,trans.rpsiz>
	stent	<stnum,spakst,,trans.spsiz>
	stent	<stnum,snpdst,,trans.spad>
	stent	<stnum,rnpdst,,trans.rpad>
	stent	<onoff,timmsg,,flags.timflg>
	stent	<pr8bit>
	stent	<onechr,eolst,,trans.seol>
	stent	<srchkw,blokst,blktab,trans.chklen>
	stent	<msg2,cm2st,cm1st,flags.comflg>
	stent	<onoff,debon,,flags.debug>
	dw	0		; end of table
sttbuf	db	2000 dup (?)	; big buffer for status msg.
datas	ends

code	segment	public
	extrn cmcfrm:near, prserr:near, comnd:near, dobaud:near
	extrn cmgtch:near, repars:near, coms:near, vts:near, defkey:near
	extrn inicpt:near, prompt:near, nout:near, prtscr:near
	extrn prkey:near
	assume	cs:code,ds:datas

; This is the SET command.
 
SETCOM  PROC    NEAR
        mov dx,offset settab    ; Parse a keyword from the set table.
        mov bx,offset sethlp
        mov ah,cmkey
        call comnd
         jmp r
        call bx
	 nop
	 nop
	 nop
	jmp rskp
SETCOM	endp

docom	proc	near
	mov	dx,offset mcctab
	mov	bx,0
	mov	ah,cmkey
	call	comnd
	 jmp	r
	push	bx
	mov	ah,cmcfm
	call	comnd
	 pop	bx
	 ret
	 nop
	pop	bx
	cmp	taklev,maxtak		; room in take level?
	jl	docom2			; yes, continue
	mov	dx,offset ermes4	; else complain
	jmp	reterr
docom2:	inc	taklev			; increment take level (overflow)
	add	takadr,size takinfo
	shl	bx,1
	mov	si,mactab[bx]		; point to macro
	mov	cl,[si]			; get size from macro
	mov	ch,0
	inc	si			; point to actual definition
	mov	bx,takadr		; point to current buffer
	mov	[bx].takfcb,0ffh	; flag as a macro
	mov	[bx].takptr,si		; point to beginning of def
	mov	[bx].takchl,cl		; # of chars left in buffer
	mov	[bx].takcnt,cx		; and in definition
	mov	word ptr [bx].takcnt+2,0 ; zero high order...
	jmp	rskp
docom	endp
 
; the define command
dodef	proc	near
	cmp	macnum,macmax		; get current macro count
	jl	dode1			; no, go on
	mov	dx,offset ermes1	; else complain
	jmp	reterr			; and return

dode1:	mov	ah,cmtxt
	mov	bx,offset defkw+1	; buffer for keyword
	mov	dx,offset macmsg
	call	comnd
	 ret
	 nop
	 nop
	cmp	ah,0
	jne	dode2
	ret
dode2:	push	es
	mov	bx,ds
	mov	es,bx
	cld
	mov	cl,ah
	mov	ch,0			; length
	mov	si,offset defkw+1	; pointer to keyword
	mov	ah,0			; # of chars in keyword
; uppercase keyword, look for end
dode3:	lodsb				; get a byte
	cmp	al,'a'
	jb	dode4
	cmp	al,'z'
	ja	dode4
	sub	al,'a'-'A'
	mov	[si-1],al		; uppercase if necessary
dode4:	inc	ah			; increment word count
	cmp	al,' '			; is it the break character?
	loopne	dode3			; no, loop thru rest of word
dode5:	jne	dode6			; ended with break char?
	dec	ah			; yes, don't count in length
dode6:	mov	defkw,ah		; store length in front of it
	add	ah,4			; add keyword overhead length
	cmp	ah,rmlft		; will it fit in buffer
	jb	dode7			; yes, keep going
	mov	dx,offset ermes2	; else complain
	jmp	reterr

dode7:	sub	rmlft,ah		; subtract space used in tbl
	mov	di,defptr		; pointer to free space
	inc	macnum			; count the macro
	mov	bx,macnum
	shl	bx,1			; double for word idx!!!
	mov	mactab[bx],di		; install into table
	mov	[di],cl			; store length
	inc	di
	jcxz	dode10			; no copy if 0 length

; copy definition into buffer, changing commas to crs
dode8:	lodsb				; get a byte
	cmp	al,','			; comma?
	jne	dode9			; no, keep going
	mov	al,cr			; else replace with cr
dode9:	stosb
	loop	dode8			; keep copying

dode10:	mov	defptr,di		; update free ptr
	mov	bl,defkw
	mov	bh,0
	lea	di,defkw+1[bx]		; end of keyword
	mov	al,'$'
	stosb
	mov	ax,macnum
	stosb				; low-order
	mov	al,0			; high-order is always 0.
	stosb

; now install into table
	pop	es
	mov	bx,offset mcctab
	mov	dx,offset defkw
	call	addtab
	jmp	rskp
dodef	endp

; add an entry to a keyword table
; enter with bx/ table address, dx/ ptr to new entry
; no check is made to see if the entry fits in the table.
addtab	PROC	NEAR
	push	es
	cld
	mov	ax,ds
	mov	es,ax		; address data segment
	mov	bp,bx		; remember where tbl starts
	mov	cl,[bx]		; pick up length of table
	mov	ch,0
	inc	bx		; point to actual table...
addta1:	push	cx		; preserve count
	mov	si,dx		; point to entry
	lodsb			; get length of new entry
	mov	cl,[bx]		; and length of table entry...
	mov	ah,0		; assume they're the same size
	cmp	al,cl		; are they the same?
	lahf			; remember result of comparison...
	jae	addta2		; is new smaller? no, use table length
	mov	cl,al		; else use length of new entry
addta2:	mov	ch,0
	lea	di,[bx+1]	; point to actual keyword
	repe	cmpsb		; compare strings
	pop	cx		; restore count
	jb	addta4		; below, insert before this one
	jne	addta3		; not below or same, keep going
	sahf			; same. get back result of length comparison
	jb	addta4		; if new len is smaller, insert here
	jne	addta3		; if not same size, keep going
	mov	si,bx		; else this is where entry goes
	jmp	short addta6	; no insertion required...
addta3:	mov	al,[bx]
	mov	ah,0
	add	bx,ax		; skip this entry
	add	bx,4		; len + $ + value...
	loop	addta1		; and keep looking
addta4:	mov	si,bx		; this is first location to move
	mov	di,bx
	inc	ds:byte ptr [bp] ; remember we're adding one...
	jcxz	addta6		; no more entries, forget this stuff
	mov	bh,0		; this stays 0
addta5:	mov	bl,[di]		; get length
	lea	di,[bx+di+4]	; end is origin + length + 4 for len, $, value
	loop	addta5		; loop thru remaining keywords
	mov	cx,di
	sub	cx,si		; compute # of bytes to move
	push	si		; preserve loc for new entry
	mov	si,di		; first to move is last
	dec	si		; minus one
	mov	di,dx		; new entry
	mov	bl,[di]		; get length
	lea	di,[bx+si+4]	; dest is source + length of new + 4
	std			; move backwards
	rep	movsb		; move the table down
	cld			; put flag back
	pop	si
addta6:	mov	di,si		; this is where new entry goes
	mov	si,dx		; this is where it comes from
	mov	cl,[si]		; length
	mov	ch,0
	add	cx,4		; overhead bytes
	rep	movsb		; stick it in
	pop	es
	ret			; and return
addtab	endp

; Show defined macros.
SHOMAC	PROC	NEAR
	mov ah,cmtxt
	mov bx,offset rdbuf
	mov dx,offset shmmsg
	call comnd
	 jmp r 
	cmp ah,0		; Bare CR means show all macros.
	jne shom2		; No, he wants specific macro expanded.
	mov si,offset mcctab	; Table of macro names.
	lodsb
	mov cl,al		; Number of macro entries.
	mov ch,0
shom0:	jcxz shom1		; Done if none left to display.
	lodsb			; Length of macro name.
	push ax			; Don't forget it.
	mov ah,prstr
	mov dx,offset crlfsp	; Go to new line.
	int dos
	mov dx,si		; Print macro name.
	int dos
	mov dx,offset eqs
	int dos
	pop ax
	mov ah,0
	add si,ax		; Skip over name.
	inc si			; Get to macro number.
	mov bx,[si]		; Pick it up.
	call expmac		; Expand the macro.
	dec cx
	add si,2		; Skip over macro number.
	jmp shom0		; And do the rest.
shom1:	mov ah,prstr
	mov dx,offset crlf
	int dos
	jmp rskp
shom2:	mov ah,prstr
	mov dx,offset ermes3
	int dos
	jmp rskp
SHOMAC	ENDP

; Expand the macro, called with BX/macro number.
expmac:	push si
	push cx
	mov si,offset mactab	; Table of address expansions.
	shl bx,1		; Double and use as index into table.
	mov si,[si+bx]		; Get address of expansion in question.
	mov ax,si		; Address of string.
	inc ax			; Don't print length.
	mov cl,[si]		; Length of string.
	mov ch,0
	call prkey		; Print it.
	pop cx
	pop si
	ret

seteof	proc	near
	mov ah,cmkey
	mov bx,0
	mov dx,offset seoftab
	call comnd
	 jmp r
	push bx
	mov ah,cmcfm
	call comnd
	 jmp seteo1		; error return...
	 nop
	pop bx
	mov flags.eofcz,bl	; set value
	jmp rskp		; and return
seteo1:	pop bx
	ret
seteof	endp

;       This is the ESCAPE character SET subcommand.     [6 start]
 
ESCAPE  PROC    NEAR
	call cmgtch		; Get a char.
	cmp ah,0
	jns es1			; Terminator or no?
	and ah,7FH		; Turn off minus bit.
	cmp ah,'?'
	jne es0
	mov dx,offset eschlp
	mov ah,prstr
	int dos
	mov dx,offset crlf
	int dos
	mov dx,comand.cmprmp
	int dos
	mov bx,comand.cmdptr
	mov al,'$'
	mov [bx],al
	mov dx,offset comand.cmdbuf
	int dos
	dec comand.cmcptr		; Ignore dollar sign.
	dec comand.cmccnt
	mov comand.cmaflg,0
	jmp repars
es0:	mov ah,prstr
	mov dx,offset ermes3
	int dos
	ret
es1:  	mov temp,ax
	call cmcfrm
	 jmp es0
	 nop			; Take up 3 bytes.
	mov ax,temp
	mov trans.escchr,ah	; Save new value.
	ret
ESCAPE  ENDP			; [6 end]

; 	This is the End-of-line character SET subcommand.

EOLSET	PROC	NEAR
	mov min,0
	mov max,1FH
	mov sum,0
	mov tmp,10
	mov temp1,0
	mov desta,offset trans.seol
	mov numhlp,offset eolhlp
	mov numerr,offset eolerr
	jmp num0		; Common routine for parsing numerical input.
EOLSET	ENDP

num0:	call cmgtch		; Get the first char into AH.
	cmp ah,0
	js num1
	cmp ah,'0'
	jl num1
	cmp ah,'9'
	ja num1
	mov temp1,1
	sub ah,30H
	mov dl,ah
	mov al,sum
	mul tmp
	add al,dl
	mov sum,al
	jmp num0
num1:	and ah,7FH
	cmp ah,CR
	jne num2
	cmp temp1,0
	je num21
	mov al,sum
	cmp al,min
	jl num3
	cmp al,max
	jg num3
	mov bx,desta
	mov [bx],al
	ret
num2:	cmp ah,03FH		; Question mark?
	je num4
num21:	mov ah,prstr
	mov dx,offset ermes3
	int dos
	jmp prserr
num3:	mov ah,prstr
	mov dx,numerr
	int dos
	jmp prserr
num4:	mov ah,prstr
	mov dx,numhlp
	int dos
	mov dx,offset crlf
	int dos
	mov dx,comand.cmprmp
	int dos
	mov bx,comand.cmdptr
	mov al,'$'
	mov [bx],al
	mov dx,offset comand.cmdbuf
	int dos
	dec comand.cmcptr		; Don't count the dollar sign.
	dec comand.cmccnt		; Or the question mark.
	mov comand.cmaflg,0		; Check for more input.
	jmp repars
 
;       This is the LOCAL echo SET subcommand.
 
LCAL    PROC    NEAR
        mov dx,offset ontab
        mov bx,0
        mov ah,cmkey
        call comnd
         jmp r
        push bx                 ; Save the parsed value.
        mov ah,cmcfm
        call comnd              ; Get a confirm.
         jmp lcl0		;  Didn't get a confirm.
	 nop
        pop bx
	mov si,portval
        mov [si].ecoflg,bl     ; Set the local echo flag.
	mov [si].hndflg,bl	; This goes on/off with local echo.
	xor bl,01		; Toggle this.
	mov [si].floflg,bl	; This is the opposite.
	ret
lcl0:	pop bx
	ret
LCAL    ENDP
 
;       This is the VT52 emulation SET subcommand.
 
VT52EM  PROC    NEAR
	call vts
	ret
VT52EM  ENDP
 
; This is the SET subcommand to choose between COM1 and COM2. [19b]
  
COMSET  PROC    NEAR
	call coms
	ret
COMSET  ENDP

FILWAR  PROC    NEAR
        mov dx,offset ontab
        mov bx,0
        mov ah,cmkey
        call comnd
         jmp r
        push bx
        mov ah,cmcfm
        call comnd              ; Get a confirm.
         jmp fil0		; Didn't get a confirm.
	 nop
        pop bx
        mov flags.flwflg,bl     ; Set the filewarning flag.
	ret
fil0:	pop bx
	ret
FILWAR  ENDP

;       This is the SET aborted-file command.  [20d]
 
ABFSET  PROC    NEAR
        mov dx,offset abftab
        mov bx,0
        mov ah,cmkey
        call comnd
         jmp r
        push bx
        mov ah,cmcfm
        call comnd              ; Get a confirm.
         jmp abf0		;  Didn't get a confirm.
	 nop
        pop bx
        mov flags.abfflg,bl     ; Set the aborted file flag.
	ret
abf0:	pop bx
	ret
ABFSET  ENDP

;       This is the SET Parity command.				[10 start]
 
SETPAR  PROC    NEAR
        mov dx,offset partab
        mov bx,0
        mov ah,cmkey
        call comnd
         jmp r
        push bx
        mov ah,cmcfm
        call comnd              ; Get a confirm.
         jmp par0		;  Didn't get a confirm.
	 nop
        pop bx
	mov si,portval
        mov [si].parflg,bl	; Set the parity flag.
	cmp bl,parnon		; Resetting parity to none? [21b]
	je setp0		; Yes, reset 8 bit quote character. [21b]
	mov trans.ebquot,dqbin	; Else, do quoting.  [21b]
	ret			; That's it.  [21b]
setp0:	mov trans.ebquot,'Y'	; If none, say will quote upon request. [21b]
	ret
par0:	pop bx
	ret
SETPAR  ENDP							; [10 end]

; Sets debugging mode on and off.

DEBST	PROC    NEAR
        mov dx,offset ontab
        mov bx,0
        mov ah,cmkey
        call comnd
         jmp r
        push bx
        mov ah,cmcfm
        call comnd              ; Get a confirm.
         jmp deb0		; Didn't get a confirm.
	 nop
        pop bx
        mov flags.debug,bl	; Set the DEBUG flag.
	ret
deb0:	pop bx
	ret
DEBST   ENDP

; Turn bell on or off.    [17a start]

BELLST	PROC	NEAR
	mov dx,offset ontab
	mov bx,0
	mov ah,cmkey
	call comnd
	 jmp r
	push bx
	mov ah,cmcfm
	call comnd
	 jmp bel0
	 nop
	pop bx
	mov flags.belflg,bl
	ret
bel0:	pop bx
	ret
BELLST	ENDP                      ;  [17a end]

; Toggle echo'ing of TAKE file to be either ON or OFF.
TAKSET	PROC	NEAR
	mov dx,offset ontab
	mov bx,0
	mov ah,cmkey
	call comnd
	 jmp r
	push bx
	mov ah,cmcfm
	call comnd
	 jmp tak0
	 nop
	pop bx
	mov flags.takflg,bl
	ret
tak0:	pop bx
	ret
TAKSET	ENDP                      ;  [17a end]

; Set timer ON/OFF during file transfer.
TIMSET	PROC	NEAR
	mov dx,offset ontab
	mov bx,0
	mov ah,cmkey
	call comnd
	 jmp r
	push bx
	mov ah,cmcfm
	call comnd
	 jmp tim0
	 nop
	pop bx
	mov flags.timflg,bl
	ret
tim0:	pop bx
	ret
TIMSET	ENDP                      ;  [17a end]

; Allow user to change the "Kermit-MS>" prompt.
PROMSET	PROC	NEAR
	mov ah,cmtxt
	mov bx,offset prm		; Read in the prompt.
	mov dx,offset prmmsg
	call comnd
	 jmp r
	cmp ah,0			; Just a bare CR?
	jne prom0
	mov ax,offset kerm
	jmp prom1
prom0:	mov byte ptr [bx],'$'		; End of string.
	mov ax,offset prm
prom1:	mov prmptr,ax			; Remember it.
	jmp rskp
PROMSET	ENDP

; Set Flow-Control subcommand.
FLOSET	PROC	NEAR
        mov dx,offset flotab
  	xor bx,bx
        mov ah,cmkey
        call comnd
         jmp r
        push bx
        mov ah,cmcfm
        call comnd              ; Get a confirm.
         jmp flox		; Didn't get a confirm.
	 nop
        pop bx
	mov si,portval
	mov [si].flowc,bx	; Flow control value.
	cmp bx,0		; Turning it off?
	je flo0			; Yes.
        mov [si].floflg,1	; Say we're doing flow control.
	mov [si].hndflg,0	; So don't do handshaking.
	ret
flo0:	mov [si].floflg,bl	; Say we're not doing flow control.
	ret
flox:	pop bx
	ret
FLOSET	ENDP

; Set Handshake subcommand.
HNDSET	PROC	NEAR
        mov dx,offset hndtab
        mov bx,0
        mov ah,cmkey
        call comnd
         jmp r
        push bx
        mov ah,cmcfm
        call comnd              ; Get a confirm.
         jmp hndx		; Didn't get a confirm.
	 nop
        pop bx
	mov si,portval
	cmp bl,0		; Setting handshake off?
	je hnd0			; Yes.
	mov [si].floflg,0	; Else, turn flow control off.
	mov [si].hndflg,1	; And turn on handshaking.
	mov [si].hands,bl	; Use this char as the handshake.
	ret
hnd0:	mov [si].hndflg,0	; No handshaking.
	mov [si].floflg,1	; If one is off, the other is on.
	ret
hndx:	pop bx
	ret
HNDSET	ENDP

; Set block check type sub-command.
BLKSET	PROC	NEAR
        mov dx,offset blktab
        mov bx,0
        mov ah,cmkey
        call comnd
         jmp r
        push bx
        mov ah,cmcfm
        call comnd              ; Get a confirm.
         jmp blk0		; Didn't get a confirm.
	 nop
        pop bx
	mov trans.chklen,bl	; Use this char as the handshake.
	mov inichk,bl		; Save here too.
	ret
blk0:	pop bx
	ret
BLKSET	ENDP

; Set destination for incoming file.
DESSET	PROC	NEAR
        mov dx,offset destab
        mov bx,0
        mov ah,cmkey
        call comnd
         jmp r
        push bx
        mov ah,cmcfm
        call comnd              ; Get a confirm.
         jmp des0		; Didn't get a confirm.
  	 nop
        pop bx
        mov flags.destflg,bl	; Set the destination flag.
	ret
des0:	pop bx
	ret
DESSET	ENDP

; Set default disk for sending/receiving, etc.
DSKSET	PROC	NEAR
	mov comand.cmcr,1	; Don't want filename specified.
	mov ah,cmifi		; Parse for drive specification.
	mov dx,offset rdbuf	; Read into handy buffer.
	mov bx,offset dskhlp	; Text of help message.
	call comnd
	 jmp r
	mov ah,cmcfm
	call comnd
	 jmp r
	cmp flags.nmoflg,0	; Fail if specified file name.
	je dsk1
dsk0:	mov ah,prstr
	mov dx,offset dskerr	; Illegal drive specification.
	int dos
	ret
dsk1:	mov bx,offset rdbuf
	mov ah,[bx]		; Get the drive they said to use.
	cmp ah,0		; Did they type a bare CR?
	je dsk0			; Yes, complain.
	mov curdsk,ah		; And remember it.
	dec ah
	mov dl,ah
	mov ah,seldsk
	int dos
	ret
DSKSET	ENDP	

;  This function sets the baud rate.

BAUDST  PROC    NEAR
        mov dx,offset bdtab
        mov bx,0
        mov ah,cmkey
        call comnd
         jmp r
	push bx
	mov ah,cmcfm
	call comnd		; Get a confirm.
	 jmp bau0		; Didn't get one.
 	 nop
	pop bx
	mov si,portval
	mov ax,[si].baud	; Remember original value. [25]
	mov [si].baud,bx	; Set the baud rate.
	call dobaud		; Use common code. [19a] 
	ret
bau0:	pop bx
	ret
BAUDST  ENDP

SENDSET	PROC	NEAR
	mov stflg,'S'		; Setting SEND parameter 
sndst0: mov dx,offset stsrtb    ; Parse a keyword.
        mov bx,0
        mov ah,cmkey
        call comnd
         jmp r
        call bx
	 nop
	 nop
	 nop
	jmp rskp
SENDSET	ENDP

recset:	mov stflg,'R'		; Setting RECEIVE paramter.
	jmp sndst0

remset	proc	near
	mov	ah,cmkey
	mov	dx,offset ontab
	mov	bx,0
	call	comnd
	 jmp	r
	push	bx		; save parsed value
	mov	ah,cmcfm
	call	comnd		; confirm
	 pop	bx
	 ret			; return on failure
	 nop
	pop	bx
	mov	flags.remflg,bl	; set remote setting
	jmp	rskp		; and return
remset	endp

; Set send/receive start-of-header.
srsoh:	mov min,0
	mov max,1FH
	mov sum,0
	mov tmp,10
	mov desta,offset trans.ssoh	; Assume SEND.
	cmp stflg,'S'			; Setting SEND paramter?
	je srsoh0
	mov desta,offset trans.rsoh
srsoh0:	mov numhlp,offset eolhlp	; Reuse help message.
	mov numerr,offset soherr
	mov temp1,0
	jmp num0		; Common routine for parsing numerical input.

; Set send/receive timeout.
srtim:	mov min,0
	mov max,94
	mov sum,0
	mov tmp,10
	mov desta,offset trans.stime	; Assume SEND.
	cmp stflg,'S'			; Setting SEND paramter?
	je srtim0
	mov desta,offset trans.rtime
srtim0:	mov numhlp,offset timhlp	; Reuse help message.
	mov numerr,offset timerr
	mov temp1,0
	jmp num0		; Common routine for parsing numerical input.

; Set send/receive packet length.
srpack:	mov min,20
	mov max,94
	mov sum,0
	mov tmp,10
	mov desta,offset trans.spsiz
	cmp stflg,'S'		; Setting SEND paramter?
	je srpak0
	mov desta,offset trans.rpsiz
srpak0:	mov numhlp,offset pakhlp
	mov numerr,offset pakerr
	mov temp1,0
	jmp num0		; Parse numerical input.

; Set send/receive number of padding characters.
srnpd:	mov min,0
	mov max,99
	mov sum,0
	mov tmp,10
	mov desta,offset trans.spad
	cmp stflg,'S'		; Setting SEND paramter?
	je srnpd0
	mov desta,offset trans.rpad
srnpd0:	mov numhlp,offset npdhlp
	mov numerr,offset npderr
	mov temp1,0
	jmp num0		; Parse numerical input.

; Set send/receive padding character.
srpad:	mov min,0
	mov max,127
	mov sum,0
	mov tmp,10
	mov srtmp,0FFH		; Haven't seen anything yet.
	mov desta,offset srtmp
	mov numhlp,offset padhlp
	mov numerr,offset paderr
	mov temp1,0
	mov savsp,sp
	call num0		; Parse numerical input.
	mov sp,savsp
	mov temp,offset trans.spadch
	cmp stflg,'S'
	je srpad1
	mov temp,offset trans.rpadch
srpad1:	mov bx,offset srtmp
	mov ah,[bx]
	cmp ah,0FFH		; Did they end up not doing the command?
	je srpad3
	cmp ah,127		; This is allowed.
	je srpad2
	cmp ah,32
	jb srpad2		; Between 0 and 31 is OK too.
	mov ah,prstr
	mov dx,offset paderr
	int dos
	ret
srpad2:	mov bx,temp		; Set the real pad char.
	mov [bx],ah
srpad3:	ret

; Set send/receive control character prefix.
srquo:	mov min,33
	mov max,126
	mov sum,0
	mov tmp,10
	mov desta,offset trans.rquote	; Used for outgoing packets.
	cmp stflg,'S'			; Setting outgoing quote char?
	je srquo0	
	mov desta,offset trans.squote	; For incoming quote char.
srquo0:	mov numhlp,offset quohlp
	mov numerr,offset quoerr
	mov temp1,0
	jmp num0			; Parse numerical input.

;       This is the STATUS command.
 
STATUS  PROC    NEAR
        mov ah,cmcfm
        call comnd              ; Get a confirm.
         jmp r                  ;  Didn't get a confirm.
	mov dx,offset crlf
	mov ah,prstr
	int dos			; initial crlf
        call stat0
	mov cx,di		; End of buffer
	sub cx,ax		; Get length of buffer.
	dec cx			; Account for null.
	mov di,ax		; Buffer pointer.
	call prtscr		; Put data onto the screen. 
        jmp rskp
STATUS	ENDP
 
; Return a pointer to status message in AX, ptr to end in DI.

STAT0   PROC    NEAR
	push	es
	mov	ax,ds
	mov	es,ax		; address data segment
	cld			; make sure strings go the right way
	mov	di,offset sttbuf ; point to destination buffer
	mov	bx,offset sttab	; table to control printing
	mov	al,' '		; start with a space
	stosb			; in the buffer
	mov	ax,0		; need-new-line flag
stat01:	cmp	word ptr [bx],0	; end of table?
	je	stat02		; yes, exit routine
	push	bx
	push	di		; remember important values
	push	ax
	call	[bx].sttyp	; call the appropriate routine
	pop	ax
	pop	cx		; return buffer value
	pop	bx		; and ptr
	or	ax,ax		; do we need a newline?
	jne	stat03		; yes, go put one in
	sub	cx,di		; else see how many columns they used
	add	cx,40		; this is where we'd like to be
; if cx is negative here, we have a problem...
	mov	al,' '
	rep	stosb		; add right # of spaces
	mov	ax,1		; note we need a newline next time
	jmp	short stat04	; and keep looping around
stat03:	mov	cx,3
	mov	si,offset crlfsp
	rep	movsb		; append crlf to string
	xor	ax,ax		; reset newline flag
stat04:	add	bx,size stent	; advance to next one
	jmp	stat01
stat02:	mov	al,0		; end buffer
	stosb
	mov	ax,offset sttbuf
	pop	es		; restore this
	ret			; and return
STAT0	ENDP

; handler routines for status
; all are called with di/ destination buffer, bx/ stat ptr.  They
; can change any register but the segment registers, must update
; di to the end of the buffer.

; copy the message into the buffer
stmsg	proc	near
	mov	si,[bx].msg	; get message address
stms1:	lodsb			; get a byte
	stosb			; drop it off
	cmp	al,'$'		; end of message?
	jne	stms1		; no, keep going
	dec	di		; else back up ptr
	ret			; and return
stmsg	endp

; get address of test value in stent.  Returns address in si
stval	proc	near
	mov	si,[bx].basval	; get base value
	cmp	si,0		; any there?
	je	stva1		; no, keep going
	mov	si,[si]		; yes, use as base address
stva1:	add	si,[bx].tstcel	; add offset of test cell
	ret			; and return it
stval	endp

; print a single character
onechr	proc	near
	call	stmsg		; copy message part first
	call	stval		; pick up test value address
	mov	al,[si]		; this is char to print
	cmp	al,' '		; printable?
	jae	onech1		; yes, keep going
	add	al,64		; make printable.
	mov	byte ptr [di],'^'
	inc	di		; note ctrl char
onech1:	stosb			; drop char off
	ret			; and return
onechr	endp

; numeric field...
stnum	proc	near
	call	stmsg		; copy message
	call	stval		; pick up value address
	mov	al,[si]		; get value
	mov	ah,0		; high order is 0
	call	outnum		; put number into buffer
	ret			; and return
stnum	endp

; translate the number in ax...
outnum	proc	near
	cwd
	mov	bx,10
	div	bx		; divide to get digit
	push	dx		; save remainder digit
	or	ax,ax		; test quotient
	jz	outnu1		; zero, no more of number
	call	outnum		; else call for rest of number
outnu1:	pop	ax		; get digit back
	add	al,'0'		; make printable
	stosb			; drop it off
	ret			; and return
outnum	endp

; on/off field
onoff	proc	near
	call	stmsg		; copy message
	call	stval		; get value cell
	mov	al,[si]
	mov	si,offset onmsg
	mov	cx,2		; assume 2-byte 'ON' message
	or	al,al		; test value
	jnz	onof1		; on, have right msg
	mov	si,offset offmsg
	mov	cx,3
onof1:	rep	movsb		; copy right message in
	ret			; and return
onoff	endp

; print first message if false, second if true
msg2	proc	near
	call	stval		; get value cell
	mov	al,[si]
	mov	si,[bx].msg	; assume off
	or	al,al		; is it?
	jz	msg21		; yes, continue
	mov	si,[bx].val2	; else use alternate message
msg21:	jmp	stms1		; handle copy and return
msg2	endp

; search a keyword table for a value, print that value
srchkw	proc	near
	call	stmsg		; first print message
	call	stval
	mov	al,[si]		; get value to hunt for
	mov	ah,0		; high order is 0
	mov	bx,[bx].val2	; this is table address
	jmp	prttab		; and look in table.
srchkw	endp

; Print the drive name.
drnum	proc	near
	call	stmsg		; copy message part first
	call	stval		; pick up test value address
	mov	al,[si]		; this is char to print
	add	al,'@'		; Make it printable.
	stosb
	mov	byte ptr [di],':'
	inc	di		; end with a colon
	ret			; and return
drnum	endp

; print 8-bit quoting
pr8bit	proc	near
	mov	bl,trans.ebquot	; get quote char
	mov	si,offset ebyst	; assume no 8-bit quoting
	cmp	bl,'Y'		; on request only?
	je	pr8bi1		; yes, continue
	mov	si,offset ebvst	; else variable
pr8bi1:	call	stms1		; copy message in
	cmp	bl,'Y'		; not doing it?
	je	pr8bi2		; no, forget this part
	mov	[di],bl		; else drop off char too
	inc	di
pr8bi2:	ret			; and return
pr8bit	endp

; Print the handshake.
prhnd:	mov si,offset handst	; copy in initial message
	call stms1
	mov si,offset nonmsg	; assume no handshake
	mov bx,portval
	cmp [bx].hndflg,0	; Is handshaking in effect?
	jne prh0		; Yes, print what we're using.
	jmp stms1		; no, say so and return
prh0:	mov al,'^'		; Doing handshaking with control char.
	stosb
	mov al,[bx].hands
	add al,40H		; Make printable.
	stosb			; put in buffer
	ret			; and return

; Print the pad character in AL.
prpad:	cmp al,127		; Are they using a delete?
	jne prpad0
	mov ah,prstr
	mov dx,offset delmsg
	int dos
	ret
prpad0:	mov dl,'^'
	mov ah,conout
	push ax
	int dos
	pop ax
	mov dl,al
	add dl,40H		; Make printable.
	int dos
	ret

; Print value from table.  BX/address of table, AL/value of variable.
prttab:	mov cl,[bx]		; Number of entries in our table.
	inc bx			; Point to the data.
prtt0:	mov dl,[bx]		; Length of keyword.
	inc bx			; Point to keyword.
	mov dh,0
	inc dx			; Account for "$" in table.
	mov si,dx		; Put to index register.
	cmp ax,[bx+si]		; Is this the one?
	je prtt1
	add bx,dx		; Go to end of keyword.
	add bx,2		; Point to next keyword.
	dec cl			; Any more keywords to check?
	jnz prtt0		; Yes, go to it.
	mov bx,offset prterr
prtt1:	mov si,bx
prtt2:	jmp stms1		; copy in message
	ret			; and return

;	This routine prints out the escape character in readable format.  

ESCPRT	PROC	NEAR		; [6 start]
	mov dl,trans.escchr
	cmp dl,' '
	jge escpr2
	push dx
	mov ah,prstr
	mov dx,offset esctl
	int dos
	pop dx
	add dl,040H		; Make it printable.
escpr2:	mov ah,conout
	int dos
	ret
ESCPRT	ENDP			; [6 end]
 
; Print information on the baud rate. [19a]

BAUDPRT	PROC	 NEAR
	mov si,portval
	mov ax,[si].baud
	mov dx,offset b48st	; Assume 4800 baud.
	cmp ax,B4800
	jnz bdprt0
	jmp bdprt2
bdprt0:	mov dx,offset b12st
	cmp ax,B1200
	jnz bdprt1
	jmp bdprt2
bdprt1:	mov dx,offset b18st
	cmp ax,B1800
	jz bdprt2
	mov dx,offset b24st
	cmp ax,B2400
	jz bdprt2
	mov dx,offset b96st
	cmp ax,B9600
	jz bdprt2
	mov dx,offset b03st
	cmp ax,B0300
	jz bdprt2
	mov dx,offset b04st
	cmp ax,B00455
	jz bdprt2
	mov dx,offset b05st
	cmp ax,B0050
	jz bdprt2
	mov dx,offset b07st
	cmp ax,b0075
	jz bdprt2
	mov dx,offset b11st
	cmp ax,B0110
	jz bdprt2
	mov dx,offset b13st
	cmp ax,B01345
	jz bdprt2
	mov dx,offset b15st
	cmp ax,B0150
	jz bdprt2
	mov dx,offset b06st
	cmp ax,B0600
	je bdprt2
	mov dx,offset b20st
	cmp ax,B2000
	jz bdprt2
	mov dx,offset b19st
	cmp ax,B19200
	jz bdprt2
	mov dx,offset b38st
	cmp ax,B38400
	jz bdprt2
	mov dx,offset unrec	; Unrecognized baud rate.
bdprt2:	mov si,dx		; this is baud rate
bdprt3:	jmp stms1		; go copy it and return
BAUDPRT	ENDP
 
setkey	proc	near		
	cmp	setktab,0	; any table?
	jne	setk0		; yes, use it
	mov	dx,offset ermes5
	jmp	reterr		; else print error message
setk0:	mov	dx,offset setktab	; set key options
	mov	bx,offset setkhlp
	mov	ah,cmkey
	call	comnd
	 jmp	r
	cmp	bx,-1		;[jd] do we have scan code?
	jne	setk1		;[jd] yes, skip this part

	mov	ah,cmtxt
	mov	bx,offset rdbuf	; handy buffer
	mov 	dx,offset sk1msg
	call	comnd
	 jmp	r		; fail return
	mov	si,offset rdbuf	; this is parsed number
	call	atoi		; Convert input to real number.
	 jmp	reterr		; No good.
	mov	bx,ax		; put accumulation into bl
setat3:	cmp	bx,0		; is scan code 0?
	jne	setk2		; no, have scan code, look for def

setk1:	push	bx		; save our scan code
	mov	ah,cmcfm
	call	comnd
	 jmp 	short setkx	; no good, pop bx and return
	nop			; waste a byte
	pop	bx
; scan code is in bl, ask for string part
setk2:	push	bx
	mov	dx,offset defpmp
	call	prompt
	mov	ah,cmtxt
	mov	bx,offset rdbuf
	mov	dx,offset sk2msg
	call	comnd		; read the definition
	 jmp	short setkx	; pop bx and fail return
	 nop
	mov	cl,ah
	mov	ch,0		; set up length of definition
	pop	ax		; get scan code back
	mov	si,offset rdbuf	; point to definition
	call	defkey		; go define the key
	ret			; use ret for now...
	jmp	rskp		; and return
setkx:	pop	bx		; pop junk off stack
	ret			; and return
setkey	endp

; Convert input in buffer pointed to by SI to real number which is returned
; in AX.  Return on failure, return skip on success.
ATOI	PROC	NEAR
	mov	cl,ah		; Number of chars of input.
	mov	ch,0		; size of string
	jcxz	atoi4		; Fail on no input.
	mov	ax,0		; init sum
	mov	bh,0		; high order of this stays 0.
atoi0:	xchg	al,bl		; save current sum
	lodsb			; grab a byte
	cmp	al,' '		; leading space?
	jne	atoi1		; no, continue
	xchg	al,bl		; put sum back
	jmp	short atoi2	; and continue loop
atoi1:	cmp	al,'9'
	ja	atoi3		; out of range, done
	cmp	al,'0'
	jb	atoi3
	xchg	al,bl		; put sum back into al
	mul	ten		; shift one digit
	sub	bl,'0'		; convert to binary
	add	ax,bx		; add to sum
atoi2:	loop	atoi0		; loop thru all chars
atoi3:	jmp	rskp
atoi4:	mov	dx,offset erms23	; complain and return
	ret
ATOI	ENDP

;  addition for capture of raw output

setcpt	proc	near
	test	flags.capflg,0FFH
	jz	setcp1			; no capture file, keep going
	mov	dx,offset erms24
	jmp	reterr
setcp1:	mov 	comand.cmcr,0		; Filename must be specified.
	mov	ah,cmifi
	mov	dx,offset cptfcb
	mov 	bx,offset filhlp
	call	comnd
	 jmp	r
	mov	ah,cmcfm
	call	comnd			; confirm with carriage return
	 jmp	r
	mov	ah,delf
	mov	dx,offset cptfcb
	int	dos			; open up file
	mov	ah,makef
	mov	dx,offset cptfcb
	int	dos
	mov 	cptfcb+32,0

	call	inicpt			; init capture variables
	mov	flags.capflg,0FFH	; know we have capture routine
	jmp	rskp		; and return

setcpt	endp

; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.
 
RSKP    PROC    NEAR
	pop bp
	add bp,3
	push bp
        ret
RSKP    ENDP
 
; Jumping here is the same as a ret.
 
R       PROC    NEAR
        ret
R       ENDP

; routine to print an error message, then retskp
; expects message in dx
reterr	proc	near
	mov	ah,prstr
	int	dos
	jmp	rskp
reterr	endp

code	ends 
	end//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msset.asm
	/bin/echo -n '	'; /bin/ls -ld msset.asm
fi

knutson@ut-ngp.UUCP (Jim Knutson) (10/05/84)

: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting msterm.asm'
sed 's/^X//' <<'//go.sysin dd *' >msterm.asm
	public	clscpt, defkey, cptfcb, inicpt, clscpi, telnet
	public  dopar, shokey, prkey
	include msdefs.h

datas 	segment	public 'datas'
	extrn	flags:byte, trans:byte, buff:byte, portval:word

targ	termarg	<0,1,80,24,cptchr,2dch,0,scntab,deftab,0,,parnon>
ssp	dw	0		; Save SP in Telnet.
crlf    db      cr,lf,'$'
tmp	db	?,'$'
temp	dw	0
temp1   dw      ?               ; Temporary storage.
temp2   dw      ?               ; Temporary storage.
tmsg1	db	cr,lf,'[Connecting to host, type $' 
tmsg3	db	' C to return to PC]',cr,lf,cr,lf,cr,lf,'$'
tmsg2	db	cr,lf,'[Back at micro]',cr,lf,'$'
erms22	db	cr,lf,'?No capture file open$' ;[jd]
esctl	db	'Control-$'         ; [6]

inthlp  db	cr,lf,' ?  This message'
	db	cr,lf,' C  Close the connection'
	db	cr,lf,' S  Status of the connection'
	db	cr,lf,' B  Send a break'
	db	cr,lf,' M  Toggle mode line'
	db	cr,lf,' Q  Quit logging'
	db	cr,lf,' R  Resume logging'
	db	cr,lf,' 0  Send a null'
	db	cr,lf,' Typing the escape character will send it to the host'
	db	0

intprm	db	'Command>$'

CPTFCB	DB	25H DUP (?)
CAPBUF	DB	200 DUP (?)
CAPBP	DW	?
CAPLFT	DB	?

SCNTLEN	EQU	200		; MAX # OF DEFINITIONS ONE can have
defbsiz	equ	400		; combined length of all definitions...
scntab	dw	scntlen dup (?)	; scan codes redefined
deftab	dw	scntlen dup (?) ; pointer to definition strings
defbuf	db	defbsiz dup (?)
defptr	dw	defbuf		; pointer starts at beginning
deflen	dw	defbsiz		; amt of space left in buffer
sttmsg	db	'Type space to continue$'
shkmsg	db	cr,lf,'Press key: $'
datas	ends

code	segment	public
	extrn 	comnd:near, outchr:near, stat0:near
	extrn	escprt:near, clrbuf:near, term:near
	extrn	cmblnk:near, locate:near, prtchr:near
	extrn	beep:near, puthlp:near
	extrn	serini:near,serrst:near, sendbr:near, showkey:near
	assume	cs:code, ds:datas

; the show key command.

shokey	proc	near
	mov	ah,cmcfm		; confirm with carriage return
	call	comnd
	 jmp	r			; uh oh...
	mov	dx,offset shkmsg
	mov	ah,prstr
	int	dos			; print a prompt for it
	mov	ax,offset targ		; give it terminal arg block.
	call	showkey			; show them the key definition
	push	ax
	push	cx			; save results
	mov	dx,offset crlf
	mov	ah,prstr
	int	dos
	pop	cx
	pop	ax
	call	prkey			; print the buffer
	mov	dx,offset crlf
	mov	ah,prstr
	int	dos
	jmp	rskp			; and return
shokey	endp

; pass a string pointer in ax, length in cx.
; Prints the string, quoting any unprintables, except crlf.

prkey	proc	near
	mov	si,ax			; copy string ptr
	jcxz	prke6			; no string, stop here
prke1:	push	cx			; save counter
	lodsb				; get a byte
	and	al,7fH			; only consider low-order 7 bits.
	cmp	al,' '			; printable?
	jb	prke2			; no, print the hard way
	cmp	al,7fH			; maybe a delete?
	jne	prke4			; no, can just put into string
prke2:	jcxz	prke3			; last char, can't be crlf
	cmp	al,cr			; carriage return?
	jne	prke3			; no, go on
	cmp	byte ptr [si],lf	; followed by linefeed?
	jne	prke3
	mov	ah,prstr
	mov	dx,offset crlf
	int	dos			; else just print crlf
	inc	si			; skip over lf
	pop	cx			; careful...
	dec	cx
	push	cx
	jmp	short prke5
prke3:	push	ax			; preserve the char
	mov	ah,conout
	mov	dl,'\'
	int	dos			; print the quote character
	pop	ax
	call	proct			; print the octal byte
	jmp	short prke5
prke4:	mov	dl,al			; normal char, just print it
	mov	ah,conout
	int	dos
prke5:	pop	cx			; restore count
	loop	prke1
prke6:	ret				; and return
prkey	endp

; print the byte in al as an octal number
proct	proc	near
	mov	dl,al			; get the byte
	and	dl,7h			; keep low-order byte
	mov	cl,3
	shr	al,cl			; shift to get next digit
	jz	proc1			; 0, no more to print
	push	dx			; else save current digit
	call	proct			; print rest
	pop	dx
proc1:	mov	ah,conout
	add	dl,'0'			; make printable
	int	dos
	ret
proct	endp

;	This is the CONNECT command.
 
TELNET 	PROC	NEAR
	mov ah,cmcfm
	call comnd		; Get a confirm.
	 jmp r			;  Didn't get a confirm.
	mov ah,prstr		; Output
	mov dx,offset crlf	; a crlf.
	int dos
	call domsg		; Reassure user. [19b]
	mov al,targ.flgs	; get present flags
	and al,modoff		; this is only one we can keep around
	or al,havtt		; defaults (!)
	cmp flags.debug,0	; debug mode?
	jz tel0			; no, keep going
	or al,trnctl		; yes, show control chars
tel0:	cmp flags.vtflg,0	; vt52 emulation?
	jz tel1
	or al,emheath
tel1:	mov bx,portval
	cmp [bx].ecoflg,0	; echoing?
	jz tel2
	or al,lclecho
tel2:	mov targ.flgs,al	; store flags
	mov ah,flags.comflg
	mov targ.prt,ah		; Port 1 or 2
	mov ah,trans.escchr
	mov targ.escc,ah
	mov ah,[bx].parflg
	mov targ.parity,ah
	mov ax,[bx].baud
	mov targ.baudb,al
	mov ah,flags.capflg
	and ah,capt
	or targ.flgs,ah
	call serini		; init serial port
tem:	mov ax,offset targ	; Point to terminal arguments
	call term
	or targ.flgs,scrsam	; assume screen is the same.
intchr:	mov ah,dconio		; Direct console I/O.
	mov dl,0FFH		; Input.
	int dos			; Get a char.
	jz intchr		; no char, keep looking
	mov ah,al
	jz intchr		; If so, go until we get a char.
	cmp ah,' '		; space - ignore it
	je tem
	mov bh,ah		; Save the actual char.
	and ah,not ('a'-'A')	; Convert to upper case.
	or ah,40H		; convert ctl-char to actual char.
	cmp ah,'C'		; Is it close?
	jne intch1
	call serrst		; reset serial port
	jmp rskp		; and return
intch1: cmp ah,'S'		; Is it status?
	jnz intch2
	call stat0		; If so, call stat0.
	call puthlp		; put help on screen
	mov dx,offset sttmsg
	mov ah,prstr
	int dos
intch1a:mov ah,8		; console input, no echo
	int dos
	cmp al,' '		; space?
	jne intch1a
	and targ.flgs,not scrsam ; remember screen changed.
	jmp tem
intch2: cmp ah,'B'		; Send a break? [20g]
	jne intch3		; No. [20g]
	call sendbr		; Yes, so send a break. [20g]
	jmp tem			; And return.  [20g]
intch3:	cmp ah,'M'		; mode line?
	jne intch4
	xor targ.flgs,modoff	; toggle mode line
	jmp tem			; and reconnect
intch4:	cmp bh,'?'		; Is it help?
	jne intch5		; If not, go to the next check.
	mov ax,offset inthlp	; If so, get the address of the help message.
	call puthlp		; write help msg
	mov dx,offset intprm
	mov ah,prstr		; Print it.
	int dos
	and targ.flgs,not scrsam ; remember screen changed
	jmp intchr		; Get another char.
intch5: cmp bh,trans.escchr	; Is it the escape char?
	jne intch7		; If not, go send a beep to the user.
intch6: mov ah,al
	call outchr
	nop
	nop
	nop
	jmp tem			; Return, we are done here.
intch7:	cmp ah,'Q'		; maybe want to stop logging?
	jne intch8
	test targ.flgs,capt	; not capturing, can't do this
	jz intc10
	and targ.flgs,not capt ; stop capturing
	jmp tem			; and resume
intch8:	cmp ah,'R'		; maybe resume?
	jne intch9		; no, keep going
	cmp flags.capflg,0	; can we capture?
	jz intc10		; no, forget it
	test targ.flgs,capt	; already capturing?
	jnz intc10		; yes, can't toggle back on then
	or targ.flgs,capt	; else turn flag on
	jmp tem			; and resume
intch9:	cmp bh,'0'		; perhaps want a null (note original chr in bh)
	jne intc10
	mov ah,0
	call outchr
	nop
	nop
	nop
	jmp tem
intc10:	call beep
	jmp tem
TELNET  ENDP

; Reassure user about connection to the host.  Tell him what escape
; sequence to use to return and the communications port and baud
; rate being used.   [19b] 

DOMSG	PROC	NEAR
	mov ah,prstr
	mov dx,offset tmsg1
	int dos
	call escprt
	mov ah,prstr
	mov dx,offset tmsg3
	int dos
	ret
DOMSG	ENDP


; Set parity for character in Register AL.

dopar:	push bx
	mov bx,portval
	cmp [bx].parflg,parnon	; No parity?			[10 start]
	je parret		; Just return
	cmp [bx].parflg,parevn	; Even parity?
	jne dopar0
	and al,07FH		; Strip parity.
	jpe parret		; Already even, leave it.
	or al,080H		; Make it even parity.
	jmp parret
dopar0:	cmp [bx].parflg,parmrk	; Mark parity?
	jne dopar1
	or al,080H		; Turn on the parity bit.
	jmp parret
dopar1:	cmp [bx].parflg,parodd	; Odd parity?	
	jne dopar2
	and al,07FH		; Strip parity.
	jpo parret		; Already odd, leave it.
	or al,080H		; Make it odd parity.
	jmp parret
dopar2: and al,07FH		; Space parity - turn off parity bit.
parret:	pop bx
	ret					; [10 end]

inicpt	proc	near
	mov	capbp,offset capbuf
	mov	caplft,128		; init buffer ptr & chrs left
	ret				; and return
inicpt	endp


cptchr	proc	near			; capture routine, char in al
	push	di
	mov	di,capbp
	mov	byte ptr [di],al
	inc	di
	mov	capbp,di		; restore pointer
	pop	di
	dec	caplft			; decrement chars remaining
	jnz	cptch1			; more room, forget this part
	call	cptdmp			; dump the info
	call	inicpt			; re-init ptrs.
cptch1:	ret				; and return
cptchr	endp

cptdmp	proc	near			; empty the capture buffer
	push	ax
	push	dx
	mov	ah,setdma
	mov	dx,offset capbuf	; the capture routine buffer
	int	dos
	mov	ah,writef
	mov	dx,offset cptfcb
	int	dos			; write out the block
;*** must be fixed... check error returns, disable capturing,
;*** figure out how to put dma address back
	mov	dx,offset buff
	mov	ah,setdma
	int	dos			; put dma back
	pop	dx
	pop	ax
	ret
cptdmp	endp

clscpt	proc	near
	test	flags.capflg,0FFH	; doing capture
	jnz	clscp1			; yes, go ahead
	mov	dx,offset erms22
	mov	ah,prstr
	int	dos
	jmp	rskp
clscp1:	mov	ah,cmcfm
	call	comnd
	 jmp	r
clscpi:	mov	al,'Z'-64		; control-z for eof...
	call	cptchr			; output to file
	mov	al,caplft
	cmp	al,128			; is buffer empty?
	je	clscp2			; yes, forget this stuff
	call	cptdmp			; dump buffer (preserves registers)
clscp2:	mov	ah,0
	sub	word ptr cptfcb+16,ax	; subtract remaining from low filsize
	sbb	word ptr cptfcb+18,0	; and from high size (with borrow)
	mov	ah,closf
	mov	dx,offset cptfcb
	int	dos			; close up file
	mov	flags.capflg,0		; no longer capturing...
	jmp	rskp			; and return
clscpt	endp

; enter with ax/scan code to define, si/ pointer to definition, cx/ length
; of definition.  Defines it in definition table.
;*** somewhere should check for overflow etc of defbuf, and of scntab
defkey	proc	near
	push	ax		; save scan code
	mov	ax,ds
	mov	es,ax		; address data segment
	mov	di,defptr	; this is where the def gets built
	inc	di		; leave a byte for length
defk1:	lodsb			; get a byte from the source
	cmp	al,'\'		; escape?
	jne	defk2		; no, just deposit him
	dec	cx		; count available is one less
	call	trnesc		; translate the escape sequence
	inc	cx		; account for '\' (loop will decrement again).
defk2:	stosb			; drop off character
	loop	defk1		; and keep going while we have more
	mov	ax,di		; get ptr to end
	dec	ax		; back up pointer to end
	mov	si,defptr	; pick up old ptr value
	sub	ax,si		; this is actual length used
	mov	byte ptr [si],al ; fill in length of entry
	mov	defptr,di	; this is next free byte
; definition address is in si
	pop	ax		; recover scan code
	mov	cx,targ.klen	; length of scan table
	jcxz	defk4		; not there, just go add it
	mov	di,offset scntab ; the scan code table
	repne	scasw		; look for this one
	jne	defk4		; not defined already
	sub	di,offset scntab + 2 ; compute index into table
	mov	deftab[di],si	; fill in address
	ret			; and return
defk4:	mov	di,targ.klen	; get length again
	inc	di
	cmp	di,scntlen
	ja	defk5		;** ignore def if over size
	mov	targ.klen,di	; update length
	shl	di,1		; double for word index
	mov	scntab[di-2],ax	; put scan code into table
	mov	deftab[di-2],si	; and fill in definition
defk5:	ret			; that's it
defkey	endp

; enter with si/ source pointer, cx/ count
; converts an escape sequence, updates all pointers
trnesc	proc	near
	push	bx
	push	dx		; preserve these
	mov	al,0		; this is current accumulation
	jcxz	trnes2		; empty string, forget it
	mov	bl,3		; this is max # of digits to use
	mov	bh,8		; this is radix
trnes1:	mov	dl,[si]
	cmp	dl,'0'
	jb	trnes2		; out of range, stop here
	cmp	dl,'7'
	ja	trnes2
	inc	si		; accept character
	sub	dl,'0'		; convert to binary
	mul	bh		; shift accumulation
	add	al,dl		; add to accumulation
	dec	bl		; decrement digit counter
	loopnz	trnes1		; and keep trying
trnes2:	pop	dx
	pop	bx
	ret			; and return
trnesc	endp

; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.
 
RSKP	PROC	NEAR
	pop bp
	add bp,3
	push bp
	ret
RSKP	ENDP
 
; Jumping here is the same as a ret.
 
R	PROC	NEAR
	ret
R	ENDP

code	ends 
	end

//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msterm.asm
	/bin/echo -n '	'; /bin/ls -ld msterm.asm
fi
/bin/echo 'Extracting msapc.hlp'
sed 's/^X//' <<'//go.sysin dd *' >msapc.hlp

This note describes the capabilities of MS-DOS MSKermit as implemented for
the NEC Advanced Personal Computer (APC).  The APC system dependent portion of
the code (both port and terminal handlers) resides in the file MSXAPC.ASM.
This is the only module required in addition to those that all implementations
need.

MSKermit on the APC supports both the standard serial port (as port 1) and
the optional (H14) add-on serial port (as port 2).  Port selection is
performed using the SET PORT command.  Any baud rate up to 38400 is legal
although 38400 has never been tested and may not work well.  The port is
always configured as 8 data bits, no parity, and 1 stop bit.  Any necessary
parity is supplied by Kermit.

The interrupt vector used by the optional port is jumper-selectable.  Kermit
is set up to use IR8 by default, so if another vector is required the
MSXAPC.ASM file must be altered and reassembled.  The changes are well
commented, and only amount to changing the value of a conditional.

Terminal mode support on the APC is relatively primitive.  The only provisions
that have been made for emulation are in the operating system firmware, which
supports a limited subset of both VT100/ANSI and ADM-3A commands.

Two pages of screen memory and rollback are also provided by the firmware.
Control-uparrow scrolls back one line, while control-downarrow scrolls forward
a line.

Screen printing is performed by the CRTDUMP resident extension to MS-DOS
using the control-print command.  The print (or control-P) key alone causes
Kermit to toggle echoing of the screen display to the printer.

Key redefinition is provided, but only for the keyboard keys.  The function
keys must be defined using the system's KEY program.  There is no direct
access to the keyboard's scan codes on the APC, so instead each key is
redefined by its ASCII value, limiting the usefulness of this function.

The default escape character is control-].  Bugs in the firmware prevent
this control sequence from being returned to the program, however, so it
is necessary to instead use the left arrow key which sends the control-]
code.  Another alternative is to redefine the escape character in your
MSKERMIT.INI initialization file.


Despite these limitations, MSKermit for the APC is being released so that
the benefits of its file transfer capability are available to APC MS-DOS
users.  It is to be hoped that these users will take it on themselves to
enhance the capabilities.
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msapc.hlp
	/bin/echo -n '	'; /bin/ls -ld msapc.hlp
fi
/bin/echo 'Extracting mskermit.hlp'
sed 's/^X//' <<'//go.sysin dd *' >mskermit.hlp


               KERMIT VERSION 2.26 FOR MS-DOS AND PC-DOS

                             July 26, 1984

Kermit-MS is a program that provides terminal emulation and file trans-
fer for Intel 8088- and 8086-based microcomputers running the MS-DOS or
PC-DOS operating system.  The Kermit file transfer protocol was
developed and the Kermit-MS program were developed at the Columbia
University Center for Computing Activities.


* Program Operation

Kermit-MS can be run interactively, from a batch file, or as an
"external" DOS command.  Commands consist of one or more fields,
separated by "whitespace" -- one or more spaces or tabs.  Upon startup,
the program executes any commands found in the file MSKERMIT.INI in the
current path.

X. Interactive Operation:

To run Kermit-MS interactively, invoke the program from DOS command
level by typing its name.  When you see the command's prompt,
"Kermit-MS>", you may type Kermit commands repeatedly until you are
ready to exit the program.  You can use these special characters while
typing commands.

BACKSPACE  Delete the character most recently typed.  May be typed
        repeatedly to delete backwards.  You may also use DELETE,
        RUBOUT, or equivalent keys.

CTRL-W  Delete the most recent "word", or field, on the command line.
        May be typed repeatedly.

CTRL-U  Delete the entire command line.

CTRL-C  Cancel the current command and return to the "Kermit-MS>"
        prompt.

?       Type a brief message describing what you are expected to type in
        the current field.

ESC     If enough characters have been supplied in the current field
        (keyword or file name) to uniquely identify it, supply the
        remainder of the field and position to the next field of the
        command.  Otherwise, sound a beep.

=       Wildcard character for matching single characters in filenames,
        equivalent to MS-DOS "?".

X. Command Line Invocation:

Kermit-MS may also be invoked with command line arguments from DOS com-
mand level, for instance:

    A>kermit send foo.bar
or
    A>kermit set port 1, set baud 9600, connect

When invoked with command line arguments, Kermit-MS will behave as if it
were an external DOS command, like MODE.  Note that several commands may
be given on the command line, separated by commas.


* Kermit-MS Commands

Kermit-MS V2.26 has the following commands:

          BYE  to remote server.
        CLOSE  log file and stop logging remote session.
      CONNECT  as terminal to remote system.
       DEFINE  macros of Kermit-MS commands.
       DELETE  local files.
    DIRECTORY  listing of local files.
           DO  a macro expansion.
         EXIT  from Kermit-MS.
       FINISH  Shut down remote server.
          GET  remote files from server.
         HELP  about Kermit-MS.
        LOCAL  prefix for local file management commands.
          LOG  remote terminal session.
       LOGOUT  remote server.
         PUSH  to MS-DOS command level.
         QUIT  from Kermit-MS
      RECEIVE  files from remote Kermit.
       REMOTE  prefix for remote file management commands.
          RUN  an MS-DOS program.
         SEND  files to remote Kermit.
       SERVER  mode of remote operation.
          SET  various parameters.
         SHOW  various parameters.
        SPACE  inquiry.
       STATUS  inquiry.
         TAKE  commands from file.

The following SET commands are available in Kermit-MS:

                BAUD  Communications port line speed
                BELL  Whether to beep at the end of a transaction
    BLOCK-CHECK-TYPE  Level of error checking for file transfer
               DEBUG  Display packet contents during file transfer
        DEFAULT-DISK  Default disk drive for file i/o
         DESTINATION  Default destination device for incoming files
         END-OF-LINE  Packet terminator
                 EOF  Method for determining or marking end of file
              ESCAPE  Escape character for CONNECT
        FLOW-CONTROL  Enable or disable XON/XOFF
           HANDSHAKE  Half-duplex line turnaround option
             HEATH19  Heath/Zenith-19 terminal emulation
          INCOMPLETE  What to do with an incompletely received file
                 KEY  Specify key redefinitions, or "keystroke macros"
          LOCAL-ECHO  Specify which host does the echoing during CONNECT
              PARITY  Character parity to use
                PORT  Select a communications port
              PROMPT  Change the "Kermit-MS>" prompt to something else
             RECEIVE  Request remote Kermit to use specified parameters
              REMOTE  For running Kermit-MS interactively from back port
                SEND  Use the specified parameters during file transfer
           TAKE-ECHO  Control echoing of commands from TAKE files
               TIMER  Enable/disable timeouts during file transfer
             WARNING  Specify how to handle filename collisions

The STATUS command shows the values of parameters which may be SET.


* Command Macros

Kermit-MS provides a facility for combining commands into "macros."
Command macro definitions may be included in your MSKERMIT.INI file,
TAKEn explicitly from a specified file, or typed interactively, and may
be invoked with the DO command.


* Command Macros

Kermit-MS command macros are constructed with the DEFINE command.  The
syntax is 

    DEFINE macro-name  [command [, command [, ...]]]

Any Kermit-MS commands may be included.  Example:

    define telenet set parity mark, set baud 1200, connect

A Kermit-MS command macro is invoked using the DO command.  For in-
stance, Kermit-MS comes with a predefined macro to allow convenient
setup for IBM communications; to invoke it, you would type 

    do ibm

The IBM macro is defined as "parity mark, handshake xon, local-echo on,
timer on".  You can delete or replace this definition by adding a new
(perhaps null) definition, such as

    define ibm parity even, handshake cr, local-echo on, timer on]
or
    define ibm

Command macro definitions can be displayed with the SHOW MACROS command.


* Terminal Emulation

Here are the terminal emulation options for the systems presently sup-
ported by Kermit-MS:

  System         EscChar   Cabilities   Terminal Service
  IBM PC, XT       ^]      R M P K      Heath19 emulation
  DEC Rainbow      ^]      R   P K      VT102 firmware
  HP-150           ^]      R            HP-2623 firmware
  Wang PC          ^A                   Wang firmware
  Generic DOS      ^]                   Depends on system

Under Capabilities, R means rollback, M means mode line, P means printer
control, and K means key redefinition.

IBM PC/XT Kermit can disable Heath-19 emulation and use an external con-
sole device driver like ANSI.SYS instead.

The escape character is used to regain the attention of Kermit-MS.  When
you type the escape character, Kermit-MS waits for you to follow it with
a single character command:

  ?   Help -- prints the available single-character commands.
  C   Close the connection and return to Kermit-MS prompt level.
  S   Show the status of the connection.
  B   Send a BREAK signal to the port.
  0   (the digit zero) Send a NUL (ASCII 0) to the port.
  Q   Temporarily quit logging the remote session.
  R   Resume logging the remote session.
  M   Toggle the mode line, i.e. turn it off if it is on & vice versa.
  ^]  (or whatever you have set the escape character to be)
      Typing the escape character twice sends one copy of it to the con-
      nected host.

Typing any other character (except the space bar, which is the "null
command") after the escape character will cause Kermit-MS to beep, but
will do no harm.  The escape character can be changed to something other
than Control-Rightbracket by using the SET ESCAPE command.

Kermit-MS includes several advanced features for use during terminal
emulation, including screen scroll, printer control, and key redefini-
tions.

X. Screen Scroll

Kermit-MS provides several pages of screen memory, which may be scrolled
up and down using keys as follows:

  Function            IBM PC/XT   Rainbow           HP-150
  Screen Down         PgDn        PrevScreen        Prev
  Line Down           Ctrl-PgDn   Ctrl-PrevScreen   Shift-UpArrow
  Screen Up           PgUp        NextScreen        Next
  Line Up             Ctrl-PgUp   Ctrl-NextScreen   Shift-DownArrow
  Top of Memory       Home
  Bottom of Memory    End

X. Printer Control

A locally attached printer may be controlled in the normal manner, on
most systems.  Pushing the "Print Screen" key (shifted on some systems)
will cause the current contents of the screen to be printed or spooled;
holding down CTRL while depressing Print Screen will start or stop the
spooling of incoming characters to the printer.

CTRL-Print-Screen can be simulated with the Kermit-MS LOG PRN and CLOSE
commands.

X. Key Redefinitions

Use SHOW KEY to find out the scan code of the key you want to redefine,
then use SET KEY SCAN xxx to define the new value.  Control characters
are entered in the definition string as \ooo (a backslash followed by 2
or 3 octal digits denoting the ASCII value of the character).
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 mskermit.hlp
	/bin/echo -n '	'; /bin/ls -ld mskermit.hlp
fi
/bin/echo 'Extracting msmkboo.c'
sed 's/^X//' <<'//go.sysin dd *' >msmkboo.c
X/* MSNKBOO.C
 *
 * This program takes a file and encodes it into printable characters.
 * These printable files can then be decoded by the programs MSPCBOOT.BAS
 * or MSPCTRAN.BAS as the need may be.  The file is encoded by taking
 * three consecutive eight bit bytes and dividing them into four six bit
 * bytes.  An ASCII zero was then added to the resulting four characters.
 * to make them all printable ASCII characters in the range of the
 * character zero to the character underscore.  In order to reduce the
 * size of the file null repeat count was used.  The null repeat count
 * compresses up to 78 consecutive nulls into only two characters.  This
 * is done by using the character tilde (~) as an indication that a group
 * of repetitive nulls has occured.  The character following the tilde is
 * number of nulls in the group.  The number is also converted in to a
 * printable character by adding an ASCII zero.  The highest number of
 * nulls is therefore the highest printable character tilde.  This is
 * equal to tilde minus zero nulls or 78 nulls.  Because of the three
 * byte to four byte encoding the repeat counting can only start with
 * the first character of a three byte triplet.
 *
 * This C program was written specifically for the DEC-20 and as such
 * will not easily be transported to another system.  The main problem
 * lies in the file I/O routines.  It is necessary to make sure that
 * untranslated eight bit bytes are input from the input file.  The 
 * main change would be to make the OPEN statement reflect this for 
 * your particular system and brand of UNIX and C.  The rest of the
 * program should be transportable with little or no problems.
 *
 */


#include <stdio.h>		/* Standard UNIX i/o definitions */
#include <file.h>

X/* Symbol Definitions */

#define MAXPACK		80	/* Maximum packet size */

#define MYRPTQ		'~'	/* Repeat count prefix I will use */
#define DATALEN		78	/* Length of data buffer */

#define TRUE		-1	/* Boolean constants */
#define FALSE		0

X/* Macros */

#define tochar(ch)  ((ch) + '0')

X/* Global Variables */

int	size,			/* Size of present data */
	maxsize,		/* Max size for data field */
        nc,			/* Number of input chars */
        oc,			/* Number of output chars */
	fd,			/* File pointer of file to read/write */
	ofd,
	rpt,			/* repeat count */
	rptq,			/* repeat quote */
	rptflg,			/* repeat processing flag */
	eoflag,			/* Set when file is empty. */
	otot;			/* What char number we are processing. */

char	t,			/* Current character */
	one,
	two,
	three,
	*filnam,		/* Current file name */
        *ofile,
	packet[MAXPACK];	/* Packet buffer */

main(argc,argv)				/* Main program */
int argc;				/* Command line argument count */
char **argv;				/* Pointers to args */
{
    char sfile();		        /* Send file routine & ret code */
    if (--argc != 2) usage();		/* Make sure there's a command line. */
    rptq = MYRPTQ;			/* Repeat Quote */
    rptflg = TRUE;			/* Repeat Count Processing Flag */

    filnam = *++argv;			/* Get file to send */
    ofile = *++argv;			/* Output file to create */
    sfile();
    printf("Done, in: %d, out: %d, efficiency: %.2f%%\n",nc,oc,(100.0*nc)/oc);
}

X/*
   S F I L E - Send a whole file
*/

char sfile()				/* Send a file */
{
    char *i;

    fd = open(filnam,FATT_RDONLY|FATT_BINARY|FATT_DEFSIZE);
    if (fd < 0)				/* Report any errors */
    {
        printf("\n?Error opening file \"%s\"\n",filnam);
        exit(1);
    }

    ofd = open(ofile,FATT_WRONLY|FATT_CREATE|FATT_BINARY);
    if (ofd < 0)
    {
        printf("\n?error opening file \"%s\"\n",ofile);
        exit(1);
    }

    oc = strlen(filnam);		/* Get the string length. */
    for (i=filnam; *i != '\0'; i++)	/* Uppercase the file name. */
	if (*i >= 'a' && *i <= 'z') *i ^= 040;
    write(ofd,filnam,oc);	 	/* Write the file name in the file. */
    write(ofd,"\r\n",2);

    maxsize = DATALEN - 5;
    rpt = 0;				/* Zero the repeat count. */
    oc = nc = 0;			/* Output & input character counts. */
    otot = 1;				/* Start with first char of triplet. */
    while (getbuf() > 0)		/* While not EOF, get a packet. */
    {
	while (size < DATALEN - 1) packet[size++] = ' ';
	packet[size++] = '\r';		/* Explicit CRLF. */
	packet[size++] = '\n';
	packet[size] = '\0';
        oc += size;			/* Count output size. */
        write(ofd,packet,size);		/* Write the packet to the file. */
        printf("%d: %s",size,packet);	/* Print on the screen for testing. */
    }
}
X/*
   G E T B U F -- Do one packet.
*/

getbuf()				/* Fill one packet buffer. */
{
    if (eoflag != 0) return(-1);	/* If at the end of file, stop. */
    size = 0;
    while((t = getch()) >= 0)		/* t == -1 means EOF. */
    {
	nc++;				/* Count the character. */
        process(t);			/* Process the character. */
	if (size >= maxsize)		/* If the packet is full, */
	{
            packet[size] = '\0';	/*  terminate the string. */
            return(size);
        }
    }
    eoflag = -1;			/* Say we hit the end of the file. */
    process(0);				/* Clean out any remaining chars. */
    process(0);
    process(' ');
    packet[size] = '\0';		/* Return any partial final buffer. */
    return(size);
}

X/* P R O C E S S -- Do one character. */

process(a)
char a;
{
    if (otot == 1)			/* Is this the first of three chars? */
    {
        if (a == 0)			/* Is it a null? */
        {
	    if (++rpt < 78)		/* Below max nulls, just count. */
                return;
	    else if (rpt == 78)		/* Reached max number, must output. */
	    {
                packet[size++] = rptq;	/* Put in null repeat char and */
                packet[size++] = tochar(rpt); /* number of nulls. */
                packet[size] = '\0';
                rpt = 0;
	        return;
	    }
        }
	else
        {
	    if (rpt == 1)		/* Just one null? */
	    {
		one = 0;		/* Say the first char was a null. */
		two = a;		/* This char is the second one. */
		otot = 3;		/* Look for the third char. */
		rpt = 0;		/* Restart null count. */
		return;
	    }
	    if (rpt > 1)		/* Some number of nulls? */
	    {
                packet[size++] = rptq;	/* Insert the repeat prefix */
                packet[size++] = tochar(rpt); /* and count. */
                packet[size] = '\0';
                rpt = 0;		/* Reset repeat counter. */
 	    }
	    one = a;			/* Set first character. */
	    otot = 2;			/* Say we are at the second char. */
	}
    }
    else if (otot == 2)
    {
	two = a;			/* Set second character. */
	otot = 3;			/* Say we are at the third char. */
    }
    else
    {
        three = a;
	otot = 1;			/* Start over at one. */
	pack(one,two,three);		/* Pack in the three characters. */
    }
}

X/* This routine does the actual three character to four character encoding.
 * The concept is relatively straight forward.  The first output character
 * consists of the first (high order or most significant) six bits of the
 * first input character.  The second output character is made from the
 * remaining two low order bits of the first input character and the first
 * four high order bits of the second input character.  The third output
 * character is built from the last four low order bits of the second input
 * character and the two high order bits of the third input character.  The
 * fourth and last output character consists of the six low order bit of
 * the third input character.  In this way the three eight bit input char-
 * acters (for a total of 24 bits) are divided into four six bit output
 * characters (also for a total of 24 bits).  In order to make the four
 * output characters printable an ASCII zero is then added to each of them.
 *
 */

pack(x,y,z)
char x,y,z;
{
    packet[size++] = tochar((x >> 2) & 077);
    packet[size++] = tochar(((x & 003) << 4) | ((y >> 4) & 017));
    packet[size++] = tochar(((y & 017) << 2) | ((z >> 6) & 003));
    packet[size++] = tochar(z & 077);
    packet[size] = '\0';
}

getch()					/* Get next (or pushed) char. */
{
   char a;

   return((read(fd,&a,1) > 0) ? a : -1); /* (or -1 if EOF) */
}

usage()					/* Give message if user makes */
{					/* a mistake in the command. */
    fprintf(stderr,"usage: msmkboo inputfile outputfile\n");
    exit(1);
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msmkboo.c
	/bin/echo -n '	'; /bin/ls -ld msmkboo.c
fi
/bin/echo 'Extracting mspctran.bas'
sed 's/^X//' <<'//go.sysin dd *' >mspctran.bas
1    'Use this BASIC program on the PC if you have the printable file 
2    'MSKERMIT.BOO already on the PC to convert it to an executable
3    'file.  This program takes about 30 minutes to run on a PC with
4    'floppy disks.
5    ' Bill Catchings, June 1984
6    ' Columbia University Center for Computing Activities

10   t$ = time$				' Save the time.
20   defint a-z				' Integer to gain some speed.
30   n$ = chr$(0)
40   z = asc("0")
50   t = asc("~")-z
60   def fnuchr%(a$)=asc(a$)-z
70   open "MSKERMIT.BOO" for input as #1

100  input#1,f$				' Is this the right file?
110  if len(f$) > 20 then goto 900
120  open f$ for output as #2
130  print "Outputting to "+f$

200  if eof(1) then goto 800		' Exit nicely on end of file.
210  input#1,x$				' Get a line.
220  y$ = ""				' Clear the output buffer.
230  goto 400

300  print#2,y$;			' Print output buffer to file.
310  goto 200				' Get another line.

400  if len(x$) < 4 goto 300		' Is the input buffer empty?
410  a = fnuchr%(x$)
420  if a = t then goto 700		' Null repeat character?
430  q$=mid$(x$,2,3)			' Get the quadruplet to decode.
440  x$=mid$(x$,5)
450  b = fnuchr%(q$)
460  q$ = mid$(q$,2)
470  c = fnuchr%(q$)
480  q$ = mid$(q$,2)
490  d = fnuchr%(q$)

500  y$ = y$ + chr$(((a * 4) + (b \ 16)) and 255) ' Decode the quad.
510  y$ = y$ + chr$(((b * 16) + (c \ 4)) and 255)
520  y$ = y$ + chr$(((c * 64) + d) and 255)
530  goto 400				' Get another quad.

700  x$ = mid$(x$,2)			' Expand the nulls.
710  r = fnuchr%(x$)			' Get the number of nulls.
715 print " Null: ",r
720  x$ = mid$(x$,2)
730   for i=1 to r			' Loop, adding nulls to string.
740   y$ = y$ + n$
750   next
760  print#2,y$;			' Output the nulls to the file.
770  y$ = ""				' Clear the output buffer.
780  goto 400

800  print "Processing complete, elapsed time: "+t$+" to "+time$
810  print "Output in "+f$
820  close #1,#2
830  goto 9999

900  print "?The version of the MSKERMIT.BOO file is incorrect"
910  goto 820

9999 end
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 mspctran.bas
	/bin/echo -n '	'; /bin/ls -ld mspctran.bas
fi
/bin/echo 'Extracting msrbboo.bas'
sed 's/^X//' <<'//go.sysin dd *' >msrbboo.bas
1 DEFINT A-Z:ZRUBOUT$=CHR$(8)+" "+CHR$(8):ZESCAPE$=CHR$(27):'Sreen utility definitions B.E.
2 ZLEADIN$=ZESCAPE$+"[":ZCLEAR$=ZLEADIN$+"J":ZHOME$=ZLEADIN$+"0;0H"
3 ZDOUBLE1$=ZESCAPE$+"#3":ZDOUBLE2$=ZESCAPE$+"#4":WIDTH 255
4 ZBOLD$=ZLEADIN$+"1m":ZBLINK$=ZLEADIN$+"5m":ZSAVE$=ZESCAPE$+"7"
5 ZREVERSE$=ZLEADIN$+"7m":ZOFF$=ZLEADIN$+"0m":ZREST$=ZESCAPE$+"8"
6 ZGRAPHON$=ZESCAPE$+"(0":ZGRAPHOFF$=ZESCAPE$+"(B":ZBACKER$=ZLEADIN$+"0K"
7 ZKEYPAD$=ZESCAPE$+"=":ZBELL$=CHR$(7):ZCLRLIN$=ZLEADIN$+"2K"
8 DEF FNXY$(ZX,ZY)=ZLEADIN$+MID$(STR$(INT(ZX)),2)+";"+MID$(STR$(INT(ZY)),2)+"H":'Cursor Adressing function (ZX=Line[1..24],ZY=Column[1..80])
9 GOTO 25:'This to be modified to GOTO Start of program <===================
10 ZSTRING$="":ZORGL=ZLENGTH:PRINT ZSAVE$+ZREVERSE$+STRING$(ZORGL,95)+ZOFF$+STRING$(ZORGL,8);:'General Input-GOSUB (Input:ZLENGTH, OUTPUT:ZLENGTH,ZSTRING,ZNUMBER,ZRANDOM)
11 ZTEMP$=INKEY$:ZRANDOM=(ZRANDOM MOD 2000)+1:IF LEN(ZTEMP$)=0 THEN 11'Wait for Char
12 IF ASC(ZTEMP$)=127 OR ASC(ZTEMP$)=8 THEN 17 ELSE IF ASC(ZTEMP$)=21 THEN PRINT ZREST$+ZBACKER$;:ZLENGTH=ZORGL:GOTO 10 ELSE PRINT ZTEMP$;'RUBOUT
13 IF ASC(ZTEMP$)=3 THEN GOTO 9999 ELSE IF ZTEMP$ >= "a" THEN ZTEMP$=CHR$(ASC(ZTEMP$)-32)'Uppercase Modify GOTO xx to Control-C intercept <=====================
14 IF ASC(ZTEMP$)=13 THEN PRINT:GOTO 16'RETURN finishes
15 ZSTRING$=ZSTRING$+ZTEMP$:ZLENGTH=ZLENGTH-1:IF ZLENGTH >0 THEN 11
16 ZLENGTH=LEN(ZSTRING$):ZNUMBER=VAL(ZSTRING$): RETURN
17 IF LEN(ZSTRING$)>0 THEN ZLENGTH=ZLENGTH +1:ZSTRING$=LEFT$(ZSTRING$,(LEN(ZSTRING$)-1)):PRINT ZRUBOUT$;:GOTO 11 ELSE PRINT ZBELL$;: GOTO 11'Cleanup after RUBOUT
18 'End of VT100 definitions *****

19	'Use this BASIC program on the CP/M side of the Rainbow (with
20	'Microsoft MBasic-86) to translate the MSRB100.BOO file on
21	'your CP/M disk to binary .EXE format, then from the MS-DOS
22	'side use RDCPM to transfer the result to the MS-DOS file
23	'system.  This program takes about 30 minutes to run on a Rainbow
24	'with floppy disks.
25	'- Bill Catchings, CU; modified for Rainbow by Bernie Eiben, DEC.
26	PRINT ZHOME$+ZCLEAR$;"Rainbow 4for3 Code Expander Version 1"
30	PRINT:PRINT: N$ = CHR$(0)
40	Z = ASC("0")
50	T = ASC("~")-Z
60	DEF FNUCHR%(A$)=ASC(A$)-Z
61	PRINT "FILE-NAME to Expand : ";:ZLENGTH=13:GOSUB 10:'Get Input
70	OPEN "I",1,ZSTRING$
100	INPUT#1,F$			' Is this the right file?
110	IF LEN(F$) > 20 THEN GOTO 900
120	OPEN "O",2,F$			' Ouput-name from file
130	PRINT "Outputting to "+F$
200	IF EOF(1) THEN GOTO 800		' Exit nicely on end of file.
210	INPUT#1,X$			' Get a line.
220	Y$ = ""				' Clear the output buffer.
230	GOTO 400
300	PRINT#2,Y$;			' Print output buffer to file.
310	GOTO 200			' Get another line.
400	IF LEN(X$) < 4 GOTO 300		' Is the input buffer empty?
410	A = FNUCHR%(X$)
420	IF A = T THEN GOTO 700		' Null repeat character?
430	Q$=MID$(X$,2,3)			' Get the quadruplet to decode.
440	X$=MID$(X$,5)
450	B = FNUCHR%(Q$)
460	Q$ = MID$(Q$,2)
470	C = FNUCHR%(Q$)
480	Q$ = MID$(Q$,2)
490	D = FNUCHR%(Q$)
500	Y$ = Y$ + CHR$(((A * 4) + (B \ 16)) AND 255) ' Decode the quad.
510	Y$ = Y$ + CHR$(((B * 16) + (C \ 4)) AND 255)
520	Y$ = Y$ + CHR$(((C * 64) + D) AND 255)
530	GOTO 400			' Get another quad.
700	X$ = MID$(X$,2)			' Expand the nulls.
710	R = FNUCHR%(X$)			' Get the number of nulls.
715	PRINT FNXY$(6,5)+ZCLRLIN$;" Null: ",R
720	X$ = MID$(X$,2)
730	FOR I=1 TO R			' Loop, adding nulls to string.
740	Y$ = Y$ + N$
750	NEXT
760	PRINT#2,Y$;			' Output the nulls to the file.
770	Y$ = ""				' Clear the output buffer.
780	GOTO 400
800	PRINT "Processing complete"
810	PRINT "Output in "+F$
820	CLOSE #1,#2
830	GOTO 9999
900	PRINT "?The FORMAT of the ",ZSTRING$," file is incorrect"
910	GOTO 820
9999	END
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msrbboo.bas
	/bin/echo -n '	'; /bin/ls -ld msrbboo.bas
fi
/bin/echo 'Extracting msrbboo.hlp'
sed 's/^X//' <<'//go.sysin dd *' >msrbboo.hlp
Date: Thu 13 Sep 84 16:32:44-EDT
From: Frank da Cruz <SY.FDC@CU20B.ARPA>
Subject: Rainbow MS-DOS Kermit Bootstrapping
To: Info-Kermit@CU20B

Users of DEC Rainbow 100s have complained that there's no bootstrapping
procedure they can use for getting the new MS-DOS Kermit onto the Rainbow
over the communication line.  The problem was that Basic was not available
for MS-DOS on the Rainbow (or else it was so new that no one had it yet),
so the Microsoft Basic program we provided for decoding the .BOO (4-for-3
encoded) binary file could not be used on the Rainbow.

Now, thanks to Bernie Eiben at DEC, we have a version of the Basic program
that will run on the CP/M-86 side of the Rainbow.  It's a reworking of
MSPCTRAN.BAS, which assumes that you already have the .BOO file on your
CP/M disk.  It builds an .EXE file, which you can then move to the MS-DOS
side of your Rainbow by booting MS-DOS and then using RDCPM to get the
file from the CP/M-format disk.  How you get the .BOO file onto the CP/M
disk in the first place is another question.  Either you use some file
capture utility -- commercial or otherwise -- or else you go through the
DDT bootstrap procedure given for CP/M-80 Kermit (since the Rainbow is
also a CP/M-80 system).

Bernie's program is in KER:MSRBBOO.BAS.
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msrbboo.hlp
	/bin/echo -n '	'; /bin/ls -ld msrbboo.hlp
fi
/bin/echo 'Extracting msrbemacs.ini'
sed 's/^X//' <<'//go.sysin dd *' >msrbemacs.ini
; EMACS function key setup for Kermit-MS/Rainbow
;
; C-@ (set mark) on SELECT
set key select
\00
;
; M-h (select region) on CTRL-SELECT
set key scan 1313
\33h
;
; C-U 12 C-X C-I (rigidly indent region 12 spaces) on TAB
set key scan 9
\25\61\62\30\11
; C-X C-I (rigidly indent region) on SHIFT-TAB
set key scan 521
\30\11
;
; C-S (forward search) on FIND
set key find
\23
;
; C-R (reverse search) on CTRL-FIND
set key scan 1307
\22
;
; M-D (delete word) on REMOVE
set key remove
\33d
;
; M-K (delete sentence) on CTRL-REMOVE
set key scan 1311
\33k
;
; C-P (up line) on uparrow
set key scan 295
\20
;
; M-[ (up paragraph) on CTRL-uparrow
set key scan 1319
\33[
;
; C-X [ (up page) on SHIFT-uparrow
set key scan 807
\30[
;
; M-< (top of file) on CTRL-SHIFT-uparrow
set key scan 1831
\33<
;
; C-B (back character) on leftarrow
set key scan 301
\02
;
; C-A (beginning of line) on CTRL-leftarrow
set key scan 1325
\01
;
; M-A (back sentence) on SHIFT-leftarrow
set key scan 813
\33a
;
; C-N (next line) on downarrow
set key scan 297
\16
; M-] (down paragraph) on CTRL-downarrow
set key scan 1321
\33]
;
; C-X ] (down page) on SHIFT-downarrow
set key scan 809
\30]
;
; M-> (end of file) on CTRL-SHIFT-downarrow
set key scan 1833
\30>
;
; C-F (forward character) on rightarrow
set key scan 299
\06
;
; C-E (end of line) on CTRL-rightarrow
set key scan 1323
\05
;
; M-E (end of sentence) on SHIFT-rightarrow
set key scan 811
\33e
;
; C-X E (do keyboard macro) on DO
set key scan 257
\30e
;
; C-U C-X E (do keyboard macro 4x) on CTRL-DO
set key scan 1281
\25\30e
;
; C-U 8 C-X E (do keyboard macro 8x) on SHIFT-DO
set key scan 769
\25\70\30e
;
; C-U C-U C-X E (do keyboard macro 16x) on CTRL-SHIFT-DO
set key scan 1793
\25\25\30e
;
; ^_ on HELP
set key scan 256
\37
;
; C-X C-Z on EXIT
set key scan 271
\30\32
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msrbemacs.ini
	/bin/echo -n '	'; /bin/ls -ld msrbemacs.ini
fi
/bin/echo 'Extracting msxapc.asm'
sed 's/^X//' <<'//go.sysin dd *' >msxapc.asm
; Kermit system dependent module for NEC Advanced Personal Computer (APC)
; Ron Blanford, University of Washington, August 1984

	public	serini, serrst, clrbuf, outchr, coms, vts, dodel,
	public	ctlu, cmblnk, locate, lclini, prtchr, dobaud, clearl,
	public	dodisk, getbaud, beep,
	public	count, xofsnt, puthlp, putmod, clrmod, poscur
	public	sendbr, term, machnam, setktab, setkhlp, showkey
	include msdefs.h

false	equ	0
true	equ	1

; port assignments for 8251 serial controllers

;		Standard interface

mndata	equ	30H		; Data port (read/write)
mnst1a	equ	32H		; Status port (when read)
mncmda	equ	32H		; Command port (when written)
mnst2a	equ	34H		; Alternate status port (when read)
mnmska	equ	34H		; Mask port (when written)
mntdca	equ	36H		; Transmit disable port (write only)

;		Optional (H14) interface

mndatb	equ	31H		; Data port (read/write)
mnst1b	equ	33H		; Status port (when read)
mncmdb	equ	33H		; Command port (when written)
mnst2b	equ	35H		; Alternate status port (when read)
mnmskb	equ	35H		; Mask port (when written)
mntdcb	equ	37H		; Transmit disable port (write only)

; Status bits from mnst1

txrdy	equ	01H		; Bit for output ready.
rxrdy	equ	02H		; Bit for input ready.

; Command values for mncmd

ccmd	equ	37H		; RTS & DTR high, RX & TX enabled, reset ERR
cbrk	equ	08H		; break enabled
cmode	equ	40H		; enable mode reset
mmode	equ	4EH		; 16x rate, 8 data, no parity, 1 stop

; Mask values for mnmsk

txmsk	equ	01H		; disables transmit ready interrupt
rxmsk	equ	02H		; disables receive ready interrupt
tbemsk	equ	04H		; disables transmit buffer empty interrupt


; port assignments for 8253 timers

;		Standard interface

tmdata	equ	2BH		; data port
tmcmda	equ	27H		; command port

;		Optional (H14) interface

tmdatb	equ	61H		; data port
tmcmdb	equ	67H		; command port

; values for tmcmd which select timer channel and mode

tmsela	equ	76H		; Channel 1, mode 3 (standard port)
tmselb	equ	36H		; Channel 0, mode 3 (optional port)

; Timer information for current port selection

tmrinfo	struc
tmdat	dw	0		; data port
tmcmd	dw	0		; command port
tmsel	db	0		; byte which selects channel and mode
tmrinfo	ends


; port assignments for 8259 interrupt controllers

;		Standard interface

intcmda	equ	20H		; Command port (master controller)
intmska	equ	22H		; Mask port
ictmsk	equ	08H		; Timer interrupt mask (to master)
icsmska	equ	02H		; Standard serial interrupt mask (to master)
icsvcta equ	11H		; Interrupt vector for standard interface

;		Optional (H14) interface

; The interrupt request vector for the optional (H14) serial interface is
; jumper-selectable to any of vectors IR2, IR5, IR8, or IR12.  NEC recommends
; that IR8 be used, so that has been selected as the default here.  To use
; any of the other vectors, set the following conditionals appropriately.
; Only one of the following should be true:

IR2	equ	false		; interrupt vector 2
IR5	equ	false		; interrupt vector 5
IR8	equ	true		; interrupt vector 8
IR12	equ	false		; interrupt vector 12

	IF IR2
intcmdb equ	20H		; Command port (master controller)
intmskb equ	22H		; Mask port
icsmskb equ	04H		; Interrupt mask
icsvctb equ	12H		; Interrupt table index
	ENDIF

	IF IR5
intcmdb equ	20H		; Command port (master controller)
intmskb equ	22H		; Mask port
icsmskb equ	20H		; Interrupt mask
icsvctb equ	15H		; Interrupt table index
	ENDIF

	IF IR8
intcmdb equ	28H		; Command port (slave controller)
intmskb equ	2AH		; Mask port
icsmskb equ	02H		; Interrupt mask
icsvctb equ	19H		; Interrupt table index
	ENDIF

	IF IR12
intcmdb equ	28H		; Command port (slave controller)
intmskb equ	2AH		; Mask port
icsmskb equ	20H		; Interrupt mask
icsvctb equ	1DH		; Interrupt table index
	ENDIF

; generic end of interrupt for intcmd

icEOI	equ	20H


; miscellaneous constants

ctrlP	equ	10H		; Key that toggles printer echo
mntrgh	equ	bufsiz*3/4	; High XON/XOFF trigger = 3/4 of buffer full.

; external variables used:
; drives - # of disk drives on system
; flags - global flags as per flginfo structure defined in pcdefs
; trans - global transmission parameters, trinfo struct defined in pcdefs
; portval - pointer to current portinfo structure (currently either port1
;    or port2)
; port1, port2 - portinfo structures for the corresponding ports

; global variables defined in this module:
; xofsnt, xofrcv - tell whether we saw or sent an xoff.

datas 	segment	public 'datas'
	extrn	drives:byte,flags:byte, trans:byte
	extrn	portval:word, port1:byte, port2:byte

machnam	db	'NEC APC$'
nyimsg	db	cr,lf,'Not yet implemented$'
badbd	db	cr,lf,'Unimplemented baud rate$'
lstpos	dw	0		; column position for printer echoing
crlf	db	cr,lf,'$'
delstr  db      BS,' ',BS,'$'	; Delete string.
clrlin  db      cr,'$'		; Clear line (just the cr part).
ceolseq	db	esc,'[K$'	; Clear to end of line
cpseq	db	esc,'=rc'	; rc replaced by row and column before display
clrseq	db	01EH,01AH,'$'	; Home cursor and clear screen

; The following color values were selected to look well on both monochrome
; and color monitors.  In particular, the normal color should be at normal
; intensity on the monochrome monitor (green, blue, or cyan), and the bold
; color should be at bold intensity (red, purple, yellow, or white).

nrmseq	db	esc,'[0m$'	; reset to normal video (green)
invseq	db	esc,'[7m$'	; start reverse video (green)
bldseq	db	esc,'[19m$'	; start bold video (purple)

ourarg	termarg	<>
modem	mdminfo	<mndata,mnst1a,mncmda,0,0,0,0>
timer	tmrinfo	<tmdata,tmcmda,tmsela>
ourflgs	db	0		; flags for telnet options
fprint	equ	80H		;   echo screen output to printer

oldsera	dw	?		; old serial handler for standard port
oldsega	dw	?		; segment of above
oldmska	db	?		; old interrupt controller mask
portina	db	0		; Has comm port been initialized.

oldserb	dw	?		; old serial handler for optional port
oldsegb	dw	?		; segment of same.
oldmskb	db	?		; old interrupt controller mask
portinb	db	0		; Has comm port been initialized.

xofsnt	db	0		; Say if we sent an XOFF.
xofrcv	db	0		; Say if we received an XOFF.

; variables for serial interrupt handler
source	db	bufsiz DUP (?)	; Buffer for data from port.
srcpnt	dw	0		; Pointer in buffer (DI).
count	dw	0		; Number of chars in int buffer.
savesi	dw	0		; Save SI register here.	
	dw	80 DUP (?)	; local stack for interrupt processing
mnstk	dw	?
mnsp	dw	?		; remote stack info
mnsseg	dw	?

shkbuf	db	300 dup (?)	; room to display key definition
shkmsg	db	'  Scan code: '
shkmln	equ	$-shkmsg
shkms1	db	cr,lf,'  Definition: '
shkm1ln	equ	$-shkms1

setktab	db	2
	mkeyw	'BACKSPACE',08H
	mkeyw	'SCAN',-1

setkhlp	db	cr,lf,'BACKSPACE, or SCAN followed by decimal ASCII code$'

comptab	db	7
	mkeyw	'1',1
	mkeyw	'2',0
	mkeyw	'COM1',1
	mkeyw	'COM2',0
	mkeyw	'H14',0
	mkeyw	'OPTIONAL',0
	mkeyw	'STANDARD',1

bddat	label	word
	dw	0D30H		; 45.5 baud
	dw	0C00H		; 50 baud
	dw	0800H		; 75 baud
	dw	0574H		; 110 baud
	dw	0476H		; 134.5 baud
	dw	0400H		; 150 baud
	dw	0200H		; 300 baud
	dw	0100H		; 600 baud
	dw	0080H		; 1200 baud
	dw	0055H		; 1800 baud
	dw	004DH		; 2000 baud
	dw	0040H		; 2400 baud
	dw	0020H		; 4800 baud
	dw	0010H		; 9600 baud
	dw	0008H		; 19200 baud
	dw	0004H		; 38400 baud (not tested - may not work)

datas	ends

code	segment	public
	extrn	comnd:near, dopar:near
	assume	cs:code,ds:datas

; local initialization routine, called by Kermit initialization.

LCLINI	PROC	NEAR
	cld
	mov flags.vtflg,0	; turn off heath emulation
	mov dx,offset nrmseq	; set to our normal background color
	call tmsg
	ret
LCLINI	ENDP

; this is called by Kermit initialization.  It checks the
; number of disks on the system, sets the drives variable
; appropriately.  The only problem is that a value of two
; is returned for single drive systems to be consistent
; with the idea of the system having logical drives A and
; B.  Returns normally.  

DODISK	PROC	NEAR
	mov ah,gcurdsk		; current disk value to AL.
	int dos
	mov dl,al		; put current disk in DL.
	mov ah,seldsk		; select current disk.
	int dos			; get number of drives in AL.
	mov drives,al
	ret
DODISK	ENDP

; show the definition of a key.  The terminal argument block (which contains
; the address and length of the definition tables) is passed in ax.
; Returns a string to print in AX, length of same in CX.
; Returns normally.

; On the APC there is no direct access to the keyboard; the best we
; can do is use direct console I/O to get a key value which has already
; been translated to some extent by the operating system.

SHOWKEY	PROC	NEAR
	push es
	push ax			; save the terminal argument block
	mov bx,ds
	mov es,bx		; address data segment
	cld
showk1:	mov ah,dconio		; get scan value
	mov dx,0FFH
	int dos
	jz showk1
	mov ah,0
	push ax			; save scan code
	mov di,offset shkbuf	; move 'Scan code' message to buffer
	mov si,offset shkmsg
	mov cx,shkmln
	rep movsb
	call nout		; add scan code to buffer
	mov si,offset shkms1	; move 'Definition' message to buffer
	mov cx,shkm1ln
	rep movsb
	pop ax			; retrieve scan code
	pop bx			; and terminal argument block
	mov cx,[bx].klen	; length of translation table
	jcxz showk3		; no table, key not defined
	push di
	mov di,[bx].ktab	; get table address
	repne scasw		; look for scan code
	mov si,di
	pop di
	jne showk3		; not defined
	sub si,[bx].ktab	; compute entry offset in table
	sub si,2
	add si,[bx].krpl	; index to replacement
	mov si,[si]		; get its address
	mov cl,[si]		; get its length
	mov ch,0
	inc si
	rep movsb		; transfer replacement to display buffer
showk3:	mov ax,offset shkbuf	; return address of buffer in ax
	mov cx,di		; and length in cx
	sub cx,ax
	pop es
	ret
SHOWKEY	ENDP

; copy numeric value from AX to ASCII buffer indicated by DI.  DI is updated.

NOUT	PROC	NEAR
	mov dx,0		; zero high word
	mov bx,10		; divide
	div bx
	push dx			; save remainder digit
	or ax,ax		; anything left?
	jz nout1		; no, start output phase
	call nout
nout1:	pop ax			; retrieve a digit
	add al,'0'		; make it ASCII
	stosb			; put it in buffer
	ret
NOUT	ENDP

; skip returns if no character available at port,
; otherwise returns with char in al, # of chars in buffer in dx.

PRTCHR  PROC    NEAR
	call chkxon		; see if we have to xon the host.
	cmp count,0
	jnz prtch2
	jmp rskp		; No data - check console.
prtch2:	pushf			; save current interrupt value
	cli			; disable interrupts while manipulating pointers
	mov si,savesi
	lodsb			; get a byte
	cmp si,offset source + bufsiz	; bigger than buffer?
	jb prtch1		; no, keep going
	mov si,offset source	; yes, wrap around
prtch1:	dec count
	mov savesi,si 
	mov dx,count		; return # of chars in buffer
	popf			; restore original interrupt flag
	ret
PRTCHR  ENDP

; local routine to see if we have to transmit an xon

CHKXON	PROC	NEAR
	push bx
	mov bx,portval
	cmp [bx].floflg,0	; doing flow control?
	je chkxo1		; no, skip all this
	cmp xofsnt,false	; have we sent an xoff?
	je chkxo1		; no, forget it
	cmp count,mntrgh	; below trigger?
	jae chkxo1		; no, forget it
	mov ax,[bx].flowc	; ah gets xon
	call outchr		; send it
	 nop			;  ignore failure
	 nop
	 nop
	mov xofsnt,false	; remember we've sent an xon.
chkxo1:	pop bx			; restore register
	ret			; and return
CHKXON	ENDP

; Put the char in AH to the serial port.  This assumes the
; port has been initialized.  Should honor xon/xoff.  Skip returns on
; success, returns normally if the character cannot be written.

OUTCHR	PROC	NEAR
	mov bp,portval
	cmp ds:[bp].floflg,0	; Are we doing flow control.
	je outch2		; No, just continue.
	sub cx,cx		; clear counter
outch1:	cmp xofrcv,true		; Are we being held?
	jne outch2		; No - it's OK to go on.
	loop outch1		; held, try for a while
	mov xofrcv,false	; timed out, force it off and fall thru.
outch2:	push dx			; Save register.
	sub cx,cx
	mov al,ah		; Parity routine works on AL.
	call dopar		; Set parity appropriately.
	mov ah,al		; Don't overwrite character with status.
	mov dx,modem.mdstat	; port status register
outch3:	in al,dx
	test al,txrdy		; Transmitter ready?
	jnz outch4		; Yes
	loop outch3
	 jmp outch5		; Timeout
outch4:	mov al,ah		; Now send it out
	mov dx,modem.mddat
	out dx,al
	pop dx
	jmp rskp
outch5:	pop dx
	ret
OUTCHR	ENDP

; Send a break out the current serial port.  Returns normally.

SENDBR	PROC	NEAR
	mov dx,modem.mdcom	; send to command port
	mov al,cbrk+ccmd	; add break to normal command
	out dx,al
	sub cx,cx		; wait a while
sndbr1:	loop sndbr1
	mov al,ccmd		; restore normal command
	out dx,al
	ret			; and return.
SENDBR	ENDP

; Clear the input buffer. This throws away all the characters in the
; serial interrupt buffer.  This is particularly important when
; talking to servers, since NAKs can accumulate in the buffer.
; Returns normally.

CLRBUF	PROC	NEAR
	pushf			; save current interrupt value
	cli			; disable interrupts
	mov ax,offset source	; reset pointers to beginning of buffer
	mov srcpnt,ax
	mov savesi,ax
	mov count,0
	popf			; restore original interrupt value
	ret
CLRBUF	ENDP

; Set the baud rate for the current port, based on the value in the
; portinfo structure.  On entry, previous value of baud rate is saved in AX.
; Returns normally.

DOBAUD	PROC	NEAR
	mov bp,portval
	mov bx,ds:[bp].baud	;make sure new value is valid
	shl bx,1
	add bx,offset bddat
	cmp word ptr [bx],0FFH
	jne dobd0
	mov ds:[bp].baud,ax	;replace bad rate with previous value
	mov dx,offset badbd
	jmp tmsg
dobd0:	mov dx,timer.tmcmd	;timer command port
	mov al,timer.tmsel	;select proper channel and mode
	out dx,al
	mov ax,[bx]		;get timer initializer for this rate
	mov dx,timer.tmdat	;timer data port
	out dx,al		;output low byte
	mov al,ah
	out dx,al		;output high byte
	ret
DOBAUD	ENDP

; Get the current baud rate from the serial card and set it
; in the portinfo structure for the current port.  Returns normally.
; This is used during initialization.

GETBAUD	PROC	NEAR
	mov bx,portval		; no way to determine baud rate on APC
	mov [bx].baud,B1200	;  so set default baud rate to 1200
	ret
GETBAUD	ENDP

; Set the mode for the current port.  This is part of the serial
; initialization routine.

DOMODE	PROC	NEAR
	mov dx,modem.mdcom	;send 3 zeros to command port to reset chip
	mov al,0
	out dx,al
	mov al,0
	out dx,al
	mov al,0
	out dx,al
	mov al,cmode		;enable mode setting
	out dx,al
	push ax			;allow 8251 time to reset
	pop ax
	push ax
	pop ax
	mov al,mmode		;mode: 16x rate, 8 data, no parity, 1 stop
	out dx,al
	mov al,ccmd		;RTS & DTR high, RX & TX enabled, reset errors
	out dx,al
	ret
DOMODE	ENDP

; set the current port.

COMS	PROC	NEAR
	mov dx,offset comptab	;get port selection
	mov bx,0
	mov ah,cmkey
	call comnd
	 jmp r
	push bx
	mov ah,cmcfm		;get a confirmation
	call comnd
	 jmp comx
	 nop
	pop bx
	mov flags.comflg,bl	;save port selection
	cmp flags.comflg,1
	jne coms2
	mov ax,offset port1	;set to run on port 1
	mov portval,ax
	call resetb		;reset port 2, if in use
	call inita		;set up port 1
	ret
coms2:	mov ax,offset port2	;set to run on port 2
	mov portval,ax
	call reseta		;reset port 1, if in use
	call initb		;set up port 2
	ret
comx:	pop bx
	ret
COMS	ENDP

; initialization for using serial port.  This routine performs
; any initialization necessary for using the serial port, including
; setting up interrupt routines, setting buffer pointers, etc.
; Doing this twice in a row should be harmless (this version checks
; a flag and returns if initialization has already been done).
; SERRST below should restore any interrupt vectors that this changes.
; Returns normally.

SERINI	PROC	NEAR
	cmp flags.comflg,1
	jne seri2
	call resetb
	call inita
	ret
seri2:	call reseta
	call initb
	ret
SERINI	ENDP

; Reset the serial port.  This is the opposite of serini.  Calling
; this twice without intervening calls to serini should be harmless.
; Returns normally.

SERRST	PROC	NEAR
	call reseta		;reset port 1
	call resetb		;reset port 2
	ret
SERRST	ENDP

; Local routine to initialize the standard serial port

INITA	PROC	NEAR
	cmp portina,1		; Did we initialize port already? [21c]
	je inita0		; Yes, so just leave. [21c]
	push es
	cli			; Disable interrupts
	mov ax,offset port1
	mov portval,ax
	xor ax,ax		; Address low memory
	mov es,ax
	mov ax,es:[4*icsvcta]	; save standard port interrupt vector
	mov oldsera,ax
	mov ax,es:[4*icsvcta+2]
	mov oldsega,ax
	mov ax,offset serint	; point to our routine
	mov es:[4*icsvcta],ax	; point at our serial routine
	mov es:[4*icsvcta+2],cs	; our segment
	mov dx,intmska		; set up standard port...
	in al,dx
	mov oldmska,al		; save old master controller mask

;	NEC recommends that the timer interrupt be disabled during interrupt-
;	driven serial I/O, but this disables the clock display and keyboard
;	repeat.  I have not had any problems leaving it enabled, so I will
;	leave it alone here.  If problems develop, uncomment the following
;	line to disable timer interrupts. -- RonB

;	or al,ictmsk		; disable timer interrupt
	and al,not icsmska	; enable serial interrupt at master controller
	out dx,al
	mov dx,mnmska		; enable serial interrupt at port
	mov al,txmsk+tbemsk	; disable tx and tbe interrupts (enable rx)
	out dx,al
	mov dx,mntdca		; enable operation of serial port
	mov al,0
	out dx,al
	mov modem.mddat,mndata
	mov modem.mdstat,mnst1a
	mov modem.mdcom,mncmda
	mov timer.tmdat,tmdata
	mov timer.tmcmd,tmcmda
	mov timer.tmsel,tmsela
	call domode
	call dobaud
	mov portina,1		; Remember port has been initialized.
	call clrbuf		; Clear input buffer. 
	sti			; Allow interrupts
	pop es
inita0:	ret
INITA	ENDP

; Local routine to initialize the optional (H14) serial port

INITB	PROC	NEAR
	cmp portinb,1		; Did we initialize port already? [21c]
	je initb0		; Yes, so just leave. [21c]
	push es
	cli			; Disable interrupts
	mov ax,offset port2
	mov portval,ax
	xor ax,ax		; Address low memory
	mov es,ax
	mov ax,es:[4*icsvctb]	; save optional port interrupt vector
	mov oldserb,ax
	mov ax,es:[4*icsvctb+2]
	mov oldsegb,ax
	mov ax,offset serint	; point to our routine
	mov es:[4*icsvctb],ax	; point at our serial routine
	mov es:[4*icsvctb+2],cs	; our segment
	mov dx,intmskb		; set up optional port...
	in al,dx
	mov oldmskb,al		; save old master or slave controller mask
	and al,not icsmskb	; enable serial interrupt at controller
	out dx,al
	mov dx,mnmskb		; enable serial interrupt at port
	mov al,txmsk+tbemsk	; disable tx and tbe interrupts (enable rx)
	out dx,al
	mov dx,mntdcb		; enable operation of serial port
	mov al,0
	out dx,al
	mov modem.mdstat,mnst1b
	mov modem.mddat,mndatb
	mov modem.mdcom,mncmdb
	mov timer.tmdat,tmdatb
	mov timer.tmcmd,tmcmdb
	mov timer.tmsel,tmselb
	call domode
	call dobaud
	mov portinb,1		; Remember port has been initialized.
	call clrbuf		; Clear input buffer. 
	sti			; Allow interrupts
	pop es
initb0:	ret
INITB	ENDP

; Reset standard serial port

RESETA	PROC	NEAR
	cmp portina,0		; Did we reset port already?
	je rsta0		; Yes, so just leave.
	push es
	cli			; Disable interrupts
	xor ax,ax		; Address low memory
	mov es,ax
	mov ax,oldsera		; Restore interrupt vector
	mov es:[4*icsvcta],ax
	mov ax,oldsega
	mov es:[4*icsvcta+2],ax
	mov dx,intmska		; restore old master controller mask
	mov al,oldmska
	out dx,al
	mov dx,mnmska		; disable serial interrupts at port
	mov al,txmsk+rxmsk+tbemsk
	out dx,al
	mov portina,0		; Remember port has been reset
	sti			; Allow interrupts
	pop es
rsta0:	ret
RESETA	ENDP

; Reset optional (H14) serial port

RESETB	PROC	NEAR
	cmp portinb,0		; Did we reset port already?
	je rstb0		; Yes, so just leave.
	push es
	cli			; Disable interrupts
	xor ax,ax		; Address low memory
	mov es,ax
	mov ax,oldserb		; Restore interrupt vector
	mov es:[4*icsvctb],ax
	mov ax,oldsegb
	mov es:[4*icsvctb+2],ax
	mov dx,intmskb		; restore old slave controller mask
	mov al,oldmskb
	out dx,al
	mov dx,mnmskb		; disable serial interrupts at port
	mov al,txmsk+rxmsk+tbemsk
	out dx,al
	mov portinb,0		; Remember port has been reset
	sti			; Allow interrupts
	pop es
rstb0:	ret
RESETB	ENDP


; serial port interrupt routine.  This is not accessible outside this
; module, handles serial port receiver interrupts.

SERINT	PROC  NEAR
	push ds			; save these on remote stack
	push ax
	mov ax,seg datas	; get our own data segment
	mov ds,ax
	mov mnsp,sp		; save remote stack information
	mov mnsseg,ss
	mov sp,offset mnstk	; switch to local stack
	mov ss,ax
	push es			; and save remaining registers
	push bp
	push di
	push si
	push dx
	push cx
	push bx
	mov es,ax
	call mnproc		; process the interrupt
	mov al,icEOI
	cmp flags.comflg,1	; If using standard port
	je intr1
	mov dx,intcmdb		;    or H14 vectored to master
	cmp dx,intcmda
	je intr1		;    only signal End of Interrupt to master,
	out dx,al		; otherwise signal to both slave and master.
intr1:	mov dx,intcmda
	out dx,al
	pop bx			; restore registers from stack
	pop cx
	pop dx
	pop si
	pop di
	pop bp
	pop es
	mov ax,mnsseg		; switch back to remote stack
	mov ss,ax
	mov ax,mnsp
	mov sp,ax
	pop ax
	pop ds
	iret

; handler for serial input

mnproc:	cld
	mov di,srcpnt		; get buffer pointer
	mov dx,modem.mdstat	; is data available?
	in al,dx
	test al,rxrdy
	jz mnpro7
	mov dx,modem.mddat	; read data
	in al,dx
	or al,al
	jz mnpro7		; Ignore nulls.
	cmp al,7FH		; Ignore rubouts, too.
	jz mnpro7
	mov ah,al
	and ah,7fH		; only consider low-order 7 bits for flow ctl.
	mov bp,portval
	cmp ds:[bp].floflg,0	; Doing flow control?
	je mnpro4		; Nope.
	mov bx,ds:[bp].flowc	; Flow control char (BH = XON, BL = XOFF).
	cmp ah,bl		; Is it an XOFF?
	jne mnpro3		; Nope, go on.
	mov xofrcv,true		; Set the flag.
	jmp short mnpro7
mnpro3:	cmp ah,bh		; Get an XON?
	jne mnpro4		; No, go on.
	mov xofrcv,false	; Clear our flag.
	jmp mnpro7
mnpro4:	stosb
	cmp di,offset source + bufsiz
	jb mnpro5		; not past end...
	mov di,offset source	; wrap buffer around
mnpro5:	mov srcpnt,di		; update ptr
	inc count
	cmp ds:[bp].floflg,0	; Doing flow control?
	je mnpro7		; No, just leave.
	cmp xofsnt,true		; Have we sent an XOFF?
	je mnpro7		; Yes.
	cmp count,mntrgh	; Past the high trigger point?
	jbe mnpro7		; No, we're within our limit.
	mov ah,bl		; Get the XOFF.
	call outchr		; Send it.
	 nop			;   ignore failure.
	 nop
	 nop
	mov xofsnt,true		; Remember we sent it.
mnpro7:	ret

SERINT	ENDP

; Dumb terminal emulator.  Anyone wishing to enhance it is encouraged
; to do so.

TERM	PROC	NEAR
	mov si,ax		; save argument block locally
	mov di,offset ourarg
	mov ax,ds
	mov es,ax
	mov cx,size termarg
	rep movsb

term1:	call prtchr		; Serial port input processor
	 jmp short term2	;  ...have a char
	 nop
	jmp termk		; no char, continue
term2:	and al,7FH		; only use ASCII in terminal mode
	push ax
	mov dl,al
	mov ah,conout
	int dos			; display char
	pop ax
	test ourarg.flgs,capt	; are we capturing output?
	jz term3
	push ax
	call ourarg.captr
	pop ax
term3:	test ourflgs,fprint	; are we echoing to printer?
	jz termk
	call lstchr

termk:	mov ah,dconio		; Keyboard input processor
	mov dl,0FFH
	int dos			; check console
	jz term1		; no char, continue
	cmp al,ourarg.escc	; is it the escape char?
	je termx
	cmp al,ctrlP		; is it the print toggle?
	jne term6
	xor ourflgs,fprint
	jmp term1
term6:	call trnout		; translate key and send it out
	jmp term1
termx:	ret

; do appropriate translations on input key, and transmit

trnout:	mov ah,0
	test ourarg.flgs,havtt	; is there a translation table?
	jz trnou2
	mov cx,ourarg.klen	; get table length and origin
	mov di,ourarg.ktab
	repne scasw		; look for key
	jne trnou2		; if not found, just send it
	sub di,ourarg.ktab	; reset to offset of replacement
	sub di,2
	add di,ourarg.krpl
	mov si,[di]
	mov cl,[si]		; get length of replacement
	mov ch,0
	jcxz trnou3		; if length is zero, send nothing
	inc si
trnou1:	lodsb			; get replacement character
	push si
	push cx
	call sndhst		; send it to port
	pop cx
	pop si
	loop trnou1		; continue until translation complete
	ret
trnou2:	call sndhst		; plain characters go out as they are
trnou3:	ret

; send character in AL to port, with possible local echo

sndhst:	push ax
	mov ah,al
	call outchr		; send char to port
	 nop			;  ...don't care if it fails
	 nop
	 nop
	pop ax
	test ourarg.flgs,lclecho ; doing local echo?
	jz sndhs2
	mov dl,al
	mov ah,conout
	int dos			;  if so, display char
sndhs2:	ret

; send character to printer.  The only special case is the tab, which must
; be expanded to spaces because MS-DOS doesn't.

lstchr:	cmp al,tab
	jne lstch2
	mov ax,lstpos		; current column position
	mov cx,8		; # of spaces = 8 - (column % 8)
	div cl
	sub cl,ah
	add lstpos,cx		; update the column position
	mov al,' '
lstch1:	call lstch4		; print all the spaces
	loop lstch1
	ret
lstch2:	cmp al,cr		; CR returns column count to zero
	jne lstch3
	mov lstpos,0
lstch3:	cmp al,' '		; only printable characters are counted
	jb lstch4
	cmp al,del
	je lstch4
	inc lstpos
lstch4: mov dl,al		; print the character in any case
	mov ah,lstout
	int dos
	ret

TERM	ENDP

; Set heath emulation on/off.

VTS	PROC	NEAR
	mov dx,offset nyimsg
	jmp tmsg
VTS	ENDP

; Position the cursor according to contents of DX:
; DH contains row, DL contains column.  Returns normally.

POSCUR	PROC	NEAR
	push si
	cmp dh,25		; out of range just assumes high value
	jb poscu1
	mov dh,24
poscu1:	cmp dl,80
	jb poscu2
	mov dl,79
poscu2:	add dx,2020H		; add offset for ADM cursor addressing
	mov cpseq+2,dh
	mov cpseq+3,dl
	mov si,offset cpseq	; print sequence (ESC=rc)
	mov cx,4
posc1:	lodsb
	mov dl,al
	mov ah,conout
	int dos
	loop posc1
	pop si
	ret
POSCUR	ENDP

; Locate; homes the cursor.  Returns normally.

LOCATE  PROC	NEAR
	mov dx,0		; Go to top left corner of screen.
	jmp poscur
LOCATE  ENDP

; Delete a character from the terminal.  This works by printing
; backspaces and spaces.  Returns normally.

DODEL	PROC	NEAR
	cmp al,del		; Del character needs extra backspace
	jne dodel1
	mov dl,bs
	mov ah,conout
	int dos
dodel1:	mov dx,offset delstr	; Erase weird character.
	jmp tmsg
DODEL	ENDP

; Move the cursor to the left margin, then clear to end of line.
; Returns normally.

CTLU	PROC	NEAR
	mov dx,offset clrlin	; this just goes to left margin...
	call tmsg
	jmp clearl		; now clear line
CTLU	ENDP

; Clear to the end of the current line.  Returns normally.

CLEARL	PROC	NEAR
	mov dx,offset ceolseq	; clear sequence
	jmp tmsg
CLEARL	ENDP

; This routine blanks the screen.  Returns normally.

CMBLNK	PROC	NEAR
	mov dx,offset clrseq	; clear screen sequence
	jmp tmsg
CMBLNK  ENDP

; write a line in inverse video at the bottom of the screen...
; the line is passed in dx, terminated by a $.  Returns normally.

PUTMOD	PROC	NEAR
	push dx			; preserve message
	mov dx,24*100H		; line 24
	call poscur
	mov dx,offset invseq	; put into inverse video
	call tmsg
	pop dx			; print the message
	call tmsg
	mov dx,offset nrmseq	; normal video
	jmp tmsg
PUTMOD	ENDP

; clear the mode line written by putmod.  Returns normally.

CLRMOD	PROC	NEAR
	mov dx,24*100H
	call poscur
	jmp clearl
CLRMOD	ENDP

; Put a help message on the screen.  This one uses bold video...
; pass the message in ax, terminated by a null.  Returns normally.

PUTHLP	PROC	NEAR
	push ax			; save pointer to message
	mov dx,offset crlf
	call tmsg
	mov dx,offset bldseq	; set to bold video
	call tmsg
	pop si			; retrieve pointer to message
puth1:	lodsb			; get a character
	cmp al,0
	je puth2		; stop if terminator
	mov dl,al		; otherwise display the character
	mov ah,conout
	int dos
	jmp puth1
puth2:	mov dx,offset nrmseq	; reset to normal video
	call tmsg
	mov dx,offset crlf
	call tmsg
	ret
PUTHLP	ENDP

; Produce a short beep.  Returns normally.

BEEP	PROC	NEAR
	mov dl,bell
	mov ah,conout
	int dos
	ret
BEEP	ENDP 
 
; Prints $-terminated message in dx, for local use only

TMSG	PROC	NEAR
	mov ah,prstr
	int dos
	ret
TMSG	ENDP

; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.
 
RSKP    PROC    NEAR
	pop bp
	add bp,3
	push bp
        ret
RSKP    ENDP
 
; Jumping here is the same as a ret.
 
R       PROC    NEAR
        ret
R       ENDP

code	ends 
	end
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msxapc.asm
	/bin/echo -n '	'; /bin/ls -ld msxapc.asm
fi
/bin/echo 'Extracting msxdmb.asm'
sed 's/^X//' <<'//go.sysin dd *' >msxdmb.asm
code	segment	public
code	ends
datas	segment	public 'datas'
datas	ends
stack	segment	stack 'stack'
stack	ends
	end
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msxdmb.asm
	/bin/echo -n '	'; /bin/ls -ld msxdmb.asm
fi
/bin/echo 'Extracting msxdmb.hlp'
sed 's/^X//' <<'//go.sysin dd *' >msxdmb.hlp
MSXDMB.ASM is a dummy file to make the segments come out in the right order
on the Rainbow version of Kermit-MS.  It must be included with the other
modules, as the first one.
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msxdmb.hlp
	/bin/echo -n '	'; /bin/ls -ld msxdmb.hlp
fi
/bin/echo 'Extracting msxgen.asm'
sed 's/^X//' <<'//go.sysin dd *' >msxgen.asm
; Generic MS DOS Kermit module

	public	serini, serrst, clrbuf, outchr, coms, vts, dodel,
	public	ctlu, cmblnk, locate, lclini, prtchr, dobaud, clearl,
	public	dodisk, getbaud, beep
	public	count, xofsnt, puthlp, putmod, clrmod, poscur
	public	sendbr, term, machnam, setktab, setkhlp, showkey
	include msdefs.h

false	equ	0
true	equ	1
instat	equ	6
rddev	equ	3fH
open	equ	3dH

; external variables used:
; drives - # of disk drives on system
; flags - global flags as per flginfo structure defined in pcdefs
; trans - global transmission parameters, trinfo struct defined in pcdefs
; portval - pointer to current portinfo structure (currently either port1
;    or port2)
; port1, port2 - portinfo structures for the corresponding ports

; global variables defined in this module:
; xofsnt, xofrcv - tell whether we saw or sent an xoff.

datas 	segment	public 'datas'
	extrn	drives:byte,flags:byte, trans:byte
	extrn	portval:word, port1:byte, port2:byte

machnam	db	'Generic MS-DOS 2.0$'
erms20	db	cr,lf,'?Warning: System has no disk drives$' ; [21a]
erms40	db	cr,lf,'?Warning: Unrecognized baud rate$'
erms41	db	cr,lf,'?Warning: Cannot open com port$'
erms50	db	cr,lf,'Error reading from device$'
hnd1	db	cr,lf,'Enter a file handle.  Check your DOS manual if you are '
	db	cr,lf,'not certain what value to supply (generally 3).$'
hnd2	db	cr,lf,'Handle: $'
hnderr	db	cr,lf,'Warning: Handle not known.  Any routine using the '
	db	cr,lf,'communications port will probably not work.$'
hndhlp	db	cr,lf,'A four digit file handle $'
badbd	db	cr,lf,'Unimplemented baud rate$'
noimp	db	cr,lf,'Command not implemented.$'
shkmsg	db	'Not implemented.'
shklen	equ	$-shkmsg
setktab	db	0
setkhlp	db	0
crlf    db      cr,lf,'$'
delstr  db      BS,BS,'  ',BS,BS,'$' 	; Delete string. [21d]
clrlin  db      cr,'$'			; Clear line (just the cr part).
clreol	db	'^U',cr,lf,'$'		; Clear line.
telflg	db	0		; non-zero if we're a terminal.
xofsnt	db	0		; Say if we sent an XOFF.
xofrcv	db	0		; Say if we received an XOFF.
count	dw	0		; Number of chars in int buffer.
prthnd	dw	0		; Port handle.
prttab	dw	com2,com1
com1	db	'COM1',0
com2	db	'COM2',0
tmp	db	?,'$'
temp	dw	0
temp1   dw      ?               ; Temporary storage.
temp2   dw      ?               ; Temporary storage.
rdbuf	db	20 dup(?)	; Buffer for input.

; Entries for choosing communications port. [19b]
comptab	db	04H
	db	01H,'1$'
	dw	01H
	db	01H,'2$'
	dw	00H
	db	04H,'COM1$'
	dw	01H
 	db	04H,'COM2$'
	dw	00H

ourarg	termarg	<>

datas	ends

code	segment	public
	extrn	comnd:near, dopar:near, prserr:near, atoi:near, prompt:near
	assume	cs:code,ds:datas

; this is called by Kermit initialization.  It checks the
; number of disks on the system, sets the drives variable
; appropriately.  Returns normally.  

DODISK	PROC	NEAR
	mov ah,gcurdsk			; Current disk value to AL.
	int dos
	mov dl,al			; Put current disk in DL.
	mov ah,seldsk			; Select current disk.
	int dos				; Get number of drives in AL.
	mov drives,al
	ret
DODISK	ENDP

; Clear the input buffer. This throws away all the characters in the
; serial interrupt buffer.  This is particularly important when
; talking to servers, since NAKs can accumulate in the buffer.
; Do nothing since we are not interrupt driven.  Returns normally.

CLRBUF	PROC	NEAR
	ret
CLRBUF	ENDP

; Clear to the end of the current line.  Returns normally.

CLEARL	PROC	NEAR
	mov ah,prstr
	mov dx,offset clreol
	int dos
	ret
CLEARL	ENDP

; Put the char in AH to the serial port.  This assumes the
; port has been initialized.  Should honor xon/xoff.  Skip returns on
; success, returns normally if the character cannot be written.

outchr:	mov bp,portval
	cmp ds:[bp].floflg,0	; Are we doing flow control.
	je outch2		; No, just continue.
	xor cx,cx		; clear counter
outch1:	cmp xofrcv,true		; Are we being held?
	jne outch2		; No - it's OK to go on.
	loop outch1		; held, try for a while
	mov xofrcv,false	; timed out, force it off and fall thru.
outch2:	push dx			; Save register.
	mov al,ah		; Parity routine works on AL.
	call dopar		; Set parity appropriately.
	mov dl,al
	mov ah,punout		; Output char in DL to comm port.
	int dos
	pop dx
	jmp rskp

; This routine blanks the screen.  Returns normally.

CMBLNK	PROC	NEAR
	mov ah,prstr
	mov dx,offset crlf	; Can't do anything else.
	int dos
	ret
CMBLNK  ENDP

; Homes the cursor.  Returns normally.

LOCATE  PROC	NEAR
	mov dx,0		; Go to top left corner of screen.
	jmp poscur
LOCATE  ENDP

; Write a line at the bottom of the screen...  
; the line is passed in dx, terminated by a $.  Returns normally.
putmod	proc	near
	push	dx		; preserve message
	mov	dx,1800h	; now address line 24
	call	poscur
	pop	dx		; get message back
	mov	ah,prstr
	int	dos		; write it out
	ret			; and return
putmod	endp

; clear the mode line written by putmod.  Returns normally.
clrmod	proc	near
	mov	dx,1800h
	call	poscur		; Go to bottom row.
	call	clearl		; Clear to end of line.
	ret
clrmod	endp

; Put a help message on the screen.  
; Pass the message in ax, terminated by a null.  Returns normally.
puthlp	proc	near
	push	ax		; preserve this
	mov 	ah,prstr
	mov 	dx,offset crlf
	int 	dos
	pop	si		; point to string again
puthl3:	lodsb			; get a byte
	cmp	al,0		; end of string?
	je	puthl4		; yes, stop
	mov 	dl,al
	mov	ah,dconio
	int	dos		; else write to screen
	jmp	puthl3		; and keep going
puthl4:	mov 	ah,prstr
	mov 	dx,offset crlf
	int 	dos
	ret
puthlp	endp

; Set the baud rate for the current port, based on the value
; in the portinfo structure.  Returns normally.

DOBAUD	PROC	NEAR
	mov ah,prstr
	mov dx,offset noimp	; Say it's not implemented.
	int dos
	mov bx,portval
	mov [bx].baud,0FFFFH	; So it's not a recognized value.
	ret			; Must be set before starting Kermit.
DOBAUD	ENDP

; Get the current baud rate from the serial card and set it
; in the portinfo structure for the current port.  Returns normally.
; This is used during initialization.

GETBAUD	PROC	NEAR
	ret			; Can't do this.
GETBAUD	ENDP


; Use for DOS 2.0 and above.  Check the port status.  If no data, skip
; return.  Else, read in a char and return.
PRTCHR	PROC    NEAR
	push bx
	push cx
	push si
	push bp
	call chkxon
	mov bx,prthnd
	mov al,instat
	mov ah,ioctl
	int dos
	or al,al
	jz prtch4		; not ready...
	mov bx,prthnd
	mov ah,rddev
	mov cx,1
	mov dx,offset temp
	int dos
	cmp al,5		; Error condition.
	je prt3x
	cmp al,6		; Error condition
	je prt3x
	mov al,byte ptr temp
	mov bp,portval
	cmp ds:[bp].parflg,PARNON	; no parity?
	je prtch3		; then don't strip
	and al,7fh		; else turn off parity
prtch3:	pop bp
	pop si
	pop cx
	pop bx
	ret
prt3x:	mov ah,prstr
	mov dx,offset erms50
	int dos
prtch4:	pop bp
	pop si
	pop cx
	pop bx
	jmp rskp		; no chars...
PRTCHR  ENDP

; Local routine to see if we have to transmit an xon
chkxon	proc	near
	push	bx
	mov	bx,portval
	cmp	[bx].floflg,0	; doing flow control?
	je	chkxo1		; no, skip all this
	cmp	xofsnt,false	; have we sent an xoff?
	je	chkxo1		; no, forget it
	mov	ax,[bx].flowc	; ah gets xon
	call	outchr		; send it
	nop
	nop
	nop			; in case it skips
	mov	xofsnt,false	; remember we've sent the xon.
chkxo1:	pop	bx		; restore register
	ret			; and return
chkxon	endp

; Send a break out the current serial port.  Returns normally.
SENDBR	PROC	NEAR
	ret
SENDBR	ENDP

; Position the cursor according to contents of DX:
; DH contains row, DL contains column.  Returns normally.
POSCUR	PROC	NEAR
	ret
POSCUR	ENDP

; Delete a character from the terminal.  This works by printing
; backspaces and spaces.  Returns normally.

DODEL	PROC	NEAR
	mov ah,prstr
	mov dx,offset delstr	; Erase weird character.
	int dos			
	ret
DODEL	ENDP

; Move the cursor to the left margin, then clear to end of line.
; Returns normally.

CTLU	PROC	NEAR
	mov ah,prstr
	mov dx,offset clrlin
	int dos
	call clearl
	ret
CTLU	ENDP

; Set the current port.  

COMS	PROC	NEAR
        mov dx,offset comptab
        mov bx,0
        mov ah,cmkey
        call comnd
         jmp r
        push bx
        mov ah,cmcfm
        call comnd              ; Get a confirm.
         jmp comx		;  Didn't get a confirm.
	 nop
        pop bx
        mov flags.comflg,bl     ; Set the comm port flag.
	cmp flags.comflg,1	; Using Com 1?
	jne coms0		; Nope.
	mov ax,offset port1
	mov portval,ax
	ret
coms0:	mov ax,offset port2
	mov portval,ax
	ret
comx:	pop bx
	ret
COMS	ENDP

; Set heath emulation on/off.

VTS	PROC	NEAR
	jmp notimp
VTS	ENDP

notimp:	mov ah,prstr
	mov dx,offset noimp
	int dos
	jmp prserr

; Initialize variables to values used by the generic MS DOS version.

lclini:	mov flags.vtflg,0	; Don't to terminal emulation.
	call opnprt		; Get file handle for comm port.
	ret

; Get a file handle for the communications port.  Use DOS call to get the
; next available handle.  If it fails, ask user what value to use (there
; should be a predefined handle for the port, generally 3).  The open
; will fail if the system uses names other than "COM1" or "COM2".
opnprt:	mov al,flags.comflg
	mov ah,0
	mov si,ax
	shl si,1		; double index
	mov dx,prttab[si]
	mov ah,open
	mov al,2
	int dos
	jnc opnpr2
	mov ah,prstr		; It didn't like the string.
	mov dx,offset erms41
	int dos
	mov dx,offset hnd1
	int dos
opnpr0:	mov dx,offset hnd2	; Ask user to supply the handle.
	call prompt
	mov ah,cmtxt
	mov bx,offset rdbuf	; Where to put input.
	mov dx,offset hndhlp	; In case user wants help.
	call comnd
	 jmp opnpr3		; Maybe user typed a ^C.
	 nop
	mov si,offset rdbuf
	call atoi		; Convert to real number
	 jmp opnpr0		; Keep trying. 
	 nop
	mov prthnd,ax		; Value returned in AX
	ret
opnpr2:	mov prthnd,ax		; Call succeeded.
	ret
opnpr3:	cmp flags.cxzflg,'C'	; Did user type a ^C?
	jne opnpr4		; No, don't say anything.
	mov ah,prstr		; Else, issue a warning.
	mov dx,offset hnderr
	int dos
opnpr4:	ret			; Yes, fail.

showkey:
	mov ax,offset shkmsg
	mov cx,shklen
	ret

; Initialization for using serial port.  Returns normally.
SERINI	PROC	NEAR
	cld			; Do increments in string operations
	call clrbuf		; Clear input buffer. 
	ret			; We're done.
SERINI	ENDP

; Reset the serial port.  This is the opposite of serini.  Calling
; this twice without intervening calls to serini should be harmless.
; Returns normally.

SERRST	PROC	NEAR
	ret			; All done.
SERRST	ENDP

; Produce a short beep.  The PC DOS bell is long enough to cause a loss
; of data at the port.  Returns normally.

BEEP	PROC	NEAR
	mov dl,bell
	mov ah,dconio
	int dos
	ret
BEEP	ENDP 
 
; Dumb terminal emulator.  Doesn't work too well above 1200 baud (and
; even at 1200 baud you sometimes lose the first one or two characters
; on a line).  
term	proc	near
	mov si,ax		; this is source
	mov di,offset ourarg	; place to store arguments
	mov ax,ds
	mov es,ax		; address destination segment
	mov cx,size termarg
	rep movsb		; copy into our arg blk
term1:	call prtchr
	jmp short term2		; have a char...
	nop
	nop
	jmp short term3		; no char, go on
term2:	push ax
	and al,7fh		; mask off parity for terminal
	mov dl,al
	mov ah,conout
	int dos			; go print it
	pop ax
	test ourarg.flgs,capt	; capturing output?
	jz term3		; no, forget it
	call ourarg.captr	; else call the routine
term3:	mov ah,dconio
	mov dl,0ffh
	int dos
	jz term1		; no character, go on
	cmp al,ourarg.escc	; escape char?
	je term4		; yes, exit
	push ax			; save char
	mov ah,al
	or ah,80H		; turn on hi bit so DOS doesn't interfere
	call outchr		; output the character
	nop
	nop
	nop
	pop ax
	test ourarg.flgs,lclecho ; echoing?
	jz term1		; no, continue loop
	mov dl,al
	mov ah,dconio
	int dos
	jmp term1		; else echo and keep going
term4:	ret
term	endp

; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.
 
RSKP    PROC    NEAR
	pop bp
	add bp,3
	push bp
        ret
RSKP    ENDP
 
; Jumping here is the same as a ret.
 
R       PROC    NEAR
        ret
R       ENDP

code	ends 
	end//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msxgen.asm
	/bin/echo -n '	'; /bin/ls -ld msxgen.asm
fi

knutson@ut-ngp.UUCP (Jim Knutson) (10/05/84)

: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting msxhp150.asm'
sed 's/^X//' <<'//go.sysin dd *' >msxhp150.asm
	public	serini, serrst, clrbuf, outchr, coms, vts, dodel, ctlu
	public	cmblnk, locate, prtchr, dobaud, clearl, lclini
	public	dodisk, getbaud, beep, setkhlp, setktab
	public	machnam, xofsnt, count, term, poscur
	public	clrmod, putmod, puthlp, sendbr, showkey
	include msdefs.h

false	equ	0
true	equ	1

wrdev	equ	40H
rddev	equ	3fH
open	equ	3dH
close	equ	3eH
rdchan	equ	2
e_send_break equ 6
e_ioctl	equ	44h		; MSODS io control fct

datas 	segment	public 'datas'
	extrn	drives:byte, flags:byte, trans:byte
	extrn	portval:word, port1:byte, port2:byte
machnam	db	'HP-150$'
erms20	db	cr,lf,'?Warning: System has no disk drives$' ; [21a]
erms40	db	cr,lf,'?Warning: Unrecognized baud rate$'
erms41	db	cr,lf,'?Warning: Cannot open com port$'
noimp	db	cr,lf,'Command not implemented.$'
setktab	db	0
setkhlp	db	0
shkmsg	db	'Not implemented.'
shklen	equ	$-shkmsg
crlf    db      cr,lf,'$'
comphlp	db	cr,lf,'1 (COM1)   2 (COM2)$'		; [19b]
delstr  db      BS,BS,'  ',BS,BS,'$' 	; Delete string. [21d]
clrlin  db      cr,esc,'K$'
xofsnt	db	0		; Say if we sent an XOFF.
xofrcv	db	0		; Say if we received an XOFF.
invseq	db	esc,'&dB$'	; Reverse video.
nrmseq	db	esc,'&d@$'	; Normal mode.
ivlseq	db	80 dup (' '),cr,'$' 	; Make a line inverse video
tmp	db	?,'$'
temp	dw	0
temp1   dw      ?               ; Temporary storage.
temp2   dw      ?               ; Temporary storage.

; Entries for choosing communications port. [19b]

comptab	db	04H
	db	01H,'1$'
	dw	01H
	db	01H,'2$'
	dw	00H
	db	04H,'COM1$'
	dw	01H
 	db	04H,'COM2$'
	dw	00H

; variables for serial interrupt handler

source	db	bufsiz DUP(?)	; Buffer for data from port.
bufout	dw	0		; buffer removal ptr
count	dw	0		; Number of chars in int buffer.
bufin	dw	0		; buffer insertion ptr
telflg	db	0		; Are we acting as a terminal. [16] [17c]
clreol	db	esc,'K$'
prttab	dw	com2,com1
com1	db	'COM1',0
com2	db	'COM2',0
blank	db	esc,'H',esc,'J$'
movcur	db	esc,'&a'
colno	db	20 dup (?)
ten	db	10
prthnd	dw	0
tempbuf dw	10 dup(?)
ourarg	termarg	<>
datas	ends

code	segment	public
	extrn	comnd:near, dopar:near, prserr:near
	assume	cs:code,ds:datas

; See how many disk drives we have.
DODISK	PROC	NEAR
	mov ah,gcurdsk			; Current disk value to AL.
	int dos
	mov dl,al			; Put current disk in DL.
	mov ah,seldsk			; Select current disk.
	int dos				; Get number of drives in AL.
	mov drives,al
	ret
DODISK	ENDP

; Clear the input buffer before sending a packet. [20e]

CLRBUF	PROC	NEAR
	cli
	mov ax,offset source
	mov bufin,ax
	mov bufout,ax
	mov count,0
	sti
clrb1:	call prtchr		; get a character
	 jmp clrb1		; until there aren't any more
	 nop
	ret
CLRBUF	ENDP

; Common routine to clear to end-of-line. [19a]

CLEARL	PROC	NEAR
	mov dx,offset clreol
	mov ah,prstr
	int dos
	ret
CLEARL	ENDP

dobaud	proc	near
	jmp notimp
dobaud	endp

; Send the break signal out data comm.
sendbr:	mov al,e_send_break
	jmp  dc_ioctl

; Set some data comm ioctl option.  AL has function code.
dc_ioctl proc	near
	mov ah,8h
	mov tempbuf,ax
	mov dx,offset tempbuf
	mov ah,e_ioctl
	mov al,3
	mov bx,prthnd
	mov cx,2
	int 21h
	ret
dc_ioctl endp


outchr:	mov bp,portval
	cmp ds:[bp].floflg,0	; Are we doing flow control.
	je outch2		; No, just continue.
	xor cx,cx		; clear counter
outch1:	cmp xofrcv,true		; Are we being held?
	jne outch2		; No - it's OK to go on.
	loop outch1		; held, try for a while
	mov xofrcv,false	; timed out, force it off and fall thru.
outch2:	push dx			; Save register.
	push cx
	push bx
	cmp prthnd,0		; do we have a port handle?
	jne outch3		; yes, go on
	push ax
	call opnprt		; open the port
	pop ax
outch3: mov byte ptr temp,ah	; save character
	mov bx,prthnd
	mov ah,wrdev
	mov cx,1
	mov dx,offset temp
	int dos
	pop bx
	pop cx
	pop dx
	jmp rskp

opnprt:	mov al,flags.comflg
	mov ah,0
	mov si,ax
	shl si,1		; double index
	mov dx,prttab[si]
	mov ah,open
	mov al,2
	int dos
	jnc opnpr1
	mov ah,prstr
	mov dx,offset erms41
	int dos
	ret
opnpr1:	mov prthnd,ax
	ret

; This routine blanks the screen.

CMBLNK	PROC	NEAR		; This is stolen from the IBM example.
	mov ah,prstr
	mov dx,offset blank
	int dos
	ret
CMBLNK  ENDP

LOCATE  PROC	NEAR
	mov dx,0		; Go to top left corner of screen.
	jmp poscur		; callret...
LOCATE  ENDP

GETBAUD	PROC	NEAR
	ret
GETBAUD	ENDP


; skip returns if no character available at port,
; otherwise returns with char in al, # of chars in buffer in dx.
PRTCHR	PROC    NEAR
	push bx
	push cx
	push si
	push bp
	cmp count,0		; no characters?
	jne prtch2		; no, go fill buffer
	cmp prthnd,0		; have a handle yet?
	jne prtch1		; yes, keep going
	call opnprt
prtch1:	mov bx,prthnd
	mov al,rdchan
	mov ah,ioctl
	mov dx,offset source	; buffer to read into
	mov cx,bufsiz		; length of buffer
	int dos
	mov count,ax		; reset count
	or ax,ax
	jz prtch4		; still no chars
	mov bufout,offset source ; this is output ptr
prtch2:	dec count
	mov dx,count		; return count in dx
	mov si,bufout
	lodsb			; get character
	mov bufout,si		; update ptr
	mov bp,portval
	cmp ds:[bp].parflg,PARNON	; no parity?
	je prtch3		; then don't strip
	and al,7fh		; else turn off parity
prtch3:	pop bp
	pop si
	pop cx
	pop bx
	ret
prtch4:	pop bp
	pop si
	pop cx
	pop bx
	jmp rskp		; no chars...
PRTCHR  ENDP

; Position the cursor according to contents of DX.

POSCUR	PROC	NEAR
	mov	ax,ds
	mov	es,ax			; address data segment!!!
	cld
	mov	di,offset colno
	mov	al,dl			; column
	call	nout
	mov	al,'c'
	stosb
	mov	al,dh			; row
	call	nout
	mov	al,'Y'
	stosb
	mov	al,'$'
	stosb
	mov	dx,offset movcur
	mov	ah,prstr
	int	dos			; print the sequence
	ret
POSCUR	ENDP

NOUT	PROC	NEAR
	cbw			; extend to word
	div	byte ptr ten	; divide by 10
	or	al,al		; any quotient?
	jz	nout1		; no, forget this
	push	ax		; save current result
	call	nout		; output high order
	pop	ax		; restore
nout1:	mov	al,ah		; get digit
	add	al,'0'		; make printable
	stosb
	ret			; put in buffer and return
NOUT	endp

; Write a line in inverse video at the bottom of the screen...
; the line is passed in dx, terminated by a $.  Returns normally.
putmod	proc	near
	push	dx		; preserve message
	mov	dx,24 * 100H	; line 24
	call	poscur
	mov	dx,offset invseq ; put into inverse video
	mov	ah,prstr
	int	dos
	pop	dx
	int 	dos
	mov	dx,offset nrmseq ; normal videw
	int	dos
	ret			; and return
putmod	endp

; Clear the mode line written by putmod.  Returns normally.
clrmod	proc	near
	mov	dx,24 * 100H
	call	poscur
	call	clearl
	ret
clrmod	endp

; Put a help message one the screen in reverse video.  Pass
; the message in AX, terminated by a null.  Returns normally.
; The message is put wherever the cursor currently is located.
puthlp	proc	near
	push ax
	mov ah,prstr		; Leave some room before the message.
	mov dx,offset crlf
	int dos
	pop si			; Put message address here.
puth0:	mov ah,prstr
	mov dx,offset invseq	; Put into reverse video.
	int dos
	mov ah,prstr
	mov dx,offset ivlseq	; Make line inverse video
	int dos
puth1:	lodsb
	cmp al,0		; Terminated with a null.
	je puth2
	mov dl,al
	mov ah,conout
	int dos	
	cmp al,lf		; Line feed?
	je puth0		; Yes, clear the next line.
	jmp puth1		; Else, just keep on writing.
puth2:	mov dx,offset crlf
	mov ah,prstr
	int dos
	mov dx,offset nrmseq	; Normal video.
	int dos
	ret
puthlp	endp

; Perform a delete.

DODEL	PROC	NEAR
	mov ah,prstr
	mov dx,offset delstr	; Erase weird character.
	int dos			
	ret
DODEL	ENDP

; Perform a Control-U.

CTLU	PROC	NEAR
	mov ah,prstr
	mov dx,offset clrlin
	int dos
	ret
CTLU	ENDP

COMS	PROC	NEAR
        mov dx,offset comptab
        mov bx,offset comphlp
        mov ah,cmkey
        call comnd
         jmp r
        push bx
        mov ah,cmcfm
        call comnd              ; Get a confirm.
         jmp comx		;  Didn't get a confirm.
	 nop
        pop bx
        mov flags.comflg,bl     ; Set the comm port flag.
	cmp flags.comflg,1	; Using Com 1?
	jne coms0		; Nope.
	mov ax,offset port1
	mov portval,ax
	ret
coms0:	mov ax,offset port2
	mov portval,ax
	ret
comx:	pop bx
	ret
COMS	ENDP

VTS	PROC	NEAR
	jmp notimp
VTS	ENDP

notimp:	mov ah,prstr
	mov dx,offset noimp
	int dos
	jmp prserr

lclini:	ret

showkey:
	mov ax,offset shkmsg
	mov cx,shklen
	ret

;  Common initialization for using serial port.

SERINI	PROC	NEAR
	call opnprt
	call clrbuf		; Clear input buffer. [20e]
	ret			; We're done. [21c]
SERINI	ENDP

SERRST	PROC	NEAR
	mov bx,prthnd
	cmp bx,0		; none there?
	je serrs1		; no, don't try to close.
	mov ah,close
	int dos			; close handle
	mov prthnd,0
serrs1:	ret			; All done. [21c]
SERRST	ENDP

; Generate a short beep.

BEEP	PROC	NEAR
	mov dl,bell
	mov ah,conout
	int dos	
	ret
BEEP	ENDP 
 
; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.
 
RSKP    PROC    NEAR
	pop bp
	add bp,3
	push bp
        ret
RSKP    ENDP
 
; Jumping here is the same as a ret.
 
R       PROC    NEAR
        ret
R       ENDP

term	proc	near
	mov si,ax		; this is source
	mov di,offset ourarg	; place to store arguments
	mov ax,ds
	mov es,ax		; address destination segment
	mov cx,size termarg
	rep movsb		; copy into our arg blk
term1:	call prtchr
	jmp short term2		; have a char...
	nop
	nop
	jmp short term3		; no char, go on
term2:	push ax
	mov dl,al
	and dl,7fh		; mask off parity for terminal
	mov ah,dconio
	int dos			; write out the character
	pop ax
	test ourarg.flgs,capt	; capturing output?
	jz term3		; no, forget it
	call ourarg.captr	; else call the routine
term3:	mov ah,dconio
	mov dl,0ffh
	int dos
	jz term1		; no character, go on
	cmp al,ourarg.escc	; escape char?
	je term4		; yes, exit
	push ax			; save char
	mov ah,al
	or ah,80H		; turn on hi bit so DOS doesn't interfere
	call outchr		; output the character
	nop
	nop
	nop
	pop ax
	test ourarg.flgs,lclecho ; echoing?
	jz term1		; no, continue loop
	mov dl,al
	mov ah,dconio
	int dos
	jmp term1		; else echo and keep going
term4:	ret
term	endp
code	ends 
	end
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msxhp150.asm
	/bin/echo -n '	'; /bin/ls -ld msxhp150.asm
fi
/bin/echo 'Extracting msxibm.asm'
sed 's/^X//' <<'//go.sysin dd *' >msxibm.asm
; Kermit system dependent module for IBM-PC

	public	serini, serrst, clrbuf, outchr, coms, vts, dodel,
	public	ctlu, cmblnk, locate, prtchr, dobaud, clearl,
	public	dodisk, getbaud, beep,
	public	count, xofsnt, puthlp, putmod, clrmod, poscur
	public	sendbr, machnam, setktab, setkhlp, lclini, showkey
	include msdefs.h

false	equ	0
true	equ	1
mntrgh	equ	bufsiz*3/4	; High point = 3/4 of buffer full.

; constants used by serial port handler

BRKBIT	EQU	040H		; Send-break bit. 
TIMER	EQU	40H		; Use to issue short beep.
PORT_B	EQU	61H		; Port B address.
MCONF	EQU	11H		; Machine configuration. 
KEYB	EQU	16H
BIOS	EQU	10H

MDMDAT1	EQU	03F8H		; Address of modem port (data). [19b]
MDMSTS1	EQU	03FDH		; Address of modem port	status. [19b]
MDMCOM1	EQU	03FBH		; Address of modem port command. [19b]
MDMDAT2	EQU	02F8H		; Port 2 address. [19b]
MDMSTS2	EQU	02FDH		; Port 2 status. [19b]
MDMCOM2	EQU	02FBH		; Port 2 command. [19b]
MDMINP	EQU	1		; Input ready bit.

MDMINTV	EQU	0030H		; Address of modem port interrupt vector.
MDINTV2 EQU	002CH		; Address for port 2. [19b] 
MDMINTO	EQU	0EFH		; Mask to enable interrupt for modem port.
MDINTO2 EQU	0F7H		; Enable interrupt level 3. [19b]
MDMINTC	EQU	010H		; Bit to set to disable interrupts for modem.
MDINTC2 EQU	008H		; Disable IRQ3. [19b]

INTCONT	EQU	0021H		; Address of 8259 interrupt controller ICW2-3.
INTCON1	EQU	0020H		; Address of 8259 ICW1.
EOICOM	EQU	0064H		; End of interrupt.
EOICOM2	EQU	0063H		; End of interrupt for COM2. [19b]

; external variables used:
; drives - # of disk drives on system
; flags - global flags as per flginfo structure defined in pcdefs
; trans - global transmission parameters, trinfo struct defined in pcdefs
; portval - pointer to current portinfo structure (currently either port1
;    or port2)
; port1, port2 - portinfo structures for the corresponding ports

; global variables defined in this module:
; xofsnt, xofrcv - tell whether we saw or sent an xoff.
; setktab - keyword table for redefining keys (should contain a 0 if
;    not implemented)
; setkhlp - help for setktab.

datas 	segment	public 'datas'
	extrn	drives:byte,flags:byte, trans:byte
	extrn	portval:word, port1:byte, port2:byte

setktab	db	12
	mkeyw	'BACKSPACE',0eh
	mkeyw	'F1',3bh
	mkeyw	'F2',3ch
	mkeyw	'F3',3dh
	mkeyw	'F4',3eh
	mkeyw	'F5',3fh
	mkeyw	'F6',40h
	mkeyw	'F7',41h
	mkeyw	'F8',42h
	mkeyw	'F9',43h
	mkeyw	'F10',44h
	mkeyw	'SCAN',-1

setkhlp	db	cr,lf,'Keyname: backspace, f1, ... f10, or "SCAN" follwed by '
	db	'decimal scan code$'
brkval	db	0		; What to send for a break.
brkadr	dw	0		; Where to send it.
modem	mdminfo	<MDMDAT1,MDMSTS1,MDMCOM1,MDMINTO,MDMINTC,EOICOM,MDMINTV>
erms20	db	cr,lf,'?Warning: System has no disk drives$' ; [21a]
erms40	db	cr,lf,'?Warning: Unrecognized baud rate$'
badbd	db	cr,lf,'Unimplemented baud rate$'
machnam	db	'IBM-PC$'
crlf	db	cr,lf,'$'
delstr  db	BS,' ',BS,'$' 	; Delete string. [21d]
clrlin  db	cr,'$'			; Clear line (just the cr part).
savsci	dw	?		; Save for serial port interrupt vector. [14]
savscs	dw	?		; Ditto.  [14]  
savbr1	dw	?		; "Break" interrupt vector. [25]
savbr2	dw	?		; Ditto. [25]
portin	db	0		; Has comm port been initialized. [21c]
xofsnt	db	0		; Say if we sent an XOFF.
xofrcv	db	0		; Say if we received an XOFF.
tmp	db	?,'$'
temp	dw	0
temp1	dw	?		; Temporary storage.
temp2	dw	?		; Temporary storage.

ontab	db	02H		; Two entries.
	db	03H,'OFF$'	; Should be alphabetized.  [19a]
	dw	00H
	db	02H,'ON$'
	dw	01H

comptab	db	04H
	db	01H,'1$'
	dw	01H
	db	01H,'2$'
	dw	00H
	db	04H,'COM1$'
	dw	01H
 	db	04H,'COM2$'
	dw	00H

; this table is indexed by the baud rate definitions given in
; pcdefs.  Unsupported baud rates should contain FF.
bddat	label	word
	dw	0FFH		; 45.5 baud  -- Not supported.
	dw	900H		; 50 baud
	dw	600H		; 75 baud
	dw	417H		; 110 baud
	dw	359H		; 134.5 baud
	dw	300H		; 150 baud
	dw	180H		; 300 baud
	dw	0C0H		; 600 baud
	dw	60H		; 1200 baud
	dw	40H		; 1800 baud
	dw	3AH		; 2000 baud
	dw	30H		; 2400 baud
	dw	18H		; 4800 baud
	dw	0CH		; 9600 baud
	dw	0FFH		; 19200 baud -- Not supported.
	dw	0FFH		; 38400 baud -- Not supported.

; variables for serial interrupt handler

source	db	bufsiz DUP(?)	; Buffer for data from port.
srcpnt	dw	0		; Pointer in buffer (DI).
count	dw	0		; Number of chars in int buffer.
savesi	dw	0		; Save SI register here.	
telflg	db	0		; Are we acting as a terminal.
mst	dw	0		; Modem status address.
mdat	dw	0		; Modem data address.
mdeoi	db	0		; End-of-Interrupt value.

rbtrn	db	7fH		; rubout

shkbuf	db	300 dup (?)	; room for definition
shkmsg	db	'  Scan code: '
shkmln	equ	$-shkmsg
shkms1	db	cr,lf,'  Definition: '
shkm1ln	equ	$-shkms1
datas	ends

code	segment	public
	extrn	comnd:near, dopar:near, defkey:near, gss:near
	assume	cs:code,ds:datas

; local initialization

lclini	proc	near
	mov	ax,0eH		; scan code for arrow key
	mov	si,offset rbtrn	; translate to rubout
	mov	cx,1		; one char translation
	call	defkey
	mov brkval,BRKBIT	; What to send for a break.
	mov ax,modem.mdcom	; Where to send it.
	mov brkadr,ax
	ret
lclini	endp

; this is called by Kermit initialization.  It checks the
; number of disks on the system, sets the drives variable
; appropriately.  Returns normally.  

DODISK	PROC	NEAR
	int mconf			; Get equipment configuration.
	mov ah,al			; Store AL value for a bit.
	and al,01H			; First, look at bit 0.
	jz dodsk0			; No disk drives -- forget it.
	mov al,ah			; Get back original value.
	mov cl,6			; Shift over bits 6 and 7.
	shr al,cl			; To positions 0 and 1.
	inc al				; Want 1 thru 4 (not 0 thru 3).
	mov drives,al			; Remember how many. 
	ret
dodsk0:	mov ah,prstr			; Print a warning message.
	mov dx,offset erms20		; I'm not sure if things will
	int dos				; work with only a cassette.
	mov drives,0			; Say there aren't any drives.
	ret
DODISK	ENDP

; show the definition of a key.  The terminal argument block (which contains
; the address and length of the definition tables) is passed in ax.
; Returns a string to print in AX, length of same in CX.
; Returns normally.
showkey	proc	near
	push	es
	push	ax		; save the ptr
	mov	bx,ds
	mov	es,bx		; address data segment
	cld
showk1:	xor	ah,ah
	int	keyb		; read a char
	push	ax		; save the character
	call	gss		; get shift state
	pop	bx
	mov	ah,al		; shift state to ah
	mov	al,bh		; scan code to al
	push	ax		; remember scan code
	mov	di,offset shkbuf
	mov	si,offset shkmsg
	mov	cx,shkmln
	rep	movsb		; copy in initial message
	call	nout		; write out scan code
	mov	si,offset shkms1
	mov	cx,shkm1ln	; second message
	rep	movsb
	pop	ax		; get scan code back
	pop	bx		; and terminal arg block
	mov	cx,[bx].klen	; and length
	jcxz	showk2		; no table, not defined
	push	di		; remember output ptr
	mov	di,[bx].ktab	; get key table
	repne	scasw		; search for a definition for this
	mov	si,di		; remember result ptr
	pop	di		; get output ptr back
	jne	showk2		; not defined, forget it
	sub	si,[bx].ktab	; compute offset from beginning
	sub	si,2		; minus 2 for pre-increment
	add	si,[bx].krpl	; get index into replacement table
	mov	si,[si]		; pick up replacement
	mov	cl,[si]		; get length
	mov	ch,0
	inc	si
	rep	movsb		; copy into buffer
showk2:	mov	ax,offset shkbuf ; this is buffer
	mov	cx,di
	sub	cx,ax		; length
	pop	es
	ret			; and return
showkey	endp

; Clear the input buffer. This throws away all the characters in the
; serial interrupt buffer.  This is particularly important when
; talking to servers, since NAKs can accumulate in the buffer.
; Returns normally.

CLRBUF	PROC	NEAR
	cli
	mov ax,offset source
	mov srcpnt,ax
	mov savesi,ax
	mov count,0
	sti
	ret
CLRBUF	ENDP

; Clear to the end of the current line.  Returns normally.

CLEARL	PROC	NEAR
	mov ah,3		; Clear to end of line.
	mov bh,0
	int bios		; Get current cursor position
	mov cx,dx
	mov dl,79
	mov ah,7
	mov al,0
	mov bh,7
	int bios
	ret
CLEARL	ENDP

; Put the char in AH to the serial port.  This assumes the
; port has been initialized.  Should honor xon/xoff.  Skip returns on
; success, returns normally if the character cannot be written.

outchr:	mov bp,portval
	cmp ds:[bp].floflg,0	; Are we doing flow control.
	je outch2		; No, just continue.
	xor cx,cx		; clear counter
outch1:	cmp xofrcv,true		; Are we being held?
	jne outch2		; No - it's OK to go on.
	loop outch1		; held, try for a while
	mov xofrcv,false	; timed out, force it off and fall thru.
outch2:	push dx			; Save register.
	sub cx,cx
	mov al,ah		; Parity routine works on AL.
	call dopar		; Set parity appropriately.
	mov ah,al		; Don't overwrite character with status.
	mov dx,modem.mdstat	; Get port status.
outch3:	in al,dx
	test al,20H		; Transmitter ready?
	jnz outch4		; Yes
	loop outch3
	 jmp outch5		; Timeout
outch4:	mov al,ah		; Now send it out
	mov dx,modem.mddat
	out dx,al
	pop dx
	jmp rskp
outch5:	pop dx
	ret

; This routine blanks the screen.  Returns normally.

CMBLNK	PROC	NEAR		; This is stolen from the IBM example.
	mov cx,0
	mov dx,184FH
	mov bh,7
	mov ax,600H
	int bios
	ret
CMBLNK  ENDP

; Locate: homes the cursor.  Returns normally.

LOCATE  PROC	NEAR
	mov dx,0		; Go to top left corner of screen.
	jmp poscur
LOCATE  ENDP

; write a line in inverse video at the bottom of the screen...
; the line is passed in dx, terminated by a $.  Returns normally.
putmod	proc	near
	push	dx		; preserve message
	mov	cx,1800h
	mov	dx,184fh
	mov	ax,600h		; scroll to clear the line
	mov	bh,70h		; inverse video
	int	bios
	mov	dx,1800h	; now address line 24
	call	poscur
	pop	dx		; get message back
	mov	ah,prstr
	int	dos		; write it out
	ret			; and return
putmod	endp

; clear the mode line written by putmod.  Returns normally.
clrmod	proc	near
	mov	cx,1800h
	mov	dx,184fh
	mov	ax,600h
	mov	bh,7h
	int	bios
	ret
clrmod	endp

; put a help message on the screen.  This one uses reverse video...
; pass the message in ax, terminated by a null.  Returns normally.
puthlp	proc	near
	push	ax		; preserve this
	mov	si,ax		; point to it
	mov	dh,1		; init counter
puthl1:	lodsb			; get a byte
	cmp	al,lf		; linefeed?
	jne	puthl2		; no, keep going
	inc	dh		; count it
	jmp	puthl1		; and keep looping
puthl2:	cmp	al,0		; end of string?
	jne	puthl1		; no, keep going
	mov	ax,600h		; scroll to clear window
	xor	cx,cx		; from top left
	mov	dl,4fh		; to bottom right of needed piece
	mov	bh,70h		; inverse video
	int	bios
	call	locate		; home cursor
	pop	si		; point to string again
puthl3:	lodsb			; get a byte
	cmp	al,0		; end of string?
	je	puthl4		; yes, stop
	mov	ah,14
	int	bios		; else write to screen
	jmp	puthl3		; and keep going
puthl4:	mov	dx,24 * 100H	; go to last line
	jmp	poscur		; position and return
puthlp	endp

; Set the baud rate for the current port, based on the value
; in the portinfo structure.  Returns normally.

DOBAUD	PROC	NEAR
	mov bp,portval
	mov temp1,ax		; Don't overwrite previous rate. [25]
	mov ax,ds:[bp].baud	; Check if new rate is valid. [25]
	mov tmp,2
	mul tmp			; Get index into baud table.
	mov bx,offset bddat	; Start of table.
	add bx,ax
	mov ax,[bx]		; The data to output to port.
	cmp ax,0FFH		; Unimplemented baud rate.
	jne dobd0
	mov ax,temp1		; Get back orginal value.
	mov ds:[bp].baud,ax	; Leave baud rate as is.
	mov ah,prstr
	mov dx,offset badbd	; Give an error message.
	int dos
	ret
dobd0:	mov temp1,ax		; Remember value to output. [25]
	mov dx,modem.mdcom	; LCR -- Initialize baud rate. [19b]
	in al,dx
	mov bl,al
	or ax,80H
	out dx,al
	mov dx,modem.mddat	; [19b]
	mov ax,temp1
	out dx,al
	inc dx
	mov al,ah
	out dx,al
	mov dx,modem.mdcom	; [19b]
	mov al,bl
	out dx,al
	ret
DOBAUD	ENDP

; Get the current baud rate from the serial card and set it
; in the portinfo structure for the current port.  Returns normally.
; This is used during initialization.

GETBAUD	PROC	NEAR
	mov dx,modem.mdcom	; Get current Line Control Register value.
	in al,dx
	mov bl,al		; Save it.
	or ax,80H		; Turn on to access baud rate generator.
	out dx,al
	mov dx,modem.mddat	; Divisor latch.
	inc dx
	in al,dx		; Get hi order byte.
	mov ah,al		; Save here.
	dec dx
	in al,dx		; Get lo order byte.
	push ax	
	mov dx,modem.mdcom
	mov al,bl		; Restore old value.
	out dx,al
	pop ax
	cmp ax,0FFFFH		; Who knows what this is.
	je getb2
	mov bx,offset bddat	; Find rate's offset into table.
	mov cl,0		; Keep track of index.
getb0:	cmp ax,[bx]
	je getb1
	inc cl
	cmp cl,baudsiz		; At the end of the list.
	jge getb2
	add bx,2
	jmp getb0
getb1:	mov ch,0
	mov bp,portval
	mov ds:[bp].baud,cx	; Set baud rate.
	ret
getb2:	mov ah,prstr
	mov dx,offset erms40
	int dos
	ret
GETBAUD	ENDP

; skip returns if no character available at port,
; otherwise returns with char in al, # of chars in buffer in dx.
PRTCHR  PROC	NEAR
	call chkxon		; see if we need to xon
	cmp count,0
	jnz prtch2
	jmp rskp		; No data - check console.
prtch2:	mov si,savesi
	lodsb			; get a byte
	cmp si,offset source + bufsiz	; bigger than buffer?
	jb prtch1		; no, keep going
	mov si,offset source	; yes, wrap around
prtch1:	dec count
	mov savesi,si 
	mov dx,count		; return # of chars in buffer
	ret
PRTCHR  ENDP

; local routine to see if we have to transmit an xon
chkxon	proc	near
	push	bx
	mov	bx,portval
	cmp	[bx].floflg,0	; doing flow control?
	je	chkxo1		; no, skip all this
	cmp	xofsnt,false	; have we sent an xoff?
	je	chkxo1		; no, forget it
	cmp	count,mntrgh	; below trigger?
	jae	chkxo1		; no, forget it
	mov	ax,[bx].flowc	; ah gets xon
	call	outchr		; send it
	nop
	nop
	nop			; in case it skips
	mov	xofsnt,false	; remember we've sent the xon.
chkxo1:	pop	bx		; restore register
	ret			; and return
chkxon	endp

; Send a break out the current serial port.  Returns normally.
SENDBR	PROC	NEAR
	push cx
	push dx
	push ax
	xor cx,cx		; Clear loop counter.
	mov dx,brkadr		; Port address.  [19b]
	in al,dx		; Get current setting.
	or al,brkval		; Set send-break bit(s).
	out dx,al		; Start the break.
pause:	loop pause		; Wait a while.
	xor al,brkval		; Clear send-break bit(s).
	out dx,al		; Stop the break.
	pop ax
	pop dx
	pop cx
	ret			; And return.
SENDBR	ENDP

; Position the cursor according to contents of DX:
; DH contains row, DL contains column.  Returns normally.

POSCUR	PROC	NEAR
	mov ah,2		; Position cursor.
	mov bh,0
	int bios
	ret
POSCUR	ENDP

; Delete a character from the terminal.  This works by printing
; backspaces and spaces.  Returns normally.

DODEL	PROC	NEAR
	mov ah,prstr
	mov dx,offset delstr	; Erase weird character.
	int dos			
	ret
DODEL	ENDP

; Move the cursor to the left margin, then clear to end of line.
; Returns normally.

CTLU	PROC	NEAR
	mov ah,prstr
	mov dx,offset clrlin
	int dos
	call clearl
	ret
CTLU	ENDP

; set the current port.  

COMS	PROC	NEAR
	mov dx,offset comptab
	mov bx,0
	mov ah,cmkey
	call comnd
	 jmp r
	push bx
	mov ah,cmcfm
	call comnd		; Get a confirm.
	 jmp comx		;  Didn't get a confirm.
	 nop
	pop bx
	mov flags.comflg,bl	; Set the comm port flag.
	cmp flags.comflg,1	; Using Com 1?
	jne coms0		; Nope.
	mov ax,offset port1
	mov portval,ax
	mov modem.mddat,MDMDAT1	; Set COM1 defaults.
	mov modem.mdstat,MDMSTS1
	mov modem.mdcom,MDMCOM1
	mov modem.mddis,MDMINTC
	mov modem.mden,MDMINTO
	mov modem.mdmeoi,EOICOM
	mov modem.mdintv,MDMINTV
	mov brkadr,MDMCOM1
	ret
coms0:	mov ax,offset port2
	mov portval,ax
	mov modem.mddat,MDMDAT2	; Set COM2 defaults.
	mov modem.mdstat,MDMSTS2
	mov modem.mdcom,MDMCOM2
	mov modem.mddis,MDINTC2
	mov modem.mden,MDINTO2
	mov modem.mdmeoi,EOICOM2
	mov modem.mdintv,MDINTV2
	mov brkadr,MDMCOM2
	ret
comx:	pop bx
	ret
COMS	ENDP

; Set heath emulation on/off.

VTS	PROC	NEAR
	mov dx,offset ontab
	mov bx,0
	mov ah,cmkey
	call comnd
	 jmp r
	push bx
	mov ah,cmcfm
	call comnd		; Get a confirm.
	 jmp vt0		;  Didn't get a confirm.
	 nop
	pop bx
	mov flags.vtflg,bl	; Set the VT52 emulation flag.
	ret
vt0:	pop bx
	ret
VTS	ENDP

; initialization for using serial port.  This routine performs
; any initialization necessary for using the serial port, including
; setting up interrupt routines, setting buffer pointers, etc.
; Doing this twice in a row should be harmless (this version checks
; a flag and returns if initialization has already been done).
; SERRST below should restore any interrupt vectors that this changes.
; Returns normally.

SERINI	PROC	NEAR
	push es
	cmp portin,0		; Did we initialize port already? [21c]
	jne serin0		; Yes, so just leave. [21c]
	cli			; Disable interrupts
	cld			; Do increments in string operations
	xor ax,ax		; Address low memory
	mov es,ax
	mov bx,modem.mdintv	; Save serial card interrupt vector. [19b]
	mov ax,es:[bx]
	mov savsci,ax
	mov ax,offset serint	; And point it to my routine
	mov es:[bx],ax
	add bx,2		; Save CS register too. [19b]
	mov ax,es:[bx]
	mov savscs,ax
	mov es:[bx],cs
	mov portin,1		; Remember port has been initialize. 
	call clrbuf		; Clear input buffer. 
	mov ax,modem.mdstat
	mov mst,ax		; Use this address for status. 
	mov ax,modem.mddat
	mov mdat,ax		; Use this address for data. 
	mov al,modem.mdmeoi
	mov mdeoi,al		; Use to signify end-of-interrupt. 
	in al,21H		; Set up 8259 interrupt controller
	and al,modem.mden	; Enable INT3 or INT4. 
	out 21H,al
	mov dx,modem.mdcom	; Set up the serial card. 
	mov al,3
	out dx,al
	mov dl,0F9H
	mov al,1		; Set up interrupt enable register
	out dx,al
	mov dl,0FCH		; Enable interrupts from serial card
	mov al,0BH
	out dx,al
	sti			; Allow interrupts
	mov dl,0F8H
	in al,dx
serin0:	pop es
	ret			; We're done.
SERINI	ENDP

; Reset the serial port.  This is the opposite of serini.  Calling
; this twice without intervening calls to serini should be harmless.
; Returns normally.

SERRST	PROC	NEAR
	push es			; preserve this
	cmp portin,0		; Reset already? 
	je srst1		; Yes, just leave. 
	cli			; Disable interrupts
	mov dx,03FCH		; Disable modem interrupts
	cmp flags.comflg,1	; Using port 1 ? 
	je srst0		; Yes - continue. 
	mov dh,02		; Set for port 2. 
srst0:	mov al,3
	out dx,al
	in al,21H		; Interrupt controller
	or al,modem.mddis	; Inhibit IRQ3 or IRQ4. 
	out 21H,al
	xor bx,bx		; Address low memory
	mov es,bx
	mov bx,modem.mdintv	; Restore the serial card int vector 
	mov ax,savsci
	mov es:[bx],ax
	add bx,2		; Restore CS too. 
	mov ax,savscs
	mov es:[bx],ax
	mov portin,0		; Reset flag.
	sti
srst1:	pop es
	ret			; All done.
SERRST	ENDP

; serial port interrupt routine.  This is not accessible outside this
; module, handles serial port receiver interrupts.

SERINT  PROC  NEAR
	push bx
	push dx
	push ax
	push es
	push di
	push ds
	push bp
	push cx
	cld
	mov ax,seg datas
	mov ds,ax		; address data segment
	mov es,ax
	mov di,srcpnt		; Registers for storing data.
	mov dx,mst		; Asynch status port. [19b]
	in al,dx
	test al,mdminp		; Data available?
	jz retint		; Nope.
	mov dx,mdat		; [19b]
	in al,dx
	cmp telflg,0		; File transfer or terminal mode? [17c]
	jz srint0
	and al,7FH		; Terminal mode (7 bits only). 
srint0: or al,al
	jz retint		; Ignore nulls.
	mov ah,al
	and ah,7fH		; strip parity temporarily
	cmp ah,7FH		; Ignore rubouts, too.
	jz retint
	mov bp,portval
	cmp ds:[bp].floflg,0	; Doing flow control?
	je srint2		; Nope.
	mov bx,ds:[bp].flowc	; Flow control char (BH = XON, BL = XOFF).
	cmp al,bl		; Is it an XOFF?
	jne srint1		; Nope, go on.
	mov xofrcv,true		; Set the flag.
	jmp retint
srint1:	cmp al,bh		; Get an XON?
	jne srint2		; No, go on.
	mov xofrcv,false	; Clear our flag.
	jmp retint
srint2:	stosb
	cmp di,offset source + bufsiz
	jb srint3		; not past end...
	mov di,offset source	; wrap buffer around
srint3:	inc count
	cmp ds:[bp].floflg,0	; Doing flow control?
	je retint		; No, just leave.
	cmp xofsnt,true		; Have we sent an XOFF?
	je retint		; Yes.
	cmp count,mntrgh	; Past the high trigger point?
	jbe retint		; No, we're within our limit.
	mov ah,bl		; Get the XOFF.
	call outchr		; Send it.
	nop
	nop
	nop			; ignore failure.
	mov xofsnt,true		; Remember we sent it.
retint:	mov srcpnt,di
	sti
	mov al,mdeoi		; [19b]
	out intcon1,al		; Send End-of-Interrupt to 8259.
	pop cx
	pop bp
	pop ds
	pop di
	pop es
	pop ax
	pop dx
	pop bx
intret:	iret
SERINT	ENDP

; Produce a short beep.  The PC DOS bell is long enough to cause a loss
; of data at the port.  Returns normally.

BEEP	PROC	NEAR
	mov al,10110110B	; Gen a short beep (long one losses data.)
	out timer+3,al		; Code snarfed from Technical Reference.
	mov ax,533H
	out timer+2,al
	mov al,ah
	out timer+2,al
	in al,port_b
	mov ah,al
	or al,03
	out port_b,al
	sub cx,cx
	mov bl,1
beep0:  loop beep0
	dec bl		
	jnz beep0
	mov al,ah
	out port_b,al
	ret
BEEP	ENDP 
 
; put the number in ax into the buffer pointed to by di.  Di is updated
nout	proc	near
	mov	dx,0		; high order is always 0.
	mov	bx,10
	div	bx		; divide to get digit
	push	dx		; save remainder digit
	or	ax,ax		; test quotient
	jz	nout1		; zero, no more of number
	call	nout		; else call for rest of number
nout1:	pop	ax		; get digit back
	add	al,'0'		; make printable
	stosb			; drop it off
	ret			; and return
nout	endp

; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.
 
RSKP	PROC	NEAR
	pop bp
	add bp,3
	push bp
	ret
RSKP	ENDP
 
; Jumping here is the same as a ret.
 
R	PROC	NEAR
	ret
R	ENDP

code	ends 
	end
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msxibm.asm
	/bin/echo -n '	'; /bin/ls -ld msxibm.asm
fi
/bin/echo 'Extracting msxrb.asm'
sed 's/^X//' <<'//go.sysin dd *' >msxrb.asm
; Kermit system dependent module for Rainbow
; Jeff Damens, July 1984

	public	serini, serrst, clrbuf, outchr, coms, vts, dodel,
	public	ctlu, cmblnk, locate, lclini, prtchr, dobaud, clearl,
	public	dodisk, getbaud, beep,
	public	count, xofsnt, puthlp, putmod, clrmod, poscur
	public	sendbr, term, machnam, setktab, setkhlp, showkey
	include msdefs.h

; rainbow-dependent screen constants

scrseg	equ	0ee00H		; screen segment
latofs	equ	0ef4h		; ptrs to line beginnings, used by firmware
l1ptr	equ	latofs		; ptr to first line
llptr	equ	latofs+23*2	; ptr to last line
csrlin	equ	0f42h		; current cursor line.

; level 1 console definitions

fnkey	equ	100H		; function key flag
shfkey	equ	200H		; shift key
ctlkey	equ	400H		; control key
cplk	equ	800H

prvkey	equ	23H
nxtkey	equ	25H
brkkey	equ	65H
prtkey	equ	3

false	equ	0
true	equ	1
mntrgh	equ	bufsiz*3/4	; High point = 3/4 of buffer full.

mnstata	equ	042H		;Status/command port A
mnstatb	equ	043H		;Status/command port B.
mndata	equ	040H		;Data port.
mndatb	equ	041H
mnctrl	equ	002H		;Control port.
serchn	equ	0A4H		; interrupt to use
serch1	equ	044H		; use this too for older rainbows.

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

fastcon	equ	29H		; fast console handler
firmwr	equ	18H

swidth	equ	132		; screen width
slen	equ	24		; screen length
npgs	equ	5		; # of pages to remember

stbrk	equ	15		; start sending a break
enbrk	equ	16		; stop sending break.

; external variables used:
; drives - # of disk drives on system
; flags - global flags as per flginfo structure defined in pcdefs
; trans - global transmission parameters, trinfo struct defined in pcdefs
; portval - pointer to current portinfo structure (currently either port1
;    or port2)
; port1, port2 - portinfo structures for the corresponding ports

; global variables defined in this module:
; xofsnt, xofrcv - tell whether we saw or sent an xoff.

; circular buffer ptr
cbuf	struc
pp	dw	?			; place ptr in buffer
bend	dw	?			; end of buffer
orig	dw	?			; buffer origin
lcnt	dw	0			; # of lines in buffer.
cbuf	ends

; answerback structure
ans	struc
anspt	dw	?			; current pointer in answerback
ansct	db	?			; count of chars in answerback
ansseq	dw	?			; pointer to whole answerback
anslen	db	?			; original length
ansrtn	dw	?			; routine to call.
ans	ends

datas 	segment	public 'datas'
	extrn	drives:byte,flags:byte, trans:byte
	extrn	portval:word, port1:byte, port2:byte

setktab	db	22
	mkeyw	'F4',fnkey+5h
	mkeyw	'F5',fnkey+65h
	mkeyw	'F6',fnkey+7h
	mkeyw	'F7',fnkey+9h
	mkeyw	'F8',fnkey+0Bh
	mkeyw	'F9',fnkey+0Dh
	mkeyw	'F10',fnkey+0Fh
	mkeyw	'F11',esc
	mkeyw	'F12',bs
	mkeyw	'F13',lf
	mkeyw	'F14',fnkey+11h
	mkeyw	'F17',fnkey+13h
	mkeyw	'F18',fnkey+15h
	mkeyw	'F19',fnkey+17h
	mkeyw	'F20',fnkey+19h
	mkeyw	'FIND',fnkey+1bh
	mkeyw	'INSERTHERE',fnkey+1dh
	mkeyw	'REMOVE',fnkey+1fh
	mkeyw	'SCAN',-1
	mkeyw	'SELECT',fnkey+21h

ourflgs	db	0		; our flags
fpscr	equ	80H		; flag definitions...

crlf	db	cr,lf
setkhlp	db	' F4 ... F20 or SCAN$'
machnam	db	'Rainbow$'
nyimsg	db	cr,lf,'Not yet implemented$'
delstr  db      BS,' ',BS,'$' 	; Delete string.
clrlin  db      cr,'$'		; Clear line (just the cr part).
oldser	dw	?		; old serial handler
oldseg	dw	?		; segment of above
old1ser	dw	?		; old serial handler, alternate address
old1seg	dw	?		; segment of same.
portin	db	0		; Has comm port been initialized.
xofsnt	db	0		; Say if we sent an XOFF.
xofrcv	db	0		; Say if we received an XOFF.
iobuf	db	5 dup (?)	; buffer for ioctl

phbuf	db	swidth dup (?)
gopos	db	esc,'['
rowp	db	20 dup (?)
clrseq	db	esc,'[H',esc,'[J$'
ceolseq	db	esc,'[K$'
invseq	db	esc,'[7m$'
nrmseq	db	esc,'[0m$'
ivlatt	db	swidth dup (0fH) ; a line's worth of inverse attribute

; special keys.
spckey	dw	prvkey,nxtkey,brkkey,prtkey,prtkey+ctlkey,prvkey+ctlkey
	dw	nxtkey+ctlkey,brkkey+ctlkey
spclen	equ	($-spckey)/2
; special key handlers.  Must parallel spckey
spchnd	dw	prvscr,nxtscr,sendbr,prtscr,togprt,prvlin,nxtlin,sendbr

; arrow and PF keys
arrkey	db	27H,29H,2bH,2dH,59H,5cH,5fH,62H
arrlen	equ	$-arrkey
; translations for arrow and PF keys, must parallel arrkey
arrtrn	dw	uptrn,dntrn,rgttrn,lfttrn
	dw	pf1trn,pf2trn,pf3trn,pf4trn

; keypad keys
keypad	db	2fh,32h,35h,38h,3bh,3eh,41h,44h,47h,4ah,4dh,50h,53h,56h
keypln	equ	$-keypad
; keytrn and altktrn must parallel keypad
keytrn	db	'0123456789-,.',cr
altktrn	db	'pqrstuvwxymlnM'

keyptr	dw	keytrn		; pointer to correct translation table
akeyflg	db	0		; non-zero if in alt keypad mode.

; arrow and PF key translations
uptrn	db	3,esc,'[A'
dntrn	db	3,esc,'[B'
rgttrn	db	3,esc,'[C'
lfttrn	db	3,esc,'[D'
pf1trn	db	3,esc,'OP'
pf2trn	db	3,esc,'OQ'
pf3trn	db	3,esc,'OR'
pf4trn	db	3,esc,'OS'

ourarg	termarg	<>

; variables for serial interrupt handler
source	db	bufsiz DUP(?)	; Buffer for data from port.
srcpnt	dw	0		; Pointer in buffer (DI).
count	dw	0		; Number of chars in int buffer.
savesi	dw	0		; Save SI register here.	
telflg	db	0		; non-zero if we're a terminal. NRU.
respkt	db	10 dup (?)	; ioctl packet
ivec	dw	tranb		; transmit empty B
	dw	tranb		; status change B
	dw	tranb		; receive b
	dw	tranb		; special receive b
	dw	stxa		; transmit empty a
	dw	sstata		; status change a
	dw	srcva		; receive a
	dw	srcva		; special receive a

; multi-screen stuff

bsize	equ	swidth*slen*npgs ; # of bytes needed to store screens
tbuf	db	bsize dup (?)
bbuf	db	bsize dup (?)	; top and bottom buffers
topbuf	cbuf	<tbuf,tbuf+bsize-1,tbuf,0>
botbuf	cbuf	<bbuf,bbuf+bsize-1,bbuf,0>
tlbuf	db	swidth dup (?)	; top line temp buffer
blbuf	db	swidth dup (?)	; bottom line temp buffer
rlbuf	db	swidth dup (?)	; line temp buffer
prbuf	db	swidth dup (?)	; print temp buffer
topdwn	db	esc,'[H',esc,'M$' ; go to top, scroll down
botup	db	esc,'[24;0H',esc,'D$' ; go to bottom, scroll up
curinq	db	esc,'[6n$'	; cursor inquiry
posbuf	db	20 dup (?)	; place to store cursor position
gtobot	db	esc,'[24;0H$'	; go to bottom of screen.
ourscr	db	slen*swidth dup (?)
ourattr	db	slen*swidth dup (?) ; storage for screen and attributes
inited	db	0		; terminal handler not inited yet.
dosmsg	db	'?Must be run in version 2.05 or higher$'
anssq1	db	esc,'[c'
ansln1	equ	$-anssq1
anssq2	db	esc,'Z'
ansln2	equ	$-anssq2
eakseq	db	esc,'='
dakseq	db	esc,'>'
ansbk1	ans	<anssq1,ansln1,anssq1,ansln1,sndans> ; two answerbacks
ansbk2	ans	<anssq2,ansln2,anssq2,ansln2,sndans>
ansbk3	ans	<eakseq,2,eakseq,2,enaaky> ; enable alt keypad
ansbk4	ans	<dakseq,2,dakseq,2,deaaky> ; disable alt keypad
ansret	db	esc,'[?6c'
ansrln	equ	$-ansret
shkbuf	db	300 dup (?)	; room for definition
shkmsg	db	'  Scan code: '
shkmln	equ	$-shkmsg
shkms1	db	cr,lf,'  Definition: '
shkm1ln	equ	$-shkms1
datas	ends

code	segment	public
	extrn	comnd:near, dopar:near
	assume	cs:code,ds:datas

; local initialization routine, called by Kermit initialization.

lclini	proc	near
; make sure this is DOS version 2.05 or higher...
	mov	ah,dosver
	int	dos
	xchg	al,ah		; put major version in ah, minor in al
	cmp	ax,205H		; is it 2.05?
	jae	lclin1		; yes, go on
	mov	dx,offset dosmsg
	call	tmsg
	mov	ax,4c00H	; exit(0)
	int	dos
lclin1:	mov	flags.vtflg,0	; turn off heath emulation
	ret
lclini	endp

; this is called by Kermit initialization.  It checks the
; number of disks on the system, sets the drives variable
; appropriately.  The only problem is that a value of two
; is returned for single drive systems to be consistent
; with the idea of the system having logical drives A and
; B.  Returns normally.  

DODISK	PROC	NEAR
	mov ah,gcurdsk			; Current disk value to AL.
	int dos
	mov dl,al			; Put current disk in DL.
	mov ah,seldsk			; Select current disk.
	int dos				; Get number of drives in AL.
	mov drives,al
	ret
DODISK	ENDP

; show the definition of a key.  The terminal argument block (which contains
; the address and length of the definition tables) is passed in ax.
; Returns a string to print in AX, length of same in CX.
; Returns normally.
showkey	proc	near
	push	es
	push	ax		; save the ptr
	cld
showk1:	mov	di,6		; get level one char
	int	firmwr
	cmp	cl,0ffH
	jne	showk1		; wait until char available
	mov	bx,ds
	mov	es,bx		; address data segment
	and	ax,not cplk	; no caps lock
	push	ax		; remember scan code
	mov	di,offset shkbuf
	mov	si,offset shkmsg
	mov	cx,shkmln
	rep	movsb		; copy in initial message
	call	nout		; write out scan code
	mov	si,offset shkms1
	mov	cx,shkm1ln	; second message
	rep	movsb
	pop	ax		; get scan code back
	pop	bx		; and terminal arg block
	mov	cx,[bx].klen	; and length
	jcxz	showk2		; no table, not defined
	push	di		; remember output ptr
	mov	di,[bx].ktab	; get key table
	repne	scasw		; search for a definition for this
	mov	si,di		; remember result ptr
	pop	di		; get output ptr back
	jne	showk2		; not defined, forget it
	sub	si,[bx].ktab	; compute offset from beginning
	sub	si,2		; minus 2 for pre-increment
	add	si,[bx].krpl	; get index into replacement table
	mov	si,[si]		; pick up replacement
	mov	cl,[si]		; get length
	mov	ch,0
	inc	si
	rep	movsb		; copy into buffer
showk2:	mov	ax,offset shkbuf ; this is buffer
	mov	cx,di
	sub	cx,ax		; length
	pop	es
	ret			; and return
showkey	endp

; Clear the input buffer. This throws away all the characters in the
; serial interrupt buffer.  This is particularly important when
; talking to servers, since NAKs can accumulate in the buffer.
; Returns normally.

CLRBUF	PROC	NEAR
	cli
	mov ax,offset source
	mov srcpnt,ax
	mov savesi,ax
	mov count,0
	sti
	ret
CLRBUF	ENDP

; Clear to the end of the current line.  Returns normally.

CLEARL	PROC	NEAR
	mov	dx,offset ceolseq	; clear sequence
	jmp	tmsg
CLEARL	ENDP

; Put the char in AH to the serial port.  This assumes the
; port has been initialized.  Should honor xon/xoff.  Skip returns on
; success, returns normally if the character cannot be written.

outchr:	mov bp,portval
	cmp ds:[bp].floflg,0	; Are we doing flow control.
	je outch2		; No, just continue.
	xor cx,cx		; clear counter
outch1:	cmp xofrcv,true		; Are we being held?
	jne outch2		; No - it's OK to go on.
	loop outch1		; held, try for a while
	mov xofrcv,false	; timed out, force it off and fall thru.
outch2:	push dx			; Save register.
	sub cx,cx
	mov al,ah		; Parity routine works on AL.
	call dopar		; Set parity appropriately.
	mov ah,al		; Don't overwrite character with status.
	mov dx,mnstata		; port status register
outch3:	in al,dx
	test al,txrdy		; Transmitter ready?
	jnz outch4		; Yes
	loop outch3
	 jmp outch5		; Timeout
outch4:	mov al,ah		; Now send it out
	mov dx,mndata
	out dx,al
	pop dx
	jmp rskp
outch5:	pop dx
	ret

; This routine blanks the screen.  Returns normally.

CMBLNK	PROC	NEAR
	mov	dx,offset clrseq ; clear screen sequence
	jmp	tmsg
CMBLNK  ENDP

; Locate; homes the cursor.  Returns normally.

LOCATE  PROC	NEAR
	mov dx,0		; Go to top left corner of screen.
	jmp poscur
LOCATE  ENDP

; write a line in inverse video at the bottom of the screen...
; the line is passed in dx, terminated by a $.  Returns normally.
putmod	proc	near
	push	dx		; preserve message
	mov	dx,24 * 100H	; line 24
	call	poscur
	mov	dx,offset invseq ; put into inverse video
	call	tmsg
	pop	dx
	call	tmsg		; print the message
	mov	dx,offset nrmseq ; normal videw
	call	tmsg
	ret			; and return
putmod	endp

; clear the mode line written by putmod.  Returns normally.
clrmod	proc	near
	mov	dx,24 * 100H
	call	poscur
	call	clearl
	ret
clrmod	endp

; Put a help message on the screen.  This one uses reverse video...
; pass the message in ax, terminated by a null.  Returns normally.
puthlp	proc	near
	push	ax
	mov	dx,slen * 100H	; go to bottom line
	call	poscur
	pop	ax
	push	es
	mov	bx,ds
	mov	es,bx		; address data segment
	mov	si,ax		; convenient place for this
	mov	bx,101H		; current line/position
puthl1:	mov	di,offset phbuf	; this is destination
	xor	cx,cx		; # of chars in the line
puthl2:	lodsb			; get a byte
	cmp	al,cr		; carriage return?
	je	puthl2		; yes, ignore it
	cmp	al,lf		; linefeed?
	je	puthl3		; yes, break the loop
	cmp	al,0
	je	puthl3		; ditto for null
	dec	cx		; else count the character
	stosb			; deposit into the buffer
	jmp	puthl2		; and keep going
puthl3:	add	cx,80		; this is desired length of the whole
	mov	al,' '
	rep	stosb		; fill the line
	push	bx
	push	si
	push	es		; firmware likes to eat this one
	mov	ax,0		; send chars and attributes
	mov	cx,80		; this is # of chars to send
	mov	dx,offset ivlatt ; this are attributes to send
	mov	si,offset phbuf	; the actual message
	mov	di,14H		; send direct to screen
	mov	bp,ds		; need data segment as well
	int	firmwr		; go send it
	pop	es
	pop	si
	pop	bx		; restore everything
	inc	bx		; next line
	cmp	byte ptr [si-1],0 ; were we ended by a 0 last time?
	jne	puthl1		; no, keep looping
	pop	es		; else restore this
	ret			; and return
puthlp	endp

; Set the baud rate for the current port, based on the value
; in the portinfo structure.  Returns normally.

; no baud rate.
DOBAUD	PROC	NEAR
	mov	dx,offset nyimsg
	call	tmsg
	mov	bx,portval
	mov	[bx].baud,-1		; keep baud rate unknown.
	ret
DOBAUD	ENDP

; Get the current baud rate from the serial card and set it
; in the portinfo structure for the current port.  Returns normally.
; This is used during initialization.

GETBAUD	PROC	NEAR
	ret			; no baud rate for now.
GETBAUD	ENDP


; skip returns if no character available at port,
; otherwise returns with char in al, # of chars in buffer in dx.
PRTCHR  PROC    NEAR
	call chkxon		; see if we have to xon the host.
	cmp count,0
	jnz prtch2
	jmp rskp		; No data - check console.
prtch2:	mov si,savesi
	lodsb			; get a byte
	cmp si,offset source + bufsiz	; bigger than buffer?
	jb prtch1		; no, keep going
	mov si,offset source	; yes, wrap around
prtch1:	dec count
	mov savesi,si 
	mov dx,count		; return # of chars in buffer
	ret
PRTCHR  ENDP

; local routine to see if we have to transmit an xon
chkxon	proc	near
	push	bx
	mov	bx,portval
	cmp	[bx].floflg,0	; doing flow control?
	je	chkxo1		; no, skip all this
	cmp	xofsnt,false	; have we sent an xoff?
	je	chkxo1		; no, forget it
	cmp	count,mntrgh	; below trigger?
	jae	chkxo1		; no, forget it
	mov	ax,[bx].flowc	; ah gets xon
	call	outchr		; send it
	nop
	nop
	nop			; in case it skips
	mov	xofsnt,false	; remember we've sent an xon.
chkxo1:	pop	bx		; restore register
	ret			; and return
chkxon	endp

; Send a break out the current serial port.  Returns normally.
SENDBR	PROC	NEAR
	push bx
	push cx
	push dx
	push ax
	mov ah,ioctl
	mov al,3		; write to control channel.
	mov bx,3		; aux port handle
	mov dx,offset iobuf
	mov iobuf,stbrk		; start sending a break
	int dos
	xor cx,cx		; clear loop counter
pause:	loop pause		; Wait a while.
	mov ah,ioctl
	mov al,3
	mov bx,3
	mov dx,offset iobuf
	mov iobuf,enbrk		; stop sending the break
	int dos
	pop ax
	pop dx
	pop cx
	pop bx
	ret			; And return.
SENDBR	ENDP

; Position the cursor according to contents of DX:
; DH contains row, DL contains column.  Returns normally.

POSCUR	PROC	NEAR
	add	dx,101H		; start at 1,1
	push	es
	push	dx
	cld
	mov	ax,ds
	mov	es,ax		; address right segment
	mov	di,offset rowp
	mov	al,dh		; row comes first
	mov	ah,0
	call	nout
	mov	al,';'
	stosb			; separated by a semi
	pop	dx
	mov	al,dl
	mov	ah,0
	call	nout
	mov	al,'H'
	stosb			; end w/H
	mov	byte ptr [di],'$' ; and dollar sign
	mov	dx,offset gopos
	call	tmsg
	pop	es
	ret
POSCUR	ENDP

; Delete a character from the terminal.  This works by printing
; backspaces and spaces.  Returns normally.

DODEL	PROC	NEAR
	mov dx,offset delstr	; Erase weird character.
	jmp tmsg
DODEL	ENDP

; Move the cursor to the left margin, then clear to end of line.
; Returns normally.

CTLU	PROC	NEAR
	mov dx,offset clrlin	; this just goes to left margin...
	call tmsg
	jmp clearl		; now clear line
CTLU	ENDP

; set the current port.

COMS	PROC	NEAR
	mov	dx,offset nyimsg
	jmp	tmsg
COMS	ENDP

; Set heath emulation on/off.

VTS	PROC	NEAR
	mov	dx,offset nyimsg
	jmp	tmsg
VTS	ENDP

; initialization for using serial port.  This routine performs
; any initialization necessary for using the serial port, including
; setting up interrupt routines, setting buffer pointers, etc.
; Doing this twice in a row should be harmless (this version checks
; a flag and returns if initialization has already been done).
; SERRST below should restore any interrupt vectors that this changes.
; Returns normally.

SERINI	PROC	NEAR
	push es
	cmp portin,0		; Did we initialize port already? [21c]
	jne serin0		; Yes, so just leave. [21c]
	cli			; Disable interrupts
	cld			; Do increments in string operations
	xor ax,ax		; Address low memory
	mov es,ax
	mov ax,es:[4*serchn]	; get old serial handler
	mov oldser,ax		; save.
	mov ax,es:[4*serchn+2]	; get segment
	mov oldseg,ax		; save segment as well
	mov ax,es:[4*serch1]	; this is alternate for older rainbows
	mov old1ser,ax
	mov ax,es:[4*serch1+2]
	mov old1seg,ax		; pretty silly, huh?
	mov ax,offset serint	; point to our routine
	mov word ptr es:[4*serchn],ax ; point at our serial routine
	mov word ptr es:[4*serch1],ax ; have to set both of these
	mov es:[4*serchn+2],cs	; our segment
	mov es:[4*serch1+2],cs
	mov al,030h		;[DTR] enable RTS and DTR
	out mnctrl,al		;[DTR]
	mov portin,1		; Remember port has been initialized.
	call clrbuf		; Clear input buffer. 
	sti			; Allow interrupts
serin0:	pop es
	ret			; We're done.
SERINI	ENDP

; this is used to by serini
prtset	proc	near
	lodsb			; get a byte
	or	al,al
	jz	prtse1		; end of table, stop here
	out	dx,al		; else send it out
	jmp	prtset		; and keep looping
prtse1:	ret			; end of routine
prtset	endp

; Reset the serial port.  This is the opposite of serini.  Calling
; this twice without intervening calls to serini should be harmless.
; Returns normally.

SERRST	PROC	NEAR
	push es			; preserve this
	cmp portin,0		; Reset already? 
	je srst1		; Yes, just leave. 
	cli			; Disable interrupts
	xor ax,ax
	mov es,ax		; address segment 0
	mov ax,oldser
	mov es:[4*serchn],ax
	mov ax,oldseg
	mov es:[4*serchn+2],ax
	mov ax,old1ser
	mov es:[4*serch1],ax
	mov ax,old1seg
	mov es:[4*serch1+2],ax	; restore old handlers
	mov portin,0		; Reset flag.
srst1:	pop es
	ret			; All done.
SERRST	ENDP

; serial port interrupt routine.  This is not accessible outside this
; module, handles serial port receiver interrupts.

serint	PROC  NEAR
	push bx
	push dx
	push ax
	push es
	push di
	push ds
	push bp
	push cx
	cld
	mov ax,seg datas
	mov ds,ax		; address data segment
	mov es,ax
	mov di,srcpnt		; Registers for storing data.
	mov dx,mnstatb		; Asynch status port.
	mov al,0		; innocuous value
	out dx,al		; send out to get into a known state...
	mov al,2		; now address register 2
	out dx,al
	in al,dx		; read interrupt cause
	cmp al,7		; in range?
	ja serin7		; no, just dismiss (what about reset error?)
	mov bl,al
	shl bl,1		; double for word index
	mov bh,0
	call ivec[bx]		; call appropriate handler
serin7:	mov dx,mnstata		; reload port address
	mov al,38H
	out dx,al		; tell the port we finished with the interrupt
	pop cx
	pop bp
	pop ds
	pop di
	pop es
	pop ax
	pop dx
	pop bx
intret:	iret

; handler for serial receive, port A
srcva:	mov dx,mnstata
	mov al,0
	out dx,al		; put into known state...
	in al,dx
	test al,rxrdy		; Data available?
	jnz srcva1		; yes, go read it
	jmp srcva7
srcva1:	mov al,30H		; reset any errors
	out dx,al
	mov dx,mndata
	in al,dx		; read the character
	cmp telflg,0		; File transfer or terminal mode?
	jz srcva2
	and al,7FH		; Terminal mode (7 bits only). 
srcva2: or al,al
	jz srcva7		; Ignore nulls.
	cmp al,7FH		; Ignore rubouts, too.
	jz srcva7
	mov ah,al
	and ah,7fH		; only consider low-order 7 bits for flow ctl.
	mov bp,portval
	cmp ds:[bp].floflg,0	; Doing flow control?
	je srcva4		; Nope.
	mov bx,ds:[bp].flowc	; Flow control char (BH = XON, BL = XOFF).
	cmp ah,bl		; Is it an XOFF?
	jne srcva3		; Nope, go on.
	mov xofrcv,true		; Set the flag.
	jmp short srcva7
srcva3:	cmp ah,bh		; Get an XON?
	jne srcva4		; No, go on.
	mov xofrcv,false	; Clear our flag.
	jmp srcva7
srcva4:	stosb
	cmp di,offset source + bufsiz
	jb srcva5		; not past end...
	mov di,offset source	; wrap buffer around
srcva5:	mov srcpnt,di		; update ptr
	inc count
	cmp ds:[bp].floflg,0	; Doing flow control?
	je srcva7		; No, just leave.
	cmp xofsnt,true		; Have we sent an XOFF?
	je srcva7		; Yes.
	cmp count,mntrgh	; Past the high trigger point?
	jbe srcva7		; No, we're within our limit.
	mov ah,bl		; Get the XOFF.
	call outchr		; Send it.
	nop
	nop
	nop			; ignore failure.
	mov xofsnt,true		; Remember we sent it.
srcva7:	ret

; The interrupt is for the 'B' port - transfer control to
; the original handler and hope for the best.
tranb:	pushf			; put flags on stack to simulate interrupt
	call	dword ptr [old1ser] ; call old handler
	ret			; and return

stxa:	mov	dx,mnstata
	mov	al,28H		; reset transmit interrupt
	out	dx,al
	ret

sstata:	mov	dx,mnstata
	mov	al,10H		; reset status interrupt
	out	dx,al
	ret

SERINT	ENDP

; Produce a short beep.  The PC DOS bell is long enough to cause a loss
; of data at the port.  Returns normally.

BEEP	PROC	NEAR
	mov dl,bell
	mov ah,conout
	int dos
	ret
BEEP	ENDP 
 
; put the number in ax into the buffer pointed to by di.  Di is updated
nout	proc	near
	mov	dx,0		; high order is always 0.
	mov	bx,10
	div	bx		; divide to get digit
	push	dx		; save remainder digit
	or	ax,ax		; test quotient
	jz	nout1		; zero, no more of number
	call	nout		; else call for rest of number
nout1:	pop	ax		; get digit back
	add	al,'0'		; make printable
	stosb			; drop it off
	ret			; and return
nout	endp


term	proc	near
	mov si,ax		; this is source
	mov di,offset ourarg	; place to store arguments
	mov ax,ds
	mov es,ax		; address destination segment
	mov cx,size termarg
	cld
	rep movsb		; copy into our arg blk
	cmp inited,0		; inited yet?
	jz term1		; no, keep going
	test ourarg.flgs,scrsam	; do they want us to leave it alone?
	jnz term1		; yes, skip redisplay.
	call rstscr		; restore screen
term1:	mov inited,1		; remember inited
term2:	call prtchr
	jmp short term3		; have a char...
	nop
	nop
	jmp short term6		; no char, go on
term3:	and al,7fh		; turn off parity for terminal
	mov bx,offset ansbk1	; check 1st answerback
	call ansbak		; check for answerback
	mov bx,offset ansbk2	; maybe second answerback
	call ansbak
	mov bx,offset ansbk3
	call ansbak
	mov bx,offset ansbk4
	call ansbak
	cmp al,lf		; linefeed?
	jne term4		; no, keep going
	call scrprep		; need to save top line
	mov al,lf		; get char back
term4:	push ax
	int fastcon		; go print it
	pop ax
	test ourarg.flgs,capt	; capturing output?
	jz term5		; no, forget it
	push ax
	call ourarg.captr	; else call the routine
	pop ax
term5:	test ourflgs,fpscr	; print screen toggled on?
	jz term6		; no, keep going
	mov dl,al
	mov ah,lstout		; printer output
	int dos
term6:	mov di,6		; get level 1 character
	push es
	int firmwr
	pop es			; don't let firmware steal registers.
	cmp cl,0ffh		; character available?
	je term7		; no, do something else
	cmp cl,1		; maybe level 2 sequence around
	jne term2		; no, forget it
	mov di,2		; get level 2 character
	push es
	int firmwr
	pop es
	cmp cl,0ffh		; did we really get one?
	jne term2		; no, something strange happening.
	jmp term6		; else skip and keep trying.
term7:	test ax,fnkey		; function-type key?
	jnz term8		; yes, can't be escape character
	cmp al,ourarg.escc	; escape char?
	je term9		; yes, exit
term8:	call trnout		; perform necessary translations, output char
	jmp term2		; and loop around
term9:	call savscr		; save screen
	ret			; and return
term	endp

; enter with current terminal character in al, answerback ptr in bx.
; calls answerback routine if necessary.
; This can be used to make the emulator recognize any sequence.
ansbak	proc	near
	push	ax		; preserve this
	mov	si,[bx].anspt	; get current pointer
	cmp	al,[si]		; is it correct?
	jne	ansba1		; no, reset pointers and go on
	inc	[bx].anspt	; increment pointer
	dec	[bx].ansct	; decrement counter
	jnz	ansba2		; not done, go on
	push	bx
	call	[bx].ansrtn	; send answerback
	pop	bx
ansba1:	mov	ax,[bx].ansseq	; get original sequence
	mov	[bx].anspt,ax
	mov	al,[bx].anslen	; and length
	mov	[bx].ansct,al
ansba2:	pop	ax
	ret
ansbak	endp

; send the answerback message.
sndans	proc	near
	mov	si,offset ansret ; this is what we say
	mov	cx,ansrln	; length of same
sndan1:	lodsb			; get a byte
	mov	ah,al
	push	si
	push	cx
	call	outchr
	nop
	nop
	nop
	pop	cx
	pop	si
	loop	sndan1
	ret
sndans	endp

; enable alternate keypad mode
enaaky	proc	near
	mov	akeyflg,1	; remember alternate mode
	mov	keyptr,offset altktrn ; set correct translate table
	ret
enaaky	endp

; disable alternate keypad mode
deaaky	proc	near
	mov	akeyflg,0
	mov	keyptr,offset keytrn
	ret
deaaky	endp

; enter with char and flags in ax.  Does any necessary character translations,
; then outputs character
trnout	proc	near
	and ax,not cplk		; forget about caps lock key
	test ourarg.flgs,havtt	; any translate table?
	jz trnou2		; no, just output normally
	mov cx,ourarg.klen
	mov di,ourarg.ktab	; get redefined keys
	repne scasw		; look for this one
	jne trnou2		; not found, try something else
	sub di,ourarg.ktab
	sub di,2		; get index
	add di,ourarg.krpl	; get translation address
	mov si,[di]		; this is translation
	mov cl,[si]
	inc si			; pick up length, increment past it
	mov ch,0
	jcxz trnou6		; no translation, just return
trnou1:	lodsb			; get a char
	push si
	push cx
	call sndhst		; send the character
	pop cx
	pop si
	loop trnou1		; loop thru rest of translation
	ret			; and return
trnou2:	test ax,fnkey		; function key?
	jz trnou5		; no, keep going
	and ax,not fnkey	; turn off function bit.
	mov di,offset spckey	; our special keys
	mov cx,spclen		; length of special key table
	repne scasw		; look for it in our table
	jne trnou3		; not found, maybe arrow key...
	sub di,offset spckey+2	; get index
	call spchnd[di]		; call appropriate handler
	ret			; and return
trnou3:	mov di,offset arrkey	; look for an arrow-type key...
	mov cx,arrlen		; length of arrow key table
	repne scasb		; is it an arrow key?
	jne trnou4		; no, forget it
	sub di,offset arrkey+1	; get index into table
	shl di,1		; double for word index
	mov si,arrtrn[di]	; get translation
	mov cl,[si]
	inc si
	mov ch,0
	jmp trnou1		; go send translation
trnou4:	mov di,offset keypad	; look for a keypad key.
	mov cx,keypln
	repne scasb		; is it in keypad?
	jne trnou6		; no, forget it
	sub di,offset keypad+1
	add di,keyptr		; index into correct translation table
	mov al,[di]		; get translation
	cmp akeyflg,0		; in alternate keypad mode?
	je trnou5		; no, just send the char
	push ax			; else save the character
	mov al,esc
	call sndhst
	mov al,'O'
	call sndhst		; send prefix
	pop ax			; get the character back and fall thru...
trnou5:	call sndhst		; send the character
trnou6:	ret
trnout	endp

; handle the print screen key
prtscr	proc	near
	push	ds		; save data segment
	mov	ax,scrseg
	mov	ds,ax		; address screen segment
	mov	cx,slen		; # of lines on screen
	mov	bx,0		; current line #
prtsc1:	push	cx		; save counter
	push	bx		; and line ptr
	mov	si,ds:[latofs+bx] ; get ptr to line
	mov	cx,swidth	; max # of chars/line
	mov	di,offset prbuf	; print buffer
prtsc2:	lodsb			; get a byte
	or	al,al		; is it a null?
	jne	prtsc3		; no, go on
	mov	al,' '		; yes, replace by space
prtsc3:	stosb			; drop it off
	cmp	al,' '		; is it a space?
	je	prtsc4		; yes, go on
	mov	dx,cx		; else remember count at last non-space
prtsc4:	cmp	al,0ffH		; end of line?
	loopne	prtsc2		; continue if not end
	mov	cx,dx		; count at last non-space, plus 1
	neg	cx
	add	cx,swidth+1	; figure out # of chars to print
	mov	dx,offset prbuf
	push	ds		; save this temporarily
	mov	ax,es
	mov	ds,ax		; address data segment to print
	jcxz	prtsc5		; 0 length, keep going
	mov	bx,4		; standard printer device
	mov	ah,writef2	; write call
	int	dos		; write to the printer
prtsc5:	mov	ah,writef2
	mov	bx,4
	mov	dx,offset crlf
	mov	cx,2
	int	dos		; follow line with a crlf
	pop	ds
	pop	bx
	pop	cx		; restore counters
	add	bx,2		; point to next line
	loop	prtsc1		; and keep going
	pop	ds		; restore registers
	ret			; and return
prtscr	endp

; toggle print flag...
togprt	proc	near
	xor	ourflgs,fpscr	; toggle flag
	ret			; and return
togprt	endp

; Send a character to the host, handle local echo
sndhst	proc	near
	push ax			; save the character
	mov ah,al
	call outchr
	nop
	nop
	nop
	pop ax
	test ourarg.flgs,lclecho ; echoing?
	jz sndhs2		; no, exit
	cmp al,lf		; scrolling? ***
	jne sndhs1		; no, go on
	call scrprep
	mov al,lf
sndhs1:	int fastcon
sndhs2:	ret			; and return
sndhst	endp


; print a message to the screen.  Returns normally.
tmsg	proc	near
	mov	ah,prstr
	int	dos
	ret
tmsg	endp

; save the screen for later
savscr	proc	near
	push	ds
	mov	ax,scrseg
	mov	ds,ax
	mov	cx,slen		; # of lines to do
	mov	bx,0		; current line #
	mov	di,offset ourscr ; place to save screen
	mov	dx,offset ourattr ; and to save attributes
savsc1:	push	cx		; save current count
	mov	si,ds:[latofs+bx] ; get line ptr
	mov	cx,swidth	; # of chars/line
	rep	movsb		; copy it out
	mov	si,ds:[latofs+bx]
	add	si,1000H	; this is where attributes start
	xchg	dx,di		; this holds attribute ptr
	mov	cx,swidth	; # of attrs to move
	rep	movsb
	xchg	dx,di
	pop	cx		; restore counter
	add	bx,2		; increment line ptr
	loop	savsc1		; save all lines and attributes
	pop	ds
	call	savpos		; might as well save cursor pos
	ret
savscr	endp

; restore the screen saved by savscr
rstscr	proc	near
	call	cmblnk		; start by clearing screen
	mov	si,offset ourscr ; point to saved screen
	mov	dx,offset ourattr ; and attributes
	mov	cx,slen		; # of lines/screen
	mov	bx,101H		; start at top left corner
rstsc1:	push	bx
	push	cx
	push	si		; save ptrs
	push	dx
	mov	ax,si		; this is source
	call	prlina		; print the line
	pop	dx
	pop	si
	pop	cx
	pop	bx
	add	si,swidth	; point to next line
	add	dx,swidth	; and next attributes
	inc	bx		; address next line
	loop	rstsc1		; keep restore lines
	call	rstpos		; don't forget position
	ret
rstscr	endp

; circular buffer management for screen.
; for these to work correctly, the buffer size MUST be a multiple
; of the screen width.

; put a line into the circular buffer.  Pass the buffer structure
; in bx, the pointer to the line in ax.
putcirc	proc	near
	push	si
	push	di
	push	cx
	push	dx
	mov	di,[bx].pp		; pick up buffer ptr
	add	di,swidth		; increment to next avail slot
	cmp	di,[bx].bend		; past end?
	jb	putci1			; no, leave alone
	mov	di,[bx].orig		; else start at beginning
putci1:	mov	[bx].pp,di		; update ptr
	mov	si,ax			; this is source
	mov	cx,swidth
	rep	movsb			; copy into buffer
	cmp	[bx].lcnt,npgs*slen	; can we increment it?
	jae	putci2			; no, keep going
	inc	[bx].lcnt		; else count this line
putci2:	pop	dx
	pop	cx
	pop	di
	pop	si			; restore registers
	ret
putcirc	endp

; get a line from the circular buffer, removing it from the buffer.
; returns with carry on if the buffer is empty.
; pass the buffer structure in bx, the buffer to copy the line into
; in ax.
getcirc	proc	near
	push	si
	push	di
	push	cx
	push	dx
	cmp	[bx].lcnt,0		; any lines in buffer?
	jne	getci1			; yes, ok to take one out.
	stc				; else set carry
	jmp	short getcir3		; and return
getci1:	mov	si,[bx].pp		; this is source
	mov	di,ax			; this is dest
	mov	cx,swidth		; # of chars to copy
	rep	movsb
	mov	si,[bx].pp		; get ptr again
	sub	si,swidth		; move back
	cmp	si,[bx].orig		; compare to origin
	jae	getcir2			; still in range, continue
	mov	si,[bx].bend		; else use end of buffer
	sub	si,swidth-1		; minus length of a piece
getcir2:mov	[bx].pp,si		; update ptr
	dec	[bx].lcnt		; decrement # of lines in buffer
	clc				; make sure no carry
getcir3:pop	dx
	pop	cx
	pop	di
	pop	si
	ret
getcirc	endp

; prepares for scrolling by saving the top line in topbuf.
scrprep	proc	near
	push	ds		; preserve data segment
	mov	ax,scrseg
	mov	ds,ax		; address screen segment
	cmp	byte ptr [ds:csrlin],slen ; are we at the bottom?
	pop	ds		; restore in case we're returning...
	jne	scrpr1		; no, don't save it
; alternate entry that doesn't check if we're on the bottom row.
savtop:	push	es
	push	ds
	mov	ax,ds
	mov	es,ax
	mov	ax,scrseg
	mov	ds,ax		; address screen segment
	mov	si,ds:word ptr [l1ptr] ; get ptr to top line
	mov	di,offset tlbuf	; this is where it goes
	mov	cx,swidth	; # of bytes to copy
	rep	movsb		; get the top line
	pop	ds
	pop	es		; restore segments
	mov	bx,offset topbuf ; top buffer ptr
	mov	ax,offset tlbuf	; this is where line is now
	call	putcirc		; put into circular buffer
scrpr1:	ret			; and return
scrprep	endp

; get the screen's bottom line into the buffer in ax.
getbot	proc	near
	push	es
	push	ds
	push	ax
	mov	ax,ds
	mov	es,ax
	mov	ax,scrseg
	mov	ds,ax
	mov	si,ds:word ptr [llptr] ; get ptr to bottom line
	pop	di		; destination is on stack
	mov	cx,swidth	; # of bytes to copy
	rep	movsb		; get the top line
	pop	ds		; restore segments
	pop	es
	ret
getbot	endp

; handle the previous screen button...

prvscr	proc	near
	mov	ax,offset tlbuf
	mov	bx,offset topbuf
	call	getcirc
	jc	prvsc3		; no lines, forget it
	call	savpos		; save cursor position
	mov	ax,offset botbuf ; place to put screen
	mov	bx,slen		; else just use last line on screen
	mov	dx,-1		; move backwards
	call	rolscr		; save current screen
	call	cmblnk		; clear screen
	mov	cx,slen		; # of lines per screenfull
prvsc1:	mov	bl,cl		; this is current line
	mov	bh,1		; this is column
	mov	ax,offset tlbuf ; where to get the line from
	push	cx		; save count
	call	prlin		; put the line on the screen
	mov	ax,offset tlbuf
	mov	bx,offset topbuf
	call	getcirc		; get another line
	pop	cx
	jc	prvsc2		; no more, exit loop
	loop	prvsc1		; loop for all lines
prvsc2:	call	rstpos		; restore screen position
prvsc3:	ret			; and return
prvscr	endp

; handle the next screen button...

nxtscr	proc	near
	mov	ax,offset tlbuf
	mov	bx,offset botbuf
	call	getcirc		; get a line from the bottom
	jc	nxtsc3		; no lines, forget it
	call	savpos		; save cursor pos
	mov	ax,offset topbuf ; place to put screen
	mov	bx,1		; start with first line
	mov	dx,1		; move backwards
	call	rolscr		; save current screen
	call	cmblnk		; clear screen
	mov	cx,slen		; # of lines per screenfull
nxtsc1:	mov	bl,slen+1
	sub	bl,cl		; this is current line
	mov	bh,1		; this is column
	mov	ax,offset tlbuf ; where to get the line from
	push	cx		; save count
	call	prlin		; put the line on the screen
	mov	ax,offset tlbuf	; where to put the next line
	mov	bx,offset botbuf
	call	getcirc		; try to get another
	pop	cx
	jc	nxtsc2		; no more, break loop
	loop	nxtsc1		; loop for all lines
nxtsc2:	call	rstpos		; restore cursor position
nxtsc3:	ret			; and return
nxtscr	endp

; save a screen by rolling them into a circular buffer.
; enter with ax/ circular buffer ptr, bx/ first line to get
; dx/ increment

rolscr	proc	near
	shl	dx,1		; double increment for word ptr
	dec	bx		; ptr starts at 0
	shl	bx,1		; convert to word ptr
	mov	cx,slen		; # of lines to save
rolsc1:	push	cx
	push	dx
	push	bx
	push	ax
	push	ds
	mov	ax,scrseg
	mov	ds,ax		; address screen
	mov	si,ds:[latofs+bx] ; get current line
	mov	di,offset rlbuf ; place to put it
	mov	cx,swidth	; # of bytes to move
	rep	movsb		; get the lne
	pop	ds		; restore segment
	pop	bx		; this is desired circ buffer ptr
	mov	ax,offset rlbuf ; this is where the line is
	call	putcirc		; save in circular buffer
	mov	ax,bx		; put buffer ptr back where it belongs
	pop	bx		; get line pos back
	pop	dx		; and increment
	pop	cx		; don't forget counter
	add	bx,dx		; move to next line
	loop	rolsc1		; loop thru all lines
	ret			; and return
rolscr	endp

; move screen down a line, get one previous line back

prvlin	proc	near		; get the previous line back
	mov	ax,offset tlbuf	; place to put line temporarily
	mov	bx,offset topbuf ; where to get lines from
	call	getcirc		; try to get a line
	jc	prvli1		; no more, just return
	mov	ax,offset blbuf	; place for bottom line
	call	getbot		; fetch bottom line
	mov	ax,offset blbuf
	mov	bx,offset botbuf
	call	putcirc		; save in circular buffer
	call	savpos		; save cursor position
	mov	dx,offset topdwn ; home, then reverse index
	call	tmsg
	mov	ax,offset tlbuf	; point to data
	mov	bx,0101H	; print line at top of screen
	call	prlin		; print the line
	call	rstpos		; restore cursor position
prvli1:	ret			; and return
prvlin	endp

; move screen up a line, get one bottom line back
nxtlin	proc	near
	mov	ax,offset blbuf	; place to put line temporarily
	mov	bx,offset botbuf ; where to get lines from
	call	getcirc		; try to get a line
	jc	nxtli1		; no more, just return
	call	savtop		; save one line off of top
	call	savpos		; save cursor position
	mov	dx,offset botup ; go to bottom, then scroll up a line
	call	tmsg
	mov	ax,offset blbuf	; point to data
	mov	bx,0100H + slen	; print at bottom line
	call	prlin		; print the line
	call	rstpos		; restore cursor position
nxtli1:	ret			; and return
nxtlin	endp

; save cursor position
savpos	proc	near
	mov	dx,offset curinq ; where is the cursor?
	call	tmsg
	mov	posbuf,esc	; put an escape in the buffer first
	mov	di,offset posbuf+1
savpo1:	mov	ah,8		; read, no echo
	int	dos
	cmp	al,'R'		; end of report?
	je	savpo2		; yes
	stosb			; no, save it
	jmp	savpo1		; and go on
savpo2:	mov	al,'H'		; this ends the sequence when we send it
	stosb
	mov	byte ptr [di],'$' ; need this to print it later
	ret			; and return
savpos	endp

; restore the position saved by savpos
rstpos	proc	near
	mov	dx,offset posbuf
	call	tmsg		; just print this
	ret			; and return
rstpos	endp

; print a ff-terminated line at most swidth long...  Pass the line in ax.
; cursor position should be in bx.
; prlina writes attributes as well, which should be passed in dx.
prlin	proc	near
	mov	bp,2		; print characters only
	jmp	short prli1
prlina:	xor	bp,bp		; 0 means print attributes as well.
prli1:	push	es		; this trashes es!!!
	mov	si,ax		; better place for ptr
	mov	di,ax		; need it here for scan
	mov	cx,swidth	; max # of chars in line
	mov	al,0ffh		; this marks the end of the line
	repne	scasb		; look for the end
	jne	prli2		; not found
	inc	cx		; account for pre-decrement
prli2:	neg	cx
	add	cx,swidth	; figure out length of line
	jcxz	prli3		; 0-length line, skip it.
	mov	ax,2		; writing characters
	mov	bp,ds		; wants segment here
	mov	di,14H		; fast write to screen
	int	firmwr		; pos is in bx, char ptr in si
prli3:	pop	es		; restore register
	ret			; and return
prlin	endp


; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.
 
RSKP    PROC    NEAR
	pop bp
	add bp,3
	push bp
        ret
RSKP    ENDP
 
; Jumping here is the same as a ret.
 
R       PROC    NEAR
        ret
R       ENDP

code	ends 
	end
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msxrb.asm
	/bin/echo -n '	'; /bin/ls -ld msxrb.asm
fi
/bin/echo 'Extracting msxsys.doc'
sed 's/^X//' <<'//go.sysin dd *' >msxsys.doc

               SPECIFICATION FOR KERMIT SYSTEM-DEPENDENT MODULES

                      by Jeff Damens, Columbia University

All the system-independent global data structures used in Kermit-MS are defined
in the file MSDEFS.H.

The routine MSXxxx.ASM contains system-dependent support for system xxx, except
for terminal emulation, which is in MSXxxx.ASM, described below.

The  routines  in the MSX module may change any registers but the stack pointer
and segment registers, unless otherwise noted.  A routine that  returns  via  a
RET  instruction is said to return normally; a routine that skip returns is one
that returns to three bytes past the normal return address.

Global variables that must be defined in the system-dependent module:

XXOFSNT          byte.  This should be set to a non-zero value if we  are  doing
                flow  control  and  have  sent  an XOFF character to the remote
                host, zero otherwise.

MACHNAM         byte.  A  $-terminated  string  identifying  the  machine  this
                version of Kermit is for; it is printed when Kermit starts up.

SETKTAB         byte.  A keyword table associating terminal key names to 16-bit
                scan  code  values, used in the set key command.  If the kermit
                version can accept arbitrary decimal values as scan codes,  the
                word "SCAN" should appear in the table with a scan value of -1.
                If  key  redefinition is not implemented, the first byte of the
                table should be a zero.

SETKHLP         byte.  A $-terminated string to be printed when ? is  typed  in
                the  SET KEY command.  This is usually simply a list of the key
                names in  SETKTAB.    SETKHLP  must  be  defined  even  if  key
                redefinition  is not implemented, to satisfy the linker; if key
                redefinition  is  not  implemented,  SETKHLP  will   never   be
                displayed.

COUNT           word.   The number of characters in the serial input buffer, if
                known.  This is how Kermit knows to send an XON if  the  serial
                handler  has  sent an XOFF.  If the number of characters in the
                buffer isn't known, COUNT should be 0.

These are the required entry points for the system dependent  dependent  module
MSXxxx.ASM.

SERINI

Parameters      None.

Returns         Normally, no return value.

Description     Perform  any initialization that must be done before the serial
                port can  be  used,  including  setting  baud  rate,  interrupt
                vectors,  etc.  Parity and baud rate should be set according to
                the values in the PORTINFO structure.   The  external  variable
                PORTVAL  points to the PORTINFO structure for the current port.
                Calling SERINI more than once without an  intervening  call  to
                SERRST should have no effect.


SERRST

Parameters      None.

Returns         Normally, no return value.

Description     Undoes  any  initialization done by SERINI, including resetting
                the serial port, restoring any  interrupt  vectors  changed  by
                SERINI,   etc.     Calling  this  more  than  once  without  an
                intervening call to SERINI should be harmless.


CLRBUF

Parameters      None.

Returns         Normally, no return value.

Description     Remove and discard from the  serial  port's  input  buffer  any
                characters  sent by the remote host that have not yet been read
                by Kermit, and set COUNT to 0.  This  is  used  before  a  file
                transfer  to flush NAK's that accumulate in the buffer when the
                remote host is in server mode.


OUTCHR

Parameters      A character in AH.

Returns         Skip returns if the character  has  been  transmitted;  returns
                normally  if  the character can not be transmitted because of a
                hardware error.

Description     Sends the character in AH out  the  currently  selected  serial
                port.    OUTCHR  can  assume  that SERINI will have been called
                previously. OUTCHR should call the external  routine  DOPAR  to
                set  the parity of the character if the communications hardware
                doesn't automatically set  parity.    Flow  control  should  be
                honored;  the external variable PORTVAL contains a pointer to a
                PORTINFO structure (as  defined  in  MSDEFS.H)  containing  the
                current flow control definitions.


COMS

Parameters      None.

Returns         Normally   if  a  parse  error  is  encountered,  skip  returns
                otherwise.

Description     Called by the SET PORT command.  On  a  machine  with  multiple
                serial  ports,  COMS  should  parse for the name or number of a
                serial port and make that the port used by succeeding calls  to
                SERINI, PRTCHR, OUTCHR, and SERRST.  It should set the external
                variable   PORTVAL  to  point  to  one  of  the  external  port
                structures  PORT1  or  PORT2,  and  set  COMFLG  in  the  FLAGS
                structure to 1 for port one, 0 for port 2.  For implementations
                that  use  only one serial port, COMS should print a message to
                that effect and skip return.


VTS

Parameters      None.

Returns         Normally  if  a  parse  error  is  encountered,  skip   returns
                otherwise.

Description     Parses  for  an  ON  or  OFF,  sets HEATH-19 emulation while in
                terminal emulation appropriately.  The VTFLG field of the FLAGS
                structure should be set non-zero if HEATH-29 emulation  is  on,
                zero  otherwise.  If HEATH-19 emulation is not done, VTS should
                print a message and skip return.


DODEL

Parameters      None.

Returns         Normally, no return value.

Description     Erases the character immediately to the left of the cursor from
                the screen, then backs up the cursor.


CTLU

Parameters      None.

Returns         Normally, no return value.

Description     Move the cursor to the left margin, then clear the line.


CMBLNK

Parameters      None.

Returns         Normally, no return value.

Description     Clears the screen and homes the cursor.


LOCATE

Parameters      None.

Returns         Normally, no return value.

Description     Homes the cursor.


LCLINI

Parameters      None.

Returns         Normally, no return value.

Description     Performs any system-dependent initialization required  by  this
                implementation.


PRTCHR

Parameters      None.

Returns         Normally,  with  the next character from the currently selected
                serial port in AL.  Skip returns if no character is available.

Description     Reads the next character from the current serial port.   PRTCHR
                can assume SERINI has been called previously, and should handle
                flow control correctly.


DOBAUD

Parameters      None.

Returns         Normally, no return value.

Description     Sets  the baud rate for the current port.  The baud rate should
                be obtained from the BAUD  field  of  the  PORTINFO  structure,
                pointed to by the external variable PORTVAL.


CLEARL

Parameters      None.

Returns         Normally, no return value.

Description     Clears from the cursor to the end of the current line.


DODISK

Parameters      None.

Returns         Normally, no return value.

Description     Sets  the external variable DRIVES to the number of disk drives
                attached to the machine.


GETBAUD

Parameters      None.

Returns         Normally, no return value.

Description     Store current baud rate of the currently selected port  in  the
                BAUD  field of the current PORTINFO structure, which is pointed
                to by PORTVAL.  If the baud rate is to default to a  particular
                value,  this  routine  can store that value into the BAUD field
                instead.


BEEP

Parameters      None.

Returns         Normally, no return value.

Description     Rings the terminal bell.


PUTHLP

Parameters      A pointer to a string in AX.

Returns         Normally, no return value.

Description     Writes the null-terminated string given in AX to the  terminal.
                This  is used to display help and status messages.  The IBM and
                Rainbow versions write the string in a reverse video box.


PUTMOD

Parameters      A pointer to a string in AX.

Returns         Normally, no return value.

Description     Writes the null-terminated string given in AX to the last  line
                of the screen, in inverse video if possible.


CLRMOD

Parameters      None.

Returns         Normally, no return value.

Description     Clears the line written by PUTMOD.


POSCUR

Parameters      Row in DH, column in DL.

Returns         Normally, no return value.

Description     Positions  the  cursor to the row and column given in DX.  Rows
                and columns both originate at 0 (not 1!).


SENDBR

Parameters      None.

Returns         Normally, no return value.

Description     Send a break to the current serial port.


SHOWKEY

Parameters      Pointer to a terminal argument block in AX (see TERM below).

Returns         Normally, with a string pointer in AX and  the  length  of  the
                string in CX.

Description     Called  by the SHOW KEY command.  Reads a key from the terminal
                and  returns  a  string   containing   implementation-dependent
                information  about  the  key.  In  the  usual  case, the string
                contains the key's (machine-dependent) scan code, and the key's
                definition (if any) from the  terminal  argument  block.    The
                length  of  the  returned string should be returned in CX.  The
                string may contain any characters; unprintable characters  will
                be  quoted  when  the string is printed.  If the implementation
                does not support key redefinition, SHOWKEY may return a  static
                string saying so.


TERM

Parameters      Pointer to terminal argument block in AX.

Returns         Normally, no return value.

Description     Do  terminal  emulation,  based  on  argument  block  described
                below...



The terminal emulator is  supplied  in  the  file  MSYxxx.ASM.    The  terminal
argument block passed to the terminal emulator has the following fields:

FLGS            Byte containing flags.  Flags are:

                SCRSAM (80H)    If   on,   the   terminal   emulator  shouldn't
                                re-display the screen when entered.

                CAPT (40H)      Capture output.  If on, the routine  passed  in
                                field  CAPTR is called with each character sent
                                to the screen.

                EMHEATH (20H)   Emulate a Heath-19 terminal if on.

                HAVTT (10H)     A key redefinition table is present.

                TRNCTL (08H)    Print control character X  as  ^X  (useful  for
                                debugging).

                MODOFF (04H)    Do not display emulator mode line if on.

                LCLECHO (01H)   Echo  keyboard  characters  on  the  screen  in
                                addition to sending them to the port.

PRT             Port to use for terminal emulation, used  only  in  mode  line.
                This is just a copy of COMFLG in FLAGS.

COLS            Number of columns on screen.

ROWS            Number of rows on screen.

CAPTR           Routine  to  call  to with each character sent to the screen if
                CAPT flag is on.  Characters are passed in AL.

BELLD           Bell divisor (used only on IBM).

KLEN            Number of keys in key redefinition table, if HAVTT flag is on.

KTAB            Address of key redefinition table.  The key redefinition  table
                is a table of KLEN 16-bit scan codes.  Each (machine dependent)
                scan code represents a key that is redefined.

KRPL            Address  of  key  replacement table.  The key replacement table
                parallels the key redefinition table given in KTAB.  Entries in
                the replacement table are  16-bit  pointers  to  redefinitions.
                Each  redefinition  has  a  one-byte  length,  followed  by the
                definition.

ESCC            Escape character (single byte).  When this character  is  typed
                to the emulator, it should return.

BAUDB           byte.    Bits  describing the baud rate so it can be printed on
                the mode line.  This is  a  copy  of  the  BAUD  field  in  the
                PORTINFO  structure.    Currently  used  only  on the IBM.  See
                MSDEFS.H for possible values.

PARITY          byte.  Current parity to print on the mode line.    This  is  a
                copy  of PARFLG in the PORTINFO structure.  Currently used only
                on the IBM.  See MSDEFS.H for possible values.
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msxsys.doc
	/bin/echo -n '	'; /bin/ls -ld msxsys.doc
fi

knutson@ut-ngp.UUCP (Jim Knutson) (10/05/84)

: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting msxwng.asm'
sed 's/^X//' <<'//go.sysin dd *' >msxwng.asm
	public  serini, serrst, clrbuf, outchr, coms, vts, dodel, ctlu
        public  cmblnk, locate, lclini, prtchr, dobaud, clearl
        public  dodisk, getbaud, beep, term, puthlp
	public	count, poscur, machnam, sendbr, putmod, clrmod
	public	setktab, setkhlp, xofsnt, showkey
        include msdefs.h

false   equ     0
true	equ	1	

ctrla	equ	1		; Control-A.

auxin	equ	3
auxout	equ	4
auxfil	equ	3		; file number of aux file.
iordy	equ	6		; input ready function
write	equ	40h
wbios	equ	88h
settrp	equ	02h
clrtrp	equ	03h
chrrdy	equ	01h
txrdy	equ	02h

rcvdat	equ	1080h
rcvstat equ	1082h
rcvmod	equ	1084h
rcvcmd	equ	1086h
trdat	equ	1088h
trmod	equ	108ch
wrcmd	equ	108eh

; mode bits
mod1	equ	4dh			; clock rate, 8 bits, 1 stop bit
mod2	equ	30h			; internal clock

; command register bits
txen	equ	01h
dtr	equ	02h
rxen	equ	04h
brk	equ	08h
clrerr	equ	10h
rts	equ	20h


datas   segment public 'datas'
        extrn   drives:byte, flags:byte, trans:byte
	extrn	portval:word, port1:byte, port2:byte, swchar:byte

portin	db	0
crlf    db      cr,lf,'$'
machnam	db	'FIELD TEST Wang$'
noimp	db	cr,lf,'Command not implemented.$'
shkmsg	db	'Not implemented.'
shklen	equ	$-shkmsg
xofsnt	db	0		; Say if we sent an XOFF.
xofrcv	db	0		; Say if we received an XOFF.
setktab	db	0
setkhlp	db	0
invseq	db	esc,'[7m$'	; Reverse video on.
nrmseq	db	esc,'[0m$'	; Reverse video off.
ivlseq	db	79 dup (' '),cr,'$' 	; Make a line inverse video
comphlp db      cr,lf,'1 (COM1)   2 (COM2)$'    
delstr  db      BS,' ',BS,'$' 	; Delete string.
clrlin	db	cr,esc,'[K$'
tmp     db      ?,'$'
temp    dw      0
temp1   dw      ?               ; Temporary storage.
temp2   dw      ?               ; Temporary storage.

; Entries for choosing communications port. [19b]
comptab db      04H
        db      01H,'1$'
        dw      01H
        db      01H,'2$'
        dw      00H
        db      04H,'COM1$'
        dw      01H
        db      04H,'COM2$'
        dw      00H

; variables for serial interrupt handler

source  db      bufsiz DUP(?)   ; Buffer for data from port.
bufout  dw      0               ; buffer removal ptr
count   dw      0               ; Number of chars in int buffer.
bufin   dw      0               ; buffer insertion ptr
telflg  db      0               ; Are we acting as a terminal. [16] [17c]
clreol  db      esc,'[0K$'
blank	db	esc,'[H',esc,'[J$'
movcur  db      esc,'['
colno   db      20 dup (?)
ten     db      10
prthnd  dw      0
ourarg  termarg <>
; must parallel baud rate defs in pcdefs.
baudtab db	0ffh		; 45.5 baud (not supported)
	db	0		; 50
	db	1		; 75
	db	2		; 110
	db	3		; 134.5
	db	4		; 150
	db	5		; 300
	db	6		; 600
	db	7		; 1200
	db	8		; 1800
	db	9		; 2000
	db	10		; 2400
	db	12		; 4800
	db	14		; 9600
	db	15		; 19.2k
	db	0ffh		; 38.4k (ha)

nbaud	equ	$-baudtab
qid	dw	?
prtcnt	dw	?
trqid	dw	?
tmqid	dw	?
brflg	db	?
datas   ends

code    segment public
        extrn   comnd:near, dopar:near, prserr:near
        assume  cs:code,ds:datas

DODISK  PROC    NEAR
	mov ah,gcurdsk			; Current disk value to AL.
	int dos
	mov dl,al			; Put current disk in DL.
	mov ah,seldsk			; Select current disk.
	int dos				; Get number of drives in AL.
	mov drives,al
	ret
DODISK  ENDP

; Clear the input buffer before sending a packet. [20e]

CLRBUF  PROC    NEAR
	mov	ah,ioctl
	mov	bx,auxfil
	mov	al,iordy
	int	dos
	cmp	al,0ffh
	jne	clrb1			; not ready, keep going
	mov	ah,auxin
	int	dos
	jmp	clrbuf			; read char and keep going.
clrb1:	mov	count,0
	mov ax,offset source
	mov bufin,ax
        mov bufout,ax
        ret
CLRBUF  ENDP

; Common routine to clear to end-of-line. [19a]

CLEARL  PROC    NEAR
        mov dx,offset clreol
        mov ah,prstr
        int dos
        ret
CLEARL  ENDP

; This routine should set the baud rate for the current port but it
; is actually done in SERINI.
dobaud	proc	near
	ret
dobaud  endp

; Send a break out the current serial port.  Returns normally.
sendbr:	push dx
	push ax
	push cx
	push ds			; preserve data segment
	mov ax,cs
	mov ds,ax		; handler is in code segment
	mov al,settrp
	mov bx,txrdy		; interrupt on transmitter empty
	mov cx,0		; interrupt immediately
	mov dx,offset sendb1	; handler routine
	int wbios
	pop ds
	mov trqid,bx
	push ds
	mov ax,cs
	mov ds,ax
	mov al,settrp
	mov bx,0		; 10 ms timer
	mov cx,21		; after 21 times - approx 200 ms.
	mov dx,offset sendb2	; timer interrupt
	int wbios
	pop ds
	mov tmqid,bx

	mov brflg,1
	mov dx,rcvcmd
	in al,dx		; Read command register.
	or al,brk+txen		; Set send-break bit.
	mov dx,wrcmd		; Write command register.
	out dx,al
pause:	cmp brflg,0
	jne pause		; while non-zero, keep going
	mov al,clrtrp		; clear the trap
	mov bx,trqid
	int wbios
	mov al,clrtrp
	mov bx,tmqid
	int wbios
	pop cx
	pop ax
	pop dx
	ret

sendb1	proc	far
	ret
sendb1	endp

sendb2	proc	far
	push ax
	push ds
	mov ax,seg datas
	mov ds,ax
	mov brflg,0
	mov dx,rcvcmd
	in al,dx
	and al,not (txen + brk)
	mov dx,wrcmd
	out dx,al
	pop ds
	pop ax
	ret
sendb2	endp


; Write a line in inverse video at the bottom of the screen...
; the line is passed in dx, terminated by a $.  Returns normally.
putmod	proc	near
	push	dx		; preserve message
	mov	dx,24 * 100H	; line 24
	call	poscur
	mov	dx,offset invseq ; put into inverse video
	mov	ah,prstr
	int	dos
	pop	dx
	int 	dos
	mov	dx,offset nrmseq ; normal videw
	int	dos
	ret			; and return
putmod	endp

; Clear the mode line written by putmod.  Returns normally.
clrmod	proc	near
	mov	dx,24 * 100H
	call	poscur
	call	clearl
	ret
clrmod	endp

; Put a help message one the screen in reverse video.  Pass
; the message in AX, terminated by a null.  Returns normally.
; The message is put wherever the cursor currently is located.
puthlp	proc	near
	push ax
	mov ah,prstr		; Leave some room before the message.
	mov dx,offset crlf
	int dos
	mov dx,offset invseq	; Put into reverse video.
	int dos
	pop si			; Put message address here.
puth0:	mov ah,prstr
	mov dx,offset ivlseq	; Make line inverse video
	int dos
puth1:	lodsb
	cmp al,0		; Terminated with a null.
	je puth2
	mov dl,al
	mov ah,conout
	int dos	
	cmp al,lf		; Line feed?
	je puth0		; Yes, clear the next line.
	jmp puth1		; Else, just keep on writing.
puth2:	mov dx,offset nrmseq	; Normal video.
	mov ah,prstr
	int dos
	mov dx,offset crlf
	int dos
	ret
puthlp	endp

outchr: push dx                 ; Save register.
	mov al,ah
	call dopar
	mov dl,al
	mov ah,auxout
	int dos
	pop	dx
	jmp rskp


; This routine blanks the screen.

CMBLNK	PROC	NEAR
        mov ah,prstr
        mov dx,offset blank
        int dos
        ret
CMBLNK  ENDP

LOCATE  PROC    NEAR
        mov dx,0                ; Go to top left corner of screen.
        jmp poscur              ; callret...
LOCATE  ENDP

GETBAUD PROC    NEAR
	cld
	mov dx,rcvmod
	in al,dx
	in al,dx		; get second mode word
	and	al,0fh		; isolate baud rate
	mov cx,nbaud
	mov di,offset baudtab
	mov bx,ds
	mov es,bx		; address correct segment
	mov bx,portval
	repne scasb		; look for baud rate
	jne getb1		; mystery baud rate...
	sub di,offset baudtab + 1
	mov [bx].baud,di	; store baud rate in comm area
	ret			; and return
getb1:	mov [bx].baud,-1	; unknown baud rate
        ret
GETBAUD ENDP

; skip returns if no character available at port,
; otherwise returns with char in al, # of chars in buffer in dx.
PRTCHR  PROC    NEAR
prtchx: cmp count,0
	je prtch4		; empty buffer, forget it.
	mov si,bufout
	lodsb
	cmp si,offset source + bufsiz
	jb prtch1
	mov si,offset source
prtch1: mov bufout,si
	dec count
	push bx
	mov bx,portval
        cmp [bx].parflg,PARNON ; no parity?
        je prtch3               ; then don't strip
        and al,7fh              ; else turn off parity
prtch3: mov dx,count		; chars left in buffer
	pop bx
        ret
prtch4: jmp rskp		; no chars...
PRTCHR  ENDP

; Position the cursor according to contents of DX.

POSCUR  PROC    NEAR
        mov     ax,ds
        mov     es,ax                   ; address data segment!!!
        cld
        mov     di,offset colno
        mov     al,dh                   ; row
	inc	al
        call    nout
        mov     al,';'
        stosb
        mov     al,dl                   ; col
	inc	al
        call    nout
        mov     al,'H'
        stosb
        mov     al,'$'
        stosb
        mov     dx,offset movcur
        mov     ah,prstr
        int     dos                     ; print the sequence
        ret
POSCUR  ENDP

NOUT	PROC	NEAR
        cbw                     ; extend to word
        div     byte ptr ten    ; divide by 10
        or      al,al           ; any quotient?
        jz      nout1           ; no, forget this
        push    ax              ; save current result
        call    nout            ; output high order
        pop     ax              ; restore
nout1:  mov     al,ah           ; get digit
        add     al,'0'          ; make printable
        stosb
        ret                     ; put in buffer and return
NOUT    endp

; Perform a delete.

DODEL   PROC    NEAR
        mov ah,prstr
        mov dx,offset delstr    ; Erase weird character.
        int dos                 
        ret
DODEL   ENDP

; Perform a Control-U.

CTLU    PROC    NEAR
        mov ah,prstr
        mov dx,offset clrlin
        int dos
        ret
CTLU    ENDP

COMS    PROC    NEAR
        mov dx,offset comptab
        mov bx,offset comphlp
        mov ah,cmkey
        call comnd
         jmp r
        push bx
        mov ah,cmcfm
        call comnd              ; Get a confirm.
         jmp comx               ;  Didn't get a confirm.
	 nop
	pop bx
        mov flags.comflg,bl     ; Set the comm port flag.
	cmp flags.comflg,1	; Using Com 1?
	jne coms0		; Nope.
	mov ax,offset port1
	mov portval,ax
	ret
coms0:	mov ax,offset port2
	mov portval,ax
        ret
comx:	pop bx
	ret
COMS    ENDP

VTS     PROC    NEAR
	jmp notimp
VTS     ENDP

notimp:	mov ah,prstr
	mov dx,offset noimp
	int dos
	jmp prserr

lclini: mov trans.escchr,ctrla	; Use Control-A as escape char.
	mov swchar,'/'
	ret

showkey:
	mov ax,offset shkmsg
	mov cx,shklen
	ret


;     Common initialization for using serial port.

SERINI  PROC    NEAR
	cmp portin,0		; already inited?
	jne serin1		; yes, skip it
	mov portin,1		; remember inited
	mov dx,rcvcmd
	in al,dx		; read cmd register to reset mode ptr.
	mov dx,trmod
	mov al,mod1
	out dx,al
	push bx
	mov bx,portval
	mov si,[bx].baud
	pop bx
	mov al,baudtab[si]
	or al,mod2
	out dx,al
	mov dx,wrcmd
	mov al,txen+dtr+rxen+clrerr+rts
	out dx,al		; enable transmit and receive
	call clrbuf		; empty buffer
	mov al,settrp
	mov bx,chrrdy		; interrupt on character ready
	mov cx,0		; interrupt immediately
	mov dx,offset serint	; handler routine
	mov prtcnt,0		; no characters in yet
	push ds
	mov si,cs
	mov ds,si
	int wbios
	pop ds
	or al,al
	jne serin1
	mov qid,bx		; preserve trap identification
serin1: ret			; We're done. [21c]
SERINI  ENDP

SERRST  PROC    NEAR
	cmp portin,0		; already de-inited?
	je serrs1		; yes, skip this
	mov portin,0
	mov al,clrtrp
	mov bx,qid
	int wbios
serrs1:	ret
SERRST  ENDP

; serial interrupt handler
serint	proc	far
	push ds
	push ax
	push dx
	push di
	mov ax,seg datas
	mov ds,ax
	mov di,bufin
	mov dx,rcvdat
	in al,dx
	mov [di],al
	inc di
	cmp di,offset source + bufsiz
	jb sernt1
	mov di,offset source
sernt1: mov bufin,di
	inc count
	pop di
	pop dx
	pop ax
	pop ds
	ret
serint	endp


; Generate a short beep.

BEEP    PROC    NEAR
        mov dl,bell
        mov ah,conout
        int dos 
        ret
BEEP    ENDP 
 
; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.
 
RSKP    PROC    NEAR
        pop bp
        add bp,3
        push bp
        ret
RSKP    ENDP
 
; Jumping here is the same as a ret.
 
R       PROC    NEAR
        ret
R       ENDP

term    proc    near
        mov si,ax               ; this is source
        mov di,offset ourarg    ; place to store arguments
        mov ax,ds
        mov es,ax               ; address destination segment
        mov cx,size termarg
        rep movsb               ; copy into our arg blk
term1:  call prtchr
        jmp short term2         ; have a char...
        nop
        nop
        jmp short term3         ; no char, go on
term2:	and al,7fh
	push ax
        mov dl,al
	mov ah,dconio
        int dos                 ; write out the character
        pop ax
        test ourarg.flgs,capt   ; capturing output?
        jz term3                ; no, forget it
        call ourarg.captr       ; else call the routine
term3:  mov ah,dconio
        mov dl,0ffh
        int dos
	or al,al
        jz term1                ; no character, go on
        cmp al,ourarg.escc      ; escape char?
        je term4                ; yes, exit
        push ax                 ; save char
        mov ah,al
        call outchr             ; output the character
        nop
        nop
        nop
        pop ax
        test ourarg.flgs,lclecho ; echoing?
        jz term1                ; no, continue loop
        mov dl,al
        mov ah,dconio
        int dos
        jmp term1               ; else echo and keep going
term4:	ret
term    endp
code	ends
        end
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msxwng.asm
	/bin/echo -n '	'; /bin/ls -ld msxwng.asm
fi
/bin/echo 'Extracting msxz100.asm'
sed 's/^X//' <<'//go.sysin dd *' >msxz100.asm

; Kermit system dependent module for Heath/Zenith Z100

	public	serini, serrst, clrbuf, outchr, coms, vts, dodel,
	public	ctlu, cmblnk, locate, prtchr, dobaud, clearl,
	public	dodisk, getbaud, beep,
	public	count, xofsnt, puthlp, putmod, clrmod, poscur
	public	sendbr, machnam, setktab, setkhlp, lclini, showkey
	include msdefs.h

false	equ	0
true	equ	1
mntrgh	equ	bufsiz*3/4	; High point = 3/4 of buffer full.

; constants used by serial port handler

BRKBIT	EQU	048H		; Send-break bit. 

MDMCOM1	EQU	00EFH		; Address of modem port command. [19b]

; external variables used:
; drives - # of disk drives on system
; flags - global flags as per flginfo structure defined in pcdefs
; trans - global transmission parameters, trinfo struct defined in pcdefs
; portval - pointer to current portinfo structure (currently either port1
;    or port2)
; port1, port2 - portinfo structures for the corresponding ports

; global variables defined in this module:
; xofsnt, xofrcv - tell whether we saw or sent an xoff.
; setktab - keyword table for redefining keys (should contain a 0 if
;    not implemented)
; setkhlp - help for setktab.

BIOS_SEG SEGMENT AT 40H		; Define segment where BIOS really is
	ORG	6*3
BIOS_AUXOUT LABEL FAR		; AUX output routine
	ORG	26*3
BIOS_AUXFUNC LABEL FAR		; AUX: function
	ORG	27*3
BIOS_CONFUNC LABEL FAR		; CON: function
BIOS_SEG ENDS

; Function codes for BIOS_xxxFUNC
CHR_WRITE	EQU	0	; Write character
CHR_READ	EQU	1	; Read character
CHR_STATUS	EQU	2	; Get status
  CHR_SFGS	EQU	0	; Get status subfunction
  CHR_SFGC	EQU	1	; Get config subfunction
CHR_CONTROL	EQU	3	; Control function
  CHR_CFSU	EQU	0	; Set new configuration parameters
  CHR_CFCI	EQU	1	; Clear input buffer


datas 	segment	public 'datas'
	extrn	drives:byte, flags:byte, trans:byte
	extrn	portval:word, port1:byte, port2:byte

setktab	db	13
	mkeyw	'F0',96h
	mkeyw	'F1',97h
	mkeyw	'F2',98h
	mkeyw	'F3',99h
	mkeyw	'F4',9ah
	mkeyw	'F5',9bh
	mkeyw	'F6',9ch
	mkeyw	'F7',9dh
	mkeyw	'F8',9eh
	mkeyw	'F9',9fh
	mkeyw	'F10',0a0h
	mkeyw	'F11',0a1h
	mkeyw	'SCAN',-1

setkhlp	db	cr,lf,'Keyname: f0, ... f11, "HELP" or "SCAN" follwed by '
	db	'decimal scan code$'
brkval	db	0		; What to send for a break.
brkadr	dw	0		; Where to send it.
badbd	db	cr,lf,'Unimplemented baud rate$'
noimp	db	cr,lf,'Not implemented$'
machnam	db	'Heath-Zenith Z-100$'
crlf	db	cr,lf,'$'
delstr  db	BS,' ',BS,'$' 	; Delete string. [21d]
home	db	ESC,'H$'
eeolstr db	ESC,'K$'	; Erase to end of line
clrstr	db	ESC,'E$'	; Erase entire display
enamod	db	ESC,'x1$'	; Enable 25th line
dismod	db	ESC,'y1$'	; Disable 25th line
enascan	db	ESC,'y?$'	; Enable scan codes
disscan	db	ESC,'x?$'	; Disable scan codes
begrev	db	ESC,'p$'	; Enter reverse video
endrev	db	ESC,'q$'	; Exit reverse video
lin25	db	ESC,'Y8 $'	; Column 1 row 25
savcur	db	ESC,'j$'	; Save current cursor position
precur	db	ESC,'k$'	; Restore cursor to previous position
clrlin  db	cr,'$'		; Clear line (just the cr part).
xofsnt	db	0		; Say if we sent an XOFF.
xofrcv	db	0		; Say if we received an XOFF.
tmp	db	?,'$'
temp1	dw	?		; Temporary storage.

ontab	db	02H		; Two entries.
	db	03H,'OFF$'	; Should be alphabetized. [19a]
	dw	00H
	db	02H,'ON$'
	dw	01H

; this table is indexed by the baud rate definitions given in
; pcdefs.  Unsupported baud rates should contain FF.
bddat	label	word
	dw	0		; 45.5 baud
	dw	1		; 50 baud
	dw	2   		; 75 baud
	dw	3   		; 110 baud
	dw	4   		; 134.5 baud
	dw	5   		; 150 baud
	dw	6   		; 300 baud
	dw	7   		; 600 baud
	dw	8  		; 1200 baud
	dw	9  		; 1800 baud
	dw	10 		; 2000 baud
	dw	11 		; 2400 baud
	dw	12 		; 4800 baud
	dw	13 		; 9600 baud
	dw	14  		; 19200 baud
	dw	15  		; 38400 baud

; storage for port configuration
cfginfo	struc
cfclass	db	0
cfattr	db	0
cfport	dw	0
cfbaud	db	0
cfhshk	db	0
cfbctl	db	0
cfecnt	db	0
cfncnt	db	0
cfnchr	db	0
cfres	db	6 dup(?)
cfsize	db	0
cfginfo	ends

auxconf	cfginfo	<>

; variables for serial interrupt handler

count	dw	0		; Number of chars in int buffer.

ourarg	termarg	<>

shkbuf	db	300 dup (?)	; room for definition
shkmsg	db	'  Scan code: '
shkmln	equ	$-shkmsg
shkms1	db	cr,lf,'  Definition: '
shkm1ln	equ	$-shkms1
datas	ends

code	segment	public
	extrn	comnd:near, dopar:near, defkey:near
	assume	cs:code,ds:datas

; local initialization

lclini	proc	near
	mov	brkval,BRKBIT	; What to send for a break.
	mov	brkadr,MDMCOM1
 	mov	flags.vtflg,0	; Turn off true Heath mode (allows key macros)	 
	ret
lclini	endp

; this is called by Kermit initialization.  It checks the
; number of disks on the system, sets the drives variable
; appropriately.  Returns normally.  

DODISK	PROC	NEAR
	mov	ah,gcurdsk	; Current disk value to AL.
	int	dos
	mov	dl,al		; Put current disk in DL.
	mov	ah,seldsk	; Select current disk.
	int	dos
	mov	drives,al
	ret
DODISK	ENDP

; show the definition of a key.  The terminal argument block (which contains
; the address and length of the definition tables) is passed in ax.
; Returns a string to print in AX, length of same in CX.
; Returns normally.
showkey	proc	near
	push	es
	push	ax		; save the ptr
	mov	bx,ds
	mov	es,bx		; address data segment
	cld
showk1:	mov	ah,prstr
	mov	dx,offset enascan ; enable scan codes
	int	dos
	mov	ah,0ch		; char input with buffer flush
	mov	al,7
	int	dos
;	mov	ah,chr_control
;	mov	al,chr_cfci	; clear input
;	call	bios_confunc
;	mov	ah,chr_read
;	call	bios_confunc	; read a char
	push	ax
	mov	ah,prstr
	mov	dx,offset disscan ; disable scan codes
	int	dos
	pop	ax
;	push	ax		; save the character
;	call	gss		; get shift state
;	pop	bx
	mov	ah,0		; shift state to ah
;	mov	al,bh		; scan code to al
	push	ax		; remember scan code
	mov	di,offset shkbuf
	mov	si,offset shkmsg
	mov	cx,shkmln
	rep	movsb		; copy in initial message
	call	nout		; write out scan code
	mov	si,offset shkms1
	mov	cx,shkm1ln	; second message
	rep	movsb
	pop	ax		; get scan code back
	pop	bx		; and terminal arg block
	mov	cx,[bx].klen	; and length
	jcxz	showk2		; no table, not defined
	push	di		; remember output ptr
	mov	di,[bx].ktab	; get key table
	repne	scasw		; search for a definition for this
	mov	si,di		; remember result ptr
	pop	di		; get output ptr back
	jne	showk2		; not defined, forget it
	sub	si,[bx].ktab	; compute offset from beginning
	sub	si,2		; minus 2 for pre-increment
	add	si,[bx].krpl	; get index into replacement table
	mov	si,[si]		; pick up replacement
	mov	cl,[si]		; get length
	mov	ch,0
	inc	si
	rep	movsb		; copy into buffer
showk2:	mov	ax,offset shkbuf ; this is buffer
	mov	cx,di
	sub	cx,ax		; length
	pop	es
	ret			; and return
showkey	endp

; Clear the input buffer. This throws away all the characters in the
; serial interrupt buffer.  This is particularly important when
; talking to servers, since NAKs can accumulate in the buffer.
; Returns normally.

CLRBUF	PROC	NEAR
	cli
	mov 	ah,chr_control
	mov	al,chr_cfci
	call	bios_auxfunc
	mov 	count,0
	sti
	ret
CLRBUF	ENDP

; Clear to the end of the current line.  Returns normally.

CLEARL	PROC	NEAR
	mov	ah,prstr
	mov	dx,offset eeolstr	; Erase to end of line
	int	dos
	ret
CLEARL	ENDP

; Put the char in AH to the serial port.  This assumes the
; port has been initialized.  Should honor xon/xoff.  Skip returns on
; success, returns normally if the character cannot be written.

outchr:	mov	bp,portval
	cmp	ds:[bp].floflg,0 ; Are we doing flow control.
	je	outch2		; No, just continue.
	xor	cx,cx		; clear counter
outch1:	cmp	xofrcv,true	; Are we being held?
	jne	outch2		; No - it's OK to go on.
	loop	outch1		; held, try for a while
	mov	xofrcv,false	; timed out, force it off and fall thru.
outch2:	push	dx		; Save register.
	mov	al,ah		; Parity routine works on AL.
	call	dopar		; Set parity appropriately.
	call	bios_auxout
	pop	dx
	jmp	rskp

; This routine blanks the screen.  Returns normally.

CMBLNK	PROC	NEAR
	mov	ah,prstr
	mov	dx,offset clrstr
	int	dos
	ret
CMBLNK  ENDP

; Locate: homes the cursor.  Returns normally.

LOCATE  PROC	NEAR
	mov	ah,prstr
	mov	dx,offset home	; Go to top left corner of screen.
	int	dos
LOCATE  ENDP

; write a line in inverse video at the bottom of the screen...
; the line is passed in dx, terminated by a $.  Returns normally.
putmod	proc	near
	push	dx		; preserve message
	mov	ah,prstr
	mov	dx,offset savcur
	int	dos
	mov	dx,offset enamod
	int	dos
	mov	dx,offset lin25
	int	dos
	mov	dx,offset begrev
	int	dos
	pop	dx		; get message back
	int	dos		; write it out
	mov	dx,offset endrev
	int	dos
	mov	dx,offset precur
	int	dos
	ret			; and return
putmod	endp

; clear the mode line written by putmod.  Returns normally.
clrmod	proc	near
	mov	ah,prstr
	mov	dx,offset dismod
	int	dos
	ret
clrmod	endp

BEEP	PROC	NEAR
	mov	dl,07		; ASCII BEL
	mov	ah,dconio	
	int	dos             ; Ring it
	ret
BEEP	ENDP	


; put a help message on the screen.  This one uses reverse video...
; pass the message in ax, terminated by a null.  Returns normally.
puthlp	proc	near
	push	ax		; preserve this
	mov	ah,prstr
	mov	dx,offset crlf
	int	dos
	pop	si		; point to string again
puthl3:	lodsb			; get a byte
	cmp	al,0		; end of string?
	je	puthl4		; yes, stop
	mov	dl,al
	mov	ah,dconio
	int	dos
	jmp	puthl3		; and keep going
puthl4:	mov	ah,prstr
	mov	dx,offset crlf
	int	dos
	ret
puthlp	endp

; Set the baud rate for the current port, based on the value
; in the portinfo structure.  Returns normally.

DOBAUD	PROC	NEAR
	mov	bp,portval
	mov	temp1,ax	; Don't overwrite previous rate. [25]
	mov	ax,ds:[bp].baud	; Check if new rate is valid. [25]
	mov	tmp,2
	mul	tmp		; Get index into baud table.
	mov	bx,offset bddat	; Start of table.
	add	bx,ax
	mov	ax,[bx]		; The data to output to port.
	cmp	ax,0FFH		; Unimplemented baud rate.
	jne	dobd0
	mov	ax,temp1	; Get back orginal value.
	mov	ds:[bp].baud,ax	; Leave baud rate as is.
	mov	ah,prstr
	mov	dx,offset badbd	; Give an error message.
	int	dos
	ret
dobd0:	push	ax		; Save it
	mov	bx,ds		; Set up pointer to config info
	mov	es,bx		;  .  .  .
	mov	bx,offset auxconf ;  .  .  .
	mov	ah,chr_status
	mov	al,chr_sfgc	; get current config info
	call	bios_auxfunc
	pop	ax		; get baud back
	mov	auxconf.cfbaud,al
	mov	ah,chr_control	; Function is control
	mov	al,chr_cfsu	; Subfunction is set new config
	call	bios_auxfunc	; Set the configuration
	ret
DOBAUD	ENDP

; Get the current baud rate from the serial card and set it
; in the portinfo structure for the current port.  Returns normally.
; This is used during initialization.

GETBAUD	PROC	NEAR
	mov	bx,ds
	mov	es,bx
	mov	bx,offset auxconf
	mov	ah,chr_status
	mov	al,chr_sfgc	; Status function get config info
	call	bios_auxfunc
	mov	ch,0
	mov	cl,auxconf.cfbaud
	mov	bp,portval
	mov	ds:[bp].baud,cx
	ret
GETBAUD	ENDP

; skip returns if no character available at port,
; otherwise returns with char in al, # of chars in buffer in dx.
PRTCHR  PROC	NEAR
	call	chkxon		; see if we need to xon
	push	bx
	mov 	ah,chr_status
	mov 	al,chr_sfgs	; Status function get status
	call	bios_auxfunc
	cmp 	bl,0
	jnz 	prtch2
	pop 	bx
	jmp 	rskp     	; No data - check console.
prtch2:	mov	dh,0
	mov	dl,bl		; Place # of chars in dx
	mov	ah,chr_read
	call 	bios_auxfunc
	dec	dl		; Decrement number of chars
	mov	count,dx	; Save count for posterity
	pop	bx
	ret
PRTCHR  ENDP

; local routine to see if we have to transmit an xon
chkxon	proc	near
	push	bx
	mov	bx,portval
	cmp	[bx].floflg,0	; doing flow control?
	je	chkxo1		; no, skip all this
	cmp	xofsnt,false	; have we sent an xoff?
	je	chkxo1		; no, forget it
	cmp	count,mntrgh	; below trigger?
	jae	chkxo1		; no, forget it
	mov	ax,[bx].flowc	; ah gets xon
	call	outchr		; send it
	nop
	nop
	nop			; in case it skips
	mov	xofsnt,false	; remember we've sent the xon.
chkxo1:	pop	bx		; restore register
	ret			; and return
chkxon	endp

; Send a break out the current serial port.  Returns normally.
SENDBR	PROC	NEAR
	push	cx
	push	dx
	push	ax
	xor	cx,cx		; Clear loop counter.
	mov	dx,brkadr	; Port address.  [19b]
	in	al,dx		; Get current setting.
	or	al,brkval	; Set send-break bit(s).
	out	dx,al		; Start the break.
pause:	loop	pause		; Wait a while.
	xor	al,brkval	; Clear send-break bit(s).
	out	dx,al		; Stop the break.
	pop	ax
	pop	dx
	pop	cx
	ret			; And return.
SENDBR	ENDP

; Position the cursor according to contents of DX:
; DH contains row, DL contains column.  Returns normally.

POSCUR	PROC	NEAR
	push	dx
	mov	ah,CONOUT
	mov	dl,ESC
	int	dos
	mov	dl,'Y'
	int	dos
	pop	dx
	push	dx
	mov	dl,dh
	add	dl,' '
	int	dos
	pop	dx
	add	dl,' '
	int	dos
	ret
POSCUR	ENDP

; Delete a character from the terminal.  This works by printing
; backspaces and spaces.  Returns normally.

DODEL	PROC	NEAR
	mov	ah,prstr
	mov	dx,offset delstr ; Erase weird character.
	int	dos			
	ret
DODEL	ENDP

; Move the cursor to the left margin, then clear to end of line.
; Returns normally.

CTLU	PROC	NEAR
	mov	ah,prstr
	mov	dx,offset clrlin
	int	dos
	call	clearl
	ret
CTLU	ENDP

; set the current port.  

COMS	PROC	NEAR
	jmp	notimp
COMS	ENDP

; Set heath emulation on/off.

VTS	PROC	NEAR
	mov	dx,offset ontab
	mov	bx,0
	mov	ah,cmkey
	call	comnd
	 jmp	r
	push	bx
	mov	ah,cmcfm
	call	comnd			; Get a confirm.
	 jmp	vt0			; didn't get a confirm.
	 nop
	pop	bx
	mov	flags.vtflg,bl		; Set the Heath emulation flag
	ret
vt0:	pop	bx
	ret
VTS	ENDP

notimp:	mov	ah,prstr
	mov	dx,offset noimp
	int	dos
	jmp	rskp

; initialization for using serial port.  This routine performs
; any initialization necessary for using the serial port, including
; setting up interrupt routines, setting buffer pointers, etc.
; Doing this twice in a row should be harmless (this version checks
; a flag and returns if initialization has already been done).
; SERRST below should restore any interrupt vectors that this changes.
; Returns normally.

SERINI	PROC	NEAR
	ret			; We're done.
SERINI	ENDP

; Reset the serial port.  This is the opposite of serini.  Calling
; this twice without intervening calls to serini should be harmless.
; Returns normally.

SERRST	PROC	NEAR
	ret			; All done.
SERRST	ENDP


; put the number in ax into the buffer pointed to by di.  Di is updated
nout	proc	near
	mov	dx,0		; high order is always 0.
	mov	bx,10
	div	bx		; divide to get digit
	push	dx		; save remainder digit
	or	ax,ax		; test quotient
	jz	nout1		; zero, no more of number
	call	nout		; else call for rest of number
nout1:	pop	ax		; get digit back
	add	al,'0'		; make printable
	stosb			; drop it off
	ret			; and return
nout	endp


; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.
 
RSKP	PROC	NEAR
	pop	bp
	add	bp,3
	push	bp
	ret
RSKP	ENDP
 
; Jumping here is the same as a ret.
 
R	PROC	NEAR
	ret
R	ENDP

code	ends 
	end
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msxz100.asm
	/bin/echo -n '	'; /bin/ls -ld msxz100.asm
fi
/bin/echo 'Extracting msyibm.asm'
sed 's/^X//' <<'//go.sysin dd *' >msyibm.asm

	title term

	public	term, gss		; entry points
	include	msdefs.h

; some character definitions

chesc	equ	27
bel	equ	7

print_out equ	05h			; dos function to print to printer
pbout	equ	02h			; dos function to print a character
prscan	equ	72h			; print-screen scan code...
upscan	equ	49h			; up page
dnscan	equ	51h			; down page
ctlup	equ	84h			; ctl-up page
ctldn	equ	76h			; ctl-down page
homscn	equ	47h			; home screen
endscn	equ	4fh			; end of screen
screen	equ	10h			; bios screen call
kb	equ	16h			; keyboard interrupt
alt_shift equ	8H			; alt shift key down
ctl_shift equ	4H			; ctl key down
left_shift equ	2H			; left shift key down
right_shift equ	1H			; right shift key down

timer	equ	40h			; timer port
bel_prt	equ	61h			; speaker control

crt_status equ	3dah			; crt status port
disp_enb   equ	8			; display enable bit

uparr	equ	48h			; scan codes for arrow keys
dnarr	equ	50h
lftarr	equ	4bh
rgtarr	equ	4dh


modfrm	struc				; format of mode line
	db	'Esc chr: '
m_echr	db	2 dup (?)
	db	', Port: '
m_prt	db	1 dup (?)
	db	', Speed: '
m_baud	db	4 dup (?)
	db	', Parity: '
m_par	db	4 dup (?)
	db	', Echo: '
m_echo	db	3 dup (?)
	db	', Type '
m_hlp	db	2 dup (?)
	db	'? for Help'
modfrm	ends

datas	segment	public 'datas'
waste	db	100h dup (?)		;*** need this junk because assembler
					;*** generates non-relocatable offsets
					;*** for things like 
					;*** "sub di,offset foo"
					;*** if offset foo < 100H
; stuff for screen routines
flags	db	?			; status flags...
flags1	db	0			; internal flags.
prtscr	equ	80h			; print screen pressed
lnwrap	equ	40h			; line wrap enabled.
inited	equ	08h			; been here before...
cursor	dw	?
esc_ch	db	?
argadr	dw	?			; address of arg blk
ckeys	db	0,prscan,dnscan,upscan,endscn,homscn,ctlup,ctldn
	db	uparr,dnarr,lftarr,rgtarr
lckeys	equ	$-ckeys
; ckacts must parallel ckeys above...
ckacts	dw	trnbrk,trnprs,upwpg,dnwpg,endwnd,homwnd,dnwind,upwind
	dw	trnupw,trndnw,trnlfw,trnrgw

uptrn	db	esc,'A'
dntrn	db	esc,'B'
rgtrn	db	esc,'C'
lftrn	db	esc,'D'

spctab	db	chesc,cr,lf,bs,tab,bel
lspctab	equ	$-spctab
spcjmp	dw	outesc,outcr,outlf,outbs,outtab,outbel	; must match spctab
esctab	db	'YABCDEFGHIJKLM'
	db	'NOZ@[pq<vw'
lesctab	equ	$-esctab
; escjmp must parallel esctab above
escjmp	dw	movcur,curup,curdwn,currt,outbs,clrscr,outign,outign,curhom
	dw	revind,clreow,clreol,inslin,dellin,delchr,noins
	dw	vtident,entins,doansi
	dw	invvid,nrmvid,outign,dowrap,nowrap
vtidstr	db	chesc,'/K'
lvtidst	equ	$-vtidstr
coord	dw	?
insmod	db	?
wcoord	dw	?
ttstate	dw	outtt0
curattr	db	?			; current attribute
ansarg	db	?			; ansi argument value
igncnt	db	?			; # of chars to ignore
beldiv	dw	2dch			; 550 hz?
crt_mode db	?
crt_cols db	?
crt_lins db	?	
low_rgt	dw	?			; lower right corner of window
; key redefinitions
ktrntab	dw	?			; address of translation table
krpltab	dw	?			; address of replacement table
tmptab	db	0eh,3bh			; scan code for bs, f1
ktlen	dw	?
modbuf	modfrm	<>			; mode line buffer
; routine to call for captured output
captrtn	dw	?
; some static data for mode line
unkbaud	db	'Unk '			; must be 4 chars...
baudn	db	'45.5'
	db	'  50'
	db	'  75'
	db	' 110'
	db	' 135'
	db	' 150'
	db	' 300'
	db	' 600'
	db	'1200'
	db	'1800'
	db	'2000'
	db	'2400'
	db	'4800'
	db	'9600'
baudnsiz  equ	14			; # of baud rates known (tbl size / 4)
parnams	db	'Even'
	db	'Mark'
	db	'None'
	db	'Odd '			; must be 4 chars
	db	'Spc '
offmsg	db	'Off'
onmsg	db	'On '
lclmsg	db	'Lcl'
remmsg	db	'Rem'
; storage for multi-window stuff
swidth	equ	80
slen	equ	24
npgs	equ	5			; # of pages on each side
bsize	equ	swidth*slen*npgs*2
scrsav	dw	swidth*slen dup (0700H)	; a blank screen

; circular buffer.  To work properly, the buffer size should be an exact
; multiple of swidth*2
cbuf	struc
pp	dw	?			; place ptr in buffer
bend	dw	?			; end of buffer
orig	dw	?			; buffer origin
lcnt	dw	0			; # of lines in buffer.
cbuf	ends

topbuf	db	bsize dup (?)
botbuf	db	bsize dup (?)		; top and bottom windows
tlbuf	db	swidth*2 dup (?)
blbuf	db	swidth*2 dup (?)
twnd	cbuf	<topbuf,topbuf+bsize-1,topbuf,0>
bwnd	cbuf	<botbuf,botbuf+bsize-1,botbuf,0>
portno	db	?
prton	db	'Printer: on'
prtnlen	equ	$-prton
prtoff	db	'Printer: off'
prtflen	equ	$-prtoff
datas	ends

code	segment	public			; code segment
	extrn	prtchr:near,outchr:near,sendbr:near
	assume	cs:code,ds:datas,es:datas

scrini	proc	near			; init screen stuff
	mov	ah,15			; read video state...
	int	screen
	mov	crt_mode,al		; save crt mode
	cmp	ah,crt_cols		; is real # of cols < passed?
	jge	scrin1			; no
	mov	crt_cols,ah		; yes, save # of cols
scrin1:	mov	dl,crt_cols		; # of cols again
	mov	dh,crt_lins		; and # of rows
	dec	dl
	dec	dh
	mov	low_rgt,dx		; save away window address
	mov	insmod,0		; not in insert mode
	mov	dx,cursor		; assume old cursor
	test	flags1,inited		; have we been here before?
	jnz	scrin4			; yes, use old cursor
	mov	curattr,07		; else set nice screen attribute
	mov	ttstate,offset outtt0	; normal screen state
	mov	ah,3			; figure out where cursor is
	xor	bh,bh			; page 0
	int	screen			; read cursor position
	cmp	dh,crt_lins		; past logical end of screen?
	jb	scrin2			; no, keep going
	mov	dh,byte ptr low_rgt+1	; yes, just use lower right corner
scrin2:	cmp	dl,crt_cols		; maybe past right margin
	jb	scrin3			; no, use the way it is
	mov	dl,byte ptr low_rgt
scrin3:	mov	cursor,dx		; init cursor
scrin4:	mov	ah,2
	xor	bh,bh
	int	screen			; set cursor in case it moved
	ret
scrini	endp

argini	proc	near			; read passed arguments
	mov	bx,argadr		; base of argument block
	mov	al,[bx].flgs		; get flags
	and	al,capt+emheath+havtt+trnctl+lclecho+modoff
	mov	flags,al		; mask for allowable and save
	and	flags1,not (prtscr)	; these are allowable
					; (others remain).
	mov	al,[bx].prt
	cmp	al,portno		; using same port?
	je	argin1			; yes, go on
	and	flags1,not inited	; else re-init stuff
argin1:	mov	portno,al		; update port number
	mov	al,[bx].cols
	mov	crt_cols,al
	mov	al,[bx].rows
	mov	crt_lins,al		; init # of rows and cols
	mov	ax,[bx].captr
	mov	captrtn,ax		; buffer capture routine
	mov	ax,[bx].belld
	mov	beldiv,ax		; bell divisor
	mov	ax,[bx].klen
	mov	ktlen,ax		; length of key redef tbl
	mov	ax,[bx].ktab
	mov	ktrntab,ax		; save key translation table
	mov	ax,[bx].krpl
	mov	krpltab,ax
	mov	al,[bx].escc
	mov	esc_ch,al
	ret				; that's it
argini	endp

modlin	proc	near			; turn on mode line
	mov	al,esc_ch
	mov	modbuf.m_echr,' '	; first char is initial space
	mov	modbuf.m_hlp,' '	; goes here too.
	cmp	al,32			; printable?
	jnb	modl1			; yes, keep going
	add	al,40h			; made printable
	mov	modbuf.m_echr,'^'	; note control char
	mov	modbuf.m_hlp,'^'
modl1:	mov	modbuf.m_echr+1,al	; fill in character
	mov	modbuf.m_hlp+1,al
	mov	bx,argadr		; get argument block
	mov	al,[bx].baudb		; get baud bits
	mov	si,offset unkbaud	; assume unknown baud
	cmp	al,baudnsiz		; too big?
	jnb	modl2			; yes, use default
	mov	cl,2			; each is 4 bytes long
	shl	al,cl
	mov	ah,0
	add	ax,offset baudn
	mov	si,ax
modl2:	mov	cx,size m_baud		; length of baud space
	mov	di,offset modbuf.m_baud
	rep	movsb			; copy in baud rate
	mov	al,[bx].parity		; get parity code
	mov	cl,2			; each is 4 bytes long...
	shl	al,cl
	mov	ah,0
	add	ax,offset parnams	; names of parity settings
	mov	si,ax
	mov	cx,4			; each is 4 long
	mov	di,offset modbuf.m_par
	rep	movsb
	mov	si,offset lclmsg	; assume remote echoing
	test	flags,lclecho		; echoing?
	jz	modl4			; no, keep going
	mov	si,offset remmsg
modl4:	mov	cx,3			; size of on/off
	mov	di,offset modbuf.m_echo
	rep	movsb
	mov	al,'1'
	cmp	portno,1		; port 1?
	je	modl5			; yes, keep going
	mov	al,'2'
modl5:	mov	modbuf.m_prt,al		; fill in port number
	mov	cx,size modfrm		; this is size of mode line
	mov	si,offset modbuf	; mode line image
; alternate entry to write an alternate mode line
modwrt:	push	cx
	push	si			; save mode line and size
	mov	dx,24 * 100h		; 25th line for mode line
	push	word ptr curattr	; save current attributes
	mov	curattr,70h		; want inverse video
	call	clreol			; clear to end of line...
	pop	word ptr curattr	; restore attributes
	mov	dx,24 * 100h
	mov	bh,0
	mov	ah,2			; set cursor position
	int	screen
	pop	si
	pop	cx			; restore these
modl6:	lodsb				; get a byte
	mov	ah,14			; write to terminal
	mov	bh,0			; page 0
	int	screen
	loop	modl6			; write out entire mode line
	mov	dx,cursor
	mov	ah,2
	mov	bh,0
	int	screen			; put cursor back where it belongs
	ret				; and return
modlin	endp

clrmod	proc	near			; clear mode line
	mov	ax,600h			; blank window
	mov	cx,24 * 100h		; beginning of window
	mov	dx,24 * 100h + 79	; end of window
	mov	bh,07			; nice attribute
	int	screen			; clear mode line
	ret				; and return
clrmod	endp

term	proc	near			; terminal emulator entry point

	mov	argadr,ax		; save argument ptr
	push	es			; save caller's extra segment address
	mov	ax,seg datas
	mov	es,ax

	call	argini			; init options from arg address

	call	scrini			; init screen stuff

	test	flags1,inited		; have we run yet?
	jz	term1			; no, forget this part
	call	restscr			; restore screen
term1:	or	flags1,inited		; remember we've run already.

	call	clrmod			; empty mode line
	test	flags,modoff		; is mode line disabled?
	jnz	lp			; yes, skip it
	call	modlin			; turn on mode line

lp:	call	portchr			; char at port?
	 jnc	chkinp			; no, keep going
	call	outtty			; print on terminal

chkinp:	mov	ah,1
	int	kb
	jz	lp			; nothing available...
	xor	ah,ah
	int	kb			; get the char from the buffer
	push	ax			; save character temporarily
	call	gss			; get shift state into al
	mov	bl,al			; save shift state
	pop	ax

	cmp	al,esc_ch		; escape character?
	je	quit			; yes, stop here

	call	trnout			; translate if nec., output to prt
	jmp	chkinp			; and keep going

quit:	call	clrmod			; erase mode line
	call	savescr			; save screen

	mov	al,flags
	mov	bx,argadr
	mov	[bx].flgs,al		; update flags in arg block
	pop	es			; restore segment register
	ret				; and return to caller

term	endp

; get shift state into al.  We only care about shift, ctl, and alt keys.
; right shift is collapsed into left shift.
gss	proc	near
	mov	ah,2
	int	kb			; get current shift state
	mov	bl,al			; copy for a moment
	and	bl,right_shift		; mask out all but right shift
	shl	bl,1			; move right shift to left shift pos
	or	al,bl			; collapse shift bits
	and	al,(left_shift + alt_shift + ctl_shift)
	ret
gss	endp

; save the screen so we can restore it
; maybe save cursor also.
savescr	proc	near
	push	ds
	mov	si,0
	mov	di,offset scrsav	; place to put screen
	mov	cx,80*24		; # of words on screen
	call	scrseg
	push	ax			; save screen segment
	call	scrwait			; wait for screen to be ready
	pop	ds			; address screen
	rep	movsw			; save the screen
	pop	ds			; restore this
	ret				; and return
savescr	endp

; restore screen from scrsav buffer
restscr	proc	near
	push	es
	mov	si,offset scrsav	; source
	mov	di,0
	mov	cx,80*24
	call	scrseg
	mov	es,ax
	call	scrwait
	rep	movsw			; restore it
	pop	es
	ret
restscr	endp

; send the character in al out to the serial port
; handle echoing also...
outprt	proc	near
	test	flags,lclecho		; echoing?
	jz	outpr1			; no, forget it
	push	ax			; save char
	call	outtty			; print it
	pop	ax			; restore
outpr1:	mov	ah,al			; this is where outchr expects it
	call	outchr			; output to the port
	 nop
	 nop
	 nop				; skip returns...
	ret
outprt	endp

; returns with carry on if a character is available

portchr	proc	near
	call	prtchr			; character at port?
	 jmp	short portc1		; yes, go handle
	nop				; skip return is stupid...
	clc				; no carry -> no character
	ret				; and return...
portc1:	and	al,7fh			; we don't worry about parity here
	stc				; have a character
	ret				; and return
portchr	endp


; translate the scan code in ah according to the translate table
; given in ktrntab/krpltab, output to port.  If no translation,
; use ascii char in al. (should probably include shift state
; somewhere).  Shift state is in bl.
trnout	proc	near
	cmp	ah,4eh			;*** plus key thing?
	je	trnmod			; yes, go toggle mode line
trnou1:	test	flags,havtt		; translate table given?
	jz	trnou3			; no, just output character
	push	ax			; save original value
	mov	al,ah			; put scan code into ah
	mov	ah,bl			; shift state into top half.
	mov	di,ktrntab		; pick up translate tbl
	mov	cx,ktlen		; length of tbl
	repne	scasw			; look for our key
	pop	ax			; recover character
	jne	trnou3			; not found, forget it
	sub	di,ktrntab		; get index into tbl
	sub	di,2			; (minus 2 for pre-increment)
	mov	bx,krpltab		; get replacement table
	mov	si,[bx][di]		; and addr of replacement
	mov	cl,[si]			; get first byte (length)
	xor	ch,ch			; clear high-order byte
	inc	si			; point to translation string
trnou2:	lodsb				; get a byte
	push	si
	push	cx			; save important registers
	call	outprt			; send to port
	pop	cx
	pop	si
	loop	trnou2			; send all chars
	ret				; and return
trnou3:	cmp	al,0			; is it a special code?
	jne	trnou4			; no, don't do this
	mov	al,ah			; get scan code
	mov	cx,lckeys		; length of table
	mov	di,offset ckeys		; table address
	repne	scasb
	mov	al,0			; ascii code was 0...
	jne	trnou4			; not found, keep going
	sub	di,offset ckeys+1	; get table offset
	shl	di,1			; shift for word offset
	jmp	ckacts[di]		; jump to appropriate routine
trnou4:	call	outprt			; just output single char
	ret				; and return

trnmod:	test	flags,modoff		; mode line already off?
	jnz	trnm1			; yes, go turn on
	call	clrmod			; no, clear mode line here
	or	flags,modoff		; turn on flag
	ret				; and return
trnm1:	call	modlin			; turn on mode line
	and	flags,not modoff	; clear flag
	ret				; and return

trnbrk:	mov	ah,dconio
	mov	dl,0ffH
	int	dos			; read the bogus ^C DOS gets.
	call	sendbr
	ret
trnprs:	xor	flags1,prtscr		; flip the flag
	and	flags,not modoff	; turn on mode line
	mov	si,offset prton
	mov	cx,prtnlen
	test	flags1,prtscr		; did it go on?
	jnz	trnpr1			; yes, say so
	mov	si,offset prtoff
	mov	cx,prtflen
trnpr1:	call	modwrt			; write into mode line
	ret				; and return

; common entry for arrow keys
trnarr:	mov	cx,2			; length is always 2
	jmp	trnou2			; go send definition

trnupw:	mov	si,offset uptrn
	jmp	trnarr

trndnw:	mov	si,offset dntrn
	jmp	trnarr

trnlfw:	mov	si,offset lftrn
	jmp	trnarr

trnrgw:	mov	si,offset rgtrn
	jmp	trnarr

trnout	endp

; move viewing window up (screen moves down).
; alternate entry upwin2 doesn't beep if invalid.
upwind	proc	near
	mov	ax,offset tlbuf	; place to put line temporarily
	mov	bx,offset twnd ; where to get lines from
	call	getcirc		; try to get a line
	jnc	upwin3		; have a line, go show it
	call	outbel		; else ring bel
	ret			; and return
upwin2:	mov	ax,offset tlbuf
	mov	bx,offset twnd
	call	getcirc
	jnc	upwin3
	ret			; this just rets if no line avail.
upwin3:	mov	ax,offset blbuf	; place for bottom line
	call	getbot		; fetch bottom line
	mov	ax,offset blbuf
	mov	bx,offset bwnd
	call	putcirc		; save in circular buffer
	mov	ax,701h		; scroll down one line
	xor	cx,cx		; from top
	mov	dx,low_rgt	; to bottom
	mov	bh,curattr
	int	screen		; scroll it down
	mov	di,0		; offset for destination
	mov	si,offset tlbuf	; where to get line from
	mov	cx,swidth	; length of line
	push	es
	call	scrseg
	push	ax
	call	scrwait
	pop	es
	rep	movsw		; copy the line in
	pop	es		; restore this
	ret			; and return
upwind	endp


; move viewing window down a line (screen scrolls up)
; entry dwin2 does same w/out checking to see if scroll is legal
dnwind	proc	near
	mov	ax,offset blbuf	; place to put line temporarily
	mov	bx,offset bwnd ; where to get lines from
	call	getcirc		; try to get a line
	jnc	dnwin3		; have a line, go show it
	call	outbel		; else ring bel
	ret			; and return
dnwin2:	mov	ax,offset blbuf
	mov	bx,offset bwnd
	call	getcirc
	jnc	dnwin3
	ret			; this just rets if no line avail.
dnwin3:	call	scrprep		; save top line
	mov	ax,601h		; scroll up one line
	xor	cx,cx		; from top
	mov	dx,low_rgt	; to bottom
	mov	bh,curattr
	int	screen		; scroll it down
	mov	dx,low_rgt
	mov	dl,0		; get addr of last line
	call	scrloc
	mov	di,ax		; this is offset in dest
	mov	si,offset blbuf	; where to get line from
	mov	cx,swidth	; length of line
	push	es
	call	scrseg
	push	ax
	call	scrwait
	pop	es
	rep	movsw		; copy the line in
	pop	es		; restore this
	ret			; and return
dnwind	endp

; move viewing window down as much as possible...
endwnd	proc	near
	mov	cx,1000			; large number of lines
	jmp	dnwp1			; and enter dwnpg
endwnd	endp

; scroll viewing window down (contents move up) crt_lins times...
dnwpg	proc	near
	mov	cl,crt_lins
	mov	ch,0
dnwp1:	push	cx			; save this
	call	dnwin2
	pop	cx
	loop	dnwp1
	ret				; and return
dnwpg	endp

; home viewing window
homwnd	proc	near
	mov	cx,1000			; large # of lines
	jmp	upwp1			; join upwpg
homwnd	endp

; scroll viewing window up (screen moves down) a page
upwpg	proc	near
	mov	cl,crt_lins
	mov	ch,0
upwp1:	push	cx
	call	upwin2
	pop	cx
	loop	upwp1
	ret				; and return
upwpg	endp

; get the bottom line into the buffer pointed to by ax.
getbot	proc	near
	push	ds
	mov	di,ax			; save dest
	mov	cx,swidth
	mov	dx,low_rgt
	mov	dl,0
	call	scrloc
	mov	si,ax
	call	scrseg
	push	ax
	call	scrwait
	pop	ds
	rep	movsw
	pop	ds
	ret
getbot	endp

; put a line into the circular buffer.  Pass the buffer structure
; in bx, the pointer to the line in ax.
putcirc	proc	near
	push	si
	push	di
	push	cx
	push	dx
	mov	di,[bx].pp		; pick up buffer ptr
	add	di,2*swidth		; increment to next avail slot
	cmp	di,[bx].bend		; past end?
	jb	putci1			; no, leave alone
	mov	di,[bx].orig		; else start at beginning
putci1:	mov	[bx].pp,di		; update ptr
	mov	si,ax			; this is source
	mov	cx,swidth
	rep	movsw			; copy into buffer
	cmp	[bx].lcnt,npgs*slen	; can we increment it?
	jae	putci2			; no, keep going
	inc	[bx].lcnt		; else count this line
putci2:	pop	dx
	pop	cx
	pop	di
	pop	si			; restore registers
	ret
putcirc	endp

; get a line from the circular buffer, removing it from the buffer.
; returns with carry on if the buffer is empty.
; pass the buffer structure in bx, the buffer to copy the line into
; in ax.
getcirc	proc	near
	push	si
	push	di
	push	cx
	push	dx
	cmp	[bx].lcnt,0		; any lines in buffer?
	jne	getci1			; yes, ok to take one out.
	stc				; else set carry
	jmp	short getcir3		; and return
getci1:	mov	si,[bx].pp		; this is source
	mov	di,ax			; this is dest
	mov	cx,swidth		; # of chars to copy
	rep	movsw
	mov	si,[bx].pp		; get ptr again
	sub	si,2*swidth		; move back
	cmp	si,[bx].orig		; compare to origin
	jae	getcir2			; still in range, continue
	mov	si,[bx].bend		; else use end of buffer
	sub	si,2*swidth-1		; minus length of a piece
getcir2:mov	[bx].pp,si		; update ptr
	dec	[bx].lcnt		; decrement # of lines in buffer
	clc				; make sure no carry
getcir3:pop	dx
	pop	cx
	pop	di
	pop	si
	ret
getcirc	endp

; call before scrolling to save top line...
scrprep	proc	near
	push	ds
	mov	si,0			; offset of top line
	mov	cx,swidth		; length of line
	mov	di,offset tlbuf		; place to put line temporarily
	call	scrseg
	push	ax
	call	scrwait
	pop	ds
	rep	movsw			; copy the line
	pop	ds			; restore this
	mov	ax,offset tlbuf
	mov	bx,offset twnd		; this is where it goes
	call	putcirc			; put into buffer
	ret				; and return
scrprep	endp


; put the character in al to the screen
outtty	proc	near
	test	flags,capt		; capturing output?
	jz	outnoc			; no, forget this part
	push	ax			; save char
	call	captrtn			; give it captured character
	pop	ax			; restore character and keep going

outnoc:	test	flags1,prtscr		; should we be printing?
	jz	outnop			; no, keep going
	push	ax
	mov	ah,print_out
	mov	dl,al			; put character here for dos...
	int	dos
	pop	ax

outnop:	test	flags,emheath		; emulating heath?
	jnz	outnop1			; yup, go do something smart
	mov	dl,al
	mov	ah,pbout
	int	dos			; else let dos print char
	ret				; and return

outnop1:mov	dx,cursor		; these may need cursor...
	jmp	ttstate			; jump according to current state

outtt0:
	cmp	al,32			; special character?
	jb	outtt1			; yes, handle specially...

	cmp	insmod,0		; in insert mode?
	je	outnrm			; no, output normal
	push	ax			; save character
	call	inschr			; insert a character
	pop	ax
outnrm:	xor	bh,bh			; current page
	mov	cx,1			; only one char
	mov	bl,curattr		; with current attribute
	mov	ah,9
	int	screen			; put onto screen
	mov	dx,cursor		; get cursor pos
currt:	inc	dl			; bump col
	cmp	dl,crt_cols		; see if in range
	jb	setcur			; in range, go set cursor
	test	flags1,lnwrap		; in wrap mode?
	jz	outign			; no, just return w/out updating cursor
	xor	dl,dl
	inc	dh			; handle wrap
setcur:	cmp	dh,crt_lins
	jb	setc1			; not off end, keep going
	push	dx			; save row/col
	call	scrprep			; save top line in window buf
	mov	ax,0601h		; scroll up one line
	xor	cx,cx			; from 0,0
	mov	dx,low_rgt		; to 24,80
	mov	bh,curattr		; nice attribute
	int	screen			; do the scroll
	pop	dx
	mov	dh,crt_lins		; go to bottom line again...
	dec	dh
setc1:	xor	bh,bh			; page is 0
	mov	cursor,dx		; save cursor pos
	mov	ah,2
	int	screen			; set cursor
outign:	ret				; and return
; special character (in al)
outtt1:	mov	di,offset spctab	; special char table
	mov	cx,lspctab		; length of tbl
	repne	scasb			; look for char in tbl
	jz	outtt2			; found, go do something with it
	test	flags,trnctl		; are we allowed to print carets?
	jz	outign			; no, just ignore it.
	push	ax			; save char
	mov	al,'^'
	call	outtty			; print caret
	pop	ax
	add	al,'A'-1		; make printable
	jmp	outtty			; print, then return

outtt2:	mov	dx,cursor		; might need cursor pos
	sub	di,offset spctab+1	; get index of char
	shl	di,1			; double for word offset
	jmp	spcjmp[di]		; and go handle

; special char routines.  cursor is in dx, char in al

outlf:	inc	dh			; bump row
	jmp	setcur

outcr:	xor	dl,dl			; set col to 0
	jmp	setcur

outbs:	or	dl,dl
	jle	setcur			; col 0, can't back up
	dec	dl			; back up col
	jmp	setcur			; and use if reasonable

outtab:	mov	dl,byte ptr cursor	; get initial column
outta1:	mov	dh,dl			; save column ptr
	push	dx
	mov	al,' '			; output a space
	call	outtty			; convenient, huh?
	pop	dx
	mov	dl,byte ptr cursor
	cmp	dh,dl			; is it moving?
	je	outta2			; no, forget this
	test	dl,7			; is it a multiple of 8?
	jnz	outta1			; no, keep going
outta2:	ret				; else return

; stolen from bios
outbel:	mov	al,10110110b		; timer initialization
	out	timer+3,al
	mov	ax,beldiv		; bel divisor
	out	timer+2,al
	mov	al,ah
	out	timer+2,al		; output divisor
	in	al,bel_prt
	mov	ah,al			; remember original value
	or	al,3			; turn speaker on
	out	bel_prt,al
	mov	cx,8888h
outbe1:	loop	outbe1			; wait a while
	mov	al,ah
	out	bel_prt,al		; turn bell off
	ret				; and return

outesc:	mov	ttstate,offset escseq	; expect escape sequence.
	ret				; and return

; escape-char handling routines
escseq:	mov	ttstate,offset outtt0	; put state back to normal
	mov	di,offset esctab	; escape char tbl
	mov	cx,lesctab		; length of tbl
	repne	scasb			; look for it in tbl
	jz	escsq1			; found, go use it
	jmp	outtty			; not there, just print it
escsq1:	sub	di,offset esctab+1	; get offset into tbl
	shl	di,1			; convert to word offset
	jmp	escjmp[di]		; and go dispatch on it

; escape dispatch routines
revind:	cmp	dh,0
	jle	revin1
	dec	dh			; back up a row
	jmp	setcur			; and go set cursor
revin1:	push	dx			; save cursor pos
	mov	ax,701h			; scroll down one line
	xor	cx,cx			; from top
	mov	dx,low_rgt		; to bottom
	mov	bh,curattr
	int	screen			; scroll it down
	pop	dx			; restore cursor.
	mov	dh,0			; set row back to 0
	jmp	setcur

curup:	cmp	dh,0			; w/in range?
	jle	curu1			; no, skip this
	dec	dh			; else back up
curu1:	jmp	setcur			; and go set position

curdwn:	inc	dh
	jmp	setcur			; increment row (setcur can scroll!)

; currt is above

clrscr:	call	curhom			; go home cursor
	jmp	clreow			; then clear to end of window

curhom:	xor	dx,dx			; move to 0,0
	jmp	setcur

clreow:	cmp	dl,0			; at beginning of line?
	jz	clrw1			; yes, skip this part...
	push	dx			; remember cursor pos
	call	clreol			; clear to end of this line
	pop	dx
	inc	dh			; bump row
	xor	dl,dl			; start from col 0
clrw1:	cmp	dh,crt_lins		; last line on screen
	jnb	clrw2			; if not in range, forget it
	mov	ax,700h			; clear whole window
	mov	cx,dx			; this is beginning
	mov	dx,low_rgt
;	mov	dx,174fh		; this is lower right corner
	mov	bh,curattr		; default attribute
	int	screen			; go clear it
clrw2:	ret				; and return

clreol:	push	es
	mov	cl,crt_cols		; last col + 1
	sub	cl,dl			; this is # of chars to move
	xor	ch,ch
	jcxz	clrl1
	call	scrloc			; compute screen location (to ax)
	mov	di,ax
	call	scrseg
	mov	es,ax			; address screen segment
	call	scrwait			; wait for retrace
	mov	ah,curattr		; current attribute
	mov	al,' '			; fill char
	rep	stosw			; fill line with spaces
clrl1:	pop	es
	ret				; and return

inslin:	mov	al,1			; scroll one line
; alternate entry if inserting more then one line
inslin1:mov	ch,dh			; start at current row
	xor	cl,cl			; column 0
	mov	dx,low_rgt
;	mov	dx,174fh		; to bottom of screen
	mov	ah,7h			; scroll down.
	mov	bh,curattr		; attribute
	int	screen
	ret

dellin:	mov	al,1			; scroll 1 line
; alternate entry if deleting more than one line
dellin1:mov	ch,dh			; start at current row
	xor	cl,cl			; column 0
	mov	dx,low_rgt
;	mov	dx,174fh		; to bottom of screen
	mov	ah,6h			; scroll up.
	mov	bh,curattr		; attribute
	int	screen
	ret

delchr:	push	ds
	push	es
	pushf			; these may get changed...
	mov	cl,crt_cols
	dec	cl
	sub	cl,dl		; from what we're fiddling)
	xor	ch,ch
	jcxz	delch1		; none to move, forget it
	call	scrloc		; compute location
	mov	di,ax
	mov	si,ax
	add	si,2		; source is next position over
	call	scrseg		; pick up screen segment
	push	ax		; put screen segment onto stack
	mov	es,ax		; and in destination segment
	call	scrwait		; wait for retrace
	pop	ds		; address screen segment
	rep	movsw		; delete it
	mov	byte ptr [di],' ' ; kill char at end of line
delch1:	popf
	pop	es
	pop	ds
	ret

inschr:	push	ds
	push	es		; save these as well
	pushf			; might as well save flags...
	mov	dx,cursor	; this is place to do it
	mov	cl,crt_cols
	dec	cl
;	mov	cl,79		; this is last col to move, +1 for length
	sub	cl,dl		; compute distance to end
	xor	ch,ch		; clear top half of offset
	jcxz	insch1		; nothing to move...
	mov	dl,crt_cols
	sub	dl,2		; last col to move
;	mov	dl,78		; this is address of last col to move
	call	scrloc		; compute pos
	mov	si,ax
	mov	di,ax
	add	di,2		; destination is one byte over...
	std			; remember to move us backwards
	call	scrseg		; find screen segment
	mov	es,ax
	push	ax		; save screen seg on stack
	call	scrwait		; wait until save to write
	pop	ds		; address screen segment
	rep	movsw		; move each char and attribute
insch1:	popf
	pop	es
	pop	ds
	ret			; and return

noins:	mov	insmod,0		; turn off insert mode
	ret				; and return

movcur:	mov	wcoord,2		; want two coordinates...
	mov	ttstate,offset getcoord
	ret				; and return

vtident: mov	si,offset vtidstr
	mov	cx,lvtidst
vtid1:	lodsb				; get a byte from the string
	push	si			; have to save from outprt
	push	cx
	call	outprt			; send to serial port
	pop	cx
	pop	si
	loop	vtid1			; go thru all chars
	ret				; and return

entins:	mov	insmod,0ffh		; enter insert mode...
	ret				; and return

doansi:	mov	ansarg,0		; ansi argument is 0 (default)
	mov	ttstate,offset getaarg	; state is get ansi argument
	ret

getaarg:cmp	al,'0'
	jb	getaa1			; in range for digit?
	cmp	al,'9'
	ja	getaa1
	sub	al,'0'			; convert to binary
	mov	dl,al			; tuck away
	mov	al,ansarg
	mov	dh,10
	mul	dh			; shift sum
	add	al,dl			; add in this digit (what about ovfl?)
	mov	ansarg,al
	ret				; and return

getaa1:	cmp	al,'?'			; the dreaded question mark?
	jne	getaa2
	mov	ttstate,offset ignn	; we ignore these...
	mov	igncnt,2		; this is how many chars come after him
	ret

getaa2:	mov	ttstate,offset outtt0	; reset state
	mov	dx,cursor		; this needs cursor position
	mov	bl,ansarg
	xchg	al,bl			; put argument in nice place
	cmp	bl,'L'			; insert line?
	jne	getaa3
	jmp	inslin1			; and go do it

getaa3:	cmp	bl,'M'			; maybe delete line?
	jne	getaa4
	jmp	dellin1

getaa4:	ret				; ignore.

invvid:	mov	curattr,70h		; attribute for inverse video
	ret

nrmvid:	mov	curattr,07h		; attribute for normal video
	ret

dowrap:	or	flags1,lnwrap		; turn on wrap mode
	ret				; and return

nowrap:	and	flags1,not lnwrap	; turn off wrap mode
	ret				; and return

; get a coordinate.
getcoord:
	sub	al,32			; coordinates offset by 32
	mov	si,wcoord
	dec	si
	mov	byte ptr coord[si],al	; fill in appropriate coordinate
	mov	wcoord,si		; update flag
	jnz	getco1			; more needed, can't do anything yet
	mov	ttstate,offset outtt0	; reset state
	mov	dx,coord		; get coordinates
	jmp	setcur			; and go jump there
getco1:	ret

; ignore following igncnt characters
ignn:	dec	igncnt			; decrement count
	jnz	ignn1
	mov	ttstate,offset outtt0	; put state back to normal if done
ignn1:	ret

outtty	endp

; computes screen location to ax, given row and col in dx.
; trashes ax,bx
scrloc	proc	near
	mov	al,dh		; get row
	mov	bl,crt_cols	;** row size
	mul	bl		; multiply by row size
	xor	dh,dh		; clear col
	add	ax,dx		; this is current position
	sal	ax,1		; double for attributes
	ret
scrloc	endp

; puts current screen segment in ax
scrseg	proc	near
	mov	ax,0b000h		; assume bw for now
	cmp	crt_mode,7		; 7 is bw (***)
	je	scrse1
	mov	ax,0b800h		; color card
scrse1:	ret
scrseg	endp

; wait for retrace so can write to screen memory
scrwait	proc	near
	cmp	crt_mode,7		; bw mode?
	je	scrwa3			; yes, no waiting
	push	dx
	mov	dx,crt_status
scrwa1:	in	al,dx
	test	al,disp_enb		; display enable?
	jnz	scrwa1			; yes, keep waiting
scrwa2:	in	al,dx
	test	al,disp_enb		; now wait for it to go off
	jz	scrwa2			; so can have whole cycle
	pop	dx
scrwa3:	ret				; that was easy...
scrwait	endp
code	ends

if1
	%out [End of pass 1]
else
	%out [End of assembly]
endif

	end

//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msyibm.asm
	/bin/echo -n '	'; /bin/ls -ld msyibm.asm
fi
/bin/echo 'Extracting msyz100.asm'
sed 's/^X//' <<'//go.sysin dd *' >msyz100.asm

; Kermit system dependent module for Heath/Zenith Z100

	public	term
	include msdefs.h


BIOS_SEG SEGMENT AT 40H		; Define segment where BIOS really is
	ORG	4*3
BIOS_PRINT	LABEL FAR       ; Printer output
	ORG	6*3
BIOS_AUXOUT	LABEL FAR	; AUX output routine
	ORG	26*3
BIOS_AUXFUNC	LABEL FAR	; AUX: function
	ORG	27*3
BIOS_CONFUNC	LABEL FAR	; CON: function
BIOS_SEG ENDS

; Function codes for BIOS_xxxFUNC
CHR_WRITE	EQU	0	; Write character
CHR_READ	EQU	1	; Read character
CHR_STATUS	EQU	2	; Get status
  CHR_SFGS	EQU	0	; Get status subfunction
  CHR_SFGC	EQU	1	; Get config subfunction
CHR_CONTROL	EQU	3	; Control function
  CHR_CFSU	EQU	0	; Set new configuration parameters
  CHR_CFCI	EQU	1	; Clear input buffer

; Scan code definitions used for translating back to Heath ESC sequences
entscan	equ	08dh		; enter key scan code
f0scan	equ	096h		; F0 key
f1scan	equ	097h		; F1 key
f2scan	equ	098h		; F2 key
f3scan	equ	099h		; F3 key
f4scan	equ	09ah		; F4 key
f5scan	equ	09bh		; F5 key
f6scan	equ	09ch		; F6 key
f7scan	equ	09dh		; F7 key
f8scan	equ	09eh		; F8 key
f9scan	equ	09fh		; F9 key
f10scn	equ	0a0h		; F10 key
f11scn	equ	0a1h		; F11 key
f12scn	equ	0a2h		; F12 key
homscan	equ	0a9h		; Home key
upscan	equ	0a5h		; Up arrow
dnscan	equ	0a6h		; Down arrow
rtscan	equ	0a7h		; Right arrow
lfscan	equ	0a8h		; Left arrow
kpminus	equ	0adh		; keypad minus
kpdot	equ	0aeh		; keypad period
kp0	equ	0b0h		; keypad 0
kp1	equ	0b1h		; keypad 1
kp2	equ	0b2h		; keypad 2
kp3	equ	0b3h		; keypad 3
kp4	equ	0b4h		; keypad 4
kp5	equ	0b5h		; keypad 5
kp6	equ	0b6h		; keypad 6
kp7	equ	0b7h		; keypad 7
kp8	equ	0b8h		; keypad 8
kp9	equ	0b9h		; keypad 9
sentscn	equ	0cdh		; shifted enter key
sf0scan	equ	0d6h		; shifted F0 key
sf1scan	equ	0d7h		; shifted F1 key
sf2scan	equ	0d8h		; shifted F2 key
sf3scan	equ	0d9h		; shifted F3 key
sf4scan	equ	0dah		; shifted F4 key
sf5scan	equ	0dbh		; shifted F5 key
sf6scan	equ	0dch		; shifted F6 key
sf7scan	equ	0ddh		; shifted F7 key
sf8scan	equ	0deh		; shifted F8 key
sf9scan	equ	0dfh		; shifted F9 key
sf10scn	equ	0e0h		; shifted F10 key
sf11scn	equ	0e1h		; shifted F11 key
sf12scn	equ	0e2h		; shifted F12 key
shomscn	equ	0e9h		; shifted Home key
supscan	equ	0e5h		; shifted Up arrow
sdnscan	equ	0e6h		; shifted Down arrow
srtscan	equ	0e7h		; shifted Right arrow
slfscan	equ	0e8h		; shifted Left arrow
skpmins	equ	0edh		; shifted keypad minus
skpdot	equ	0eeh		; shifted keypad period
skp0	equ	0f0h		; shifted keypad 0
skp1	equ	0f1h		; shifted keypad 1
skp2	equ	0f2h		; shifted keypad 2
skp3	equ	0f3h		; shifted keypad 3
skp4	equ	0f4h		; shifted keypad 4
skp5	equ	0f5h		; shifted keypad 5
skp6	equ	0f6h		; shifted keypad 6
skp7	equ	0f7h		; shifted keypad 7
skp8	equ	0f8h		; shifted keypad 8
skp9	equ	0f9h		; shifted keypad 9

; Miscellaneous scan codes used for functions
prscan	equ	f12scn		; print-screen scan code (F12)...
brkscan	equ	0aah		; Break key


modfrm	struc				; format of mode line
	db	'Esc chr: '
m_echr	db	2 dup (?)
	db	', Speed: '
m_baud	db	4 dup (?)
	db	', Parity: '
m_par	db	4 dup (?)
	db	', Echo: '
m_echo	db	3 dup (?)
	db	', Prn: '
m_prs	db	3 dup (?)
	db	', Type '
m_hlp	db	2 dup (?)
	db	'? for Help$'
modfrm	ends

datas 	segment	public 'datas'
waste	db	100h dup(?)		; assembler problem???

flags1	db	0			; internal flags
prtscr	equ	80h			; print screen pressed flag

; Key translations - F12 is printscreen
ckeys	db	brkscan,prscan,upscan,dnscan,lfscan,rtscan,homscan
	db	entscan,f0scan,f1scan,f2scan,f3scan,f4scan,f5scan,f6scan
	db	f7scan,f8scan,f9scan,f10scn,f11scn,kpminus
	db	kpdot,kp0,kp1,kp2,kp3,kp4,kp5,kp6,kp7,kp8,kp9
	db	supscan,sdnscan,slfscan,srtscan,shomscn
	db	sentscn,sf0scan,sf1scan,sf2scan,sf3scan,sf4scan,sf5scan,sf6scan
	db	sf7scan,sf8scan,sf9scan,sf10scn,sf11scn,skpmins
	db	skpdot,skp0,skp1,skp2,skp3,skp4,skp5,skp6,skp7,skp8,skp9
lckeys	equ	$-ckeys
;ckacts must parallel ckeys above...
ckacts	dw	trnbrk,trnprs,trnupw,trndnw,trnlfw,trnrgw,trnhom,trnkpn
	dw	trnf0,trnf1,trnf2,trnf3,trnf4,trnf5,trnf6,trnf7,trnf8,trnf9
	dw	trnf10,trnf11,trnkpn,trnkpn,trnkpn,trnkpn,trnkpn
	dw	trnkpn,trnkpn,trnkpn,trnkpn,trnkpn,trnkpn,trnkpn
	dw	trnupw,trndnw,trnlfw,trnrgw,trnhom,trnkps
	dw	trnsf0,trnsf1,trnsf2,trnsf3,trnsf4,trnsf5,trnsf6,trnsf7
	dw	trnsf8,trnsf9,trnsf10,trnsf11,trnkps,trnkps
	dw	trnkps,trnskp1,trnskp2,trnskp3,trnskp4,trnskp5,trnskp6
	dw	trnskp7,trnskp8,trnskp9

enascan	db	ESC,'y?$'	; Enable scan codes
disscan	db	ESC,'x?$'	; Disable scan codes
uptrn	db	esc,'A'
dntrn	db	esc,'B'
rgtrn	db	esc,'C'
lftrn	db	esc,'D'
enttrn	db	cr		; enter key translation
homtrn	db	ESC,'H'		; home key translation
dottrn	db	'.'		; keypad . translation
mintrn	db	'-'		; keypad - translation
kp0trn	db	'0'		; keypad 0 translation
kp1trn	db	'1'		; keypad 1 translation
kp2trn	db	'2'		; keypad 2 translation
kp3trn	db	'3'		; keypad 3 translation
kp4trn	db	'4'		; keypad 4 translation
kp5trn	db	'5'		; keypad 5 translation
kp6trn	db	'6'		; keypad 6 translation
kp7trn	db	'7'		; keypad 7 translation
kp8trn	db	'8'		; keypad 8 translation
kp9trn	db	'9'		; keypad 9 translation
senttrn	db	cr		; shifted enter key translation
shomtrn	db	ESC,'H'		; home key translation
sdottrn	db	'.'		; shifted keypad . translation
smintrn	db	'-'		; shifted keypad - translation
skp0trn	db	'0'		; shifted keypad 0 translation
skp1trn	db	ESC,'L'		; shifted keypad 1 translation
skp2trn	db	ESC,'B'		; shifted keypad 2 translation
skp3trn	db	ESC,'M'		; shifted keypad 3 translation
skp4trn	db	ESC,'D'		; shifted keypad 4 translation
skp5trn	db	ESC,'H'		; shifted keypad 5 translation
skp6trn	db	ESC,'C'		; shifted keypad 6 translation
skp7trn	db	ESC,'@'		; shifted keypad 7 translation
skp8trn	db	ESC,'A'		; shifted keypad 8 translation
skp9trn	db	ESC,'N'		; shifted keypad 9 translation
f0trn	db	ESC,'J'		; F0 translation
f1trn	db	ESC,'S'		; F1 translation
f2trn	db	ESC,'T'		; F2 translation
f3trn	db	ESC,'U'		; F3 translation
f4trn	db	ESC,'V'		; F4 translation
f5trn	db	ESC,'W'		; F5 translation
f6trn	db	ESC,'P'		; F6 translation
f7trn	db	ESC,'Q'		; F7 translation
f8trn	db	ESC,'R'		; F8 translation
f9trn	db	ESC,'0I'	; F9 translation
f10trn	db	ESC,'0J'	; F10 translation
f11trn	db	ESC,'0K'	; F11 translation
f12trn	db	ESC,'0L'	; F12 translation
sf0trn	db	ESC,'E'		; shifted F0 translation
sf1trn	db	ESC,'1A'	; shifted F1 translation
sf2trn	db	ESC,'1B'	; shifted F2 translation
sf3trn	db	ESC,'1C'	; shifted F3 translation
sf4trn	db	ESC,'1D'	; shifted F4 translation
sf5trn	db	ESC,'1E'	; shifted F5 translation
sf6trn	db	ESC,'1F'	; shifted F6 translation
sf7trn	db	ESC,'1G'	; shifted F7 translation
sf8trn	db	ESC,'1H'	; shifted F8 translation
sf9trn	db	ESC,'1I'	; shifted F9 translation
sf10trn	db	ESC,'1J'	; shifted F10 translation
sf11trn	db	ESC,'1K'	; shifted F11 translation
sf12trn	db	ESC,'1L'	; shifted F12 translation


ourarg	termarg	<>
modbuf	modfrm	<>			; mode line buffer

; some static data for mode line
unkbaud	db	'Unk '			; must be 4 chars...
baudn	db	'45.5'
	db	'  50'
	db	'  75'
	db	' 110'
	db	' 135'
	db	' 150'
	db	' 300'
	db	' 600'
	db	'1200'
	db	'1800'
	db	'2000'
	db	'2400'
	db	'4800'
	db	'9600'
	db	' 19K'
	db	' 38K'

baudnsiz  equ	16			; # of baud rates known (tbl size / 4)
parnams	db	'Even'
	db	'Mark'
	db	'None'
	db	'Odd '			; must be 4 chars
	db	'Spc '
offmsg	db	'Off'
onmsg	db	'On '
lclmsg	db	'Lcl'
remmsg	db	'Rem'

datas	ends

code	segment	public
	extrn	prtchr:near,outchr:near,putmod:near,clrmod:near,sendbr:near
	assume	cs:code,ds:datas

; This is from the dumb terminal emulator routine in MSXGEN.ASM.
; Had to use bios calls because DOS calls were losing chars.
term	proc	near
	mov 	si,ax		; this is source
	mov 	di,offset ourarg ; place to store arguments
	mov 	ax,ds
	push	es		; save caller's extra segment address
	mov 	es,ax		; address destination segment
	mov 	cx,size termarg
	rep 	movsb		; copy into our arg blk
	and	flags1,not (prtscr)	; print screen mode disabled

	test	ourarg.flgs,emheath ; Are we to use Heath sequences?
	jnz	chkmod		; no, do something else
	mov	ah,prstr
	mov	dx,offset enascan ; enable scan codes
	int	dos 

chkmod:	call 	clrmod		; clear mode line
	test 	ourarg.flgs,modoff ; is mode line disabled?
	jnz	term1		; yes, skip it
	call 	modlin		; turn on mode line

term1:	call 	portchr		; char at port?
	 jnc	term3		; no, keep going
	call	outtty		; print on terminal

term3:	mov 	ah,chr_status
	mov 	al,chr_sfgs	; get number of characters
	call 	bios_confunc	; check console
	cmp 	bl,0
	jz 	term1		; no character, go on
	mov 	ah,chr_read
	call 	bios_confunc	; read it
	cmp 	al,ourarg.escc	; escape char?
	je 	term4		; yes, exit
	push 	ax		; save char
	mov 	ah,al
	call	trnout		; translate if nec., output to port
	pop 	ax
	jmp 	term1		; else echo and keep going

term4:	call	clrmod
	mov	ah,prstr
	mov	dx,offset disscan ; disable scan code generation
	int	dos
	pop	es
	ret

term	endp

; returns with carry on if a character is available

portchr	proc	near
	call	prtchr			; character at port?
	 jmp	short portc1		; yes, go handle
	nop				; skip return is stupid
	clc				; no carry -> no character
	ret
portc1:	and	al,7fh			; we don't worry about parity here
	stc				; have a character
	ret
portchr	endp

; put the character in al to the screen
outtty	proc	near
	mov 	ah,chr_write
	call 	bios_confunc
	test 	ourarg.flgs,capt ; capturing output?
	jz 	outtt1		; no, forget it
	call 	ourarg.captr	; else call the routine

outtt1:	test	flags1,prtscr	; print screen?
	jz	outtt2		; no, try something else
	call	bios_print

outtt2:	ret
outtty	endp

; send the character in al out to the serial port
; handle echoing also...
outprt	proc	near
	test	ourarg.flgs,lclecho	; echoing?
	jz	outpr1			; no, forget it
	push	ax			; save char
	call	outtty			; print it
	pop	ax			; restore
outpr1:	mov	ah,al			; this is where outchr expects it
	call	outchr			; output to the port
	 nop
	 nop
	 nop				; skip returns...
	ret
outprt	endp

modlin	proc	near			; turn on mode line
	mov	al,ourarg.escc
	mov	modbuf.m_echr,' '	; first char is initial space
	mov	modbuf.m_hlp,' '	; goes here too.
	cmp	al,32			; printable?
	jnb	modl1			; yes, keep going
	add	al,40h			; made printable
	mov	modbuf.m_echr,'^'	; note control char
	mov	modbuf.m_hlp,'^'
modl1:	mov	modbuf.m_echr+1,al	; fill in character
	mov	modbuf.m_hlp+1,al
	mov	al,ourarg.baudb		; get baud bits
	mov	si,offset unkbaud	; assume unknown baud
	cmp	al,baudnsiz		; too big?
	jnb	modl2			; yes, use default
	mov	cl,2			; each is 4 bytes long
	shl	al,cl
	mov	ah,0
	add	ax,offset baudn
	mov	si,ax
modl2:	mov	cx,size m_baud		; length of baud space
	mov	di,offset modbuf.m_baud
	rep	movsb			; copy in baud rate
	mov	al,ourarg.parity	; get parity code
	mov	cl,2			; each is 4 bytes long...
	shl	al,cl
	mov	ah,0
	add	ax,offset parnams	; names of parity settings
	mov	si,ax
	mov	cx,4			; each is 4 long
	mov	di,offset modbuf.m_par
	rep	movsb
	mov	si,offset remmsg	; assume remote echoing
	test	ourarg.flgs,lclecho	; echoing?
	jz	modl4			; no, keep going
	mov	si,offset lclmsg
modl4:	mov	cx,3			; size of on/off
	mov	di,offset modbuf.m_echo
	rep	movsb
;	mov	al,'1'
;	cmp	portno,1		; port 1?
;	je	modl5			; yes, keep going
;	mov	al,'2'
;modl5:	mov	modbuf.m_prt,al		; fill in port number
;	mov	cx,size modfrm		; this is size of mode line
;	mov	si,offset modbuf	; mode line image
	mov	si,offset offmsg	; assume printer off
	test	flags1,prtscr		; print screen enabled?
	jz	modl5			; no, keep going
	mov	si,offset onmsg
modl5:	mov	cx,3
	mov	di,offset modbuf.m_prs
	rep	movsb
	mov	dx,offset modbuf
	call	putmod
	ret				; and return
modlin	endp

; translate the scan code in ah according to the translate table
; given in ktrntab/krpltab, output to port.  If no translation,
; use ascii char in al. (should probably include shift state
; somewhere).  Shift state is in bl.
trnout	proc	near
	test	ourarg.flgs,havtt	; translate table given?
	jz	trnou3			; no, just output character
	cmp	ourarg.klen,0		; did they say we have one 
	je	trnou3			; but we really don't?
	push	ax			; save original value
	xor	ah,ah			; Zero top half
	mov	di,ourarg.ktab
	mov	cx,ourarg.klen
	repne	scasw			; look for our key
	pop	ax			; recover character
	jne	trnou3			; not found, forget it
	sub	di,ourarg.ktab
	sub	di,2			; (minus 2 for pre-increment)
	mov	bx,ourarg.krpl
	mov	si,[bx][di]		; and addr of replacement
	mov	cl,[si]			; get first byte (length)
	xor	ch,ch			; clear high-order byte
	inc	si			; point to translation string
trnou2:	lodsb				; get a byte
	push	si
	push	cx			; save important registers
	call	outprt			; send to port
	pop	cx
	pop	si
	loop	trnou2			; send all chars
	ret				; and return
trnou3:	xor	ah,ah			; get scan code
	mov	cx,lckeys		; length of table
	mov	di,offset ckeys		; table address
	repne	scasb
;	mov	al,0			; ascii code was 0...
	jne	trnou4			; not found, keep going
	sub	di,offset ckeys+1	; get table offset
	shl	di,1			; shift for word offset
	jmp	ckacts[di]		; jump to appropriate routine
trnou4:	call	outprt			; just output single char
	ret				; and return

;trnmod:	test	flags,modoff		; mode line already off?
;	jnz	trnm1			; yes, go turn on
;	call	clrmod			; no, clear mode line here
;	or	flags,modoff		; turn on flag
;	ret				; and return
;trnm1:	call	modlin			; turn on mode line
;	and	flags,not modoff	; clear flag
;	ret				; and return

trnbrk:
	call	sendbr
	ret

trnprs:	xor	flags1,prtscr		; flip the flag
	and	ourarg.flgs,not modoff	; turn on mode line
	call	modlin			; write into mode line
	ret				; and return

trn1ch:	mov	cx,1			; length is always 1
	jmp	trnou2

; common entry for arrow keys
trn2ch:	mov	cx,2			; length is always 2
	jmp	trnou2			; go send definition

trn3ch:	mov	cx,3			; length is always 3
	jmp	trnou2

trnupw:	mov	si,offset uptrn
	jmp	trn2ch

trndnw:	mov	si,offset dntrn
	jmp	trn2ch

trnlfw:	mov	si,offset lftrn
	jmp	trn2ch

trnrgw:	mov	si,offset rgtrn
	jmp	trn2ch

trnhom:	mov	si,offset homtrn
	jmp	trn2ch

trnent:	mov	si,offset enttrn
	jmp	trn1ch

trnf0:	mov	si,offset f0trn
	jmp	trn2ch

trnf1:	mov	si,offset f1trn
	jmp	trn2ch

trnf2:	mov	si,offset f2trn
	jmp	trn2ch

trnf3:	mov	si,offset f3trn
	jmp	trn2ch

trnf4:	mov	si,offset f4trn
	jmp	trn2ch

trnf5:	mov	si,offset f5trn
	jmp	trn2ch

trnf6:	mov	si,offset f6trn
	jmp	trn2ch

trnf7:	mov	si,offset f7trn
	jmp	trn2ch

trnf8:	mov	si,offset f8trn
	jmp	trn2ch

trnf9:	mov	si,offset f9trn
	jmp	trn3ch

trnf10:	mov	si,offset f10trn
	jmp	trn3ch

trnf11:	mov	si,offset f11trn
	jmp	trn3ch

trnkpn:	and	al,07fh			; strip high bits
	jmp	trnou4			; now output it

trnkps:	and	al,03fh			; strip high bits (special case)
	jmp	trnou4

trnsf0:	mov	si,offset sf0trn
	jmp	trn2ch

trnsf1:	mov	si,offset sf1trn
	jmp	trn3ch

trnsf2:	mov	si,offset sf2trn
	jmp	trn3ch

trnsf3:	mov	si,offset sf3trn
	jmp	trn3ch

trnsf4:	mov	si,offset sf4trn
	jmp	trn3ch

trnsf5:	mov	si,offset sf5trn
	jmp	trn3ch

trnsf6:	mov	si,offset sf6trn
	jmp	trn3ch

trnsf7:	mov	si,offset sf7trn
	jmp	trn3ch

trnsf8:	mov	si,offset sf8trn
	jmp	trn3ch

trnsf9:	mov	si,offset sf9trn
	jmp	trn3ch

trnsf10: mov	si,offset sf10trn
	jmp	trn3ch

trnsf11: mov	si,offset sf11trn
	jmp	trn3ch

trnskp1: mov	si,offset skp1trn
	jmp	trn2ch

trnskp2: mov	si,offset skp2trn
	jmp	trn2ch

trnskp3: mov	si,offset skp3trn
	jmp	trn2ch

trnskp4: mov	si,offset skp4trn
	jmp	trn2ch

trnskp5: mov	si,offset skp5trn
	jmp	trn2ch

trnskp6: mov	si,offset skp6trn
	jmp	trn2ch

trnskp7: mov	si,offset skp7trn
	jmp	trn2ch

trnskp8: mov	si,offset skp8trn
	jmp	trn2ch

trnskp9: mov	si,offset skp9trn
	jmp	trn2ch

trnout	endp

code	ends 
	end

//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msyz100.asm
	/bin/echo -n '	'; /bin/ls -ld msyz100.asm
fi
/bin/echo 'Extracting msz100.hlp'
sed 's/^X//' <<'//go.sysin dd *' >msz100.hlp
		  KERMIT VERSION 2.26 FOR Z-DOS

			September 20, 1984

This guide is meant to be a supplement to the MSKERMIT.HLP file.  It
describes the differences between the Z-100 version of Kermit and
the IBM-PC version.  This version has not been tested with Z-DOS
version 2.0.  LOCAL command execution may or may not work.

* Program Operation

The Z-100 version of Kermit uses a slightly different concept for
Heath emulation.  The Z-100 normally uses the same escape sequences
as the Heathkit H-19 terminal, although a slightly different key
arrangement is used.  With Heath emulation mode turned on, the normal
Z-100/H19 escape sequences will be sent by the function keys and keypad.
However, key redefinition will not be available.  Printer control is 
also not available in Heath emulation mode.  When Heath emulation
is turned off (the default), the function keys and keypad emulate the
Heath sequence as best as possible (no checks are made for alternate
or shifted keypad modes), but key redefinition is allowed.  If a problem
is encountered running a program that uses the keypad keys or function
keys, swicth to Heath emulation and see if that helps.  Heath
emulation only affects what characters are sent for keys typed at the
keyboard.  It does not change the affect of incoming characters or
escape sequences.

The Z-100 version of Kermit-MS does not support multiple communication
ports.  The modem is assumed to be connected to the AUX port.  This
port must be configured as no parity, no handshake, 1 stop bit, 8 data
bits, no pad.

* Terminal Emulation

The Z-DOS version of Kermit-MS uses the following capabilities:

	Escape Char:	^]
	Modeline
	Printer control
	Key redefinition (with Heath emulation off)

Screen scroll is not currently supported.

X. Printer Control

The contents of a screen may be printed at any time if a printscreen
driver has been installed prior to running Kermit.  This is usually
done by running the PSC command which comes with your ZDOS 1.25
disks.  After this, the screen contents may be printed using
Shift-F12.

If you wish to log a terminal session on the printer and you are not
using Heath emulation mode, the F12 function key will start or stop
the spooling of incoming characters to the printer.  This may also
be simulated by the Kermit-MS LOG PRN and CLOSE commands.

X. Key Redefinition

Key redefinitions are accomplished in the same way as with the IBM-PC
version.  However, key redefintion may only be used when Heath
emulation is turned off.
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msz100.hlp
	/bin/echo -n '	'; /bin/ls -ld msz100.hlp
fi

knutson@ut-ngp.UUCP (Jim Knutson) (10/05/84)

: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting mskermit.doc'
sed 's/^X//' <<'//go.sysin dd *' >mskermit.doc
								      79


9. MS-DOS KERMIT

Program:    Daphne Tzoar and Jeff Damens, Columbia University; contribu-
	    tions by many others.
Language:   Microsoft Macro Assembler (MASM)
Documentation:
	    Frank  da  Cruz,  Columbia	University; Herm Fischer, Litton
	    Data Systems, Van Nuys CA.
Version:    2.26
Date:	    July 1984


Kermit-MS Capabilities At A Glance:

  Local operation:		     Yes
  Remote operation:		     Yes
  Transfers text files:		     Yes
  Transfers binary files:	     Yes
  Wildcard send:		     Yes
  ^X/^Y interruption:		     Yes
  Filename collision avoidance:	     Yes
  Can time out:			     Yes
  8th-bit prefixing:		     Yes
  Repeat count prefixing:	     Yes
  Alternate block checks:	     Yes
  Terminal emulation:		     Yes
  Communication settings:	     Yes
  Transmit BREAK:		     Yes
  IBM mainframe communication:	     Yes
  Transaction logging:		     No
  Session logging:		     Yes
  Raw transmit:			     No
  Act as server:		     Yes
  Talk to server:		     Yes
  Advanced server functions:	     No
  Advanced commands for servers:     Yes
  Local file management:	     Yes
  Handle file attributes:	     No
  Command/init files:		     Yes
  Command macros:		     Yes

Kermit-MS is a program that implements the KERMIT file transfer protocol
for the IBM PC and several  other  machines  using  the	 same  processor
family	(Intel	8088  or  8086)	 and  operating system family (PC-DOS or
MS-DOS, henceforth referred to collectively  as	 MS-DOS,  versions  1.1,
2.0,  and  2.1,	 and thereafter).  This section will describe the things
you should know about the MS-DOS file system in order to make  effective
use of Kermit, and then it will describe the Kermit-MS program.

Version	 2  of MS-DOS Kermit runs on a variety of systems, including the
IBM PC and XT, the HP-150, the DEC Rainbow 100 and 100+ (MS-DOS 2.05 and
above), the Wang PC, and there is a "generic" MS-DOS version.  Version 1
was adapted at various stages of development to run on other systems  as
well,	including   the	  Heath/Zenith	100,  Tandy  2000,  Victor  9000
(Sirius-1), and Seequa Chameleon, and is still available for those  sys-
tems until support for them and others is added to version 2.
								       80


The program operates under version 1.1 or 2.0 and above of DOS, although
some  features	require	 the  functionality  of	 2.0.	 It  runs in ap-
proximately 80K of memory -- over and  above  the  memory  used	 by  DOS
--  which means that your system should have at least 128K of RAM to use
version 2 of MS-DOS Kermit; smaller systems may still use Version 1.


9.1. The MS-DOS File System

The features of the MS-DOS file system of greatest  interest  to  KERMIT
users  are  the form of the file specifications, and the distinction be-
tween pre-MS-DOS 2.0 file names and newer file names which allow  direc-
tory paths.


9.1.1. File Specifications

MS-DOS 2.x file specifications are of the form

  DEVICE:\PATHNAME\NAME.TYPE

where  the  DEVICE is a single character identifier (for instance, A for
the first floppy disk, C for the first fixed disk,  D  for  a  RAM  disk
emulator),  PATHNAME  is  up  to 63 characters of identifier(s) (up to 8
characters each) surrounded by reverse slashes, NAME is an identifier of
up to 8 characters, and TYPE is an identifier of up to 3  characters  in
length.	 Device and pathname may be omitted.  The first backslash in the
pathname may be omitted if the specified path is relative to the current
directory.    In the path field, "." means current directory, ".." means
parent directory.  Some DOS implementations (like Wang)	 may  use  slash
"/" rather than backslash in the path field.

Pathname  is normally omitted, and cannot be specified for MS-DOS 1.x or
with those commands which allow MS-DOS 1.x use.	  Device  and  directory
pathnames,  when  omitted, default to either the user's current disk and
directory, or to the current directory search path as specified	 in  the
DOS  PATH  environment	variable,  depending on the context in which the
file name appears.

    When this manual says that a file is searched for "in  the	cur-
    rent path," it means that the PATH is searched first, and if the
    file  is not found, then Kermit-MS looks on the current disk and
    directory.	If the PATH environment variable  is  empty,  Kermit
    looks only at the current disk and directory.

NAME.TYPE is normally sufficient to specify a file, and only this infor-
mation is sent along by Kermit-MS with an outgoing file.

The  device,  path, name, and type fields may contain uppercase letters,
digits, and the special characters "-" (dash), "_" (underscore), and "$"
(dollar sign).	 (For  use  only  among	 MS-DOS	 processors,  additional
filename  special  characters  allowed are "#&!%'`(){}".  DOS 1.x allows
others as well.).  There are no imbedded  or  trailing	spaces.	   Other
characters  may	 be not be included; there is no mechanism for "quoting"
otherwise illegal characters in filenames.    The  fields  of  the  file
specification  are set off from one another by the punctuation indicated
								       81


above.

The  name  field is the primary identifier for the file.  The type, also
called the extension or suffix, is an indicator	 which,	 by  convention,
tells  what kind of file we have.  For instance FOO.BAS is the source of
a BASIC program named FOO;  FOO.OBJ  might  be	the  relocatable  object
module	produced  by  compiling	 FOO.BAS; FOO.EXE could be an executable
program produced by linking FOO.OBJ, and so forth.  .EXE  and  .COM  are
the normal suffixes for executable programs.

The  MS-DOS  allows  a	group  of files to be specified in a single file
specification by including the special "wildcard" characters, "*" and "?
".  A "*" matches any string of characters from the current position  to
the  end of the field, including no characters at all; a "?" matches any
single character.  Here are some examples:

  *.BAS	  All files of type BAS (all BASIC source files) in the	 current
	  directory.

  FOO.*	  Files of all types with name FOO.

  F*.*	  All files whose names start with F.

  F?X*.*  All  files whose names start with F and contain X in the third
	  position, followed by zero or more characters.

  ?.*	  All files whose names are exactly one character long.

Wildcard notation is used on many computer systems in similar ways,  and
it  is	the  mechanism	most  commonly used to instruct Kermit to send a
group of files.

    Note: Kermit-MS uses the "?" character for help  while  commands
    are	 being	typed, so the single-character wildcard in Kermit-MS
    commands is "=" rather than "?".  For example

      Kermit-MS>send =.*

    would send files of all  types  whose  names  were	exactly	 one
    character long.

Kermit-MS  users should bear in mind that other (non-MS-DOS) systems may
use different wildcard characters.  For instance the DEC-20 uses "%" in-
stead of "?" as the single character wildcard; when using  Kermit-MS  to
request a wildcard file group from a KERMIT-20 server, the Kermit-MS "="
must be replaced by the DEC-20 "%".


9.1.2. File Formats

MS-DOS	systems	 store files as bulk collections of 8 bit bytes, with no
particular differences between text, program  code,  and  binary  files.
ASCII  text files consist of lines separated by carriage-return-linefeed
sequences (CRLFs), which conforms exactly to the way  Kermit  represents
text  files  during  transmission.   Since a non-MS-DOS receiving system
might need to make distinctions as to file type, you  may  need	 to  use
								       82


various	 SET functions on the remote system to inform it that the incom-
ing file is of some particular (non-default) type, such as binary.    In
transmitting files between Kermit-MS's, regardless of file contents, the
receiving MS-DOS system is equally capable of processing text, code, and
data,  and  in	fact  has  no knowledge of how the bytes in the file are
used.

MS-DOS (unlike CP/M) is capable of pinpointing	the  end  of  file  with
precision  by keeping a byte count in the directory, so one would expect
no particular  confusion  in  this  regard.    However,	 certain  MS-DOS
programs  continue to use the CP/M convention of terminating a text file
with a Control-Z character, and won't operate correctly unless this ter-
minating byte is present.  Therefore, Kermit-MS users should be aware of
a special SET EOF option for both incoming and outbound files, described
below.

Non-MS-DOS systems may well be confused by nonstandard ASCII files  from
Kermit-MS.   Files produced by Easywriter or Word Star, for example, may
need to be converted to conventional ASCII format prior to  transmission
by  commonly  available	 "exporter"  programs.	 Spreadsheet or database
files usually need special formatting to  be  meaningful  to  non-MS-DOS
recipients  (though  they can be transmitted between MS-DOS systems with
Kermit-MS).  Furthermore, files created by word processors (such as BLUE
or Easy Writer) that store formatting data at the end of the file, after
the control-Z and before physical end, will require  special  processing
via  SET  EOF to strip the formatting data, lest they confuse non-MS-DOS
recipients.


9.2. Program Operation

Kermit-MS can be  run  interactively,  from  a	batch  file,  or  as  an
"external"  DOS	 command.    Commands  consist	of  one	 or more fields,
separated by "whitespace" -- one or more spaces or tabs.

Upon initial startup, the program executes any	commands  found	 in  the
file  MSKERMIT.INI  in	the  current path.  This initialization file may
contain command macro definitions, communications settings  for	 one  or
more  ports,  or  any  other  Kermit-MS	 commands.    Here  is	a sample
MSKERMIT.INI file:

  set warning on  ; Enable filename collision avoidance.
  ;
  ; Define some macros
  ;
  define unix set local-echo off, set flow xon, set timer off
  def ibm set parity odd, set local on, set handsh xon, set timer on
  def modem set port 2, set baud 1200
  def noisy set block-check 3, set send packet-length 40
  ;
  ; Select a port
  ;
  set port 1	  ; Select COM1 for communications,
  set baud 4800	  ; setting the speed to 4800 baud,
  connect	  ; and make a terminal connection.
								       83


Note  that  comments may be included by prefixing them with a semicolon.
The program can be run in several ways.


Interactive Operation:

To run Kermit-MS interactively, invoke	the  program  from  DOS	 command
level by typing its name.  When you see the command's prompt,

  Kermit-MS>

you  may type Kermit commands repeatedly until you are ready to exit the
program, for example:

  A>
  A>kermit

  IBM PC Kermit-MS V2.26
  Type ? for help

  Kermit-MS>send foo.*

    informational messages about the files being sent

  Kermit-MS>get bar.*

    informational messages about the files being received

  Kermit-MS>exit
  A>

During interactive operation, you may edit the command you're  currently
typing	to  erase  the character most recently typed (BACKSPACE or DEL),
the most recent field (CTRL-W), or the entire command (CTRL-U).	 In  ad-
dition, you may use the help ("?") and recognition (ESC) features freely
while  typing  Kermit-MS  commands.  A question mark typed at almost any
point in a command produces a brief description of what is  expected  or
possible  at  that  point;  for	 this reason, Kermit-MS uses "=" for the
single-character match wildcard in local filenames.  ESC  typed	 at  any
point,	even  in  a  local  filename, will cause the current field to be
filled out if what you have typed so far is sufficient to  identify  it,
and  will leave you in position to type the next field (or to type a "?"
to find out what the next field is); otherwise, the program will beep at
you and wait for you to type further characters.

Some Kermit-MS commands, like GET, SHOW KEY, SET KEY, may prompt for ad-
ditional information on subsequent lines.  If you have	reached	 one  of
these  prompts	and  then  wish	 to  cancel  the  command,  you may type
Control-C.

Summary of Kermit-MS Command Characters:

  BACKSPACE	Delete the character most recently typed.  May be  typed
		repeatedly  to	delete	backwards.    You  may	also use
		DELETE, RUBOUT, or equivalent keys.
								       84


  CTRL-W	Delete	the most recent "word", or field, on the command
		line.  May be typed repeatedly.

  CTRL-U	Delete the entire command line.

  CTRL-C	Cancel	the  current   command	 and   return	to   the
		"Kermit-MS>" prompt.

  ?		Type a brief message describing what you are expected to
		type in the current field.

  ESC		If  enough  characters have been supplied in the current
		field (keyword or file name) to	 uniquely  identify  it,
		supply	the  remainder	of the field and position to the
		next field of the command.  Otherwise, sound a beep.

  =		Wildcard character for	matching  single  characters  in
		filenames, equivalent to MS-DOS "?".


Command Line Invocation:

Kermit-MS  may also be invoked with command line arguments from DOS com-
mand level, for instance:

  A>kermit send foo.bar

or

  A>kermit set port 1, set baud 9600, connect

In this case, help  and	 recognition  are  not	available  (because  the
program	 won't	start  running	until  after you type the entire command
line), and Kermit-MS will exit after completing the specified command or
commands.    Therefore,	 when  invoked	with  command  line   arguments,
Kermit-MS  will behave as if it were an external DOS command, like MODE.
Note that several commands may be given on the command	line,  separated
by commas.


Batch Operation:

Like  other  MS-DOS programs, Kermit-MS may be operated under batch with
either command line arguments and/or TAKE files; Kermit	 will  also  run
interactively  if invoked from batch, but it will read commands from the
keyboard and not the batch file.


9.3. Kermit-MS Commands

MS-DOS Kermit implements a large subset of the commands of "ideal"  Ker-
mit.  Here's a brief summary:

	  BYE  to remote server.
	CLOSE  log file and stop logging remote session.
      CONNECT  as terminal to remote system.
								       85


       DEFINE  macros of Kermit-MS commands.
       DELETE  local files.
    DIRECTORY  listing of local files.
	   DO  a macro expansion.
	 EXIT  from Kermit-MS.
       FINISH  Shut down remote server.
	  GET  remote files from server.
	 HELP  about Kermit-MS.
	LOCAL  prefix for local file management commands.
	  LOG  remote terminal session.
       LOGOUT  remote server.
	 PUSH  to MS-DOS command level.
	 QUIT  from Kermit-MS
      RECEIVE  files from remote Kermit.
       REMOTE  prefix for remote file management commands.
	  RUN  an MS-DOS program.
	 SEND  files to remote Kermit.
       SERVER  mode of remote operation.
	  SET  various parameters.
	 SHOW  various parameters.
	SPACE  inquiry.
       STATUS  inquiry.
	 TAKE  commands from file.

The  remainder	of  this  section concentrates on the commands that have
special form or meaning for MS-DOS Kermit.  Not	 all  of  the  following
commands  are  necessarily  available on all MS-DOS systems, and some of
the commands may work somewhat differently between DOS versions.


9.3.1. Commands for File Transfer

The file transfer commands are SEND, GET, and RECEIVE.


			    THE SEND COMMAND

Syntax:	 SEND filespec1 [filespec2]

The SEND command causes a file or file group to be sent from  the  local
MS-DOS system to the Kermit on the remote system.  The remote Kermit may
be running in either server or interactive mode; in the latter case, you
should	already have given it a RECEIVE command and escaped back to your
PC.

filespec1 may contain a device designator, like "A:"  and  the	wildcard
characters  "*"	 and/or "=".  The current release of Kermit-MS, however,
does not allow pathnames in the SEND command file specification.

If filespec1 contains wildcard characters then all matching  files  will
be  sent,  in  the same order that MS-DOS would show them in a directory
listing.  If filespec1 specifies a single file, you may direct Kermit-MS
to send that file with a different name, given in filespec2.	For  in-
stance, in the command

  Kermit-MS>send foo.bar framus.widget
								       86


filespec2  begins  with the first nonblank character after filespec1 and
ends with the carriage return; thus it may contain blanks or  other  un-
usual  characters  that may be appropriate on the target machine.  Lower
case letters in filespec2 are raised to upper case for transmission.

If a file can't be opened for  read  access,  standard	MS-DOS	recovery
procedures will take place.  For example:

  Not ready error reading drive A
  Abort, Retry, Ignore?

If you select "Abort," you will be returned to DOS.

Files will be sent with their MS-DOS filename and filetype (for instance
FOO.TXT,  no device or pathname).  Each file is sent as is, with no con-
versions done on the data, except for possibly adding or deleting a ter-
minating Control-Z character (see the SET EOF command).

Once you give Kermit-MS the SEND command, the name of each file will  be
displayed  on  your  screen  as	 the transfer begins; packet, retry, and
other counts will be displayed along with informational messages  during
the  transfer.	  If  the file is successfully transferred, you will see
"Complete", otherwise  there  will  be	an  error  message.    When  the
specified operation is done, the program will sound a beep.

Several	 single-character commands may be given while a file transfer is
in progress:

  ^X  (Control-X) Stop sending the current file and go on  to  the  next
      one, if any.

  ^Z  Stop sending this file, and don't send any further files.

  ^C  Return  to Kermit-MS command level immediately without sending any
      kind of notification to the remote system.

  ^E  Like ^C, but send an Error packet to the remote Kermit in	 an  at-
      tempt to bring it back to server or interactive command level.

  CR  Simulate a timeout: resend the current packet, or NAK the expected
      one.

Control-X  and Control-Z send the proper protocol messages to the remote
Kermit to bring it gracefully to the desired state.    Control-C  leaves
the  remote  Kermit  in	 whatever  state it happens to be in.  Control-E
"aborts" any protocol that is taking place.


			  THE RECEIVE COMMAND

Syntax: RECEIVE [filespec]

The RECEIVE command tells Kermit-MS to receive a file or file group from
the other system.  Kermit-MS simply waits for the file to  arrive;  this
command	 is  not to be used when talking to a Kermit server (use GET for
that).	You should already have issued a SEND command to the remote Ker-
								       87


mit and escaped back to Kermit-MS before issuing the RECEIVE command.

If the optional filespec is provided, store the incoming file under that
name.	 The filespec may include a device designator, or may consist of
only a device designator.  The incoming file is stored on the default or
specified device (current directory in DOS 2.0 and thereafter).	  If  no
name  was  specified,  the  name from the incoming file header packet is
used; if that name is not a  legal  MS-DOS  file  name,	 Kermit-MS  will
delete	excessive characters from it, and will change illegal characters
to the letter X.

If the optional filespec was provided, but more than one  file	arrives,
the  first  file  will	be  stored  under  the	given  filespec, and the
remainder will be stored under their own names,	 but  on  the  specified
device.

If an incoming file does not arrive in its entirety, Kermit-MS will nor-
mally  discard it; it will not appear in your directory.  You may change
this behavior by using the command SET INCOMPLETE KEEP, which will cause
as much of the file as arrived to be saved in your directory.

The same single-character commands are available as during SEND:

  ^X  Request that the remote Kermit stop sending the current file,  and
      proceed  to  the	next one immediately.  Since this is an optional
      feature of the Kermit protocol, the remote Kermit might not  honor
      the request.

  ^Z  Request that the remote Kermit terminate the entire transfer; this
      is  also	an  optional feature that may or may not be supported by
      the remote Kermit.

  ^C, ^E, and CR operate in the same way as they do during SEND.

If the incoming file has the same name as a file  that	already	 exists,
and  WARNING is set ON, Kermit-MS will change the incoming name (and in-
form you how it renamed it) so as not  to  obliterate  the  pre-existing
file.	If WARNING is OFF, the original file will be overwritten; if you
type ^X or ^Z to interrupt the transfer, you'll either get a partial new
file, or else both the old and the new file of that name will  be  lost,
depending  on  SET  INCOMPLETE.	 In any case, when WARNING is off, files
with the same name as incoming files will not survive.

Caution: If an incoming file's name  (the  part	 before	 the  dot)  cor-
responds  to an MS-DOS device name, such as NUL, COM1, CON, AUX, or PRN,
output will go to that device, rather than to a	 file  with  that  name.
This is a feature of MS-DOS.
								       88


			    THE GET COMMAND

Syntax: GET remote-filespec

The GET command requests a remote KERMIT server to send the file or file
group  specified by remote-filespec.  This command can be used only when
Kermit-MS has a KERMIT server on the other end of the connection.   This
means  that  you must have CONNECTed to the other system, logged in, run
KERMIT there, issued the SERVER command, and escaped back (e.g. ^]C)  to
the  local  Kermit-MS.	If the remote Kermit does not have a SERVER com-
mand, then you should use SEND and RECEIVE as described above.

You may use the GET command to specify a different name for storing  the
incoming.    Just  type	 GET  alone  on a line, and you will be prompted
separately for the remote filespec and the local filespec:

  Kermit-MS>get
   Remote Source File: com1.txt
   Local Destination File: xcom1.txt

If more than one file arrives, only the first will be renamed.

The remote filespec is any string that can be a legal file specification
for the remote system; it is not parsed or validated locally.	 It  can
contain	 whatever wildcard or file-group notation is valid on the remote
system.	 As files arrive, their names will be displayed on your	 screen,
along  with packet traffic statistics and status messages.  You may type
^X to request that the current incoming file be cancelled, ^Z to request
that the entire incoming batch be cancelled, and ^C or ^E to return  im-
mediately to the Kermit-MS> prompt, exactly as described for the RECEIVE
command.


9.3.2. Commands for Connecting and Disconnecting

The CONNECT command connects your PC as a terminal to the remote system,
so that you can start up Kermit there.	The BYE, FINISH, and LOGOUT com-
mands allow you to shut down a remote Kermit server.

BYE		When  communicating with a remote KERMIT server, use the
		BYE command to shut down the server, log  out  its  job,
		and exit from Kermit-MS to DOS.

FINISH		Like BYE, FINISH shuts down the remote server.	However,
		FINISH	does not log out the server's job.  You are left
		at Kermit-MS prompt level so that you can  connect  back
		to the job on the remote system.

LOGOUT		The  LOGOUT command is identical to the BYE command, ex-
		cept you will remain at Kermit-MS prompt  level,  rather
		than exit to DOS, so that you can establish another con-
		nection.
								       89


The CONNECT Command

Establish  an interactive terminal connection to the system connected to
the currently selected communications port (e.g.  COM1	or  COM2)  using
full duplex (remote) echoing and no parity unless otherwise specified in
previous  SET  commands.    Get	 back  to Kermit-MS by typing the escape
character followed by the letter C. The escape character is Control-] by
default.

You can use the SET ESCAPE command to define a different escape	 charac-
ter,  and  on some systems (including the PC and XT) you can SET BAUD to
change the baud rate, and SET PORT to switch between ports.

Terminal emulation is described in greater detail in section 9.4 below.


9.3.3. Commands for File Management

Kermit-MS provides commands or managing both local and remote files.


			  THE REMOTE COMMANDS

The REMOTE keyword is a prefix for a number of commands.   It  indicates
that  the command is to be performed by the remote Kermit, which must be
running as a server.  Note that not all Kermit servers	are  capable  of
executing  all	these  commands,  and some Kermit servers may be able to
perform functions for which Kermit-MS does not yet have the  correspond-
ing  commands.	In case you send a command the server cannot execute, it
will send back a message stating that the command is unknown to it.   If
the  remote  server can execute the command, it will send the results to
your screen.  Here are the REMOTE commands which Kermit-MS may issue:

CWD [directory] Change Working Directory on the remote host.  Change the
		default source and destination area  for  file	transfer
		and  management.    You will be prompted for a password,
		which will be erased as you type it.	If  you	 do  not
		supply	a  password  (i.e.  you	 type  only  a	carriage
		return), the server will attempt to access the specified
		directory without a password.  If you do  not  supply  a
		directory  name,  your default or login directory on the
		remote system will be assumed.

DELETE filespec Delete the specified file or files on the remote system.
		In response, the remote host should display  a	list  of
		the files that were or were not successfully deleted.

DIRECTORY [filespec]  The remote system will provide a directory listing
		of the specified files.	 If no files are specified, then
		all  files  in	the  default  area  (the current working
		directory) will be listed.

HELP		The remote  host  tells	 what  server  functions  it  is
		capable of.

HOST [command]	Send  the command to the remote system's command proces-
								       90


		sor for execution.

SPACE [directory]
		Provide	 a  brief summary of disk usage in the specified
		area on the remote host.  If none specified, the default
		or current area will be summarized.

TYPE filespec	Display the contents of the  specified	remote	file  or
		files on the screen.


			   THE LOCAL COMMAND

The  LOCAL  keyword  is a prefix for a number of commands.  It indicates
that the specified command is to be executed on the local MS-DOS system.
The LOCAL prefix may be omitted.  The local commands available are:

DELETE filespec Deletes the specified file or files.   As  in  DOS,  the
		names of the deleted files are not listed, only the mes-
		sage  "file(s)	deleted"  or "file(s) not found", and if
		you give the command "delete *.*", Kermit-MS will prompt
		"Are you sure?", like DOS.

DIRECTORY [filespec]  Lists the names,	sizes,	and  creation  dates  of
		files  that  match  the given file specification.  If no
		filespec is given, the command is equivalent to DIR *.*.

SPACE		Performs the  MS-DOS  CHKDSK  function	by  running  the
		CHKDSK	program	 from  the current path, or default disk
		under DOS 1.1.

RUN filespec	Runs the specified file, which must be in .EXE	or  .COM
		format,	 from  the  specified  path  or according to the
		value of the PATH variable if no path  was  included  in
		the  filespec.	  This	command	 requires  MS-DOS 2.0 or
		higher.

PUSH		Invokes an MS-DOS command processor  "under"  Kermit-MS,
		either	COMMAND.COM or whatever shell you have specified
		with COMSPEC.  When you return	to  Kermit-MS  (for  in-
		stance,	 by  typing  the  MS-DOS EXIT command), you will
		find Kermit-MS as you left it, with all settings intact.
		This command only works in MS-DOS 2.0 or higher.

The local RUN command has various uses, one of which  is  to  supplement
the  features  of Kermit-MS.  For instance, suppose there is an involved
procedure that you regularly perform on a certain remote system --  this
might include giving commands to a modem to dial the system, looking for
a particular herald or prompt, performing a login command sequence, run-
ning a selected application, and then running Kermit to send the results
back  to your PC.  You could write a program in the compiled language of
your choice, say C or BASIC, to send the desired commands to your  modem
and  the  remote  system and to look for the appropriate responses.  You
could put all this in a Kermit-MS TAKE command file (see below), like
								       91


  run update.com
  receive

The  program,  called UPDATE in this case, does everything up to and in-
cluding starting Kermit sending	 from  the  remote  system.    When  the
program	 terminates,  the next Kermit-MS command, "receive," is executed
from the command file.	When the end of the command file is reached, in-
teractive operation is resumed.


			    THE TAKE COMMAND

Syntax: TAKE filespec

Execute Kermit commands from the specified file, which	may  include  an
explicit  path;	 if no path is specified, the value of the PATH variable
is used; if PATH has no value, then the current disk and  directory  are
searched.  The command file may include TAKE commands, but it cannot in-
clude  characters  to be sent to a remote host during terminal emulation
(i.e. after a CONNECT command).	 A command  file  may  include	comments
prefixed by semicolons.


			    THE LOG COMMAND

Syntax: LOG filespec

Specifies  that all characters that appear on your screen during CONNECT
will be recorded in the specified file.	 This allows  you  to  "capture"
files  from  a	remote	system	that  doesn't have Kermit, as well as to
record remote command typescripts.  The log is closed when you EXIT from
Kermit-MS or when you issue an explicit CLOSE command.


9.3.4. The SERVER Command

Kermit-MS is capable of acting as a Kermit server, providing file trans-
fer for users coming in through one of the  communication  ports.    The
current	 version  of Kermit-MS can send files (the user on the other end
types the GET command), receive files (the user types  SEND),  and  ter-
minate, giving control back to the console (user types BYE).

To  put Kermit-MS into server mode, first issue any desired SET commands
to select and configure the desired port, and then type the SERVER  com-
mand.	Kermit-MS will await all further instructions from the user Ker-
mit on the other end of the connection, which may be hardwired	or  con-
nected through an autoanswer modem.  For example:

  Kermit-MS>set port 1
  Kermit-MS>set baud 1200
  Kermit-MS>set timer on
  Kermit-MS>set warning on
  Kermit-MS>server
								       92


9.3.5. The SET Command

Syntax: SET parameter [value]

Establish  or  modify  various	parameters for file transfer or terminal
connection.  You can examine their values with the SHOW command.    Note
that  there  is	 no  "set  ibm"	 command;  IBM	mainframe  communication
parameters may be selected with a command macro (see below).   The  fol-
lowing SET commands are available in Kermit-MS:

		BAUD  Communications port line speed
		BELL  Whether to beep at the end of a transaction
    BLOCK-CHECK-TYPE  Level of error checking for file transfer
	       DEBUG  Display packet contents during file transfer
	DEFAULT-DISK  Default disk drive for file i/o
	 DESTINATION  Default destination device for incoming files
	 END-OF-LINE  Packet terminator
		 EOF  Method for determining or marking end of file
	      ESCAPE  Escape character for CONNECT
	FLOW-CONTROL  Enable or disable XON/XOFF
	   HANDSHAKE  Half-duplex line turnaround option
	     HEATH19  Heath/Zenith-19 terminal emulation
	  INCOMPLETE  What to do with an incompletely received file
		 KEY  Specify key redefinitions, or "keystroke macros"
	  LOCAL-ECHO  Specify which host does the echoing during CONNECT
	      PARITY  Character parity to use
		PORT  Select a communications port
	      PROMPT  Change the "Kermit-MS>" prompt to something else
	     RECEIVE  Request remote Kermit to use specified parameters
	      REMOTE  For running Kermit-MS interactively from back port
		SEND  Use the specified parameters during file transfer
	   TAKE-ECHO  Control echoing of commands from TAKE files
	       TIMER  Enable/disable timeouts during file transfer
	     WARNING  Specify how to handle filename collisions

The SET commands that are peculiar to MS-DOS Kermit are now described in
greater detail.	 The others behave as in "ideal" Kermit.


SET BAUD

Syntax: SET BAUD rate

Set  the  speed	 of  the currently selected terminal communications port
(COM1 by default) to 300, 1200, 1800, 2400, 4800, 9600 or  other  common
baud  rate.    Some implementations do not support this command.  In any
case, Kermit-MS leaves the current communication port settings alone un-
less you issue explicit SET commands to change them.


SET BELL

Syntax: SET BELL ON or OFF

Specifies whether bell (beeper) should sound upon completion of	 a  file
transfer operation.
								       93


SET DEBUG

Syntax: SET DEBUG ON or OFF

ON	Display	 the  Kermit  packet  traffic on your screen during file
	transfer.  If the debugger is loaded,  transfer	 control  to  it
	when CTRL-C is typed.  In Heath-19 terminal emulation on the IBM
	PC, display unusual control characters in uparrow notation.

OFF	Don't  display	debugging information (this is the default).  If
	debugging was in effect, turn it off.


SET DEFAULT-DISK

Syntax: SET DEFAULT-DISK x:

Specify the default disk drive to use for file transfer, directory list-
ings, and so forth.  Equivalent to typing the DOS command  for	changing
disks.


SET DESTINATION

Syntax: SET DESTINATION device

Specify the device for incoming files, DISK or PRINTER.	 SET DESTINATION
PRINTER will cause incoming files to be spooled directly to the printer.
The normal destination is DISK.


END-OF-LINE

Syntax: SET END-OF-LINE number

If  the	 remote	 system needs packets to be terminated by anything other
than carriage return, specify the decimal value	 of  the  desired  ASCII
character.


SET EOF

Syntax: SET EOF option

Controls how the end of file is handled.  The options are:

CTRL-Z		Append	a  Control-Z character to the end of an incoming
		file, unless it already ends with a Control-Z.	 Certain
		MS-DOS text editors and other applications require files
		to  be	in  this  format.  For outbound files, treat the
		first Control-Z as the end of file, and do not	send  it
		nor any characters following it.

NOCTRL-Z	(Default)  Store  incoming files exactly as is, and send
		MS DOS files exactly as	 is  (according	 to  their  byte
		count).
								       94


SET ESCAPE

Syntax: SET ESCAPE character

Specify	 the  control  character you want to use to "escape" from remote
connections back to Kermit-MS.	The default  is	 normally  ^]  (Control-
Rightbracket).	The  character is entered literally, and should normally
be chosen from the ASCII control range.


SET FLOW-CONTROL

Syntax: SET FLOW-CONTROL option

Specify the full duplex	 flow  control	to  be	done  on  the  currently
selected  port.	   The	current	 options  are  XON/XOFF	 and  NONE.  The
specified type of flow control will be done during both terminal  emula-
tion  and file transfer.  If set to XON/XOFF, HANDSHAKE is automatically
set to OFF.


SET HANDSHAKE

Syntax: SET HANDSHAKE option

Specify any half-duplex handshaking to be done on the currently selected
port.  The options are BELL, CR, LF, NONE, XOFF, or XON.  The  specified
handshaking will be done during file transfer only.  If HANDSHAKE is set
to anything other than NONE, FLOW-CONTROL is automatically set to OFF.


SET HEATH19

Syntax: SET HEATH19 ON or OFF

Specify	 whether Kermit-MS should use its built-in software facility for
emulating a Heath/Zenith-19 (H19) terminal.

ON	During CONNECT, incoming characters are to be examined	for  H19
	terminal  screen control commands (escape sequences), and if en-
	countered, the commands are to be emulated  on	the  PC	 screen.
	The  H19  codes are a superset of the popular DEC VT52 codes, so
	if your system does not support the Heath-19, you  may	tell  it
	that  your  terminal  type  is	VT52  (or  one	of the many VT52
	compatibles).  The Heath-19 codes are listed  in  section  9.10,
	below.

OFF	All  incoming  characters  will	 be  sent  to the screen "bare",
	through DOS.  If you have loaded a device driver  into	DOS  for
	the CON: device, such as ANSI.SYS, then that driver will be able
	to  interpret  the codes itself. Most non-IBM systems have their
	own screen control code interpreter built into DOS or  firmware,
	or available as a loadable device driver.

See section 9.4 for details about terminal emulation.
								       95


SET KEY

Syntax: SET KEY key-specifier

Specifies  that when the designated key is struck during terminal emula-
tion, the associated character string is sent.	The key-specifier is one
of the keywords F1, F2, ..., or SCAN followed by a scan code.	 Systems
that have a BACKSPACE key also include BACKSPACE as a keyword.

If SCAN is used, it is followed by a decimal number to indicate the scan
code  of  the  key, which you would ascertain from your system reference
manual, or else by using the  Kermit-MS	 SHOW  KEY  command.	SET  KEY
prompts	 you  on  a new line for the definition string.	 Certain charac-
ters, like ESC and CR, may not be entered literally into the string, but
can be included by inserting escape codes of the form \ooo, a  backslash
followed  by  a	 2-  or	 3-digit octal number corresponding to the ASCII
value of the desired character.	 If some other key redefinition package,
like  ProKey,  has  been  loaded,  then	 its  redefinitions  will   take
precedence over Kermit's.

The  SET  KEY  command is illustrated in the terminal emulation section,
9.4, below.


SET LOCAL-ECHO

Syntax: SET LOCAL-ECHO option

Specify how characters are echoed during terminal emulation on the  cur-
rently	selected port.	ON specifies that characters are to be echoed by
Kermit-MS (because neither the remote computer	nor  the  communications
circuitry  has	been  requested	 to  echo), and is appropriate for half-
duplex connections.  LOCAL-ECHO is  OFF	 by  default,  for  full-duplex,
remote echo operation.

When  you SET LOCAL-ECHO ON, the current HANDSHAKE (if any) is automati-
cally enabled and full-duplex FLOW-CONTROL is automatically turned  off.
When you SET LOCAL-ECHO OFF, HANDSHAKE is also disabled, and the current
mode  of  FLOW-CONTROL	(if  any)  is  enabled.	 If this behavior is un-
desired, you may override it by typing explicit	 SET  HANDSHAKE	 or  SET
FLOW commands after entering the SET LOCAL-ECHO command.


SET PARITY

Syntax: SET PARITY keyword

Specify	 the character parity to be used on the currently selected port.
The choices for SET PARITY are NONE (the default), ODD, EVEN, MARK,  and
SPACE.	NONE means no parity processing is done, and the 8th bit of each
character can be used for data when transmitting binary files.

You  will  need to SET PARITY to ODD, EVEN, MARK, or possibly SPACE when
communicating with a system, or over a network, or through modems,  con-
centrators, multiplexers, or front ends that require or impose character
parity	on  the	 communication line.  For instance, GTE Telenet requires
								       96


MARK parity.  If you neglect to SET PARITY when the communications equi-
pment requires it, the symptom may be that terminal emulation works par-
tially, and file transfer does not work at all.

If you have set parity to ODD, EVEN, MARK, or SPACE, then Kermit-MS will
request	 that  binary files will be transferred using 8th-bit-prefixing.
If the other side knows how to do 8th-bit-prefixing (this is an optional
feature of the KERMIT protocol, and not all  implementations  of  KERMIT
have it), then binary files can be transmitted successfully.  If NONE is
specified,  8th-bit-prefixing will not be requested.  Note that there is
no advantage to using parity; it only slows Kermit file	 transfer  down.
The  SET  PARITY  command  is  provided only to allow Kermit to adapt to
hardware that insists upon using parity.


SET PORT

Syntax: SET PORT number

On machines with more than one communications port, select the	port  to
use  for  file	transfer  and CONNECT.	This command lets you use a dif-
ferent asynchronous adapter, or switch between two or more  simultaneous
remote	sessions.    Subsequent	 SET  BAUD, PARITY, HANDSHAKE, FLOW, and
LOCAL-ECHO commands will apply to this port only.  SET	PORT  1	 selects
COM1, SET PORT 2 selects COM2.


SET REMOTE

Syntax: SET REMOTE ON or OFF

If  you	 wish  to run Kermit-MS interactively through the back port, for
instance after the operator has done CTTY COM1, you must give  the  com-
mand SET REMOTE ON; this suppresses the file transfer display screen, so
that the display won't interfere with the file transfer itself.


SET RECEIVE

Syntax: SET RECEIVE parameter value

At  the	 beginning of a protocol operation, request the remote Kermit to
use the given value specified parameter, or inform  Kermit-MS  that  the
remote Kermit will be using it.

PACKET-LENGTH	Ask  the  remote  Kermit  to  use  the specified maximum
		length for packets that it sends to Kermit-MS.	The nor-
		mal (and maximum) length is 94.	  Use  this  command  to
		shorten packets if the communication line is noisy; this
		will  decrease	the probability that a particular packet
		will be corrupted, and will  reduce  the  retransmission
		overhead  when	corruption  occurs, but it will increase
		the protocol overhead.

PADCHAR		Ask the remote Kermit to use the given character for in-
		terpacket padding.  Kermit-MS should never  require  any
								       97


		padding.

PADDING		Ask the remote Kermit to insert the given number of pad-
		ding  characters  before  each	packet	it  sends.  This
		should never be necessary.

START-OF-PACKET The remote Kermit will be marking the beginning of pack-
		ets with something other than Control-A.  This	will  be
		necessary  only	 if the hosts or communication equipment
		involved cannot pass a Control-A through as data.

TIMEOUT		Ask the remote Kermit to time out after the given number
		of seconds if a packet expected from Kermit-MS	has  not
		arrived.   Use this command to change the normal timeout
		interval.


SET SEND

Syntax: SET SEND parameter value

PACKET-LENGTH	Use the specified maximum length for  outbound	packets.
		Normally,  Kermit-MS uses whatever length the other Ker-
		mit requests.

PADCHAR		Use the specified  character  for  interpacket	padding.
		Some hosts may require some padding characters (normally
		NUL or DEL) before a packet.

PADDING		How many padding characters to use between packets, nor-
		mally zero.

QUOTE		Use  the  indicated  printable	character  for prefixing
		(quoting) control characters and  other	 prefix	 charac-
		ters.  The only reason to change this would be for send-
		ing a very long file that contains very many "#" charac-
		ters (the normal control prefix) as data.

START-OF-PACKET Mark the beginning of outbound packets with some control
		character  other than Control-A.  This will be necessary
		only if the remote host or the communication channel in-
		volved cannot accept a Control-A as data.    The  remote
		host  must have been given the corresponding SET RECEIVE
		START-OF-PACKET command.

TIMEOUT		Change Kermit-MS's normal timeout interval; this command
		is effective only if TIMER is set to be ON; it	is  nor-
		mally	OFF  so	 that  the  remote  KERMIT  can	 control
		timeouts.
								       98


SET TAKE-ECHO

Syntax: SET TAKE-ECHO ON or OFF

Specifies  whether  screen  display  should occur during implicit or ex-
plicit TAKE operations on MSKERMIT.INI or other Kermit-MS command files,
and during evaluation of macro definitions.  Handy for finding errors in
command files.


SET TIMER

Syntax: SET TIMER ON or OFF

Enable or disable the timer that is used during file transfer  to  break
the  deadlock  that  occurs when an expected packet does not arrive.  By
default, the timer is OFF, because Kermit-MS is usually used in conjunc-
tion with a mainframe that is doing its own timeouts.	 During	 a  file
transfer,  it  is  sufficient  for one side to do the timing out and the
mainframe is usually better equipped to adjust timeout	intervals  based
on  system  load or other conditions.  The timer should be set ON if you
are communicating with a system that cannot do	timeouts,  such	 as  IBM
VM/CMS Kermit.


SET WARNING

Syntax: SET WARNING option

Specify	 what to do when an incoming file has the same name as an exist-
ing file in the default directory of the default device.  If ON,  Kermit
will  warn  you	 when  an incoming file has the same name as an existing
file, and automatically rename the incoming file (as  indicated	 in  the
warning	 message) so as not to destroy (overwrite) the pre-existing one.
If OFF, the pre-existing file is destroyed, even if  the  incoming  file
does not arrive completely.


9.3.6. The SHOW Command

Syntax: SHOW option

Currently,  most  parameters  that  may be altered with SET commands are
displayed by the STATUS command.  The SHOW command is used for	display-
ing macro definitions and key redefinitions.

The  SHOW  MACROS  command  displays  the  definitions	of all currently
defined macros.

The SHOW KEY command allows you to determine the scan code  produced  by
pressing  a  given  key,  so that you can construct a SET KEY command to
redefine the key.  If the key already has a redefinition in effect, that
too will be displayed.	In this example, a DEC Rainbow	user  determines
the  scan  code for the accent grave key, and then redefines that key to
send ESC:
								       99


  Kermit-MS>show key
  Press a key: `
    Scan Code:	96
    Definition:
  Kermit-MS>set key scan 96
  Definition string: \33
  Kermit-MS>show key
  Press a key: `
    Scan Code:	96
    Definition: \33
  Kermit-MS>

The SHOW KEY command only works on certain systems.


9.3.7. Command Macros

Kermit-MS  provides  a	facility  for  combining commands into "macros."
Command macro definitions may be included  in  your  MSKERMIT.INI  file,
TAKEn  explicitly from a specified file, or typed interactively, and may
be invoked with the DO command.


			   THE DEFINE COMMAND

Kermit-MS command macros are constructed with the DEFINE command.    The
syntax is

  DEFINE macro-name  [command [, command [, ...]]]

Any Kermit-MS commands may be included.	 Example:

  define telenet set parity mark, set baud 1200, connect


			     THE DO COMMAND

A  Kermit-MS  command  macro  is  invoked using the DO command.	 For in-
stance, Kermit-MS comes with a	predefined  macro  to  allow  convenient
setup for IBM communications; to invoke it, you would type

  do ibm

The  IBM macro is defined as "parity mark, handshake xon, local-echo on,
timer on".  You can delete or replace this definition by  adding  a  new
(perhaps null) definition, such as

  define ibm parity even, handshake cr, local-echo on, timer on

or

  define ibm
								      100


9.4. Terminal Emulation

When you issue the CONNECT command, your PC acts as a terminal connected
to  a  remote computer through the currently selected port.  The charac-
ters you type are sent out the port, and characters that arrive	 at  the
port  are displayed on your screen.  If you have not previously issued a
SET PORT command, COM1 is used.	 If you have SET LOCAL-ECHO ON	for  the
selected  port,	 then Kermit-MS will display characters on the screen as
you type them.	If LOCAL-ECHO is OFF, then XON/XOFF flow control will be
done unless you have SET FLOW-CONTROL OFF.  If you have	 SET  PARITY  to
anything  other	 than NONE, Kermit-MS will add the appropriate parity to
each outbound character, and strip any parity from incoming ones.  While
CONNECTed, you can also communicate with an autodialer or "smart  modem"
to  control  the  communications line, hang it up, and the like; for in-
stance, typing +++ to a Hayes-like modem will allow you to  follow  that
by dialing or hangup commands.

If  Heath-19  emulation	 is  being  done,  incoming  characters	 will be
monitored for H19/VT52 escape sequences.  These will be interpreted  ac-
cording	 to the table in section 9.10.	In addition, keys on the numeric
keypad will send H19/VT52 sequences unless you disable this  feature  in
some  way,  for instance by pressing Num Lock on the IBM PC keyboard, or
with key redefinitions.

    Caution: On some systems, such as the IBM PC and  XT,  Kermit-MS
    accesses  the  screen  memory memory directly to perform certain
    H19 emulation functions  such  as  character  insert/delete	 and
    screen scroll.  Without direct screen memory access, these func-
    tions  would  be  painfully	 slow.	 Although Kermit-MS has been
    tested successfully on a variety of monochrome and	color  adap-
    ters  and  monitors,  there	 may  be combinations for which this
    method could cause video problems, such as snow.	Should	this
    occur,  you	 can alleviate the problem by setting HEATH19 emula-
    tion OFF.  In that	case,  however,	 you  remove  not  only	 the
    problems,  but  also  the  desirable features of emulation.	 But
    Kermit-MS does permit you to load  an  external  console  device
    driver,  such  as  IBM's ANSI.SYS, to provide any desired screen
    control.

Here are the terminal emulation options for the systems	 presently  sup-
ported by Kermit-MS:

  System	 EscChar   Cabilities	Terminal Service
  IBM PC, XT	   ^]	   R M P K	Heath19 emulation
  DEC Rainbow	   ^]	   R   P K	VT102 firmware
  HP-150	   ^]	   R		HP-2623 firmware
  Wang PC	   ^A			Wang firmware
  Generic DOS	   ^]			Depends on system

Under Capabilities, R means rollback, M means mode line, P means printer
control, and K means key redefinition.

IBM PC/XT Kermit can disable Heath-19 emulation and use an external con-
sole device driver like ANSI.SYS instead.

When you first issue the CONNECT command, a message (on some systems, an
								      101


inverse	 video	"mode line") will display the most important facts about
the connection you've just established, so that you can quickly diagnose
any problems.  The items displayed in the mode line include  the  escape
character,  port number, the baud rate, the parity, the echo, and how to
get help, for instance:

+---------------------------------------------------------------------+
|EscChar:^],Port:1,Baud:9600,Parity:None,Echo:Remote,Type ^]? for Help|
+---------------------------------------------------------------------+

The escape character is used to regain the attention of Kermit-MS.  When
you type the escape character, Kermit-MS waits for you to follow it with
a single character command.  For instance, the	single-character-command
"?"  produces  a  list	of  available single character commands, such as
this:

  ?   Help -- prints the available single-character commands.
  C   Close the connection and return to Kermit-MS prompt level.
  S   Show the status of the connection.
  B   Send a BREAK signal to the port.
  0   (the digit zero) Send a NUL (ASCII 0) to the port.
  Q   Temporarily quit logging the remote session.
  R   Resume logging the remote session.
  M   Toggle the mode line, i.e. turn it off if it is on & vice versa.
  ^]  (or whatever you have set the escape character to be)
      Typing the escape character twice sends one copy of it to the con-
      nected host.

Typing any other character (except the space bar,  which  is  the  "null
command")  after  the escape character will cause Kermit-MS to beep, but
will do no harm.  The escape character can be changed to something other
than Control-Rightbracket by using the SET ESCAPE command.

Kermit-MS includes several advanced features  for  use	during	terminal
emulation,  including  screen scroll, printer control, and key redefini-
tions.


Screen Scroll

Kermit-MS provides several pages of screen memory, which may be scrolled
up and down using keys as follows:

  Function	      IBM PC/XT	  Rainbow	    HP-150
  Screen Down	      PgDn	  PrevScreen	    Prev
  Line Down	      Ctrl-PgDn	  Ctrl-PrevScreen   Shift-UpArrow
  Screen Up	      PgUp	  NextScreen	    Next
  Line Up	      Ctrl-PgUp	  Ctrl-NextScreen   Shift-DownArrow
  Top of Memory	      Home
  Bottom of Memory    End

There is presently no way to assign these functions to other keys.
								      102


Printer Control

A  locally  attached  printer may be controlled in the normal manner, on
most systems.  Pushing the "Print Screen" key (shifted on some	systems)
will  cause the current contents of the screen to be printed or spooled;
holding down CTRL while depressing Print Screen will start or  stop  the
spooling  of  incoming	characters to the printer.  ^P or ^N are sent to
the host during terminal emulation, and do not toggle printing, as  they
do when you're talking directly to DOS.

CTRL-Print-Screen  can be simulated with the Kermit-MS LOG PRN and CLOSE
commands.


Key Redefinitions

Key redefinitions are useful for defining "keystroke  macros"  of  login
sequences,  frequently issued commands, and so forth, and for setting up
the terminal for use with host resident software designed to  work  with
terminals  that send predefined sequences from their function keys.  For
instance, here's a key redefinition file for arranging the  DEC	 Rainbow
keyboard into the normal ASCII keyboard layout:

  ; Make shift-comma send a left angle bracket
  set key scan 556
  <
  ; Shift-period sends a right angle bracket
  set key scan 558
  >
  ; Accent grave is where ESC is supposed to be
  set key scan 96
  \33
  ; Put accent grave on the ESC function key
  set key f11
  `

The  SET  KEY  facility may be used provide the PC with a "meta" key for
use with editors like EMACS or TVEDIT that can use "meta characters"  as
commands.   A meta key is a shift key whose effect is to turn on the 8th
(parity) bit of the character.	For instance, on the  IBM  PC  the  scan
codes  produced	 by  holding  down  ALT	 together with other keys can be
determined using SHOW KEY, and then 8-bit ASCII equivalents with the 8th
bit turned on can be defined using SET KEY; if the scan code produced by
typing ALT-a, i.e. the letter "a" (ASCII 141, octal) with  the	ALT  key
held  down,  is	 2078  (decimal),  you	would set the META equivalent to
141+200=341 (octal), or "\341" in octal SET KEY notation:

  Kermit-MS>sho key
  Press a key: ALT-a
    Scan Code: 2078
    Definition:
  Kermit-MS>set key scan 2078
  Definition String: \341

Whenever you type ALT-a with this definition in effect,	 Kermit-MS  will
transmit octal 341, rather than 141.
								      103


9.5. Installation of Kermit-MS

		 by Bill Catchings, Columbia University

If you already have Kermit on your PC, you can use it to obtain new ver-
sions  of Kermit-MS when they appear on the central system at your site.
If you do not have Kermit or any other reliable file capture facility on
your PC, and there is no one from whom you can borrow a floppy	disk  to
copy  Kermit,  then  you  should read the following instructions for in-
itially "bootstrapping" Kermit-MS from a mainframe where  it  is  stored
onto your microcomputer.

There  are  at	least  three methods of initially getting Kermit-MS onto
your PC:

   1. Try again to find a copy on diskette.

   2. Use another file capture facility to get it.

   3. Type in and run a bootstrapping program.


9.5.1. Try Again To Find A Kermit Disk

Before explaining how to bootstrap Kermit onto	your  PC,  a  disclaimer
must  be made.	Although a fair amount of thought and time has gone into
these procedures, they are far from error free.	 If they were foolproof,
there would be no need for a protocol such as Kermit.	There  are  many
places	where  things can go wrong, from something as simple as a typing
mistake to something as unavoidable and probably inevitable  as	 a  com-
munications  line  failure.   By far the easiest and best way to install
Kermit is from a floppy disk.  Before you embark on any of the following
procedures it is a good idea to check once again for a diskette to copy,
even it it contains an old version  of	Kermit.	   The	time  you  spend
searching  is  likely to be far less frustrating than the time you spend
trying to bootstrap Kermit by the methods described below.


9.5.2. Bootstrapping From the Communication Line

If you can't find a diskette with Kermit on  it,  there	 are  two  other
methods	 available  for	 bootstrapping	MS-DOS Kermit onto your PC.  The
first method is to use a file capture  method  or  other  file	transfer
protocol  to  transfer	the file to your PC.  Some systems come supplied
with facilities like this,  and	 various  public  domain  or  commercial
packages  are available.  The second method requires you to type in your
own downloading program.

In either case, you must transmit the file  from  the  system  where  it
resides	 over a communication line and into your PC.  Since version 2 of
MS-DOS Kermit is much larger  than  version  1,	 it  comes  with  a  new
bootstrapping  procedure in which the executable program is encoded much
more compactly than in the earlier "fix" files.	 The new encoding  packs
3 .EXE file bytes into 4 printable characters in the MSxxx.BOO file, and
also compresses adjacent zero bytes (of which there are many).	The .BOO
file  contains only printable characters, to ensure that downloading can
								      104


take place regardless of parity or other peculariaries of the communica-
tion channel.


9.5.2.1. Use An Existing File Capture Facility

In  the rest of this discussion of bootstrapping, the host-resident boot
X.BOO file will be referred to as MSKERMIT.BOO.	In fact, the actual name
will depend on which MS-DOS system you are using -- MSIBMPC.BOO for  the
IBM PC or XT, MSRB100.BOO for the Rainbow 100, etc.

Use  your  file	 capture  facility,  whatever it may be, to get the file
MSKERMIT.BOO onto your PC's disk, but first make sure  you  have  enough
room  for  it.	  Once	the file is on your disk, you must run the BASIC
program MSPCTRAN.BAS to decode the file	 back  into  KERMIT.EXE.    This
program can be downloaded by the same method you used with MSKERMIT.BOO.
The  program  looks  on	 your  current	disk  and directory for the file
MSKERMIT.BOO and outputs KERMIT.EXE to the same place.	  KERMIT.EXE  is
about 80K bytes, so make sure there is space for it on your disk or else
you  will  have	 to start the program over.  Since the program will take
about twenty minutes to completely translate the file you will	want  to
avoid running it more than once.


9.5.2.2. Type In Your Own Bootstrap

If  you	 can't	find  some  method for downloading the .BOO file and the
BASIC program, the second way of bootstrapping	Kermit	is  to	use  the
programs   MSPCBOOT.BAS	  and  MSBOOT.FOR  to  download	 via  your  PC's
asynchronous port from your host and  translate	 it  directly,	"on  the
fly."	 You  run  the	program MSBOOT.FOR on your host and then run the
program MSPCBOOT.BAS in BASIC on your PC.  The FORTRAN program sends the
encoded .EXE file to the BASIC program, which decodes it and  stores  it
in  executable	form  on  your	current directory as KERMIT.EXE.  A very
rudimentary form of error checking is done to allow obviously  corrupted
records to be retransmitted.  Follow this procedure:

   1. First,  you  must	 establish a connection from your PC to the
      host system.   A	high  speed  connection	 is  preferable;  a
      "clean"  line is preferable to a noisy one.  In fact, a clean
      line is essential for this procedure.  You must  be  able	 to
      log  in  to the host system over this connection.	 If your PC
      already has a terminal emulation facility, use that.  If not,
      you might need to put your PC next to a real terminal and use
      that for logging in, then switch the connector to the  PC	 at
      the  crucial  moment.  If you are using a terminal, make sure
      the terminal and PC have their communication ports set to the
      same speed.

   2. Ensure that the files MSBOOT.FOR and MSKERMIT.BOO are present
      on the host system.  MSBOOT.FOR is listed below, in case	you
      need to type it in.

   3. Get  back	 to  your PC and type in MSPCBOOT.BAS on your PC; a
      listing appears below.  There is no need to type in the  com-
      ments (anything following an apostrophe); they are only there
								      105


      to  clarify  what the program is doing.  Check very carefully
      for errors.  You should check line 80 in the program  to	see
      that  it reflects the way your system is actually set up.	 If
      necessary, substitute the correct baud rate for the  supplied
      rate of 9600, and if you are not using COM1: make that change
      as  well.	  If you are downloading from an IBM or other half-
      duplex mainframe,	 leave	line  1000  as	it  is;	 otherwise,
      replace it by a RETURN statement.	 If you type it in directly
      to BASIC make sure you save the program before you run it, so
      you won't have to type it in again in case of error.

   4. Get  back	 to  your host system and compile MSBOOT.FOR, if it
      needs compiling.	Define logical unit numbers 5 and 6  to	 be
      the  controlling	terminal, and logical unit 7 to be the file
      MSKERMIT.BOO.  On VAX/VMS systems,  for  example,	 use  these
      commands:

	$assign sys$input for005
	$assign sys$output for006
	$assign mskermit.boo for007

      On a DECSYSTEM-20, do:

	@define 5: tty:
	@define 6: tty:
	@define 7: mskermit.boo

      On a DECsystem-10, do something like this:

	.assign tty: 5:
	.assign tty: 6:
	.assign dsk: 7:
	.rename for007.dat=mskerm.boo

      On an IBM system under VM/CMS, do this:

	.filedef 5 term ( lrecl 80 recfm v
	.filedef 6 term ( lrecl 80 recfm v
	.filedef 7 disk mskermit boo ( lrecl 77 recfm f perm

   5. Set your host system up for downloading:

	 - Ensure  that	 your terminal does not automatically pause
	   at the end of a screenful of output.	 For instance, on a
	   DEC-20 you would issue the command  "terminal  no  pause
	   end-of-page".

	 - Do  whatever	 you can to disable messages from appearing
	   at your terminal while these programs are running.  This
	   would include messages from other users, mail  notifica-
	   tion,  alarms  or alerts, system messages, and so forth.
	   Such messages will interfere	 with  the  procedure,	and
	   probably render the result useless.

	 - You	should	put  your  host terminal in "local echo" or
	   "half duplex" mode, if possible.
								      106


   6. Start the MSBOOT program on your host system.

   7. Get  back	 to  the  PC.	 If you have been using a terminal,
      switch the connector to the PC.

   8. Now run the BASIC program, MSPCBOOT.BAS.	This procedure will
      take at least twenty minutes and possibly longer depending on
      line speed.  Watch your modem and/or disk	 lights	 for  reas-
      surance that something is happening.

By  using one of these installation methods, you should now have a work-
ing version of Kermit.	If you experience any  problems	 or  quirky  be-
havior	with  the  program,  it's possible that some part of it was cor-
rupted during the downloading procedure.   Perhaps  enough  usable  code
remains	 to  allow  you to transfer MSKERMIT.EXE from the host.	 If not,
you will have to repeat the downloading procedure.

Once you have Kermit-MS on your disk, you should make the disk available
to other users for copying, so that they can be	 spared	 the  tedium  of
this bootstrap procedure.

Here  is  a  listing  of MSPCBOOT.BAS.	The "outdented" PRINT statements
with line numbers ending in 5 may  be  included	 if  you  want	incoming
records	 to  be	 displayed on the screen.  You don't need to include the
comments.

1    'Run this program on the PC in conjunction with a Fortran program
2    '(MSBOOT.FOR) on the mainframe to download Kermit to the PC.  This
3    'program will run for about thirty minutes, depending on line speed.
4    ' Bill Catchings, June 1984
5    ' Columbia University Center for Computing Activities

10   t$ = time$				' Save the time.
20   defint a-z				' All integer to gain some speed.
30   n$ = chr$(0)
40   z = asc("0")
50   t = asc("~")-z
60   def fnuchr%(a$)=asc(a$)-z
70   open "com1:9600,s,7,1,cs,ds,cd" as #1

100  print#1,"O ,"			' Char constants "O", " " and ","
110  input#1,f$
120  if len(f$) < 5 then goto 110	' In case the host echos the ACK.
130  input#1,n
135 print f$+" "+str$(n)
140  if n > 20 then goto 900
150  open f$ for output as #2
160  print "Outputting to "+f$
170  goto 300				' Correct version of the file.

200  gosub 1000				' Do turnaround char processing
210  print#1,"NO"			' Tell host data was incorrect.
220  goto 320
								      107



300  gosub 1000				' Do turnaround char processing
310  print#1,"OK"			' Say the line was all right.
320  input#1,x$
330  if len(x$) < 5 then goto 320	' In case the host echos ACK/NAK
340  input#1,n
345 print x$+" "+str$(n)
350  if len(x$) <> n then goto 200	' Length doesn't match, NAK it.
360  if x$ = "&&&&&&&&&&" then goto 800 ' End of file?
370  y$ = ""				' Set output string to null.
380  goto 500

400  print#2,y$;			' Print the output string.
410  goto 300				' Go get another line.

500  if len(x$) = 0 goto 400		' Done with input string?
510  a = fnuchr%(x$)
520  if a = t then goto 700		' Null repeat character?
530  q$=mid$(x$,2,3)			' Get the quadruplet to decode.
540  x$=mid$(x$,5)
550  b = fnuchr%(q$)
560  q$ = mid$(q$,2)
570  c = fnuchr%(q$)
580  q$ = mid$(q$,2)
590  d = fnuchr%(q$)

600  y$ = y$ + chr$(((a * 4) + (b \ 16)) and 255) ' Decode the quad.
610  y$ = y$ + chr$(((b * 16) + (c \ 4)) and 255)
620  y$ = y$ + chr$(((c * 64) + d) and 255)
630  goto 500				' Get another quad.

700  x$ = mid$(x$,2)			' Expand nulls.
710  r = fnuchr%(x$)			' Get the number of nulls.
715 print " Null: ",r
720  x$ = mid$(x$,2)
730   for i=1 to r			' Loop, adding nulls to string.
740   y$ = y$ + n$
750   next
760  print#2,y$;			' Print the nulls.
770  y$ = ""				' Clear the output buffer.
780  goto 500

800  print "Processing complete, elapsed time: "+t$+" to "+time$
810  print "Output in "+f$
820  close #1,#2
830  goto 9999

900  print "?The format of the BOO file is incorrect"
910  goto 820

1000 x$ = input$(1,#1)		' Make this line RETURN for full-duplex
1010 if x$ <> chr$(17) then goto 1000	' Loop for a turn around char.
1020 return

9999 end
								      108


Here  is a listing of MSBOOT.FOR, in case you can't find it on your host
system:

C     This Fortran program should be run on the mainframe in conjunction
C     with a Basic program (MSPCBOOT.BAS) on the PC to transfer
C     MSKERMIT.BOO to the PC and translate it into KERMIT.EXE.	This
C     program uses a very rudimentary technique to try to insure that
C     the characters it sends arrive correctly.	 It just sends a count
C     of the number of characters sent after each line.	 In this way any
C     errors of character loss or insertion will be caught.  If a
C     character is just corrupted it will not be caught.  Hopefully if
C     this happens it will be in a non-critical part of the KERMIT.EXE
C     file.  The reason a simple checksum was not used was so that this
C     program could run on machines using either EBCIDIC or ASCII
C     characters.  This program should take about thirty minutes to run.

C     This program assumes that 5 and 6 are directed to the terminal and
C     7 is directed to the file MSKERMIT.BOO.

      INTEGER LINE(77), ACK(4), CHECK, OK, SPACE, COMMA

      WRITE(6,100)
100   FORMAT(' Ready to transfer data, now run MSPCBOOT.BAS on the PC.')

C     Get characters for constants (character constants are rough in
C     some FORTRANs!)
      READ (5,200) OK, SPACE, COMMA, ACK
200   FORMAT(4A1)
      GO TO 30

C     Get terminal handshake.
10    READ (5,200)ACK

C     Did the other side like it?  (Did they send OK?)
      IF (ACK(1) .NE. OK) GO TO 50

C     Yes, get new line from file.
20    READ (7,300,END=99)LINE
300   FORMAT(77A1)

C     Count the characters as some rudimentary check for noise.
      I = 1
30    IF (LINE(I) .EQ. SPACE) GO TO 40
      I = I + 1
      GO TO 30

C     Put in a comma followed by the count.
40    LINE(I) = COMMA

C     Write to TTY.
50    WRITE (6,400)LINE,I-1
400   FORMAT(' ',77A1,I2)
      GOTO 10
								      109



C     Send good-bye message.
99    WRITE (6,500)
500   FORMAT(' ',10('&'),',10')

      STOP
      END


9.6. Compatibility with Older Versions of MS-DOS Kermit

MS-DOS	Kermit	supports  many	different systems.  Like CP/M-80 KERMIT,
this support was added to the program piecemeal, at  many  sites,  using
conditional assembly.  However, before allowing the program to grow into
a  complicated	monolith like CP/M-80 KERMIT, we have broken the program
up into separate modules,  with	 system	 dependencies  isolated	 into  a
single	module consisting of compact collections of low-level primitives
for console and port i/o.

The last monolithic (single source file) release of  MS-DOS  Kermit  was
1.20.	 To this and earlier versions was added support for systems like
the Seequa Chameleon, the Tandy 2000, the Victor 9000, the  Heath/Zenith
100,  and  others.    Eventually,  support  for these systems may be in-
tegrated with the new modular version.	Meanwhile, implementations based
on these old versions will have at least the  following	 incompatibilies
from the version described here:

   - RECEIVE filespec is used instead of GET filespec.	There is no
     GET  command  in  older  versions, and no way to specify a new
     name for an incoming file.
   - No LOCAL or REMOTE commands.
   - No 8th-bit	 prefixing,  repeat  counts,  CRCs  or	2-character
     checksums.
   - No TAKE or initialization files.
   - No command macros or command line arguments.
   - No terminal session logging.

and others, depending on the specific version.


9.7. What's Missing

Kermit-MS  has plenty of room for improvement.	Features that need to be
improved or added include:

   - A built-in facility for sending files "raw" to the remote sys-
     tem, obeying current settings for parity, flow control,  hand-
     shake,  and so forth.  This might include a script interpreta-
     tion  facility  to	 allow	remote	sessions  to  be  conducted
     automatically.  For the present, this can be accomplished with
     a	user-supplied  program	invoked with the Kermit-MS RUN com-
     mand.

   - Additional	 functionality	when   running	 in   server   mode
     --	 directory  listings,  file deletion, execution of DOS com-
     mands, etc.
								      110


   - More commands when talking to remote servers -- REMOTE RENAME,
     COPY, STATUS, WHO, etc.

   - Filename  conversion  options  (normal form, handling of fully
     qualified filespecs, etc.).

   - Transaction file logging.

   - Improved command parsing; for instance, accept default  values
     for omitted trailing fields.

   - A better built-in help facility.

   - Support for Kermit file attribute packets.

   - The Kermit-MS program is quite large.  Much of the size is due
     to	 the deliberate decision to provide support for versions of
     MS-DOS prior to 2.0.  At some point, this	support	 should	 be
     removed.	 This  will not only reduce the size of the program
     considerably, but also provide much more flexibility.


9.8. Program Organization

Kermit-MS version 2 is composed of separate assembler source files,  as-
sembled separately, and linked together.  The modules are:

System/Device Independent:

    MSKERM.ASM	  Main program
    MSSEND.ASM	  File sender
    MSRECV.ASM	  File receiver
    MSSERV.ASM	  Server operation
    MSFILE.ASM	  File i/o
    MSCMD.ASM	  Command parser
    MSTERM.ASM	  CONNECT command
    MSCOMM.ASM	  Communications port buffering & flow control
    MSSET.ASM	  SET, SHOW, and STATUS commands
    MSDEFS.H	  Data structure definitions and equates

System/Device Dependent:

    MSXxxx.ASM	  System-dependent code for system xxx
    MSYxxx.ASM	  System-dependent screen and keyboard code
    MSZxxx.ASM	  Modem control (modem-dependent)

The  modular  organization  allows  easier  modification of the program,
quicker transfer  of  modified	portions  from	system-to-system.    The
modules	 are  designed	to be well-defined and self-contained, such that
they can be easily replaced.  For instance, someone who prefers	 windows
and  mice  to  typing  commands could replace the command parsing module
without having to worry about the effect on the other modules.

To assemble any of the kermit modules, file  MSDEFS.H  must  be	 on  the
default disk.
								      111


All  the  Kermit  implementations  require  the	 modules  MSCMD, MSCOMM,
MSFILE, MSKERM, MSRECV, MSSEND, MSSERV, MSSET, MSTERM.

The IBM PC version requires MSXIBM and MSYIBM as well.

The Rainbow version requires MSXRB and MSXDMB; MSXDMB must be the  first
object	file  given  to	 the  linker for Kermit to link properly for the
Rainbow.

The HP150 version requires MSXHP150, the Wang version  requires	 MSXWNG,
and the generic version requires MSXGEN.

Once  all the required object modules exist, they may be linked together
to produce Kermit.  For example, on the Rainbow:

  A>link

     Microsoft Object Linker V2.00
  (C) Copyright 1982 by Microsoft Inc.

  Object Modules [.OBJ]: msxdmb mskerm msxrb mscomm msset mssend +
  msrecv msserv msfile msterm mscmd
  Run File [MSXDMB.EXE]: kermit
  List File [NUL.MAP]: kermit

  A>


9.9. Adding Support For New Systems

You can bring Kermit-MS to systems that are not explicitly supported  in
one  of two ways -- attempt to run the "generic" MS-DOS Kermit on it, or
add explicit code to support your system.


9.9.1. Generic MS-DOS Kermit

To get started with Kermit on a new system, try running "generic" MS-DOS
Kermit; in many cases, it will run as  is.    The  generic  version  ac-
complishes  all	 its  port and console i/o through DOS calls, and during
terminal connection does not attempt to emulate any particular	kind  of
terminal.    In	 some  cases, the generic version may still require some
fiddling to run on a new system; for instance, different  systems  refer
to  their  communication  ports in different ways -- COM1, AUX, etc.  It
attempts to do this automatically by trying various DOS file handles for
the communication port, and asking you to supply  one  if  it  does  not
succeed.

Generic MS-DOS Kermit will probably run no faster than 1200 baud, and it
only works with DOS 2.0 or later.
								      112


9.9.2. Adding System-Dependent Code

The following is a guide to the system dependent module of Kermit-MS.


	   SPECIFICATION FOR KERMIT SYSTEM-DEPENDENT MODULES

		  by Jeff Damens, Columbia University

All  the system-independent global data structures used in Kermit-MS are
defined in the file MSDEFS.H.

The routine MSXxxx.ASM contains system-dependent support for system xxx,
except for terminal emulation, which is in MSXxxx.ASM, described below.

The routines in the MSX module may change any registers	 but  the  stack
pointer	 and  segment registers, unless otherwise noted.  A routine that
returns via a RET instruction is said to return normally; a routine that
skip returns is one that returns to three bytes past the  normal  return
address.

Global variables that must be defined in the system-dependent module:

XXOFSNT		byte.	This should be set to a non-zero value if we are
		doing flow control and have sent an  XOFF  character  to
		the remote host, zero otherwise.

MACHNAM		byte.	 A  $-terminated  string identifying the machine
		this version of Kermit is for; it is printed when Kermit
		starts up.

SETKTAB		byte.  A keyword table associating terminal key names to
		16-bit scan code values, used in the  set  key	command.
		If  the	 kermit	 version  can  accept  arbitrary decimal
		values as scan codes, the word "SCAN" should  appear  in
		the  table with a scan value of -1.  If key redefinition
		is not implemented, the first byte of the  table  should
		be a zero.

SETKHLP		byte.	 A  $-terminated  string to be printed when ? is
		typed in the SET KEY command.  This is usually simply  a
		list  of  the  key  names  in  SETKTAB.	 SETKHLP must be
		defined even if key redefinition is not implemented,  to
		satisfy	 the  linker;  if key redefinition is not imple-
		mented, SETKHLP will never be displayed.

COUNT		word.  The number of  characters  in  the  serial  input
		buffer,	 if  known.  This is how Kermit knows to send an
		XON if the serial handler has sent an XOFF.  If the num-
		ber of characters  in  the  buffer  isn't  known,  COUNT
		should be 0.

These  are  the required entry points for the system dependent dependent
module MSXxxx.ASM.
								      113


SERINI

Parameters	None.

Returns		Normally, no return value.

Description	Perform	 any initialization that must be done before the
		serial port can be used, including  setting  baud  rate,
		interrupt  vectors, etc.  Parity and baud rate should be
		set according to the values in the  PORTINFO  structure.
		The  external  variable	 PORTVAL  points to the PORTINFO
		structure for the current port.	   Calling  SERINI  more
		than  once  without an intervening call to SERRST should
		have no effect.


SERRST

Parameters	None.

Returns		Normally, no return value.

Description	Undoes any  initialization  done  by  SERINI,  including
		resetting  the serial port, restoring any interrupt vec-
		tors changed by SERINI, etc.   Calling	this  more  than
		once  without  an  intervening	call to SERINI should be
		harmless.


CLRBUF

Parameters	None.

Returns		Normally, no return value.

Description	Remove and discard from the serial port's  input  buffer
		any characters sent by the remote host that have not yet
		been  read  by Kermit, and set COUNT to 0.  This is used
		before a file transfer to flush NAK's that accumulate in
		the buffer when the remote host is in server mode.
								      114


OUTCHR

Parameters	A character in AH.

Returns		Skip  returns  if  the	character  has been transmitted;
		returns normally if the character can not be transmitted
		because of a hardware error.

Description	Sends the character in AH  out	the  currently	selected
		serial	port.	 OUTCHR can assume that SERINI will have
		been called previously. OUTCHR should call the	external
		routine	 DOPAR to set the parity of the character if the
		communications	hardware   doesn't   automatically   set
		parity.	   Flow	 control should be honored; the external
		variable PORTVAL contains a pointer to a PORTINFO struc-
		ture (as defined in  MSDEFS.H)	containing  the	 current
		flow control definitions.


COMS

Parameters	None.

Returns		Normally  if  a parse error is encountered, skip returns
		otherwise.

Description	Called by the SET PORT command.	 On a machine with  mul-
		tiple  serial  ports,  COMS should parse for the name or
		number of a serial port and make that the port	used  by
		succeeding  calls to SERINI, PRTCHR, OUTCHR, and SERRST.
		It should set the external variable PORTVAL to point  to
		one  of the external port structures PORT1 or PORT2, and
		set COMFLG in the FLAGS structure to 1 for port	 one,  0
		for  port  2.	 For  implementations  that use only one
		serial port, COMS should print a message to that  effect
		and skip return.


VTS

Parameters	None.

Returns		Normally  if  a parse error is encountered, skip returns
		otherwise.

Description	Parses for an ON or OFF, sets HEATH-19	emulation  while
		in terminal emulation appropriately.  The VTFLG field of
		the  FLAGS  structure should be set non-zero if HEATH-29
		emulation is on, zero otherwise.  If HEATH-19  emulation
		is not done, VTS should print a message and skip return.
								      115


DODEL

Parameters	None.

Returns		Normally, no return value.

Description	Erases the character immediately to the left of the cur-
		sor from the screen, then backs up the cursor.


CTLU

Parameters	None.

Returns		Normally, no return value.

Description	Move the cursor to the left margin, then clear the line.


CMBLNK

Parameters	None.

Returns		Normally, no return value.

Description	Clears the screen and homes the cursor.


LOCATE

Parameters	None.

Returns		Normally, no return value.

Description	Homes the cursor.


LCLINI

Parameters	None.

Returns		Normally, no return value.

Description	Performs any system-dependent initialization required by
		this implementation.
								      116


PRTCHR

Parameters	None.

Returns		Normally,  with	 the  next  character from the currently
		selected serial port in AL.  Skip returns if no	 charac-
		ter is available.

Description	Reads  the  next character from the current serial port.
		PRTCHR can assume SERINI has been called previously, and
		should handle flow control correctly.


DOBAUD

Parameters	None.

Returns		Normally, no return value.

Description	Sets the baud rate for the current port.  The baud  rate
		should	be  obtained from the BAUD field of the PORTINFO
		structure, pointed to by the external variable PORTVAL.


CLEARL

Parameters	None.

Returns		Normally, no return value.

Description	Clears from the cursor to the end of the current line.


DODISK

Parameters	None.

Returns		Normally, no return value.

Description	Sets the external variable DRIVES to the number of  disk
		drives attached to the machine.


GETBAUD

Parameters	None.

Returns		Normally, no return value.

Description	Store  current	baud rate of the currently selected port
		in the BAUD field of  the  current  PORTINFO  structure,
		which  is pointed to by PORTVAL.  If the baud rate is to
		default to a particular value, this  routine  can  store
		that value into the BAUD field instead.
								      117


BEEP

Parameters	None.

Returns		Normally, no return value.

Description	Rings the terminal bell.


PUTHLP

Parameters	A pointer to a string in AX.

Returns		Normally, no return value.

Description	Writes	the  null-terminated  string  given in AX to the
		terminal.  This is used to display help and status  mes-
		sages.	The IBM and Rainbow versions write the string in
		a reverse video box.


PUTMOD

Parameters	A pointer to a string in AX.

Returns		Normally, no return value.

Description	Writes	the  null-terminated  string  given in AX to the
		last line of the screen, in inverse video if possible.


CLRMOD

Parameters	None.

Returns		Normally, no return value.

Description	Clears the line written by PUTMOD.


POSCUR

Parameters	Row in DH, column in DL.

Returns		Normally, no return value.

Description	Positions the cursor to the row and column given in  DX.
		Rows and columns both originate at 0 (not 1!).
								      118


SENDBR

Parameters	None.

Returns		Normally, no return value.

Description	Send a break to the current serial port.


SHOWKEY

Parameters	Pointer	 to  a	terminal  argument block in AX (see TERM
		below).

Returns		Normally, with a string pointer in AX and the length  of
		the string in CX.

Description	Called	by  the	 SHOW KEY command.  Reads a key from the
		terminal    and	   returns    a	   string     containing
		implementation-dependent  information  about the key. In
		the  usual  case,  the	 string	  contains   the   key's
		(machine-dependent)  scan code, and the key's definition
		(if any) from the terminal argument block.   The  length
		of  the	 returned  string should be returned in CX.  The
		string may contain any characters;  unprintable	 charac-
		ters  will be quoted when the string is printed.  If the
		implementation does not support key redefinition,  SHOW-
		KEY may return a static string saying so.


TERM

Parameters	Pointer to terminal argument block in AX.

Returns		Normally, no return value.

Description	Do terminal emulation, based on argument block described
		below...




The  terminal emulator is supplied in the file MSYxxx.ASM.  The terminal
argument block passed to the terminal emulator has the following fields:

FLGS		Byte containing flags.	Flags are:

		SCRSAM (80H)	If on, the terminal  emulator  shouldn't
				re-display the screen when entered.

		CAPT (40H)	Capture	 output.    If	on,  the routine
				passed in field	 CAPTR	is  called  with
				each character sent to the screen.

		EMHEATH (20H)	Emulate a Heath-19 terminal if on.
								      119


		HAVTT (10H)	A key redefinition table is present.

		TRNCTL (08H)	Print  control character X as ^X (useful
				for debugging).

		MODOFF (04H)	Do not display emulator mode line if on.

		LCLECHO (01H)	Echo keyboard characters on  the  screen
				in addition to sending them to the port.

PRT		Port  to  use  for terminal emulation, used only in mode
		line.  This is just a copy of COMFLG in FLAGS.

COLS		Number of columns on screen.

ROWS		Number of rows on screen.

CAPTR		Routine to call to  with  each	character  sent	 to  the
		screen if CAPT flag is on.  Characters are passed in AL.

BELLD		Bell divisor (used only on IBM).

KLEN		Number	of keys in key redefinition table, if HAVTT flag
		is on.

KTAB		Address of key redefinition table.  The key redefinition
		table is a table  of  KLEN  16-bit  scan  codes.    Each
		(machine  dependent)  scan code represents a key that is
		redefined.

KRPL		Address of key replacement table.  The	key  replacement
		table  parallels  the  key  redefinition  table given in
		KTAB.  Entries	in  the	 replacement  table  are  16-bit
		pointers  to  redefinitions.	Each  redefinition has a
		one-byte length, followed by the definition.

ESCC		Escape character (single byte).	 When this character  is
		typed to the emulator, it should return.

BAUDB		byte.	 Bits  describing  the	baud  rate  so it can be
		printed on the mode line.  This is a copy  of  the  BAUD
		field in the PORTINFO structure.  Currently used only on
		the IBM.  See MSDEFS.H for possible values.

PARITY		byte.	 Current parity to print on the mode line.  This
		is a copy of PARFLG in the  PORTINFO  structure.    Cur-
		rently	used only on the IBM.  See MSDEFS.H for possible
		values.
								      120


9.10. Heath/Zenith-19 Control Codes

The  Heath/Zenith-19  terminal is equivalent to the DEC VT52 with exten-
sions for line and character insertion and deletion.  Items marked  with
an asterisk are not currently supported by Kermit-MS H19 emulation.


Cursor Functions

  Sequence    Mnemonic	  Definition
   ESC H      HCUH	  Cursor Home
   ESC C      HCUF	  Cursor Forward
   ESC D      HCUB	  Cursor Backward
   ESC B      HCUD	  Cursor Down
   ESC A      HCUU	  Cursor Up
   ESC I      HRI	  Reverse Index
  *ESC n      HCPR	  Cursor Position Report
  *ESC j      HSCP	  Save Cursor Position
  *ESC k      HRCP	  Set Cursor to Previously Saved Position
   ESC Y      HDCA	  Direct Cursor Addressing, 1-based:
			  31+line# 31+col# (same as VT52)



Erasing and Editing

  Sequence    Mnemonic	  Definition
   ESC E      HCD	  Clear Display (Shift Erase)
   ESC b      HBD	  Erase Beginning of Display
   ESC J      HEOP	  Erase to End of Page (Erase Key)
   ESC l      HEL	  Erase Entire Line
   ESC o      HEBL	  Erase Beginning of Line
   ESC K      HEOL	  Erase to End of Line
   ESC L      HIL	  Insert Line
   ESC M      HDL	  Delete Line
   ESC N      HDCH	  Delete Character
   ESC @      HEIM	  Enter Insert Character Mode
   ESC O      HERM	  Exit Insert Character Mode



Configuration

  Sequence    Mnemonic	  Definition
  *ESC z      HRAM	  Reset to Power-Up Configuration
  *ESC r Bn   HMBR	  Modify Baud Rate: Bn=
			    A=110,  B=150,  C=300, D=600,  E=1200,
			    F=1800, G=2000, H=2400, I=3600, J=4800,
			    K=7200, L=9600, M=19200

  *ESC x Ps   HSM	  Set Mode(s): Ps=
			    1 = Enable 25th line
			    2 = No key click
			    3 = Hold screen mode
			    4 = Block cursor
			    5 = Cursor off
								      121


			    6 = Keypad shifted
			    7 = Alternate keypad mode
			    8 = Auto line feed on CR
			    9 = Auto CR on line feed

  *ESC y Ps   HRM	  Reset mode(s): Ps=
			    1 = Disable 25th line
			    2 = Enable key click
			    3 = Exit hold screen mode
			    4 = Underscore cursor
			    5 = Cursor on
			    6 = Keypad unshifted
			    7 = Exit alternate keypad mode
			    8 = No auto line feed
			    9 = No auto CR

  *ESC <      HEAM	  Enter ANSI Mode



Modes of Operation

  Sequence    Mnemonic	  Definition
  *ESC [      HEHS	  Enter Hold Screen Mode
  *ESC \      HXHS	  Exit Hold Screen Mode
   ESC p      HERV	  Enter Reverse Video Mode
   ESC q      HXRV	  Exit Reverse Video Mode
  *ESC F      HEGM	  Enter Graphics Mode
  *ESC G      HXGM	  Exit Graphics Mode
  *ESC t      HEKS	  Enter Keypad Shifted Mode
  *ESC u      HXKS	  Exit Keypad Shifted Mode
  *ESC =      HAKM	  Enter Alternare Keypad Mode
  *ESC >      HXAM	  Exit Alternate Keypad Mode



  Additional Operations

  Sequence    Mnemonic	  Definition
  *ESC }      HDK	  Keyboard Disable
  *ESC {      HEK	  Keyboard Enable
   ESC v      HEWA	  Wrap Around at End of Line
   ESC w      HXWA	  Discard at End of Line
   ESC Z      HID	  Identify as VT52 (ESC / K)
  *ESC ]      HX25	  Transmit 25th Line
  *ESC #      HXMP	  Transmit Page

  The  Heath-19	 transmits  the	 following  sequences,	but  it will not
  respond to them if they are received.	 Kermit-MS  will  transmit  them
  only if they are programmed with SET KEY.

   ESC S      HF1	  Function Key #1
   ESC T      HF2	  Function Key #2
   ESC U      HF3	  Function Key #3
   ESC V      HF4	  Function Key #4
   ESC W      HF5	  Function Key #5
								      122


   ESC P      HF7	  Function Key #7
   ESC Q      HF8	  Function Key #8
   ESC R      HF9	  Function Key #9
								      123


Index

						RECEIVE	  86
	  ANSI.SYS   94				Recognition   83
	  Autodialer   100
						Smart Modem   100
	  Batch Operation of Kermit-MS	 84
	  Baud Rate   92			Telenet	  95
	  Beeper   92				Timeout	  98
	  Bell	 92
	  Binary Files	 96			Warning	  98
						Wildcard   81
	  Cancelling a File Transfer   86, 87
	  Command Files	  98
	  Command Macro	  99
	  CONNECT   100
	  Control-X   86, 87
	  Control-Z   86, 87

	  Debugging   93
	  DEFINE   99
	  DO Command   99

	  Eighth-Bit Prefix   96
	  EMACS	  102
	  End Of File	82, 93
	  EOF	93
	  Escape Character for CONNECT	 94

	  File Renaming	  98
	  File Warning	 98
	  Flow Control	 94

	  Generic MS-DOS Kermit	  111

	  Handshake   94
	  Heath-19 Terminal Emulation	94, 100,
		  120
	  Help	 83

	  IBM PC   79
	  Incomplete File Disposition	87

	  Key Redefinition   95

	  Local Echoing	  95

	  Macro	  99
	  META Key   102
	  Mode Line   100
	  Modem	  100
	  MS-DOS   79

	  Parity   95
	  PC-DOS   79
									i


			   Table of Contents

9. MS-DOS KERMIT						      79

9.1. The MS-DOS File System					      80
     9.1.1. File Specifications					      80
     9.1.2. File Formats					      81
9.2. Program Operation						      82
9.3. Kermit-MS Commands						      84
     9.3.1. Commands for File Transfer				      85
     9.3.2. Commands for Connecting and Disconnecting		      88
     9.3.3. Commands for File Management			      89
     9.3.4. The SERVER Command					      91
     9.3.5. The SET Command					      92
     9.3.6. The SHOW Command					      98
     9.3.7. Command Macros					      99
9.4. Terminal Emulation						     100
9.5. Installation of Kermit-MS					     103
     9.5.1. Try Again To Find A Kermit Disk			     103
     9.5.2. Bootstrapping From the Communication Line		     103
	  9.5.2.1. Use An Existing File Capture Facility	     104
	  9.5.2.2. Type In Your Own Bootstrap			     104
9.6. Compatibility with Older Versions of MS-DOS Kermit		     109
9.7. What's Missing						     109
9.8. Program Organization					     110
9.9. Adding Support For New Systems				     111
     9.9.1. Generic MS-DOS Kermit				     111
     9.9.2. Adding System-Dependent Code			     112
9.10. Heath/Zenith-19 Control Codes				     120

Index								     123
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 mskermit.doc
	/bin/echo -n '	'; /bin/ls -ld mskermit.doc
fi
/bin/echo 'Extracting msbuild.hlp'
sed 's/^X//' <<'//go.sysin dd *' >msbuild.hlp
To assemble any of the kermit modules, file MSDEFS.H must be on the
default disk. 

All the Kermit implementations require the modules
MSCMD, MSCOMM, MSFILE, MSKERM, MSRECV, MSSEND, MSSERV, MSSET, MSTERM.

The IBM PC version requires MSXIBM and MSYIBM as well.

The Rainbow version requires MSXRB and MSXDMB; MSXDMB must be the first
object file given to the linker for Kermit to link properly.

The HP150 version requires MSXHP150, the Wang version requires MSXWNG,
and the generic version requires MSXGEN.


Once all the required object modules exist, they may be linked together
to produce Kermit.  For example, on the Rainbow:

A>link

   Microsoft Object Linker V2.00
(C) Copyright 1982 by Microsoft Inc.

Object Modules [.OBJ]: msxdmb mskerm msxrb mscomm msset mssend msrecv +
msserv msfile msterm mscmd
Run File [MSXDMB.EXE]: kermit
List File [NUL.MAP]: kermit

A>

//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msbuild.hlp
	/bin/echo -n '	'; /bin/ls -ld msbuild.hlp
fi
/bin/echo 'Extracting msboot.for'
sed 's/^X//' <<'//go.sysin dd *' >msboot.for
C     This Fortran program should be run on the mainframe in conjunction
C     with a Basic program (MSPCBOOT.BAS) on the PC to transfer
C     MSKERMIT.BOO to the PC and translate it into KERMIT.EXE.  This
C     program uses a very rudimentary technique to try to insure that
C     the characters it sends arrive correctly.  It just sends a count
C     of the number of characters sent after each line.  In this way any
C     errors of character loss or insertion will be caught.  If a
C     character is just corrupted it will not be caught.  Hopefully if
C     this happens it will be in a non-critical part of the KERMIT.EXE
C     file.  The reason a simple checksum was not used was so that this
C     program could run on machines using either EBCDIC or ASCII
C     characters.  This program should take about thirty minutes to run.
C
C     This program assumes that 5 and 6 are directed to the terminal and
C     7 is directed to the file MSKERMIT.BOO.
C
C     Bill Catchings, Columbia University Center for Computing Activities
C     June 1984 (Revised September 1984)
C
      INTEGER LINE(77), ACK(4), CHECK, OK, SPACE, COMMA

      WRITE(6,100)
100   FORMAT(' Ready to transfer data, now run MSPCBOOT.BAS on the PC.')

C     Get characters for constants (character constants are rough in
C     some FORTRANs).
      READ (5,200) OK, SPACE, COMMA, ACK
200   FORMAT(4A1)
C     The following statement has been changed from GO TO 30.
      GO TO 20

C     Get terminal handshake.
10    READ (5,200)ACK

C     Did the other side like it?  (Did they send OK?)
      IF (ACK(1) .NE. OK) GO TO 50

C     Yes, get new line from file.
20    READ (7,300,END=99)LINE
300   FORMAT(77A1)

C     Count the characters as some rudimentary check for noise.
      I = 1
30    IF (LINE(I) .EQ. SPACE) GO TO 40
      I = I + 1
      GO TO 30

C     Put in a comma followed by the count.
40    LINE(I) = COMMA

C     Write to TTY.
50    WRITE (6,400)LINE,I-1
400   FORMAT(' ',77A1,I2)
      GOTO 10

C     Send good-bye message.
99    WRITE (6,500)
500   FORMAT(' ',10('&'),',10')

      STOP
      END
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 msboot.for
	/bin/echo -n '	'; /bin/ls -ld msboot.for
fi
/bin/echo 'Extracting mspcboot.bas'
sed 's/^X//' <<'//go.sysin dd *' >mspcboot.bas
1    'Run this program on the PC in conjunction with a Fortran program
2    '(MSBOOT.FOR) on the mainframe to download Kermit to the PC.  This
3    'program will run for about thirty minutes, depending on line speed.
4    ' Bill Catchings, June 1984 (Revised Sept 1984)
5    ' Columbia University Center for Computing Activities

10   t$ = time$				' Save the time.
20   defint a-z				' All integer to gain some speed.
30   n$ = chr$(0)
40   z = asc("0")
50   t = asc("~")-z
60   def fnuchr%(a$)=asc(a$)-z
70   open "com1:9600,s,7,1,cs,ds,cd" as #1

100  print#1,"O ,2"			' Char constants "O", " " and ","
110  input#1,f$
120  if len(f$) < 5 then goto 110	' In case the host echos the ACK.
130  input#1,n
135 print f$+" "+str$(n)
140  if n > 20 then goto 900
150  open f$ for output as #2
160  print "Outputting to "+f$
170  goto 300				' Correct version of the file.

200  gosub 1000				' Do turnaround char processing
210  print#1,"NO"			' Tell host data was incorrect.
220  goto 320

300  gosub 1000				' Do turnaround char processing
310  print#1,"OK"			' Say the line was all right.
320  input#1,x$
330  if len(x$) < 5 then goto 320	' In case the host echos ACK/NAK
340  input#1,n
345 print x$+" "+str$(n)
350  if len(x$) <> n then goto 200	' Length doesn't match, NAK it.
360  if x$ = "&&&&&&&&&&" then goto 800	' End of file?
370  y$ = ""				' Set output string to null.
380  goto 500

400  print#2,y$;			' Print the output string.
410  goto 300				' Go get another line.

500  if len(x$) = 0 goto 400		' Done with input string?
510  a = fnuchr%(x$)
520  if a = t then goto 700		' Null repeat character?
530  q$=mid$(x$,2,3)			' Get the quadruplet to decode.
540  x$=mid$(x$,5)
550  b = fnuchr%(q$)
560  q$ = mid$(q$,2)
570  c = fnuchr%(q$)
580  q$ = mid$(q$,2)
590  d = fnuchr%(q$)

600  y$ = y$ + chr$(((a * 4) + (b \ 16)) and 255) ' Decode the quad.
610  y$ = y$ + chr$(((b * 16) + (c \ 4)) and 255)
620  y$ = y$ + chr$(((c * 64) + d) and 255)
630  goto 500				' Get another quad.
	
700  x$ = mid$(x$,2)			' Expand nulls.
710  r = fnuchr%(x$)			' Get the number of nulls.
715 print " Null: ",r
720  x$ = mid$(x$,2)
730   for i=1 to r			' Loop, adding nulls to string.
740   y$ = y$ + n$
750   next
760  print#2,y$;			' Print the nulls.
770  y$ = ""				' Clear the output buffer.
780  goto 500

800  print "Processing complete, elapsed time: "+t$+" to "+time$
810  print "Output in "+f$
820  close #1,#2
830  goto 9999

900  print "?The format of the BOO file is incorrect"
910  goto 820

1000 x$ = input$(1,#1)		' Make this line RETURN for full-duplex
1010 if x$ <> chr$(17) then goto 1000	' Loop for a turn around char.
1020 return

9999 end//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 mspcboot.bas
	/bin/echo -n '	'; /bin/ls -ld mspcboot.bas
fi

csitc@ucbopal.CC.Berkeley.ARPA (10/08/84)

It is also possible to get source to ALL the Kermits (not just MSDOS)
by anonymous FTP to columbia-20.ARPA, with the usual "guest" password.
The files themselves are in  ps:<KERMIT>file.  They also have have
full documentation, including a manual to the protocol used in Kermit,
for those interested in writing their own versions....

(For those who may not know what Kermit is, it is essentially a file-
transfer protocol used for communications between micros and large
machines.  Most implementations also include some kind of terminal
emulation, such as for a vt52 or h/z19.)

wdn@hp2610.UUCP (10/29/84)

I'm having a problem with the ms-kermit sources that were posted to the
net recently.  The file msfile.asm will not assemble on the hp150.  Two
of the errors seem to be related to addressing modes (LEA instructions),
but the rest were undefined symbols.  The file seems to be corrupted
in the data declaration section.

PLEASE!  If anyone has a good copy of this file, will you send me a copy?

Thanks in advance...

				Bill Nestor
				hp St. Louis
				ihnp4!wuphys!hp2610!wdn