PCjs Machines

Home of the original IBM PC emulator for browsers.

Logo

PC-SIG Diskette Library (Disk #41)

[PCjs Machine "ibm5150"]

Waiting for machine "ibm5150" to load....

FILES41.TXT

---------------------------------------------------------------------------
Disk No 41    Kermit Communication System  Version 2.29              v2.3
---------------------------------------------------------------------------
MSCMD    ASM  Kermit assembly language source code
MSCOMM   ASM  "
MSFILE   ASM  "
MSKERM   ASM  "
MSRECV   ASM  "
MSSEND   ASM  "
MSSERV   ASM  "
MSSET    ASM  "
MSTERM   ASM  "
MSXDMB   ASM  "
MSXIBM   ASM  "
MSYIBM   ASM  "
MSDEFS   H    "
MSKERMIT INI  "
MSXDMB   HLP  Help file
MSBUILD  HLP  "

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
	and al,7fh		; only allow 7-bit characters.
	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,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

MSCOMM.ASM

	public	data, spack, rpack, rpack5, portval, port1, port2, hierr
	public	prtbase, nports, port3, port4
	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	78H		; largest packet we can handle

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

prtbase	label	byte
port1	prtinfo	<0FFFH,0,defpar,1,0,defhand,floxon>
port2	prtinfo	<0FFFH,0,defpar,1,0,defhand,floxon>
port3 	prtinfo	<0FFFH,0,defpar,1,0,defhand,floxon>
port4	prtinfo	<0FFFH,0,defpar,1,0,defhand,floxon>
	rept	portmax-4
	prtinfo	<0FFFH,0,defpar,1,0,defhand,floxon>
	endm

;; systems with two ports can set portval to port1 or port2.
;; systems with more than two ports can set nports higher,
;; then set portval to the address prtbase+(#-1)*size prtinfo
;; where # is the desired port.

portval	dw	port1		; Default is to use port 1.
nports	db	2		; # of known ports
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	?,'$'
prvtyp  db      0               ; Type of last packet sent. [27e]
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).
        db      ?,?,?,?         ; Space for '$' and other stuff.
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.
        mov prvtyp,ah           ; Remember packet type. [27e]
	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]
        mov bp,portval          ; Point to current port structure. [27e]
        cmp ds:[bp].ecoflg,0    ; Is the host echoing? [27e]
        jne rpak11              ; No, packets not echoed. [27e]
        cmp al,prvtyp           ; Packet type same as last sent? [27e]
        je rpack5               ; Yes, chuck echoed packet. [27e]
rpak11:	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?
	 jnz rpak2a
	jmp rpack1		;  Yes, then go start over.
rpak2a:	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]
inpkt0:	call inchr		; Get a character. [27a]
	 jmp inpkt8		;  Return failure. [27a]
	 nop			;  Make it three bytes long. [27a]
	cmp ah,trans.rsoh	; Is it SOH char? [27a]
	jne inpkt0		; No hold out for SOH. [27a]
inpkt1:	mov bx,offset recpkt	; Point to the beginning of the packet.
	mov incnt,0
	mov [bx],ah		; Add SOH to buffer. [27a]
	inc bx			; Increment pointer. [27a]
	inc incnt		; Increment counter. [27a]
inpkt2:	call inchr		; Get a character.
	 jmp inpkt8		;  Return failure. [20b]
	 nop			;  Make it three bytes long. [20b]
	cmp ah,trans.rsoh	; Starting over again? [27a]
	je inpkt1
	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]
	cmp ah,trans.rsoh	; Start of packet? [27a]
	je inpkt1		; Yes go start over. [27a]
	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

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,'PRN        '
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 quits'
	db	', Return retries'
	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.
	call poscur
	jmp clearl
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
	mov ax,offset 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
	jle outb11		; none to print, don't try
	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 flags.xflg,1	; Writing to screen?
	je abfil0		; Yes don't delete "file".
	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:	mov bx,offset 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: cmp chrcnt,0		; Any data in buffer?
	jg 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: dec chrcnt		; Decrement input count.
	lodsb
        cmp flags.eofcz,0       ; Is a control-z an end of file? [27b]
        je encd30               ; No, don't have to look for one. [27b]
        cmp al,'Z'-40H          ; Is this a control-Z? [27b]
        jne encd30              ; No, skip eof-processing. [27b]
        mov flags.eoflag,0FFH   ; Yes, set eof flag. [27b]
        jmp encod8              ; Go set character count and return. [27b]
encd30: 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
	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
	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
	int dos			; Print another crlf.
	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

MSKERM.ASM

	public	prompt, dosnum, curdsk, fpush
	include msdefs.h
;******************** Version 2.27 **********************************
; KERMIT, Celtic for "free"
;
; The name "Kermit" is a registered trade mark of Henson Associates, Inc.,
; used by permission.
;
;       Kermit-MS Program Version 2.27, December 6,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	'MACROS',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
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
	extrn	packlen: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
	cmp ah,0
	je ptake2			; empty, complain.
	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
; entry fpush (fast push...) pushes without waiting for a confirm.
; returns rskp.
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
fpush:	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.

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
	call	isfile			; does it exist as it is?
	mov	ax,bx			; ifso, just return original name
	jc	spath0			; nope...
	pop	es
	ret
spath0:	mov	si,ax
	mov	di,offset tfile		; place to copy to
	mov	dl,0			; no '\' seen yet
spath1:	lodsb
	stosb
	cmp	al,'/'			; contain path characters?
	je	spath1a
	cmp	al,'\'
	jne	spath2			; no, keep going
spath1a:mov	dl,1			; remember we've seen them
spath2:	or	al,al
	jnz	spath1			; copy name in
	or	dl,dl			; look at flag
	jnz	spath3			; path embedded, file not there, fail
; 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
	cmp	byte ptr [di-1],'/'	; does it end with switch char?
	je	spath8			; yes, don't put one in
	mov	al,'\'			; how about this one?
	cmp	byte ptr [di-1],al
	je	spath8			; yes, don't put it 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
	stc				; no file found
	ret
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

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
filopn	db	0		; Says if disk file is open.
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
	cmp filopn,0		; Disk file open?
	je abort0		; No so don't close.
	cmp flags.xflg,1	; Writing to the screen?
	je abort0		; Yes, don't close "file".
	mov ah,closf		; Close the file and ignore errors.
	mov dx,offset fcb
	int dos
abort0:	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
	cmp flags.destflg,2	; Receiving to the screen? [27c]
	je read12		; Yes no printing [27c]
	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.
	cmp flags.destflg,2	; Receiving to the screen? [27c]
	je read12a		; Yes no printing [27c]
	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]
	cmp flags.destflg,2	; Receiving to screen? [27c]
	je read51a		; Yes don't reset. [27c]
	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]
	cmp flags.destflg,2	; Receiving to screen? [27c]
	je read7a		; Yes don't reset. [27c]
	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
	mov filopn,1		; Disk file open.
	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.
	cmp flags.xflg,1	; Writing to the screen?
	je rdt32y		; Don't close "file".
	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
rdt32y:	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.destflg,0	; Add ^Z if writing to printer.
	je rdt33x
	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
rdt33x:	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
	cmp flags.xflg,1	; Writing to the screen?
	je rdat37		; Yes, don't close "file".
	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'
	mov filopn,0		; File closed now.
	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

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  $'
filopn	db	0		; Says if disk file is open.
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.
	mov filopn,1		; Disk file is open.
	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.
	mov filopn,0		; No files open.
	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.
	mov al,0		; Null to end the string.
	stosb
	pop es
	pop di
	pop si
	ret

;	Abort

ABORT	PROC	NEAR
	cmp filopn,0		; Any disk files open?
	je abort0		; No so don't do a close.
	mov ah,closf		; Close the file and ignore errors.
	mov dx,offset fcb
	int dos
abort0:	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

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 rdbuf	; 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 rdbuf
    	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 rdbuf[bx],'$' 	; terminate name for printing
	mov cnt,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 cnt,ax		; Remember number of chars we read.
	mov byte ptr [bx],'$'	; use for printing.
get5:	call ipack		; Initialize.
	 jmp get8		; Sorry can't do it.
	 nop
	mov cx,cnt		; Get back filename size.
	mov pack.argbk1,cx	; Need it here to send packet.
	inc cx			; Don't forget "$" for printing.
	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.
	rep movsb		; Perform the string move.
	pop es
	cmp flags.remflg,0	; remote mode?
	jne get6		; yes, don't print anything
	cmp flags.destflg,2	; Receiving to screen? [27c]
	je get6			; Yes skip screen stuff. [27c]
	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...
	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

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,'?Null input not allowed$' ;[jd]
erms24	db	cr,lf,'?Capture file already open (use close command)$' ;[jd]
erms25	db	cr,lf,'?Input must be numeric$'
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	03H		; Three choices. [27c]
	mkeyw	'DISK',01H
	mkeyw	'PRINTER',00H
	mkeyw	'SCREEN',02H

; 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, getbaud: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
	pop	es
	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.
	cmp bl,2		; Is dest the screen? [27c]
	jne desa		; No, then done. [27c]
	mov flags.xflg,1	; Remember it here. [27c]
	ret
desa:	mov flags.xflg,0	; Don't write to screem [27c]
	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
	call getbaud		; read baud rate first
	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		; do we have scan code?
	jne	setk1		; 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.
	mov	tmp,0		; No input yet. [27f]
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	atoi5		; out of range, fail
	cmp	al,'0'
	jb	atoi5
	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
	mov	tmp,1		; Got some legal input. [27f]
atoi2:	loop	atoi0		; loop thru all chars
	cmp	tmp,0		; Anything besides spaces? [27f]
	je	atoi4		; No so fail. [27f]
	jmp	rskp
atoi4:	mov	dx,offset erms23	; complain and return
	ret
atoi5:	mov	dx,offset erms25	; Input must be numeric
	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

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]
erms2	db	cr,lf,'?No room in definition buffer, definition ignored'
	db	cr,lf,'$'
erms3	db	cr,lf,'?No room in scan table, definition ignored',cr,lf,'$'
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,' P  Push to a new command parser'
	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: $'
ocbuf	db	4 dup (?)
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
	extrn	fpush: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
	push	si
	push	di
	pushf				; save flags...
	mov	ch,3			; # of digits to print
	mov	cl,3			; shift count
	mov	di,offset ocbuf+2	; point to end of buffer
	std				; set direction to backwards
	mov	dl,al			; copy the byte
proc1:	mov	al,dl
	and	al,7			; keep low-order byte
	add	al,'0'			; make printable
	stosb				; drop it off
	shr	dl,cl			; shift this digit out
	dec	ch
	jnz	proc1			; loop thru all
	mov	cx,3
	cld				; forward again
	mov	si,offset ocbuf
	mov	ah,conout		; console output function
proc2:	lodsb
	mov	dl,al
	int	dos
	loop	proc2			; print all digits
	popf
	pop	di
	pop	si
	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,coninq		; 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 intc11
	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 intc11		; no, forget it
	test targ.flgs,capt	; already capturing?
	jnz intc11		; 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:	cmp ah,'P'		; maybe want to push?
	jne intc11		; no, go on
	call fpush		; try pushing
	 nop
	 nop
	 nop			; isn't this silly?
	mov dx,offset sttmsg
	mov ah,prstr
	int dos
	jmp intch1a		; wait for space
intc11:	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.
defkey	proc	near
	cmp	deflen,cx
	jg	defk0		; room in buffer, continue
	mov	dx,offset erms2
	call	tmsg
	ret
defk0:	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
	sub	deflen,ax	; account for the space
	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
	ret			; that's it
defk5:	mov	dx,offset erms3
	call	tmsg
	ret
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

; print a $-message in dx
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

MSXDMB.ASM

code	segment	public
code	ends
datas	segment	public 'datas'
datas	ends
stack	segment	stack 'stack'
stack	ends
	end

MSXIBM.ASM

; Kermit system dependent module for IBM-PC

; Edit History
;  Make break be 275 ms, DT 5:51pm  Thursday, 6 December 1984

	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.
	push ax
	mov ax,275		; # of ms to wait
	call pcwait		; hold break for desired interval
	pop ax
	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

; Wait for the # of milliseconds in ax
; Thanks to Bernie Eiben for this one.
pcwait	proc	near
	mov	cx,240		; inner loop counter for 1 millisecond
pcwai1:	sub	cx,1		; inner loop takes 20 clock cycles
	jnz	pcwai1
	dec	ax		; outer loop counter
	jnz	pcwait		; wait another millisecond
	ret
pcwait	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

MSYIBM.ASM


	title term

; edit history
; Fixed destructive tab problem (received from many people)
; Fixed insert/delete line problem when at bottom/top of screen.
; Newlines never scroll in inverse video lines
; Implemented cursor save/restore (from Univ. Md.)
; "Echo" status on mode corrected.
; JD, 6 December 1984

	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'
	db	'jk'
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
	dw	savecur,restcur
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	?
oldcur	dw	0			; save'd cursor position
; 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 remmsg	; Assume remote echoing.
	test	flags,lclecho		; Is remote side echoing?
	jz	modl4			; Yes, keep going
	mov	si,offset lclmsg	; Else it's local echoing.
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
	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	ah,4eh			;*** plus key thing?
	je	trnmod			; yes, go toggle mode line
	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
wrap:	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,7			; 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
	add	dl,8			; tab is at most 8 columns
	and	dl,not 111b		; round down to a multiple of 8
	cmp	dl,crt_cols		; out of range?
	jb	setcur			; no, go set it
	test	flags1,lnwrap		; in wrap mode?
	jnz	outta1			; yes, wrap to next line
	mov	dl,byte ptr low_rgt	; else just move to right margin
	jmp	setcur
outta1:	jmp	wrap

; 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	ah,7h			; scroll down.
	mov	bh,curattr		; attribute
	cmp	ch,dh			; moving last line down?
	jne	insli2			; no, keep going
	mov	al,0			; yes, just clear it
insli2:	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
	cmp	ch,dh			; deleting last line?
	jne	delli2			; no, go on
	mov	al,0			; yes, just blank it
delli2:	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

; save cursor
savecur:
	mov	oldcur,dx
	ret

; restore cursor
restcur:
	mov	dx,oldcur
	jmp	setcur

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

MSYIBMJ.ASM


	title term

; edit history
; Fixed destructive tab problem (received from many people)
; Fixed insert/delete line problem when at bottom/top of screen.
; Newlines never scroll in inverse video lines
; Implemented cursor save/restore (from Univ. Md.)
; "Echo" status on mode corrected.
; JD, 6 December 1984

	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'
	db	'jk'
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
	dw	savecur,restcur
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	?
oldcur	dw	0			; save'd cursor position
; 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 remmsg	; Assume remote echoing.
	test	flags,lclecho		; Is remote side echoing?
	jz	modl4			; Yes, keep going
	mov	si,offset lclmsg	; Else it's local echoing.
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
	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	ah,4eh			;*** plus key thing?
	je	trnmod			; yes, go toggle mode line
	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
wrap:	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,7			; 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
	add	dl,8			; tab is at most 8 columns
	and	dl,not 111b		; round down to a multiple of 8
	cmp	dl,crt_cols		; out of range?
	jb	setcur			; no, go set it
	test	flags1,lnwrap		; in wrap mode?
	jnz	outta1			; yes, wrap to next line
	mov	dl,byte ptr low_rgt	; else just move to right margin
	jmp	setcur
outta1:	jmp	wrap

; 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,0888h		; shorten bell to 17ms [jc 12-8-84]
;	mov	cx,8888h		; commented out [jc]
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	ah,7h			; scroll down.
	mov	bh,curattr		; attribute
	cmp	ch,dh			; moving last line down?
	jne	insli2			; no, keep going
	mov	al,0			; yes, just clear it
insli2:	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
	cmp	ch,dh			; deleting last line?
	jne	delli2			; no, go on
	mov	al,0			; yes, just blank it
delli2:	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

; save cursor
savecur:
	mov	oldcur,dx
	ret

; restore cursor
restcur:
	mov	dx,oldcur
	jmp	setcur

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

Directory of PC-SIG Library Disk #0041

 Volume in drive A has no label
 Directory of A:\

FILES41  TXT       545   2-14-87  10:30a
MSCMD    ASM     32721  12-08-84   1:43a
MSCOMM   ASM     19458  12-08-84   1:47a
MSDEFS   H        9193  12-08-84   1:49a
MSFILE   ASM     31264  12-08-84   1:55a
MSKERM   ASM     35538  12-08-84   2:26a
MSRECV   ASM     20231  12-08-84   2:58a
MSSEND   ASM     32759  12-08-84   3:05a
MSSERV   ASM     24510  12-08-84   3:10a
MSSET    ASM     40709  12-08-84   3:18a
MSTERM   ASM     13334  12-08-84   3:21a
MSXDMB   ASM       121   7-28-84   5:31a
MSXIBM   ASM     22232  12-08-84   3:52a
MSYIBM   ASM     31431  12-08-84   4:26a
MSYIBMJ  ASM     31507  12-08-84   2:26p
README             565   4-11-85   2:12p
       16 file(s)     346118 bytes
                       10240 bytes free