MS-DOS Kermit sources (Part 2 of 7)

Jim Knutson knutson at ut-ngp.UUCP
Sat Oct 6 02:04:03 AEST 1984


: 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 at 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 at 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 at 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



More information about the Comp.sources.unix mailing list