; File MSSSEN.ASM
; Edit history:
;
; [2.28 jrd]
; Remove DOS 1.x fcb material
; Add support for remote display requests (Type, Dir, etc); use flags.xflg = 1
; J R Doupnik Oct 1985
; Reset flags.xflg if normal file send; (inserted just above send11:).
;       Contributed by M. T. Donovan. Done on 26 Dec 1985 [jrd]
; Added tests on remote mode to suppress message display if mode = remote.
; Add constant decsiz (def'd in header) for easy adjustment. 29 Dec 1985 [jrd]
; Rewrite user interface of Send command. Send can be forced to prompt for
;  local and remote filenames, just like Get. Remote filenames are stripped
;  of leading whitespace but may contain embedded spaces. The first character
;  of the local and remote filenames can be '#' which will be replaced by '?'
;  after the line is scanned.
; Insert (missed out) help message address in Send's prompting. 5 Jan 86
; [v2.28]
; Make packlen less conservative so we can send bigger packets.
;     (from Edgar Butt, Univ. of Md.)
; Make ack's with incorrect sequence number update the retry counter
; like a nak would; don't increment the retry count when a nak is
; found for the expected packet + 1, since this is the same as
; an ack.
; Moved handling of ack/nak to a subroutine and took out a ton
; of repeated code (motivated by a suggestion from Edgar Butt).
; JD 1 May 1985
;
        public  spar, rpar, error, error1, nout, send, flags, trans, pack
        public  dodec, doenc, curchk, inichk, packlen, send11, dtrans
        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, comand:byte
        extrn   decbuf:byte, chrcnt:word, bufpnt:word
        extrn   rptq:byte, origr:byte, rptct:byte, rptval:byte
        extrn   diskio:byte

flags   flginfo <>
trans   trinfo  <>
dtrans  trinfo  <>                      ; default trans info
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      ' A filename (possibly wild) $'
filmsg  db      ' Filename for remote system or a carriage return $'
remfnm  db      ' Remote Destination File: $'
lclfnm  db      ' Local Source File: $'

curchk  db      0               ; Use to store checksum length.
inichk  db      1               ; Original or set checksum length.
siz     dw      ?               ; Size of data from gtchr.
temp    dw      0
temp4   dw      0
sendas  dw      50 dup(0)       ; Buffer for file name.
difsiz  dw      0               ; Size of new exported 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, prompt:near
        extrn perpos:near, prtfn:near, strcpy:near, strlen: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.
; 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...

SPAR    PROC    NEAR
        mov temp4,ax            ; Save the number of arguments. [jrd]
        cmp ax,1
        jge sparx
        mov ah,dspsiz           ; Data not supplied by host, use default.
        jmp sparx2
sparx:  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
        mov ah,dtrans.stime     ; pick up default stime
        cmp al,2                ; Fewer than two pieces?
        jl spar02               ; yes, use default
spar0:  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
        mov ah,dtrans.spad      ; get default send padding
        cmp al,3                ; Fewer than three pieces?
        jl spar11               ; yes, use default
spar1:  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
        mov ah,dtrans.spadch    ; pick up default send pad character
        cmp al,4                ; Fewer than four pieces?
        jl spar21
spar2:  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
        mov ah,dtrans.seol      ; get default send eol char
        cmp al,5                ; Fewer than five pieces?
        jl spar31               ; yes, use default
spar3:  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
        mov ah,dtrans.squote    ; send quote
        cmp al,6                ; Fewer than six pieces?
        jl spar41
spar4:  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 difsiz,0            ; Assume we'll use original filename.
        mov byte ptr sendas,0   ; clear sendas name (in case none)
        mov ah,cmfile           ; get an input file spec [jrd]
        mov dx,offset diskio.string ; address of filename string [jrd]
        mov bx,offset filmsg    ; Text of help message.
        call comnd
         jmp r                  ;  Give up on bad parse.
        cmp flags.cxzflg,0      ; ^X, ^Z, ^C typed?
        je send0                ; e = no, continue
        jmp rskp                ; yes, quit
send0:  cmp ah,0                ; any text given?
        ja send0c               ; a = yes

        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 (was missed out) [jrd]
        mov ah,cmfile           ; allow paths [jrd]
        call comnd              ; try again for a local filename
         jmp r
        cmp flags.cxzflg,0      ; ^X, ^Z, ^C typed?
        je send0a               ; e = no, continue
        jmp rskp                ; yes, quit
send0a: cmp ah,0                ; user's byte count
        jne send0b              ; something was typed
        jmp r                   ; else return (gives "not confirmed" msg)

send0b: mov dx,offset remfnm    ; ask for remote name first
        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.
        mov ah,cmtxt            ; allow embedded white space. [jrd]
        call comnd
         jmp r
        cmp flags.cxzflg,0      ; ^X, ^Z, ^C typed?
        je send1                ; e = no, continue
        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 bx,offset sendas    ; look at string again.
        push es
        push di
        push si
        mov ax,ds               ; use segment 'datas' for es:
        mov es,ax
        mov si,bx               ; look at start of string, remove whitespace
send1c: cmp byte ptr [si],0     ; at terminator?
        je send1d               ; e = yes
        cmp byte ptr [si],' '   ; text (greater than space)?
        ja send1d               ; a = yes.
        inc si                  ; look at next char
        jmp send1c              ; look some more
send1d: cmp bx,si               ; did we find leading whitespace?
        je send1e               ; e = no
        mov di,bx               ; place to copy chars
        call strcpy             ;   from ds:si to ds:di
send1e: mov dx,bx               ; address of string
        call strlen             ; get its new length (returned in cx)
        mov difsiz,cx           ; store it
        pop si
        pop di
        pop es
        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 send11
        mov byte ptr [bx],'?'   ; yes. Change '#' for '?'

                                ; SEND11 is an entry point for REMote cmds.
SEND11: mov flags.nmoflg,0      ; Reset flags from fn parsing. [21a]
        cmp flags.remflg,0      ; In remote mode? [jrd]
        je snd11a               ; e = no
        mov difsiz,0            ; clear any old 'sendas' filespec
snd11a: mov ah,setdma           ; set dta address [jrd]
        mov dx,offset diskio.dta        ; [jrd]
        int dos                 ; [jrd]
        mov ah,first2           ; search for first [jrd]
        mov cx,0                ; consider only regular files [jrd]
        mov dx,offset diskio.string     ; full filename, inc paths [jrd]
        int dos
        pushf                   ; save flags [jrd]
        push dx                 ; [jrd]
        mov ah,setdma           ; restore dta to offset buff [jrd]
        mov dx,offset buff
        int dos
        pop dx                  ; [jrd]
        popf                    ; restore flags [jrd]
        jnc send16              ; carry reset = file found [jrd]
        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    ; '?Unable to find file' [jrd]
        int dos
        jmp rskp                ; pretend successful completion. [jrd]

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
        cmp flags.remflg,0      ; In remote mode? [jrd]
        jne sinit1              ; ne = yes. Don't write to screen. [jrd]
        call erpos
        mov dx,offset erms14
        mov ah,prstr
        int dos                 ; Print an error message.
sinit1: mov bx,offset erms20    ; [jrd]
        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
        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.argbk1
        mov bx,offset data      ; point to data for spar
        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.
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
        cmp flags.remflg,0      ; In remote mode? [jrd]
        jne sfile0              ; ne = yes. Don't write to screen. [jrd]
        call erpos
        mov dx,offset erms14
        mov ah,prstr
        int dos                 ; Print an error message.
sfile0: mov bx,offset erms21    ; [jrd]
        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 cl,0                ; Counter for chars in file name.
        mov si,offset diskio.name ;address of asciiz filename without paths[jrd]
        mov di,offset data      ; destination [jrd]
        call strcpy             ; copy filename there
        push dx
        mov dx,offset data
        call strlen             ; get length (w/o terminator) into cx
        pop dx
sfil13: mov ch,0
        cmp flags.remflg,0      ; remote mode?
        jne sfil13a             ; yes, no printing.
        call prtfn              ; print filename in data
sfil13a:cmp difsiz,0            ; Sending file under different name?
        je sfl13x               ; e = 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.
        cmp flags.xflg,0        ; remote display requested? [jrd]
        je sfl13y               ; e = no [jrd]
        mov ah,'X'              ; use X rahter than F packet for remote [jrd]
sfl13y: 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.
        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: mov flags.filflg,0FFH   ; 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

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
        cmp flags.remflg,0      ; In remote mode? [jrd]
        jne sdat2a              ; ne = yes. Don't write to screen. [jrd]
        call erpos
        mov dx,offset erms14
        mov ah,prstr
        int dos                 ; Print an error message.
sdat2a: mov bx,offset erms22    ; [jrd]
        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 si,offset filbuf    ; source of characters [jrd]
        mov di,offset data      ; destination [jrd]
        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.
        call acknak             ; see if ack or nak, check packet number
        cmp al,0                ; 0 => ack ok, go on
        je sdat11
        cmp al,1                ; 1 => nak, retry count incremented, try again
        jne sdat15              ; else look for other packet types...
        ret                     ; else return

sdat11: cmp     pack.argbk1,1   ; any data?
        jne     sdat23
        mov     bl,data         ; get 1st byte
        cmp     bl,'X'          ; some typed control X? [jrd]
        je      sdat24          ; e = yes.
        cmp     bl,'Z'          ; control Z? Corrects earlier proto error [jrd]
        jne     sdat23          ; not x or z, just keep going [jrd]
sdat24: mov     flags.cxzflg,bl ; set flag appropriately
        mov     pack.state,'Z'  ; simulate eof
        ret                     ; and return

sdat23: call gtchr
         jmp sdat12             ; Error go see if its EOF.
         nop                    ; make three bytes [jrd]
        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

sdat15: cmp ah,'E'              ; Is it an error packet.
        jne sdat16
        call error
sdat16: jmp abort
SDATA   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.
; Finally, 2 is returned if anything else is seen.
;

ACKNAK  PROC    NEAR
        cmp     ah,'Y'          ; ack packet?
        jne     ackna1          ; no, keep going
        mov     bx,pack.pktnum
        cmp     bx,pack.argblk  ; 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     bl,0
        xchg    bl,pack.numtry
        mov     pack.oldtry,bl  ; oldtry <- numtry, numtry <- 0
        mov     al,0            ; ack'd ok
        ret
; not a 'Y'...
ackna1: cmp     ah,'N'          ; a nak?
        je      ackna5          ; yes, go on
        mov     al,2
        ret                     ; unknown packet type
ackna5: mov     bx,pack.pktnum
        inc     bx
        and     bx,3fh
        cmp     bx,pack.argblk  ; maybe a nak for pktnum+1?
        je      ackna0          ; yes, treat as ack
; nak or bad ack, update retry stuff
ackna2: call rtpos              ; Position cursor.
        inc pack.numrtr         ; Increment the number of retries
        mov ax,pack.numrtr
        call nout               ; Write the number of retries.
        mov     al,1            ; nak code
        ret                     ; and return
ACKNAK  ENDP

;       Send EOF

SEOF    PROC    NEAR
        cmp pack.numtry,maxtry  ; Have we reached the maximum number of tries?
        jl seof1
        cmp flags.remflg,0      ; In remote mode? [jrd]
        jne seof0               ; ne = yes. Don't write to screen. [jrd]
        call erpos              ; Position cursor.
        mov dx,offset erms14
        mov ah,prstr
        int dos                 ; Print an error message.
seof0:  mov bx,offset erms23    ; [jrd]
        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.
        call acknak             ; see what they had to say
        cmp al,0                ; ack?
        je seof12               ; 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: mov ah,close2           ; DOS 2.0 close file [jrd]
        push bx                 ; [jrd]
        mov bx,diskio.handle    ; file handle [jrd]
        int dos
        pop bx                  ; [jrd]
        call gtnfil             ; Get the next file.
         jmp seof13             ;  No more.
         nop                    ; make three bytes [jrd]
        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
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
        cmp flags.remflg,0      ; In remote mode? [jrd]
        jne seot0               ; ne = yes. Don't write to screen. [jrd]
        call erpos             ; Position cursor.
        mov dx,offset erms14
        mov ah,prstr
        int dos                 ; Print an error message.
seot0:  mov bx,offset erms24    ; [jrd]
        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.
        call acknak             ; see if good ack or nak
        cmp al,0                ; ack'd ok?
        je seot12               ; yes, done with this
        cmp al,1                ; maybe a nak?
        jne seot3               ; no, check for error
        ret                     ; else just return
seot12: mov pack.state,'C'      ; Set the state to file send.
        ret

seot3:  cmp ah,'E'              ; Is it an error packet.
        jne seot4
        call error
seot4:  jmp abort
SEOT    ENDP

tryagn: call updrtr
        ret

; newfn -- move replacement name from buffer sendas to buffer data

newfn:  cmp flags.remflg,0      ; In remote mode? [jrd]
        jne newfa               ; ne = yes. Don't write to screen. [jrd]
        mov ah,prstr
        mov dx,offset asmsg     ; display ' as '
        int dos
        mov ah,dconio
newfa:  mov si,offset sendas    ; Buffer where the name is. [jrd]
        mov di,offset data
        mov cx,difsiz           ; Length of name.
        inc cx                  ; plus null terminator [jrd]
newf0:  lodsb                   ; Get a char.
        cmp al,'a'              ; before little a?
        jb newf1                ; Leave alone if less than 'a'?
        cmp al,'z'              ; after little z?
        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 cx,difsiz           ; Reset the length field.
        ret

; Do encoding.  Expect 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,decsiz       ; Buffer size. [jrd]
        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.
        mov si,offset decbuf    ; Data is here. [jrd]
        mov di,offset data      ; Move to here. [jrd]
        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,close2           ; DOS 2.0 close file [jrd]
        push bx                 ; [jrd]
        mov bx,diskio.handle    ; file handle [jrd]
        int dos
        pop bx                  ; [jrd]
        mov filopn,0            ; say file is closed now
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.
        cmp flags.remflg,0      ; In remote mode? [jrd]
        jne errorx              ; ne = yes. Don't write to screen. [jrd]
        call erpos              ; Position the cursor.
        jmp error2
error1: cmp flags.remflg,0      ; In remote mode? [jrd]
        jne errorx              ; ne = yes. Don't write to screen. [jrd]
        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
errorx: ret                     ; [jrd]
ERROR   ENDP

; Set the maximum data packet size. [21b]

PACKLEN PROC    NEAR
        mov ah,trans.spsiz      ; Maximum send packet size.
        sub ah,3                ; - sequence, type, possible #X at end
        sub ah,trans.chklen     ; And minus checksum chars.
        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]
        cmp flags.remflg,0      ; In remote mode? [jrd]
        jne nout1               ; ne = yes. Don't write to screen. [jrd]
        push ax
        push dx
        mov temp,10             ; Divide quotient by 10.
        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



