DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T m

⟦227ff27eb⟧ TextFile

    Length: 75709 (0x127bd)
    Types: TextFile
    Names: »msssen.asm«

Derivation

└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
    └─⟦71044c191⟧ »EurOpenD3/misc/kermit.ms-2.32.tar.Z« 
        └─⟦31f2d420d⟧ 
            └─⟦this⟧ »msssen.asm« 

TextFile

        NAME    msssen
; File MSSSEN.ASM
; Edit history:
; Last edit 11 Dec 1988
; 11 Dec 1988 Fix corruption decoding SPAR info when SET SEND TIMEOUT cmd
;  uses non-default time. Tnx to Drew Derbyshire for finding the effect.
; 21 Nov 1988 Version 2.32
; 1 Oct 1988 Add second CAPAS byte for 'M' (message) packets. However,
;  earlier MS Kermits (and C Kermit) are confused by this byte so do not
;  add the second CAPAS byte in RPAR parameters but do decode in SPAR.
; 16 Sept Allow dollar signs in error messages, tnx to Terry Kennedy.
; 27 August 1988 Fix 8-bit quoting: use 'N' (none) if other side makes no
;  entry in that attributes field, use dtrans.ebquot as our default.
; 1 July 1988 Version 2.31
; 12 June 1988 Add error recovery if serial port does not initialize.
; 15 May 1988 Add kstatus global status word
; 4 May 1988 Correct length of mailing address in packet. [jrd]
; 23 Feb 1988 Add Mail command. [jrd]
; 14 Feb 1988 Add Attributes packets. [jrd]
; 7 Feb 1988 Correct errlev setting at send8: [jrd]
; 27 Jan 1988 Remove serrst call, done now in mssker idle loop. [jrd]
; 24 Jan 1988 Do not decode packets unless Error types. [jrd]
; 1 Jan 1988 version 2.30

        public  spar, rpar, error, error1, nout, send, flags, trans, pack
        public  dodec, doenc, curchk, inichk, packlen, send11, dtrans, mail
        include mssdef.h

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

datas   segment public 'datas'
        extrn   buff:byte, data:byte, filbuf:byte, fsta:word
        extrn   decbuf:byte, chrcnt:word, bufpnt:word, errlev:byte
        extrn   rptq:byte, origr:byte, rptct:byte, rptval:byte, kstatus:word
        extrn   diskio:byte, maxtry:byte, imxtry:byte, portval:word

flags   flginfo <>
trans   trinfo  <>
dtrans  trinfo  <>                      ; default trans info
pack    pktinfo <>
crlf    db      cr,lf,'$'
ender   db      bell,bell,'$'
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$'
erms25  db      'Host does not support Kermit MAIL command$'
erms26  db      'Host rejected the file$'
infms2  db      cr,'             Sending: In progress',cr,lf,'$'
infms3  db      'Completed',cr,lf,'$'
infms4  db      'Failed',cr,lf,'$'
infms6  db      'Interrupted$'
remmsg1 db      'Kermit-MS: File not found$'
filhlp  db      ' A filename (possibly wild) $'
filmsg  db      ' Local Source File   or carriage return $'
remfnm  db      ' Remote Destination File: $'
lclfnm  db      ' Local Source File: $'
mailhlp db      ' Filename  mail-address   or carriage return $'
mailto  db      ' Address: $'
mailtohlp db    ' mailer address string (such as user@host or host::user)$'
mailflg db      0               ; 1 if Mail, 0 if Send command
curchk  db      0               ; Use to store checksum length
inichk  db      1               ; Original or set checksum length
siz     dw      0               ; Size of data from gtchr
difsiz  dw      0               ; Size of new exported file name
sendas  dw      55 dup(0)       ; Buffer for file name and user@host
temp    dw      0
temp4   dw      0
asmsg   db      '  as  $'
filopn  db      0               ; Says if disk file is open
ninefive dw     95              ; constant word for long packets
datas   ends

code    segment public 'code'
        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, prtasz:near
        extrn erpos:near, rtpos:near, cxmsg:near, stpos:near, decout:near
        extrn encode:near, nulref:near, decode:near, nulr:near, lnout:near
        extrn errpack:near, updrtr:near, clrmod:near, prompt:near
        extrn prtfn:near, strcpy:near, strlen:near, strcat:near, pktsize:near
        extrn pcwait:near, ihosts:near, begtim:near, endtim:near

        assume  cs:code, ds:datas

;       This routine sets up the data for init packet (either the
;       Send_init or ACK packet)
; trans.rxxx are items we are prepared to receive
; Lines marked ;M energize the second CAPAS byte. Leave them as comments for
; because earlier versions of MS Kermit (and C Kermit) are confused by it
; (fail to decode bit saying second CAPAS byte follows and thus loose sync).
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,40h                  ; 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
        mov     6[bx],ah                ; Add it to the packet
        mov     ah,trans.chklen         ; Length of checksum
        add     ah,'0'                  ; 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
                                        ; begin long packet changes
        mov     ah,2                    ; CAPAS, bit1 = can do long packets
        cmp     flags.attflg,0          ; allowing attributes packets?
        je      rpar1                   ; e = no
        or      ah,8                    ; bit #3, can do file attributes
rpar1:
;M      or      ah,1                    ; say second CAPAS byte follows
        add     ah,20h                  ; apply tochar() to byte
        mov     9[bx],ah                ; add to packet
                                        ; additional CAPAS go in here
        mov     byte ptr 10[bx],20h+20h ; Allow M (message) pkts, (#6, bit5)
        mov     ah,20h          ; WINDO field, null applied through tochar()
;M      mov     11[bx],ah               ; put into packet
        mov     10[bx],ah               ; put into packet

        push    ax                      ; save some regs
        push    dx
        mov     ax,trans.rlongp     ; long packet length which we can receive
        xor     dx,dx                   ; clear extended part for division
        div     ninefive                ; divide by 95. quo = ax, rem = dx
        add     al,20h                  ; apply tochar() to quotient
;M      mov     12[bx],al               ; add to packet
        mov     11[bx],al               ; add to packet
        add     dl,20h                  ; apply tochar() to remainder
;M      mov     13[bx],dl               ; add to packet
        mov     12[bx],dl               ; add to packet
        pop     dx                      ; restore regs
        pop     ax
;M      mov     ah,14                   ; 14 bytes of data
        mov     ah,13                   ; 13 bytes of data
        ret
RPAR    ENDP

; This routine reads in all the send init packet information
; Enter with BX/ packet address, AX/ packet length
; This could probably be done much more legibly if it were table
; driven, but I'm afraid to touch it..
;
; dtrans.xxx are the default parameters if the other side says nothing
; trans.sxxx are the active negotiated parameters we will use for sending.
SPAR    PROC    NEAR
        mov     temp4,ax                ; Save the number of arguments
        mov     ah,al                   ; number of args is now in ah
        push    si
        mov     si,bx
        cld
        cmp     ah,0                    ; any data?
        jg      spara                   ; g = yes, want more than bare minimum
        mov     al,dspsiz               ; nothing supplied by host use default
        jmp     short sparc
spara:  lodsb                           ; get the max packet size
        dec     ah                      ; ah = bytes remaining to be examined
        sub     al,' '                  ; subtract a space
        cmp     al,spmin                ; below the minimum?
        jge     sparb                   ; ge = no
        mov     al,spmin
        jmp     short sparc
sparb:  cmp     al,spmax                ; or above the maximum?
        jle     sparc                   ; le = no
        mov     al,spmax
sparc:  mov     trans.spsiz,al          ; save it
        mov     al,dtrans.stime         ; pick up default stime
        cmp     ah,0                    ; more data?
        jle     spar02                  ; le = no, use default
        lodsb                           ; get the timeout value
        dec     ah
        sub     al,' '                  ; subtract a space
        cmp     dtrans.stime,dstime     ; Is current value the default?
        je      spar0                   ; e = yes, else user value overrides
        mov     al,dtrans.stime         ; pick up user selected stime
spar0:  cmp     al,0
        jg      spar01                  ; must be non-negative
        mov     al,0                    ; negative, so use zero
spar01: cmp     al,trans.rtime          ; same as other side's timeout
        jne     spar02                  ; ne = no
        add     al,1                    ; yes, but make it a little different
spar02: mov     trans.stime,al          ; save it
        mov     al,dtrans.spad          ; get default send padding
        cmp     ah,0                    ; more data?
        jle     spar11                  ; le = no, use default
spar1:  lodsb                           ; get the number of padding chars
        dec     ah
        sub     al,' '
        cmp     al,0
        jge     spar11                  ; must be non-negative
        mov     al,0
spar11: mov     trans.spad,al
        mov     al,dtrans.spadch        ; pick up default send pad character
        cmp     ah,0                    ; more data?
        jle     spar21                  ; le = no, use default
spar2:  lodsb                           ; get the padding char
        dec     ah
        add     al,40h                  ; remove ascii bias
        and     al,7FH
        cmp     al,del                  ; Delete?
        je      spar21                  ; e = yes, then it's OK
        cmp     al,31                   ; control char?
        jle     spar21                  ; le = yes, then OK
        mov     al,0                    ; no, use null
spar21: mov     trans.spadch,al
        mov     al,dtrans.seol          ; get default send eol char
        cmp     ah,0                    ; more data?
        jle     spar31                  ; le = no, use default
spar3:  lodsb                           ; get the EOL char
        dec     ah
        sub     al,' '
        cmp     al,31                   ; control char?
        jle     spar31                  ; le = yes, then use it
        mov     al,cr                   ; else use the default
spar31: mov     trans.seol,al
        mov     al,dtrans.squote        ; send quote
        cmp     ah,0                    ; more data?
        jle     spar41                  ; le = no, use default
spar4:  lodsb                           ; get the quote char
        dec     ah
        cmp     al,' '                  ; less than a space?
        jge     spar40                  ; ge = no
        mov     al,dsquot               ; yes, use default
        jmp     spar41
spar40: cmp     al,7eh                  ; must also be less than a tilde
        jbe     spar41                  ; be = is ok
        mov     al,dsquot               ; else use default
spar41: mov     trans.squote,al
        cmp     ah,0                    ; more data?
        jg      spar5                   ; g = yes
        mov     al,dtrans.ebquot        ; use default
        mov     trans.ebquot,al
        jmp     short spar51
spar5:  lodsb                           ; get other side's 8-bit quote request
        dec     ah
        call    doquo                   ; and set quote char
spar51: cmp     ah,0                    ; more data?
        jg      spar6                   ; g = yes
        mov     trans.chklen,1          ; use default
        jmp     short spar61
spar6:  mov     al,inichk
        mov     trans.chklen,al         ;checksum length we really want to use
        lodsb                           ; get other side's checksum length
        dec     ah
        call    dochk                   ; determine what size to use
spar61: cmp     ah,0                    ; more data?
        jg      spar7                   ; g = yes
        mov     rptq,0
        jmp     short spar71
spar7:  lodsb                           ; get other side's repeat count prefix
        dec     ah
        mov     ch,drpt                 ; default repeat count prefix
        mov     rptq,0                  ; clear active repeat count prefix
        call    dorpt                   ; negotiate new prefix
spar71: mov     al,0                    ; get default operating Capabilities
        cmp     ah,0                    ; more data?
        jle     spar81                  ; le = no, use default
        lodsb                           ; get capas bitmap from other side
        dec     ah
        and     al,not (1)              ; remove least significant bit
        sub     al,20h                  ; apply unchar()
spar81: mov     trans.capas,al          ; store result in active byte
spar82: cmp     ah,0                    ; more data?
        jle     spar85                  ; le = no
        test    byte ptr [si-1],1       ; is CAPAS byte continued to next?
        jz      spar85                  ; z = no
        lodsb                           ; get 2nd CAPAS bitmap from other side
        dec     ah
        and     al,not (1)              ; remove least significant bit
        sub     al,20h                  ; apply unchar(). Store nothing
        jmp     short spar82            ; seek more CAPAS bytes
spar85: mov     al,0                    ; setup default window size
        cmp     ah,0                    ; more data?
        jle     spar9                   ; le = no, use default
        lodsb                           ; get other side's window size
        dec     ah
        sub     al,20h                  ; apply unchar()
        call    dewind                  ; negotiate window size back into al
spar9:  mov     trans.windo,al          ; store it
                                        ; decode window info
        push    cx                      ; save a reg
        xor     ch,ch
        mov     cl,trans.spsiz          ; normal packet size
        mov     trans.slongp,cx         ; assume not using long packets
        pop     cx                      ; restore reg
        cmp     ah,2                    ; more data (long packet needs two)?
        jae     spar9d                  ; ae = more to look at
        mov     ax,trans.slongp ; put above size in ax for final checks
        jmp     spar9a          ; do final checks (they want longer than us)
spar9d: test    trans.capas,2   ; do they have long packet capability?
        jz      sparx           ; z = no, skip following lp length fields
        lodsb                           ; long pkt length, high order byte
        sub     al,20h                  ; apply unchar()
        xor     ah,ah
        mul     ninefive                ; times 95 to dx(hi),ax(lo)
        mov     trans.slongp,ax         ; store that much
        lodsb                           ; long pkt length, low order byte
        sub     al,20h                  ; apply unchar()
        xor     ah,ah
        add     ax,trans.slongp         ; plus high order part
        mov     trans.slongp,ax         ; store it
        or      ax,ax           ; if result is zero then use regular packets
        jnz     spar9a                  ; non-zero, use what they want
        mov     ah,0
        mov     al,trans.spsiz          ; default to regular packet size
        mov     trans.slongp,ax ;  and ignore the CAPAS bit (no def 500 bytes)
spar9a: cmp     ax,trans.slong          ; longer than we want to do?
        jbe     spar9b                  ; be = no
        mov     ax,trans.slong          ; limit to our longest sending size
        mov     trans.slongp,ax         ; and use it
spar9b: cmp     ax,94                   ; shorter than normal packet too?
        ja      spar9c                  ; a = no
        mov     trans.spsiz,al          ; update normal packet size, again
spar9c: mov     ax,temp4                ; recover number of pieces of data
sparx:  pop     si
        ret
SPAR    ENDP

; Set 8-bit quote character based on my capabilities and the other
; Kermit's request

DOQUO   PROC    NEAR
        cmp     dtrans.ebquot,'N'       ; Can I do 8-bit quoting at all?
        je      dq3                     ; No - so forget it
        cmp     dtrans.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     al,'Y'                  ; I need quoting - can he do it?
        je      dq1                     ; Yes - then all is settled
        cmp     al,'N'                  ; No - then don't quote
        je      dq3
        cmp     al,trans.ebquot         ; Both need quoting - chars must match
        jne     dq3
dq1:    mov     al,trans.ebquot
        cmp     al,'Y'                  ; If Y or N, don't validate prefix
        je      dq2
        cmp     al,'N'
        je      dq2
        call    prechk                  ; Is it in range 33-62, 96-126?
        jnc     dq4                     ; nc = in range
        mov     al,'Y'                  ; don't do quoting
dq4:    cmp     al,trans.rquote         ; Same prefix?
        je      dq3                     ; Not allowed, so don't do quoting.
        cmp     al,trans.squote         ; Same prefix here?
        je      dq3                     ; This is illegal too
        mov     trans.ebquot,al         ; Remember what we decided on
dq2:    ret
dq3:    mov     trans.ebquot,'N'        ; Quoting will not be done
        ret
DOQUO   ENDP

; Check if prefix in AL is in the proper range: 33-62, 96-126.
; Return carry clear if in range, else return carry set.
prechk: cmp     al,33
        jb      prechk2                 ; b = out of range
        cmp     al,62
        jbe     prechk1                 ; be = in range 33-62
        cmp     al,96
        jb      prechk2                 ; b = out of range
        cmp     al,126
        ja      prechk2                 ; a = out of range 96-126
prechk1:clc                             ; carry clear for in range
        ret
prechk2:stc                             ; carry set for out of range
        ret

; Set checksum length.
dochk:  cmp     al,'1'                  ; Must be '1', '2', or '3'
        jb      doc1                    ; b = not '1' to '3'
        cmp     al,'3'
        jbe     doc2                    ; be = ok
doc1:   mov     al,'1'                  ; else use default of '1'
doc2:   sub     al,'0'                  ; remove ascii bias
        mov     trans.chklen,al         ; other side's request is do-able here
        cmp     al,trans.chklen         ; Do we want the same thing?
        je      dochk0                  ; e = yes, then we're done
        mov     trans.chklen,1          ; No, use single character checksum
dochk0: ret

; 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?
        jnc     dorpt1                  ; nc = in range
        mov     al,0                    ; don't use their value
dorpt1: cmp     al,trans.squote         ; Same as the control quote char?
        je      dorpt2                  ; Yes, that's illegal, no repeats
        cmp     al,trans.rquote         ; How about this one?
        je      dorpt2                  ; No good
        cmp     al,trans.ebquot         ; Same as eight bit quote char?
        je      dorpt2                  ; Yes, that's illegal too, no repeats
        cmp     al,ch                   ; Are we planning to use same char?
        jne     dorpt2                  ; No, that's no good either
        mov     rptq,ch                 ; Use repeat quote char now
dorpt2: ret

                                        ; negotiate window size in al
dewind: xor     al,al                   ; no windowing at our end
        ret


;       Send command
;       MAIL filspec user@node  command

SEND    PROC    NEAR
        mov     mailflg,0               ; send, not mail
        mov     temp,0
        jmp     short sendm0
MAIL:   mov     mailflg,1               ; set flag for mail command vs send
        mov     temp,1                  ; temp copy of mailflag
sendm0: mov     difsiz,0                ; Assume we'll use original filename
        mov     byte ptr sendas,0       ; clear sendas name (in case none)
        mov     dx,offset diskio.string ; address of filename string
        mov     bx,offset filmsg        ; Text of help message
        cmp     mailflg,0               ; Mail command?
        je      sendm1                  ; e = no
        mov     mailflg,0               ; clear in case error exit
        mov     bx,offset mailhlp       ; Text of help message
sendm1: mov     ah,cmfile               ; get an input file spec
        call    comnd
         ret                            ;  Give up on bad parse
         nop
         nop
        cmp     flags.cxzflg,0          ; ^X, ^Z, ^C typed?
        je      send0                   ; e = no, continue
        or      errlev,1                ; say send failed
        or      fsta.xstatus,1+80h      ; set status, failed + intervention
        mov     kstatus,1+80h           ; global status
        jmp     rskp                    ; yes, quit
send0:  cmp     ah,0                    ; any text given?
        je      send0d                  ; e = no, prompt
        cmp     temp,0                  ; Mail command?
        jne     send0c                  ; ne = yes, require address
        mov     bx,offset sendas     ; See if want to send file under dif name
        mov     dx,offset filhlp        ; In case user needs help
        mov     ah,cmtxt                ; allow embedded white space
        call    comnd
         ret
         nop
         nop
        jmp     sendm3a                 ; join common completion code

send0d: mov     dx,offset lclfnm        ; prompt for local filename
        call    prompt
        mov     dx,offset diskio.string ; reload destination of user's text
        mov     bx,offset filhlp        ; help file
        mov     ah,cmfile               ; allow paths
        call    comnd                   ; try again for a local filename
         ret
         nop
         nop
        mov     temp4,ax
        mov     ah,cmcfm
        call    comnd
         ret
         nop
         nop
        mov     ax,temp4
        cmp     flags.cxzflg,0          ; ^X, ^Z, ^C typed?
        je      send0a                  ; e = no, continue
        or      errlev,1                ; say send failed
        or      fsta.xstatus,1+80h      ; set status, failed + intervention
        mov     kstatus,1+80h           ; global status
        mov     mailflg,0
        jmp     rskp                    ; yes, quit
send0a: cmp     ah,0                    ; user's byte count
        je      send0d                  ; e = nothing was typed, get some

send0b: mov     dx,offset remfnm        ; ask for remote name first
        cmp     temp,0                  ; Mail command?
        je      sendm2                  ; e = no
        mov     dx,offset mailto        ; ask for name@host
sendm2: call    prompt

send0c: mov     bx,offset sendas     ; See if want to send file under dif name
        mov     dx,offset filhlp        ; In case user needs help
        cmp     temp,0                  ; Mail command?
        je      sendm3                  ; e = no
        mov     dx,offset mailtohlp     ; In case user needs help
sendm3: mov     ah,cmtxt                ; allow embedded white space
        call    comnd
         ret
         nop
         nop
        cmp     ah,0                    ; text entered?
        je      send0b                  ; e = no, get some
sendm3a:cmp     flags.cxzflg,0          ; ^X, ^Z, ^C typed?
        je      send1                   ; e = no, continue
        or      errlev,1                ; say send failed
        or      fsta.xstatus,1+80h      ; set status, failed + intervention
        mov     kstatus,1+80h           ; global status
        mov     mailflg,0
        jmp     rskp                    ; yes, quit
send1:  mov     al,ah                   ; store count of user's chars
        mov     ah,0
        mov     difsiz,ax               ; Remember length of new name
        mov     ax,temp                 ; get temp mailflag
        mov     mailflg,al              ; store in secure area for later
        mov     ah,trans.sdelay         ; seconds to delay before sending
        shl     ah,1                    ; times 4*256 to get millisec
        shl     ah,1                    ;  for pcwait
        mov     al,1                    ; set low byte to 1 for no delay case
        call    pcwait                  ; wait number of millisec in ax
        mov     flags.xflg,0            ; Reset flag for normal file send[mtd]
        mov     flags.cxzflg,0          ; clear interrupt flag too
        mov     bx,offset diskio.string
        cmp     byte ptr [bx],'#'       ; Is first char a replacement for '?'?
        jne     send1f                  ; ne = no
        mov     byte ptr [bx],'?'       ; yes. Change '#' for '?'
send1f: mov     bx,offset sendas
        cmp     byte ptr [bx],'#'       ; Is first char a replacement for '?'?
        jne     snd11a
        mov     byte ptr [bx],'?'       ; yes. Change '#' for '?'
        jmp     short snd11a
                                ; SEND11 is an entry point for REMote cmds
SEND11: mov     flags.nmoflg,0          ; Reset flags from fn parsing
        mov     difsiz,0                ; clear any old 'sendas' filespec
snd11a: mov     kstatus,0               ; global status, success
        mov     ah,setdma               ; set dta address
        mov     dx,offset diskio.dta
        int     dos
        mov     ah,first2               ; search for first
        mov     cx,0                    ; consider only regular files
        mov     dx,offset diskio.string ; full filename, inc paths
        int     dos
        pushf                           ; save flags
        push    dx
        mov     ah,setdma               ; restore dta to offset buff
        mov     dx,offset buff
        int     dos
        pop     dx
        popf                            ; restore flags
        jnc     send16                  ; carry reset = file found
        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
        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
        call    errpack                 ; go complain
        mov     ah,curchk
        mov     trans.chklen,ah         ; Checksum length we want to use
        jmp     abort                   ; and abort this
sen11a: mov     ah,prstr
        mov     dx,offset crlf
        int     dos
        mov     ah,prstr
        mov     dx,offset erms15        ; '?Unable to find file'
        int     dos
        or      errlev,1                ; set DOS error level
        or      fsta.xstatus,1          ; set status
        mov     kstatus,1               ; global status
        mov     ax,1            ; tell statistics this was a send operation
        call    endtim                  ; stop statistics counter
        mov     mailflg,0               ; clear Mail flag
        jmp     rskp                    ; pretend successful completion

send16: call    serini                  ; Initialize serial port
        jnc     send17                  ; nc = success
        or      errlev,1                ; say send failed
        or      fsta.xstatus,1          ; set status
        mov     kstatus,1               ; global status
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     send16a                 ; nz = yes. Don't write to screen
        mov     dx,offset erms14
        mov     ah,prstr
        int     dos                     ; Print an error message
send16a:ret                             ; return failure

send17: call    begtim                  ; get tod for start of transfer
        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
        call    ihosts                  ; initialize the host (clear NAKs)
        call    init            ; Clear the line and initialize the buffers
        test    flags.remflg,dquiet+dserial ; quiet or serial display mode?
        jnz     send2                   ; nz = yes, suppress 0 retry msg
        call    stpos                   ; Print status of file transfer
        mov     ah,prstr                ; Be informative
        mov     dx,offset infms2
        int     dos
send18: test    flags.remflg,dquiet+dserial ; quiet or serial display mode?
        jnz     send2                   ; nz = yes, suppress 0 retry msg
        call    rtpos                   ; Position cursor
        mov     ax,0                    ; set retry counts to zero
        call    nout                    ; Write the number of retries

send2:  call    nppos                   ; Number of packets sent
        mov     ax,pack.numpkt
        call    nout                    ; Write the packet number
        cmp     pack.state,'D'          ; Are we in the data send state?
        jne     send3                   ; ne = no
        call    sdata                   ; send data
        jmp     send2
send3:  cmp     pack.state,'F'          ; Are we in the file send state?
        jne     send3a                  ; ne = no
        call    sfile                   ; Call send file
        jmp     send2
send3a: cmp     pack.state,'a'          ; are we in send attributes state?
        jne     send3b                  ; ne = no
        call    sattr                   ; call send attributes
        jmp     send2
send3b: cmp     pack.state,'d'          ; are we in initialize send data state
        jne     send4                   ; ne = no
        call    sdatini                 ; do setup for file reading
        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
                                        ; Completion processor section
send7:  push    ax
;;;;    call    serrst                  ; Reset serial port
        pop     ax
        mov     mailflg,0               ; clear Mail flag
        mov     dx,offset infms3        ; Completed message
        cmp     pack.state,'C'          ; Are we in the send complete state?
        je      send8                   ; e = yes, else failure
        mov     dx,offset infms4        ; Failed message
        or      errlev,1                ; say send failed
        or      fsta.xstatus,1          ; set status
        mov     kstatus,1               ; global status

send8:  cmp     flags.cxzflg,0          ; completed normally?
        je      send8b                  ; e = yes, don't bother with this
        or      errlev,1                ; say send failed
        or      fsta.xstatus,1+80h      ; set status, failed + intervention
        mov     kstatus,1+80h           ; global status
send8b: mov     ax,1            ; tell statistics this was a send operation
        call    endtim                  ; stop statistics counter
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     send8f                  ; nz = yes, no printing
        test    flags.remflg,dserial    ; serial display mode?
        jnz     send8c                  ; nz = yes, skip positioning
        push    dx
        call    stpos
        pop     dx
send8c: mov     ah,prstr
        cmp     flags.cxzflg,0          ; Completed or interrupted?
        je      send8d                  ; e = no interruption
        mov     dx,offset infms6        ; Say transfer was interrupted
send8d: int     dos
        cmp     flags.belflg,0          ; Bell desired?
        je      send8e                  ; e = no
        mov     dx,offset ender         ; Ring them bells
        int     dos
send8e: test    flags.remflg,dserial    ; serial display mode?
        jnz     send8f                  ; nz = yes, no cursor positioning
        call    clrmod
        call    rprpos
send8f: jmp     rskp
SEND    ENDP

;       Send routines

;       Send initiate
SINIT   PROC    NEAR
        mov     dl,imxtry
        cmp     pack.numtry,dl  ; Have we reached the maximum number of tries?
        jl      sinit2                  ; l = no
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     sinit1                  ; nz = yes. Don't write to screen
        call    erpos
        mov     dx,offset erms14
        mov     ah,prstr
        int     dos                     ; Print an error message
sinit1: 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     bx,offset erms20
        call    errpack                 ; Send error packet just in case
        mov     ah,curchk
        mov     trans.chklen,ah         ; Checksum length we want to use
        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
        mov     ah,dtrans.seol          ; restore default end-of-line char
        mov     trans.seol,ah
        mov     ah,dtrans.ebquot        ; our default 8-bit quoting
        mov     trans.ebquot,ah         ; active 8-bit quoting
        call    rpar                    ; Set up the parameter information
        xchg    ah,al
        mov     ah,0
        mov     pack.datlen,ax          ; Save the number of arguments
        mov     ax,pack.numpkt          ; Get the packet number
        mov     pack.seqnum,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
        call    pktsize                 ; report packet size
        mov     ah,'S'                  ; Send initiate packet
        call    sndpak                  ; send the packet
        call    rpack                   ; Get a packet
         jmp    sini23          ; Trashed packet don't change state, retry
         nop
        push    ax
        mov     ah,curchk
        mov     trans.chklen,ah         ; Checksum length we want to use
        pop     ax
        call    acknak                  ; was it ok?
        cmp     al,0                    ; maybe an ack?
        je      sini22                  ; yes, go handle it
        cmp     al,1                    ; maybe a nak?
        jne     sinit4                  ; no, check for error or something
        ret                             ; else just return and try again
sini22: mov     ax,pack.datlen
        mov     bx,offset data          ; point to data for spar
        call    spar                    ; Read in the data
        call    packlen                 ; Get max send packet size
        mov     pack.numtry,0           ; Reset the number of tries
        cmp     mailflg,0               ; non-zero to do Mail command
        je      sini24                  ; e = send, not mail command
        cmp     flags.attflg,0          ; allowed to do file attributes?
        je      sinit6                  ; e = no, so no Mail
        test    trans.capas,8           ; can they do file attributes?
        jz      sinit6                  ; z = no, so cannot do Mail
sini24: 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
        jmp     updrtr                  ; Update retry counter and return

sinit4: cmp     ah,'M'                  ; Message packet?
        jne     sinit4e                 ; ne = no
        call    dodec                   ; decode it
        jmp     error1                  ; display it and return

sinit4e:cmp     ah,'E'                  ; Is it an error packet
        jne     sinit5
        call    error
sinit5: jmp     abort
                                        ; say Mail not supported by host
sinit6: test    flags.remflg,dquiet     ; quiet display mode?
        jnz     sinit7                  ; nz = yes. Don't write to screen
        call    erpos
        mov     dx,offset erms25
        mov     ah,prstr
        int     dos                     ; Print an error message
sinit7: mov     pack.state,'B'          ; go to EOT state
        ret
SINIT   ENDP

;       Send file header

SFILE   PROC    NEAR
        mov     dl,maxtry
        cmp     pack.numtry,dl  ; Have we reached the maximum number of tries?
        jl      sfile1                  ; l = no
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     sfile0                  ; nz = yes. Don't write to screen
        call    erpos
        mov     dx,offset erms14
        mov     ah,prstr
        int     dos                     ; Print an error message
sfile0: 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     si,offset diskio.fname  ;addr of asciiz filename without paths
        mov     di,offset data          ; destination
        call    strcpy                  ; copy filename there
        push    dx
        mov     dx,offset data
        call    strlen                  ; get length (w/o terminator) into cx
        pop     dx
        mov     ch,0
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     sfil13                  ; nz = yes, no printing
        call    prtfn                   ; print filename in data
sfil13: call    newfn           ; show possible new filename, put length in cx
        call    doenc                   ; Do encoding; length is in cx
        mov     ax,pack.pktnum          ; Get the packet number
        mov     pack.seqnum,ax
        mov     ah,'F'                  ; File header packet
        cmp     flags.xflg,0            ; remote display requested?
        je      sfl13y                  ; e = no
        mov     ah,'X'                 ; use X rather than F packet for remote
sfl13y: call    pktsize                 ; report packet size
        call    sndpak                  ; send the packet
        call    rcvpak                  ; Get a packet
        call    acknak                  ; see what they had to say
        cmp     al,0                    ; ack'd ok?
        je      sfil14                  ; yes, on to next state
        cmp     al,1                    ; maybe a nak?
        jne     sfile3                  ; no, check for error
        ret                             ; if nak, just return and try again
sfil14: call    fackmsg                 ; get/show any embedded message
        mov     pack.state,'a'          ; set file attributes as next state
        ret

sfile3: cmp     ah,'M'                  ; Message packet?
        jne     sfile4                  ; ne = no
        call    dodec                   ; decode it
        jmp     error1                  ; display it and return

sfile4: cmp     ah,'E'                  ; Is it an error packet
        jne     sfile4                  ; ne = no
        call    dodec                   ; Do all decoding
        call    error
sfile5: jmp     abort
SFILE   ENDP

; Send file attributes. Attributes: file size in bytes and kilobytes,
; file time and date, machine identification. [jrd]

SATTR   PROC    NEAR
        cmp     flags.attflg,0          ; allowed to do file attributes?
        je      satt0                   ; e = no
        test    trans.capas,8           ; can we do file attributes?
        jnz     satt1                   ; nz = yes
satt0:  mov     pack.state,'d'          ; set the state to initiate send-data
        ret
satt1:  push    es                      ; save es around this work
        push    ds
        pop     es                      ; set es to datas segment for es:di
        cld
        mov     data,'1'                ; File length (Bytes) specifier
        mov     dx,diskio.sizehi        ; high word of length
        mov     ax,diskio.sizelo        ; low word of length
        mov     di,offset data+2        ; where to store data (for lnout)
        call    lnout                   ; convert file length, write to [di++]
        mov     cx,di                   ; compute field length
        sub     cx,offset data+2
        add     cl,32                   ; field length to ascii
        mov     data+1,cl               ; length. Done with File Size
                                        ; Kilobyte attribute
        mov     byte ptr[di],'!'        ; File length (Kilobytes) specifier
        inc     di
        mov     temp4,di                ; remember place for count field
        inc     di                      ; data field
        mov     dx,diskio.sizehi        ; high word of length, from file open
        mov     ax,diskio.sizelo        ; low word of length
        add     ax,1023                 ; add 1023 to round up
        adc     dx,0
        mov     al,ah                   ; do divide by 1024 bytes
        mov     ah,dl
        mov     dl,dh                   ; divide by 256 part
        mov     dh,0
        ror     dl,1                    ; low bit to carry flag
        rcr     ax,1                    ; divide by two, with carry in
        clc
        ror     dl,1                    ; low bit to carry flag
        rcr     ax,1                    ; divide by two, with carry in
        and     dl,3fh                  ; keep low six bits
        call    lnout                   ; convert file length
        mov     cx,di                   ; compute field length
        sub     cx,temp4                ; count field location
        add     cl,32-1                 ; field length to ascii
        push    di
        mov     di,temp4                ; point at count field
        mov     byte ptr[di],cl         ; store field length
        pop     di                      ; Done with Kilobyte attribute
                                        ; File date and time:
        mov     al,'#'                  ; creation date/time specifier
        stosb                           ; and point at field length
        mov     al,17+32                ; length of date/time field, to ascii
        stosb
        mov     ah,0
        mov     al,diskio.dta+25        ; yyyyyyym from DOS via file open
        shr     al,1                    ; get year
        add     ax,1980                 ; add bias
        mov     dx,0
        call    lnout                   ; put year (1988) in buffer
        mov     ax,word ptr diskio.dta+24 ; yyyyyyyym mmmddddd  year+month+day
        shr     ax,1                    ; month to al
        mov     ah,0
        mov     cl,4
        shr     al,cl                   ; month to low nibble
        mov     byte ptr[di],'0'        ; leading digit
        inc     di
        cmp     al,9                    ; more than one digit?
        jbe     satt2                   ; be = no
        mov     byte ptr[di-1],'1'      ; new leading digit
        sub     al,10                   ; get remainder
satt2:  add     al,'0'                  ; to ascii
        stosb                           ; end of month
        mov     al,diskio.dta+24        ; get day of month
        and     al,1fh                  ; select day bits
        mov     ah,0
        mov     cl,10
        div     cl                      ; quot = al, rem = ah
        add     ax,'00'                 ; add ascii bias
        stosw                           ; leading digit and end of date
        mov     al,' '                  ; space separator
        stosb
        mov     al,diskio.dta+23        ; hours  hhhhhmmm
        mov     cl,3
        shr     al,cl                   ; move to low nibble
        mov     ah,0
        mov     cl,10
        div     cl                      ; quot = al, rem = ah
        add     ax,'00'                 ; add ascii bias
        stosw                           ; store hours
        mov     al,':'                  ; separator
        stosb
        mov     ax,word ptr diskio.dta+22 ; get minutes: hhhhhmmm mmmsssss
        mov     cl,5
        shr     ax,cl                   ; minutes to low byte
        and     al,3fh                  ; six bits for minutes
        mov     ah,0
        mov     cl,10
        div     cl
        add     ax,'00'                 ; add ascii bias
        stosw
        mov     al,':'                  ; separator
        stosb
        mov     al,byte ptr diskio.dta+22 ; get seconds (double secs really)
        and     al,1fh
        shl     al,1                    ; DOS counts by two sec increments
        mov     ah,0
        mov     cl,10
        div     cl
        add     ax,'00'                 ; add ascii bias
        stosw
        mov     ax,'".'                 ; machine indicator(.), 2 data bytes
        stosw
        mov     ax,'8U'                 ; U8 = Portable O/S, MSDOS
        stosw
        pop     es                      ; recover es register
        cmp     mailflg,0               ; Mailing?
        je      satt3                   ; e = no
        mov     byte ptr [di],'+'       ; Mail specification
        inc     di
        mov     si,offset sendas        ; user@host field
        mov     dx,si
        call    strlen                  ; get length into cl
        push    cx                      ; save address length
        inc     cl                      ; include M for disposition = mail
        add     cl,' '                  ; add ascii bias
        mov     [di],cl                 ; store in length field
        inc     di
        mov     byte ptr [di],'M'       ; mail the file
        inc     di
        pop     cx                      ; recover address length
        jcxz    satt3                   ; z = empty field
        push    es
        push    ds
        pop     es                      ; use es:di pointing to datas segment
        cld
        rep     movsb                   ; append address text to field
        pop     es
satt3:  sub     di,offset data          ; get length of data
        mov     pack.datlen,di          ; data length for packet
        call    pktsize                 ; report packet size
        mov     ax,pack.pktnum          ; get the packet number
        mov     pack.seqnum,ax
        mov     ah,'A'                  ; Attributes packet
        call    sndpak                  ; send the packet
        call    rcvpak                  ; get response
        call    acknak                  ; see what they had to say
        cmp     al,0                    ; ack'd ok?
        je      satt5                   ; e = yes, on to next state
        cmp     al,1                    ; maybe a nak?
        jne     satt6                   ; ne = no, check for error
        ret                             ; if nak, just return and try again
satt5:  cmp     pack.datlen,0           ; any data in the ACK?
        je      satt5d                  ; e = no
        cmp     data,'N'                ; are they refusing this file?
        jne     satt5d                  ; ne = no
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     satt5a                  ; nz = yes. Don't write to screen
        call    erpos                   ; Position the cursor
        mov     ah,prstr
        mov     dx,offset erms26        ; say host rejected the file
        int     dos
satt5a: mov     pack.state,'Z'          ; send EOF with Discard
        mov     flags.cxzflg,'X'        ; simulate Control-X to discard
        or      errlev,1                ; say send failed
        or      fsta.xstatus,1+80h      ; set status, failed + intervention
        mov     kstatus,1+80h           ; global status
        ret
satt5d: mov     pack.state,'d'          ; next state is initiate send-data
        ret
satt6:  cmp     ah,'E'                  ; Is it an error packet
        jne     satt7                   ; ne = no
        call    dodec                   ; Do all decoding
        call    error
satt7:  jmp     abort
SATTR   ENDP

;       Send data
;
; set up initial data buffer from file, 'd' state
sdatini proc    near
        mov     flags.filflg,0FFH       ; Indicate file buffer is empty
        mov     pack.state,'D'
        jmp     sdat23                  ; read first buffer from file
sdatini endp

; Send main body of file, 'D' state

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?
        jne     sdata1                  ; It was a ^X or ^Z
        mov     pack.state,'A'          ; It was a ^C -- abort
        ret
sdata1: mov     pack.state,'Z'          ; Else, abort sending the file
        ret
sdata2: mov     dl,maxtry
        cmp     pack.numtry,dl  ; Have we reached the maximum number of tries?
        jl      sdata3                  ; l = no
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     sdat2a                  ; nz = yes. Don't write to screen
        call    erpos
        mov     dx,offset erms14
        mov     ah,prstr
        int     dos                     ; Print an error message
sdat2a: 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     cx,siz                  ; number to transfer
        mov     pack.datlen,cx          ; length of data in packet
        call    movpak                  ; from filbuf to buffer data
        mov     ax,pack.pktnum          ; Get the packet number
        mov     pack.seqnum,ax          ; store in packet
        call    pktsize                 ; report packet size
        mov     ah,'D'                  ; Data packet
        call    sndpak                  ; send the packet
        call    rcvpak                  ; Get a packet
        call    acknak          ; see if ack or nak, check packet number
        cmp     al,0                    ; 0 => ack ok, go on
        je      sdat11                  ; ack, check for data in response
        cmp     al,1            ; 1 => nak, retry count incremented, try again
        jne     sdat15                  ; else look for other packet types
        ret                             ; else return

sdat11: cmp     pack.datlen,0           ; any data in ACK response?
        je      sdat23                  ; e = no
        call    dackmsg                 ; get/show any embedded message
        mov     bl,data                 ; get 1st byte
        cmp     bl,'X'                  ; someone typed control X?
        je      sdat24                  ; e = yes
        cmp     bl,'Z'          ; Control Z? Corrects earlier proto error
        jne     sdat23                  ; not X or Z, just keep going
sdat24: mov     flags.cxzflg,bl         ; set flag appropriately
        mov     pack.state,'Z'          ; simulate eof
        ret                             ; and return

SDAT23: call    gtchr                   ; fill buffer from file
         jmp    sdat12                  ; Error go see if its EOF
         nop                            ; make  three bytes
        mov     siz,ax                  ; Save the size of the data gotten
        ret
sdat12: cmp     ah,0FFH                 ; Is it EOF?
        je      sdat13                  ; e = yes
        jmp     abort                   ; If not give up
sdat13: mov     pack.state,'Z'          ; Set the state to EOF
        ret

sdat15: cmp     ah,'M'                  ; Message packet?
        jne     sdat16                  ; ne = no
        call    dodec                   ; decode it
        jmp     error1                  ; display it and return

sdat16: cmp     ah,'E'                  ; Is it an error packet
        jne     sdat17
        call    dodec                   ; Do all decoding
        call    error                   ; display and change state to Abort
sdat17: jmp     abort
SDATA   ENDP

;       Send EOF

SEOF    PROC    NEAR
        mov     dl,maxtry
        cmp     pack.numtry,dl  ; Have we reached the maximum number of tries?
        jl      seof1                   ; l = no
        test    flags.remflg,dquiet; quiet display mode?
        jnz     seof0                   ; nz = yes. Don't write to screen
        call    erpos                   ; Position cursor
        mov     dx,offset erms14
        mov     ah,prstr
        int     dos                     ; Print an error message
seof0:  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.seqnum,ax
        mov     pack.datlen,0           ; No data
        cmp     flags.cxzflg,0          ; Seen a ^X or ^Z?
        je      seof11                  ; Nope, send normal EOF packet
        mov     data,'D'                ; Use "D" for discard
        mov     pack.datlen,1           ; Set data size to 1
        or      errlev,1                ; say send failed
        or      fsta.xstatus,1+80h      ; set status, failed + intervention
        mov     kstatus,1+80h           ; global status
seof11: mov     cx,pack.datlen          ; Put size in CX
        call    doenc                   ; Encode the packet
        call    pktsize                 ; report packet size
        mov     ah,'Z'                  ; EOF packet
        call    sndpak                  ; send the packet
        call    rcvpak                  ; Get a packet
        call    acknak                  ; see what they had to say
        cmp     al,0                    ; ack?
        je      seof12                  ; e = yes, go close file and proceed
        cmp     al,1                    ; maybe a nak?
        jne     seof3                   ; no, check for error packet
        ret                             ; if nak, just return

seof12: call    dackmsg                 ; get/show any embedded message
        mov     ah,close2               ; DOS 2.0 close file
        push    bx
        mov     bx,diskio.handle        ; file handle
        int     dos
        pop     bx
        call    GTNFIL                  ; Get the next file
         jmp    seof13                  ;  No more
         nop                            ; make three bytes
        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
        or      errlev,1                ; say send failed
        or      fsta.xstatus,1+80h      ; set status, failed + intervention
        mov     kstatus,1+80h           ; global status
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
        mov     difsiz,0                ; clear original filename
        mov     byte ptr sendas,0       ; clear sendas name
        ret
seof3:  cmp     ah,'E'                  ; Is it an error packet?
        jne     seof4
        call    dodec                   ; Decode packet
        call    error
seof4:  jmp     abort
SEOF    ENDP


; Send EOT

SEOT    PROC    NEAR
        mov     dl,maxtry
        cmp     pack.numtry,dl  ; Have we reached the maximum number of tries?
        jl      seot1                   ; l = no
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     seot0                   ; nz = yes. Don't write to screen
        call    erpos                   ; Position cursor
        mov     dx,offset erms14
        mov     ah,prstr
        int     dos                     ; Print an error message
seot0:  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.seqnum,ax
        mov     pack.datlen,0           ; No data
        call    pktsize                 ; report packet size
        mov     ah,'B'                  ; End of Session packet
        call    sndpak                  ; send the packet
        call    rcvpak                  ; Get a packet
        call    acknak                  ; see if good ack or nak
        cmp     al,0                    ; ack'd ok?
        je      seot12                  ; e = yes, done with this
        cmp     al,1                    ; maybe a nak?
        jne     seot3                   ; ne = no, check for error
        ret                             ; else just return
seot12: call    fackmsg                 ; get/show any embedded message
        mov     pack.state,'C'          ; Set state to file completed
        ret

seot3:  cmp     ah,'E'                  ; Is it an error packet
        jne     seot4
        call    dodec                   ; Do all decoding
        call    error
seot4:  jmp     abort
SEOT    ENDP

sndpak  proc    near                    ; send packet with retries
        call    spack
         jmp    updrtr
         nop
        ret
sndpak  endp

rcvpak  proc    near                    ; receive packet with retries
        call    rpack                   ; Get a packet
         jmp    updrtr                  ;  Trashed packet, retry
         nop
        ret
rcvpak  endp

; check the current packet for an ack or nak and handle it from any of
; the send states.  Returns: 0 if an ack received with the correct expected
; packet number, or if a nak received with the NEXT packet number (the
; packet number is incremented, retry count reset); 1 if a nak or ack
; with a bad packet number is received, retry count is updated and displayed.
; A Timeout packet (type 'T') simply invokes a retry and a returned 1.
; Finally, 2 is returned if anything else is seen
;

ACKNAK  PROC    NEAR
        cmp     ah,'Y'                  ; ack packet?
        jne     ackna1                  ; ne = no, keep going
        mov     bx,pack.pktnum
        cmp     bx,pack.seqnum          ; is it what we were expecting?
        jne     ackna2                  ; no, update retries and punt
                                        ; packet ok, increment packet number
ackna0: mov     bx,pack.pktnum          ; reload packet number (!!!)
        inc     bx
        and     bx,03fh                 ; increment packet number
        mov     pack.pktnum,bx          ; store back
        inc     pack.numpkt             ; increment # of packets
        mov     pack.numtry,0
        mov     al,0                    ; ack'd ok
        ret
                                        ; not a 'Y'..
ackna1: cmp     ah,'N'                  ; a nak?
        je      ackna5                  ; yes, go on
        cmp     ah,'T'                  ; Timeout?
        je      ackna3                  ; e = yes, not a NAK but do a retry
        mov     al,2
        ret                             ; unknown packet type
ackna5: mov     bx,pack.pktnum
        inc     bx
        and     bx,3fh
        inc     fsta.nakrcnt            ; count received NAK for statistics
        cmp     bx,pack.seqnum          ; maybe a nak for pktnum+1?
        je      ackna0                  ; yes, treat as ack
        jne     ackna3
                                        ; nak or bad ack, update retry stuff
ackna2: inc     fsta.nakrcnt            ; count received NAK for statistics
ackna3: push    ax
        call    rtpos                   ; Position cursor
        inc     pack.numrtr             ; Increment the number of retries
        mov     ax,pack.numrtr
        call    nout                    ; Write the number of retries
        pop     ax
        mov     al,1                    ; nak code
        ret                             ; and return
ACKNAK  ENDP

; Display message in ACK's to D packets. Requires a leading protocol char
; and expects message to be encoded.
dackmsg proc near
        cmp     pack.datlen,1           ; any embedded message?
        jbe     dackmsgx                ; be = no (skip single char msgs)
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     dackmsgx                ; nz = yes, don't write to screen
        call    dodec                   ; decode message, including X/Z/other
        push    ax
        push    dx
        call    cxmsg                   ; clear message space in warning area
        mov     dx,offset data+1        ; point to asciiz message
        call    prtasz                  ; display it
        pop     dx
        pop     ax
dackmsgx:ret
dackmsg endp

; Display messages in ACKs to F packets. Expects message to be not encoded.
fackmsg proc    near                    ; look for in ack
        cmp     pack.datlen,0           ; any embedded message?
        je      fackmsgx                ; e = no
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     fackmsgx                ; nz = yes, don't write to screen
        push    ax
        push    dx
        call    cxmsg                   ; clear message space in warning area
        push    bx                      ; F pkt acks are not decoded so
        mov     bx,pack.datlen          ; they have no null terminator
        mov     data[bx],0              ; make a terminator
        pop     bx
        mov     dx,offset data          ; point to asciiz message
        call    prtasz                  ; display it
        pop     dx
        pop     ax
fackmsgx:ret
fackmsg endp

; newfn -- move replacement name from buffer sendas to buffer data
; update cx to new filename length
newfn:  cmp     difsiz,0                ; Sending file under different name?
        je      newf4                   ; e = no, so don't give new name
        mov     si,offset sendas        ; source field
        mov     di,offset fsta.xname    ; statistics name area
        call    strcpy
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     newfa                   ; nz = yes. Don't write to screen
        mov     ah,prstr
        mov     dx,offset asmsg         ; display ' as '
        cmp     mailflg,0               ; mail?
        je      newfn1                  ; e = no
        mov     dx,offset mailto        ; display ' To: '
newfn1: int     dos
        mov     ah,conout               ; use printable output
        cmp     mailflg,0               ; mail?
        je      newfa                   ; e = no
        mov     dx,offset sendas        ; get name
        call    prtasz                  ; print asciiz string
        jmp     newf4                   ; don't replace filename
newfa:  mov     si,offset sendas        ; Buffer where the name is
        mov     di,offset data
        mov     cx,difsiz               ; Length of name
        inc     cx                      ; plus null terminator
newf0:  lodsb                           ; Get a character into al
        stosb
        test    flags.remflg,dquiet    ; quiet display mode (should we print)?
        jnz     newf2                   ; nz = yes
        mov     dl,al                   ; set into dl for display
        int     dos                     ; Print them
newf2:  loop    newf0
        mov     cx,difsiz               ; Reset the length field
newf4:  test    flags.remflg,dserial    ; serial display mode?
        jz      newf5                   ; z = no
        mov     dx,offset crlf          ; start with cr/lf for serial display
        mov     ah,prstr
        int     dos
newf5:  ret

; Do encoding.  Expect CX to be the data size

doenc:  jcxz    doen0
        mov     chrcnt,cx               ; Number of bytes of source data
        mov     bufpnt,offset data      ; Source of data
        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.datlen,ax          ; Store length of data field
        mov     cx,ax
        call    movpak                  ; Move to data part of packet
        cmp     chrcnt,0                ; Did all chars fit into the buffer?
        jne     doen1                   ; ne = no, we have an error condition
        clc                             ; clear c bit for success
doen0:  ret
doen1:  stc                             ; set c bit for did not fit condition
        ret

; CX is set before this is called
movpak: push    es
        mov     ax,ds
        mov     es,ax
        cld
        mov     si,offset filbuf        ; Move from here
        mov     di,offset data          ; to here
        shr     cx,1                    ; divide by two (words), set carry
        jnc     movpak1                 ; nc = even number of bytes
        movsb                           ; do the single move for odd count
movpak1:cmp     cx,0
        jle     movpak2
        rep     movsw
movpak2:pop     es
        ret

; Dodecoding
dodec:
        push    ax                      ; Save packet size
        mov     bx,offset data          ; Address of data
        mov     ax,offset nulr          ; Routine to dump buffer (null)
        mov     bufpnt,offset decbuf    ; Where to put output
        mov     chrcnt,maxpack          ; Buffer size
        mov     cx,pack.datlen          ; Size of data
        jcxz    dodc0                   ; z = nothing to transfer
        call    decode
         nop
         nop
         nop
dodc0:  call    decmov          ; Move decoded data back to "data" buffer
        pop     ax
        ret

; Move decoded data from decode buffer back to "data".
decmov: push    si
        push    di
        push    es
        mov     ax,ds
        mov     es,ax
        cld
        mov     cx,bufpnt               ; Last char we added
        sub     cx,offset decbuf        ; Get actual number of characters
        mov     pack.datlen,cx          ; Remember size of real data
        mov     si,offset decbuf        ; Data is here
        mov     di,offset data          ; Move to here
        shr     cx,1                    ; divide by two (words), set carry
        jnc     decmov1                 ; nc = even number of bytes
        movsb                           ; do single move
decmov1:cmp     cx,0
        jle     decmov2                 ; le = none to do
        rep     movsw                   ; Copy the data
decmov2:mov     al,0                    ; Null to end the string
        stosb
        pop     es
        pop     di
        pop     si
        ret

;       Abort

ABORT   PROC    NEAR
        mov     difsiz,0                ; clear original filename
        mov     byte ptr sendas,0       ; clear sendas name
        mov     mailflg,0               ; clear Mail flag
        cmp     filopn,0                ; Any disk files open?
        je      abort0                  ; No so don't do a close
        mov     ah,close2               ; DOS 2.0 close file
        push    bx
        mov     bx,diskio.handle        ; file handle
        int     dos
        pop     bx
        mov     filopn,0                ; say file is closed now
abort0: mov     pack.state,'A'          ; Otherwise abort
        or      errlev,1                ; set DOS error level
        or      fsta.xstatus,1          ; set status
        mov     kstatus,1               ; global status
        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

ERROR   PROC    NEAR
        mov     pack.state,'A'          ; Set the state to abort
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     errorx                  ; nz = yes. Don't write to screen
        call    erpos                   ; Position the cursor
        jmp     error2
ERROR1: mov     ah,prstr                ; entry point for Server Generic cmds
        mov     dx,offset crlf
        int     dos
error2: mov     dx,offset data          ; error message string
        push    bx
        mov     bx,pack.datlen          ; Get the length of the data
        add     bx,dx                   ; Get to the end of the string

        mov     byte ptr [bx],0         ; terminate string
        pop     bx
        call    prtasz                  ; print asciiz string
        pop     bx
errorx: ret
ERROR   ENDP

; Set the maximum send data packet size; modified for long packets
PACKLEN PROC    NEAR
        push    ax
        push    cx
        xor     ah,ah
        mov     al,trans.spsiz  ; Maximum send packet size for Regular pkts.
        cmp     ax,trans.slongp         ; negotiated long packet max size
        jae     pack2                   ; ae = use regular packets
        mov     ax,trans.slongp         ; else use long kind
        sub     ax,3                    ; minus extended count & checksum
        cmp     ax,(95*94-1-2)          ; longer than Long?
        jle     pack2                   ; le = no, Long will do
        dec     ax                      ; minus one more for extra long count
pack2:  sub     ax,2                    ; minus Sequence, Type
        sub     al,trans.chklen         ; And minus Checksum chars
        sbb     ah,0                    ; borrow propagate
        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
        dec     ax                      ; Another 1 for 8th-bit Quoting.
pack0:  cmp     rptq,0                  ; Doing repeat character Quoting?
        je      pack1                   ; Nope, so that's all for now
        dec     ax                      ; minus repeat prefix
        dec     ax                      ;  and repeat count
pack1:  dec     ax                  ; for last char might being a control code
        mov     trans.maxdat,ax         ; Save max length for data field
        pop     cx
        pop     ax
        ret
PACKLEN ENDP

 ; Print the number in AX on the screen in decimal rather that hex

NOUT    PROC    NEAR
        test    flags.remflg,dserial    ; serial display mode?
        jnz     pnout               ; nz = use "dot and plus" for serial mode
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     nout1                   ; nz = yes. Don't write to screen
        call    decout                  ; call standard decimal output routine
nout1:  ret


pnout:  or      ax,ax                   ; display packet in serial display mode
        jz      pnoutx                  ; z = nothing to display
        push    ax                      ; for serial mode display
        push    dx                      ; output .........+.........+  etc
        mov     temp,10
        mov     dx,0
        div     temp                    ; number/10. (AX=quo, DX=rem)
        push    ax                      ; save around printing
        push    dx                      ; save around initial printing
        cmp     dx,0                    ; remainder non-zero?
        jne     pnout1                  ; ne = yes
        mov     dl,'+'                  ; symbol plus for tens
        jmp     pnout2                  ; display it
pnout1: mov     dl,'.'                  ; symbol for between tens
pnout2: mov     ah,conout               ; output to console
        int     dos
        pop     dx                      ; recover remainder
        pop     ax                      ; recover quotient
        or      dx,dx           ; check for multiples of 70, to break lines
        jnz     pnout3                  ; nz = non-zero remainder, just exit
        mov     temp,7          ; divide ax by 7 (dx is zero by construction)
        div     temp                    ; ax = quotient, dx = remainder
        or      dx,dx                   ; zero remainder?
        jnz     pnout3                  ; nz = non-zero remainder, just exit
        mov     ah,prstr                ; output cr/lf after every 70th chars
        mov     dx,offset crlf
        int     dos
pnout3: pop     dx
        pop     ax
pnoutx: ret
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
code    ends
        end