home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / ccdos / ccssen.asm < prev    next >
Assembly Source File  |  2020-01-01  |  73KB  |  1,561 lines

  1.         NAME    ccssen
  2. ; File CCSSEN.ASM
  3.  
  4. ;CHINESE
  5. ifdef   MSDOS
  6.         include msssen.dat
  7. else
  8.         include ccssen.dat
  9. endif
  10.  
  11. code    segment public 'code'
  12.         extrn serini:near, serrst:near, comnd:near, init:near
  13.         extrn spack:near, rpack:near, gtnfil:near, gtchr:near
  14.         extrn getfil:near, clrfln:near, nppos:near, rprpos:near, prtasz:near
  15.         extrn erpos:near, rtpos:near, cxmsg:near, stpos:near, decout:near
  16.         extrn encode:near, nulref:near, decode:near, nulr:near, lnout:near
  17.         extrn errpack:near, updrtr:near, clrmod:near, prompt:near
  18.         extrn prtfn:near, strcpy:near, strlen:near, strcat:near, pktsize:near
  19.         extrn pcwait:near, ihosts:near, begtim:near, endtim:near
  20.  
  21.         assume  cs:code, ds:datas
  22.  
  23. ;       This routine sets up the data for init packet (either the
  24. ;       Send_init or ACK packet)
  25. ; trans.rxxx are items we are prepared to receive
  26. ; Lines marked ;M energize the second CAPAS byte. Leave them as comments for
  27. ; because earlier versions of MS Kermit (and C Kermit) are confused by it
  28. ; (fail to decode bit saying second CAPAS byte follows and thus loose sync).
  29. RPAR    PROC    NEAR
  30.         mov     ah,trans.rpsiz          ; Get the receive packet size
  31.         add     ah,' '                  ; Add a space to make it printable
  32.         mov     [bx],ah                 ; Put it in the packet
  33.         mov     ah,trans.rtime          ; Get the receive packet time out
  34.         add     ah,' '                  ; Add a space
  35.         mov     1[bx],ah                ; Put it in the packet
  36.         mov     ah,trans.rpad           ; Get the number of padding chars
  37.         add     ah,' '
  38.         mov     2[bx],ah                ; Put it in the packet
  39.         mov     ah,trans.rpadch         ; Get the padding char
  40.         add     ah,40h                  ; Uncontrol it
  41.         and     ah,7FH
  42.         mov     3[bx],ah                ; Put it in the packet
  43.         mov     ah,trans.reol           ; Get the EOL char
  44.         add     ah,' '
  45.         mov     4[bx],ah                ; Put it in the packet
  46.         mov     ah,trans.rquote         ; Get the quote char
  47.         mov     5[bx],ah                ; Put it in the packet
  48.         mov     ah,trans.ebquot         ; Get 8-bit quote char
  49.         mov     6[bx],ah                ; Add it to the packet
  50.         mov     ah,trans.chklen         ; Length of checksum
  51.         add     ah,'0'                  ; Make into a real digit
  52.         mov     7[bx],ah
  53.         mov     ah,rptq                 ; Repeat quote char
  54.         cmp     ah,0                    ; Null means no
  55.         jne     rpar0
  56.         mov     ah,' '                  ; Send a blank instead
  57. rpar0:  mov     8[bx],ah
  58.                                         ; begin long packet changes
  59.         mov     ah,2                    ; CAPAS, bit1 = can do long packets
  60.         cmp     flags.attflg,0          ; allowing attributes packets?
  61.         je      rpar1                   ; e = no
  62.         or      ah,8                    ; bit #3, can do file attributes
  63. rpar1:
  64. ;M      or      ah,1                    ; say second CAPAS byte follows
  65.         add     ah,20h                  ; apply tochar() to byte
  66.         mov     9[bx],ah                ; add to packet
  67.                                         ; additional CAPAS go in here
  68.         mov     byte ptr 10[bx],20h+20h ; Allow M (message) pkts, (#6, bit5)
  69.         mov     ah,20h          ; WINDO field, null applied through tochar()
  70. ;M      mov     11[bx],ah               ; put into packet
  71.         mov     10[bx],ah               ; put into packet
  72.  
  73.         push    ax                      ; save some regs
  74.         push    dx
  75.         mov     ax,trans.rlongp     ; long packet length which we can receive
  76.         xor     dx,dx                   ; clear extended part for division
  77.         div     ninefive                ; divide by 95. quo = ax, rem = dx
  78.         add     al,20h                  ; apply tochar() to quotient
  79. ;M      mov     12[bx],al               ; add to packet
  80.         mov     11[bx],al               ; add to packet
  81.         add     dl,20h                  ; apply tochar() to remainder
  82. ;M      mov     13[bx],dl               ; add to packet
  83.         mov     12[bx],dl               ; add to packet
  84.         pop     dx                      ; restore regs
  85.         pop     ax
  86. ;M      mov     ah,14                   ; 14 bytes of data
  87.         mov     ah,13                   ; 13 bytes of data
  88.         ret
  89. RPAR    ENDP
  90.  
  91. ; This routine reads in all the send init packet information
  92. ; Enter with BX/ packet address, AX/ packet length
  93. ; This could probably be done much more legibly if it were table
  94. ; driven, but I'm afraid to touch it..
  95. ;
  96. ; dtrans.xxx are the default parameters if the other side says nothing
  97. ; trans.sxxx are the active negotiated parameters we will use for sending.
  98. SPAR    PROC    NEAR
  99.         mov     temp4,ax                ; Save the number of arguments
  100.         mov     ah,al                   ; number of args is now in ah
  101.         push    si
  102.         mov     si,bx
  103.         cld
  104.         cmp     ah,0                    ; any data?
  105.         jg      spara                   ; g = yes, want more than bare minimum
  106.         mov     al,dspsiz               ; nothing supplied by host use default
  107.         jmp     short sparc
  108. spara:  lodsb                           ; get the max packet size
  109.         dec     ah                      ; ah = bytes remaining to be examined
  110.         sub     al,' '                  ; subtract a space
  111.         cmp     al,spmin                ; below the minimum?
  112.         jge     sparb                   ; ge = no
  113.         mov     al,spmin
  114.         jmp     short sparc
  115. sparb:  cmp     al,spmax                ; or above the maximum?
  116.         jle     sparc                   ; le = no
  117.         mov     al,spmax
  118. sparc:  mov     trans.spsiz,al          ; save it
  119.         mov     al,dtrans.stime         ; pick up default stime
  120.         cmp     ah,0                    ; more data?
  121.         jle     spar02                  ; le = no, use default
  122.         lodsb                           ; get the timeout value
  123.         dec     ah
  124.         sub     al,' '                  ; subtract a space
  125.         cmp     dtrans.stime,dstime     ; Is current value the default?
  126.         je      spar0                   ; e = yes, else user value overrides
  127.         mov     al,dtrans.stime         ; pick up user selected stime
  128. spar0:  cmp     al,0
  129.         jg      spar01                  ; must be non-negative
  130.         mov     al,0                    ; negative, so use zero
  131. spar01: cmp     al,trans.rtime          ; same as other side's timeout
  132.         jne     spar02                  ; ne = no
  133.         add     al,1                    ; yes, but make it a little different
  134. spar02: mov     trans.stime,al          ; save it
  135.         mov     al,dtrans.spad          ; get default send padding
  136.         cmp     ah,0                    ; more data?
  137.         jle     spar11                  ; le = no, use default
  138. spar1:  lodsb                           ; get the number of padding chars
  139.         dec     ah
  140.         sub     al,' '
  141.         cmp     al,0
  142.         jge     spar11                  ; must be non-negative
  143.         mov     al,0
  144. spar11: mov     trans.spad,al
  145.         mov     al,dtrans.spadch        ; pick up default send pad character
  146.         cmp     ah,0                    ; more data?
  147.         jle     spar21                  ; le = no, use default
  148. spar2:  lodsb                           ; get the padding char
  149.         dec     ah
  150.         add     al,40h                  ; remove ascii bias
  151.         and     al,7FH
  152.         cmp     al,del                  ; Delete?
  153.         je      spar21                  ; e = yes, then it's OK
  154.         cmp     al,31                   ; control char?
  155.         jle     spar21                  ; le = yes, then OK
  156.         mov     al,0                    ; no, use null
  157. spar21: mov     trans.spadch,al
  158.         mov     al,dtrans.seol          ; get default send eol char
  159.         cmp     ah,0                    ; more data?
  160.         jle     spar31                  ; le = no, use default
  161. spar3:  lodsb                           ; get the EOL char
  162.         dec     ah
  163.         sub     al,' '
  164.         cmp     al,31                   ; control char?
  165.         jle     spar31                  ; le = yes, then use it
  166.         mov     al,cr                   ; else use the default
  167. spar31: mov     trans.seol,al
  168.         mov     al,dtrans.squote        ; send quote
  169.         cmp     ah,0                    ; more data?
  170.         jle     spar41                  ; le = no, use default
  171. spar4:  lodsb                           ; get the quote char
  172.         dec     ah
  173.         cmp     al,' '                  ; less than a space?
  174.         jge     spar40                  ; ge = no
  175.         mov     al,dsquot               ; yes, use default
  176.         jmp     spar41
  177. spar40: cmp     al,7eh                  ; must also be less than a tilde
  178.         jbe     spar41                  ; be = is ok
  179.         mov     al,dsquot               ; else use default
  180. spar41: mov     trans.squote,al
  181.         cmp     ah,0                    ; more data?
  182.         jg      spar5                   ; g = yes
  183.         mov     al,dtrans.ebquot        ; use default
  184.         mov     trans.ebquot,al
  185.         jmp     short spar51
  186. spar5:  lodsb                           ; get other side's 8-bit quote request
  187.         dec     ah
  188.         call    doquo                   ; and set quote char
  189. spar51: cmp     ah,0                    ; more data?
  190.         jg      spar6                   ; g = yes
  191.         mov     trans.chklen,1          ; use default
  192.         jmp     short spar61
  193. spar6:  mov     al,inichk
  194.         mov     trans.chklen,al         ;checksum length we really want to use
  195.         lodsb                           ; get other side's checksum length
  196.         dec     ah
  197.         call    dochk                   ; determine what size to use
  198. spar61: cmp     ah,0                    ; more data?
  199.         jg      spar7                   ; g = yes
  200.         mov     rptq,0
  201.         jmp     short spar71
  202. spar7:  lodsb                           ; get other side's repeat count prefix
  203.         dec     ah
  204.         mov     ch,drpt                 ; default repeat count prefix
  205.         mov     rptq,0                  ; clear active repeat count prefix
  206.         call    dorpt                   ; negotiate new prefix
  207. spar71: mov     al,0                    ; get default operating Capabilities
  208.         cmp     ah,0                    ; more data?
  209.         jle     spar81                  ; le = no, use default
  210.         lodsb                           ; get capas bitmap from other side
  211.         dec     ah
  212.         and     al,not (1)              ; remove least significant bit
  213.         sub     al,20h                  ; apply unchar()
  214. spar81: mov     trans.capas,al          ; store result in active byte
  215. spar82: cmp     ah,0                    ; more data?
  216.         jle     spar85                  ; le = no
  217.         test    byte ptr [si-1],1       ; is CAPAS byte continued to next?
  218.         jz      spar85                  ; z = no
  219.         lodsb                           ; get 2nd CAPAS bitmap from other side
  220.         dec     ah
  221.         and     al,not (1)              ; remove least significant bit
  222.         sub     al,20h                  ; apply unchar(). Store nothing
  223.         jmp     short spar82            ; seek more CAPAS bytes
  224. spar85: mov     al,0                    ; setup default window size
  225.         cmp     ah,0                    ; more data?
  226.         jle     spar9                   ; le = no, use default
  227.         lodsb                           ; get other side's window size
  228.         dec     ah
  229.         sub     al,20h                  ; apply unchar()
  230.         call    dewind                  ; negotiate window size back into al
  231. spar9:  mov     trans.windo,al          ; store it
  232.                                         ; decode window info
  233.         push    cx                      ; save a reg
  234.         xor     ch,ch
  235.         mov     cl,trans.spsiz          ; normal packet size
  236.         mov     trans.slongp,cx         ; assume not using long packets
  237.         pop     cx                      ; restore reg
  238.         cmp     ah,2                    ; more data (long packet needs two)?
  239.         jae     spar9d                  ; ae = more to look at
  240.         mov     ax,trans.slongp ; put above size in ax for final checks
  241.         jmp     spar9a          ; do final checks (they want longer than us)
  242. spar9d: test    trans.capas,2   ; do they have long packet capability?
  243.         jz      sparx           ; z = no, skip following lp length fields
  244.         lodsb                           ; long pkt length, high order byte
  245.         sub     al,20h                  ; apply unchar()
  246.         xor     ah,ah
  247.         mul     ninefive                ; times 95 to dx(hi),ax(lo)
  248.         mov     trans.slongp,ax         ; store that much
  249.         lodsb                           ; long pkt length, low order byte
  250.         sub     al,20h                  ; apply unchar()
  251.         xor     ah,ah
  252.         add     ax,trans.slongp         ; plus high order part
  253.         mov     trans.slongp,ax         ; store it
  254.         or      ax,ax           ; if result is zero then use regular packets
  255.         jnz     spar9a                  ; non-zero, use what they want
  256.         mov     ah,0
  257.         mov     al,trans.spsiz          ; default to regular packet size
  258.         mov     trans.slongp,ax ;  and ignore the CAPAS bit (no def 500 bytes)
  259. spar9a: cmp     ax,trans.slong          ; longer than we want to do?
  260.         jbe     spar9b                  ; be = no
  261.         mov     ax,trans.slong          ; limit to our longest sending size
  262.         mov     trans.slongp,ax         ; and use it
  263. spar9b: cmp     ax,94                   ; shorter than normal packet too?
  264.         ja      spar9c                  ; a = no
  265.         mov     trans.spsiz,al          ; update normal packet size, again
  266. spar9c: mov     ax,temp4                ; recover number of pieces of data
  267. sparx:  pop     si
  268.         ret
  269. SPAR    ENDP
  270.  
  271. ; Set 8-bit quote character based on my capabilities and the other
  272. ; Kermit's request
  273.  
  274. DOQUO   PROC    NEAR
  275.         cmp     dtrans.ebquot,'N'       ; Can I do 8-bit quoting at all?
  276.         je      dq3                     ; No - so forget it
  277.         cmp     dtrans.ebquot,'Y'       ; Can I do it if requested?
  278.         jne     dq0                     ; No - it's a must that I do it
  279.         mov     trans.ebquot,al         ; Do whatever he wants
  280.         jmp     dq1
  281. dq0:    cmp     al,'Y'                  ; I need quoting - can he do it?
  282.         je      dq1                     ; Yes - then all is settled
  283.         cmp     al,'N'                  ; No - then don't quote
  284.         je      dq3
  285.         cmp     al,trans.ebquot         ; Both need quoting - chars must match
  286.         jne     dq3
  287. dq1:    mov     al,trans.ebquot
  288.         cmp     al,'Y'                  ; If Y or N, don't validate prefix
  289.         je      dq2
  290.         cmp     al,'N'
  291.         je      dq2
  292.         call    prechk                  ; Is it in range 33-62, 96-126?
  293.         jnc     dq4                     ; nc = in range
  294.         mov     al,'Y'                  ; don't do quoting
  295. dq4:    cmp     al,trans.rquote         ; Same prefix?
  296.         je      dq3                     ; Not allowed, so don't do quoting.
  297.         cmp     al,trans.squote         ; Same prefix here?
  298.         je      dq3                     ; This is illegal too
  299.         mov     trans.ebquot,al         ; Remember what we decided on
  300. dq2:    ret
  301. dq3:    mov     trans.ebquot,'N'        ; Quoting will not be done
  302.         ret
  303. DOQUO   ENDP
  304.  
  305. ; Check if prefix in AL is in the proper range: 33-62, 96-126.
  306. ; Return carry clear if in range, else return carry set.
  307. prechk: cmp     al,33
  308.         jb      prechk2                 ; b = out of range
  309.         cmp     al,62
  310.         jbe     prechk1                 ; be = in range 33-62
  311.         cmp     al,96
  312.         jb      prechk2                 ; b = out of range
  313.         cmp     al,126
  314.         ja      prechk2                 ; a = out of range 96-126
  315. prechk1:clc                             ; carry clear for in range
  316.         ret
  317. prechk2:stc                             ; carry set for out of range
  318.         ret
  319.  
  320. ; Set checksum length.
  321. dochk:  cmp     al,'1'                  ; Must be '1', '2', or '3'
  322.         jb      doc1                    ; b = not '1' to '3'
  323.         cmp     al,'3'
  324.         jbe     doc2                    ; be = ok
  325. doc1:   mov     al,'1'                  ; else use default of '1'
  326. doc2:   sub     al,'0'                  ; remove ascii bias
  327.         mov     trans.chklen,al         ; other side's request is do-able here
  328.         cmp     al,trans.chklen         ; Do we want the same thing?
  329.         je      dochk0                  ; e = yes, then we're done
  330.         mov     trans.chklen,1          ; No, use single character checksum
  331. dochk0: ret
  332.  
  333. ; Set repeat count quote character.  The one used must be different than
  334. ; the control and eight-bit quote characters.  Also, both sides must
  335. ; use the same character
  336. dorpt:  call    prechk                  ; Is it in the valid range?
  337.         jnc     dorpt1                  ; nc = in range
  338.         mov     al,0                    ; don't use their value
  339. dorpt1: cmp     al,trans.squote         ; Same as the control quote char?
  340.         je      dorpt2                  ; Yes, that's illegal, no repeats
  341.         cmp     al,trans.rquote         ; How about this one?
  342.         je      dorpt2                  ; No good
  343.         cmp     al,trans.ebquot         ; Same as eight bit quote char?
  344.         je      dorpt2                  ; Yes, that's illegal too, no repeats
  345.         cmp     al,ch                   ; Are we planning to use same char?
  346.         jne     dorpt2                  ; No, that's no good either
  347.         mov     rptq,ch                 ; Use repeat quote char now
  348. dorpt2: ret
  349.  
  350.                                         ; negotiate window size in al
  351. dewind: xor     al,al                   ; no windowing at our end
  352.         ret
  353.  
  354.  
  355. ;       Send command
  356. ;       MAIL filspec user@node  command
  357.  
  358. SEND    PROC    NEAR
  359.         mov     mailflg,0               ; send, not mail
  360.         mov     temp,0
  361.         jmp     short sendm0
  362. MAIL:   mov     mailflg,1               ; set flag for mail command vs send
  363.         mov     temp,1                  ; temp copy of mailflag
  364. sendm0: mov     difsiz,0                ; Assume we'll use original filename
  365.         mov     byte ptr sendas,0       ; clear sendas name (in case none)
  366.         mov     dx,offset diskio.string ; address of filename string
  367. ;        mov     bx,offset filmsg        ; Text of help message
  368.         mcmsgb   filmsg, cfilmsg
  369.  
  370.         cmp     mailflg,0               ; Mail command?
  371.         je      sendm1                  ; e = no
  372.         mov     mailflg,0               ; clear in case error exit
  373. ;        mov     bx,offset mailhlp       ; Text of help message
  374.         mcmsgb  mailhlp, cmailhlp
  375.  
  376. sendm1: mov     ah,cmfile               ; get an input file spec
  377.         call    comnd
  378.          ret                            ;  Give up on bad parse
  379.          nop
  380.          nop
  381.         cmp     flags.cxzflg,0          ; ^X, ^Z, ^C typed?
  382.         je      send0                   ; e = no, continue
  383.         or      errlev,1                ; say send failed
  384.         or      fsta.xstatus,1+80h      ; set status, failed + intervention
  385.         mov     kstatus,1+80h           ; global status
  386.         jmp     rskp                    ; yes, quit
  387. send0:  cmp     ah,0                    ; any text given?
  388.         je      send0d                  ; e = no, prompt
  389.         cmp     temp,0                  ; Mail command?
  390.         je      send00
  391.         jmp     send0c                  ; ne = yes, require address
  392. send00:
  393.         mov     bx,offset sendas     ; See if want to send file under dif name
  394. ;        mov     dx,offset filhlp        ; In case user needs help
  395.         mcmsg   filhlp, cfilhlp
  396.  
  397.         mov     ah,cmtxt                ; allow embedded white space
  398.         call    comnd
  399.          ret
  400.          nop
  401.          nop
  402.         jmp     sendm3a                 ; join common completion code
  403.  
  404. send0d: 
  405. ;       mov     dx,offset lclfnm        ; prompt for local filename
  406.         mcmsg   lclfnm, clclfnm
  407.  
  408.         call    prompt
  409.         mov     dx,offset diskio.string ; reload destination of user's text
  410. ;        mov     bx,offset filhlp        ; help file
  411.         mcmsgb   filhlp, cfilhlp
  412.  
  413.         mov     ah,cmfile               ; allow paths
  414.         call    comnd                   ; try again for a local filename
  415.          ret
  416.          nop
  417.          nop
  418.         mov     temp4,ax
  419.         mov     ah,cmcfm
  420.         call    comnd
  421.          ret
  422.          nop
  423.          nop
  424.         mov     ax,temp4
  425.         cmp     flags.cxzflg,0          ; ^X, ^Z, ^C typed?
  426.         je      send0a                  ; e = no, continue
  427.         or      errlev,1                ; say send failed
  428.         or      fsta.xstatus,1+80h      ; set status, failed + intervention
  429.         mov     kstatus,1+80h           ; global status
  430.         mov     mailflg,0
  431.         jmp     rskp                    ; yes, quit
  432. send0a: cmp     ah,0                    ; user's byte count
  433.         je      send0d                  ; e = nothing was typed, get some
  434.  
  435. send0b: 
  436. ;       mov     dx,offset remfnm        ; ask for remote name first
  437.         mcmsg   remfnm, cremfnm
  438.  
  439.         cmp     temp,0                  ; Mail command?
  440.         je      sendm2                  ; e = no
  441. ;        mov     dx,offset mailto        ; ask for name@host
  442.         mcmsg  mailto, cmailto
  443.  
  444. sendm2: call    prompt
  445.  
  446. send0c: mov     bx,offset sendas     ; See if want to send file under dif name
  447. ;        mov     dx,offset filhlp        ; In case user needs help
  448.         mcmsg   filhlp, cfilhlp
  449.  
  450.         cmp     temp,0                  ; Mail command?
  451.         je      sendm3                  ; e = no
  452. ;        mov     dx,offset mailtohlp     ; In case user needs help
  453.         mcmsg  mailtohlp, cmailtohlp
  454.  
  455. sendm3: mov     ah,cmtxt                ; allow embedded white space
  456.         call    comnd
  457.          ret
  458.          nop
  459.          nop
  460.         cmp     ah,0                    ; text entered?
  461.         je      send0b                  ; e = no, get some
  462. sendm3a:cmp     flags.cxzflg,0          ; ^X, ^Z, ^C typed?
  463.         je      send1                   ; e = no, continue
  464.         or      errlev,1                ; say send failed
  465.         or      fsta.xstatus,1+80h      ; set status, failed + intervention
  466.         mov     kstatus,1+80h           ; global status
  467.         mov     mailflg,0
  468.         jmp     rskp                    ; yes, quit
  469. send1:  mov     al,ah                   ; store count of user's chars
  470.         mov     ah,0
  471.         mov     difsiz,ax               ; Remember length of new name
  472.         mov     ax,temp                 ; get temp mailflag
  473.         mov     mailflg,al              ; store in secure area for later
  474.         mov     ah,trans.sdelay         ; seconds to delay before sending
  475.         shl     ah,1                    ; times 4*256 to get millisec
  476.         shl     ah,1                    ;  for pcwait
  477.         mov     al,1                    ; set low byte to 1 for no delay case
  478.         call    pcwait                  ; wait number of millisec in ax
  479.         mov     flags.xflg,0            ; Reset flag for normal file send[mtd]
  480.         mov     flags.cxzflg,0          ; clear interrupt flag too
  481.         mov     bx,offset diskio.string
  482.         cmp     byte ptr [bx],'#'       ; Is first char a replacement for '?'?
  483.         jne     send1f                  ; ne = no
  484.         mov     byte ptr [bx],'?'       ; yes. Change '#' for '?'
  485. send1f: mov     bx,offset sendas
  486.         cmp     byte ptr [bx],'#'       ; Is first char a replacement for '?'?
  487.         jne     snd11a
  488.         mov     byte ptr [bx],'?'       ; yes. Change '#' for '?'
  489.         jmp     short snd11a
  490.                                 ; SEND11 is an entry point for REMote cmds
  491. SEND11: mov     flags.nmoflg,0          ; Reset flags from fn parsing
  492.         mov     difsiz,0                ; clear any old 'sendas' filespec
  493. snd11a: mov     kstatus,0               ; global status, success
  494.         mov     ah,setdma               ; set dta address
  495.         mov     dx,offset diskio.dta
  496.         int     dos
  497.         mov     ah,first2               ; search for first
  498.         mov     cx,0                    ; consider only regular files
  499.         mov     dx,offset diskio.string ; full filename, inc paths
  500.         int     dos
  501.         pushf                           ; save flags
  502.         push    dx
  503.         mov     ah,setdma               ; restore dta to offset buff
  504.         mov     dx,offset buff
  505.         int     dos
  506.         pop     dx
  507.         popf                            ; restore flags
  508.         jnc     send16                  ; carry reset = file found
  509.         cmp     pack.state,'R'          ; was this from a remote GET?
  510.         jne     sen11a                  ; no, print error and continue
  511. ;        mov     bx,offset remmsg1       ; else get error message
  512.         mcmsgb  remmsg1, cremmsg1
  513.         mov     ah,trans.chklen
  514.         mov     curchk,ah               ; Store checksum length we want to use
  515.         mov     trans.chklen,1          ; Send init checksum is always 1 char
  516.         call    errpack                 ; go complain
  517.         mov     ah,curchk
  518.         mov     trans.chklen,ah         ; Checksum length we want to use
  519.         jmp     abort                   ; and abort this
  520. sen11a: mov     ah,prstr
  521.         mov     dx,offset crlf
  522.         int     dos
  523.         mov     ah,prstr
  524. ;        mov     dx,offset erms15        ; '?Unable to find file'
  525.         mcmsg   erms15, cerms15
  526.  
  527.         int     dos
  528.         or      errlev,1                ; set DOS error level
  529.         or      fsta.xstatus,1          ; set status
  530.         mov     kstatus,1               ; global status
  531.         mov     ax,1            ; tell statistics this was a send operation
  532.         call    endtim                  ; stop statistics counter
  533.         mov     mailflg,0               ; clear Mail flag
  534.         jmp     rskp                    ; pretend successful completion
  535.  
  536. send16: call    serini                  ; Initialize serial port
  537.         jnc     send17                  ; nc = success
  538.         or      errlev,1                ; say send failed
  539.         or      fsta.xstatus,1          ; set status
  540.         mov     kstatus,1               ; global status
  541.         test    flags.remflg,dquiet     ; quiet display mode?
  542.         jnz     send16a                 ; nz = yes. Don't write to screen
  543. ;        mov     dx,offset erms14
  544.         mcmsg   erms14, cerms14
  545.  
  546.         mov     ah,prstr
  547.         int     dos                     ; Print an error message
  548. send16a:ret                             ; return failure
  549.  
  550. send17: call    begtim                  ; get tod for start of transfer
  551.         mov     pack.pktnum,0           ; Set the packet number to zero
  552.         mov     pack.numtry,0           ; Set the number of tries to zero
  553.         mov     pack.numpkt,0           ; Set the number of packets to zero
  554.         mov     pack.numrtr,0           ; Set the number of retries to zero
  555.         mov     pack.state,'S'          ; Set the state to receive initiate
  556.         call    ihosts                  ; initialize the host (clear NAKs)
  557.         call    init            ; Clear the line and initialize the buffers
  558.         test    flags.remflg,dquiet+dserial ; quiet or serial display mode?
  559.         jnz     send2                   ; nz = yes, suppress 0 retry msg
  560.         call    stpos                   ; Print status of file transfer
  561.         mov     ah,prstr                ; Be informative
  562. ;        mov     dx,offset infms2
  563.         mcmsg   infms2, cinfms2
  564.  
  565.         int     dos
  566. send18: test    flags.remflg,dquiet+dserial ; quiet or serial display mode?
  567.         jnz     send2                   ; nz = yes, suppress 0 retry msg
  568.         call    rtpos                   ; Position cursor
  569.         mov     ax,0                    ; set retry counts to zero
  570.         call    nout                    ; Write the number of retries
  571.  
  572. send2:  call    nppos                   ; Number of packets sent
  573.         mov     ax,pack.numpkt
  574.         call    nout                    ; Write the packet number
  575.         cmp     pack.state,'D'          ; Are we in the data send state?
  576.         jne     send3                   ; ne = no
  577.         call    sdata                   ; send data
  578.         jmp     send2
  579. send3:  cmp     pack.state,'F'          ; Are we in the file send state?
  580.         jne     send3a                  ; ne = no
  581.         call    sfile                   ; Call send file
  582.         jmp     send2
  583. send3a: cmp     pack.state,'a'          ; are we in send attributes state?
  584.         jne     send3b                  ; ne = no
  585.         call    sattr                   ; call send attributes
  586.         jmp     send2
  587. send3b: cmp     pack.state,'d'          ; are we in initialize send data state
  588.         jne     send4                   ; ne = no
  589.         call    sdatini                 ; do setup for file reading
  590.         jmp     send2
  591.  
  592. send4:  cmp     pack.state,'Z'          ; Are we in the EOF state?
  593.         jne     send5
  594.         call    seof
  595.         jmp     send2
  596. send5:  cmp     pack.state,'S'          ; Are we in the send initiate state?
  597.         jne     send6
  598.         call    sinit
  599.         jmp     send2
  600. send6:  cmp     pack.state,'B'          ; Are we in the eot state?
  601.         jne     send7
  602.         call    seot
  603.         jmp     send2
  604.                                         ; Completion processor section
  605. send7:  push    ax
  606. ;;;;    call    serrst                  ; Reset serial port
  607.         pop     ax
  608.         mov     mailflg,0               ; clear Mail flag
  609. ;        mov     dx,offset infms3        ; Completed message
  610.         mcmsg   infms3, cinfms3
  611.  
  612.         cmp     pack.state,'C'          ; Are we in the send complete state?
  613.         je      send8                   ; e = yes, else failure
  614. ;        mov     dx,offset infms4        ; Failed message
  615.         mcmsg   infms4, cinfms4
  616.  
  617.         or      errlev,1                ; say send failed
  618.         or      fsta.xstatus,1          ; set status
  619.         mov     kstatus,1               ; global status
  620.  
  621. send8:  cmp     flags.cxzflg,0          ; completed normally?
  622.         je      send8b                  ; e = yes, don't bother with this
  623.         or      errlev,1                ; say send failed
  624.         or      fsta.xstatus,1+80h      ; set status, failed + intervention
  625.         mov     kstatus,1+80h           ; global status
  626. send8b: mov     ax,1            ; tell statistics this was a send operation
  627.         call    endtim                  ; stop statistics counter
  628.         test    flags.remflg,dquiet     ; quiet display mode?
  629.         jnz     send8f                  ; nz = yes, no printing
  630.         test    flags.remflg,dserial    ; serial display mode?
  631.         jnz     send8c                  ; nz = yes, skip positioning
  632.         push    dx
  633.         call    stpos
  634.         pop     dx
  635. send8c: mov     ah,prstr
  636.         cmp     flags.cxzflg,0          ; Completed or interrupted?
  637.         je      send8d                  ; e = no interruption
  638. ;        mov     dx,offset infms6        ; Say transfer was interrupted
  639.         mcmsg   infms6, cinfms6
  640.  
  641. send8d: int     dos
  642.         cmp     flags.belflg,0          ; Bell desired?
  643.         je      send8e                  ; e = no
  644.         mov     dx,offset ender         ; Ring them bells
  645.         int     dos
  646. send8e: test    flags.remflg,dserial    ; serial display mode?
  647.         jnz     send8f                  ; nz = yes, no cursor positioning
  648.         call    clrmod
  649.         call    rprpos
  650. send8f: jmp     rskp
  651. SEND    ENDP
  652.  
  653. ;       Send routines
  654.  
  655. ;       Send initiate
  656. SINIT   PROC    NEAR
  657.         mov     dl,imxtry
  658.         cmp     pack.numtry,dl  ; Have we reached the maximum number of tries?
  659.         jl      sinit2                  ; l = no
  660.         test    flags.remflg,dquiet     ; quiet display mode?
  661.         jnz     sinit1                  ; nz = yes. Don't write to screen
  662.         call    erpos
  663. ;        mov     dx,offset erms14
  664.         mcmsg   erms14, cerms14
  665.  
  666.         mov     ah,prstr
  667.         int     dos                     ; Print an error message
  668. sinit1: mov     ah,trans.chklen
  669.         mov     curchk,ah               ; Store checksum length we want to use
  670.         mov     trans.chklen,1          ; Send init checksum is always 1 char
  671. ;        mov     bx,offset erms20
  672.         mcmsgb   erms20, cerms20
  673.  
  674.         call    errpack                 ; Send error packet just in case
  675.         mov     ah,curchk
  676.         mov     trans.chklen,ah         ; Checksum length we want to use
  677.         jmp     abort                   ; Change the state to abort
  678. sinit2: inc     pack.numtry             ; Save the updated number of tries
  679.         mov     bx,offset data          ; Get a pointer to our data block
  680.         mov     ah,dtrans.seol          ; restore default end-of-line char
  681.         mov     trans.seol,ah
  682.         mov     ah,dtrans.ebquot        ; our default 8-bit quoting
  683.         mov     trans.ebquot,ah         ; active 8-bit quoting
  684.         call    rpar                    ; Set up the parameter information
  685.         xchg    ah,al
  686.         mov     ah,0
  687.         mov     pack.datlen,ax          ; Save the number of arguments
  688.         mov     ax,pack.numpkt          ; Get the packet number
  689.         mov     pack.seqnum,ax
  690.         mov     ah,trans.chklen
  691.         mov     curchk,ah               ; Store checksum length we want to use
  692.         mov     trans.chklen,1          ; Send init checksum is always 1 char
  693.         call    pktsize                 ; report packet size
  694.         mov     ah,'S'                  ; Send initiate packet
  695.         call    sndpak                  ; send the packet
  696.         call    rpack                   ; Get a packet
  697.          jmp    sini23          ; Trashed packet don't change state, retry
  698.          nop
  699.         push    ax
  700.         mov     ah,curchk
  701.         mov     trans.chklen,ah         ; Checksum length we want to use
  702.         pop     ax
  703.         call    acknak                  ; was it ok?
  704.         cmp     al,0                    ; maybe an ack?
  705.         je      sini22                  ; yes, go handle it
  706.         cmp     al,1                    ; maybe a nak?
  707.         jne     sinit4                  ; no, check for error or something
  708.         ret                             ; else just return and try again
  709. sini22: mov     ax,pack.datlen
  710.         mov     bx,offset data          ; point to data for spar
  711.         call    spar                    ; Read in the data
  712.         call    packlen                 ; Get max send packet size
  713.         mov     pack.numtry,0           ; Reset the number of tries
  714.         cmp     mailflg,0               ; non-zero to do Mail command
  715.         je      sini24                  ; e = send, not mail command
  716.         cmp     flags.attflg,0          ; allowed to do file attributes?
  717.         je      sinit6                  ; e = no, so no Mail
  718.         test    trans.capas,8           ; can they do file attributes?
  719.         jz      sinit6                  ; z = no, so cannot do Mail
  720. sini24: mov     pack.state,'F'          ; Set the state to file send
  721.         call    getfil                  ; Open the file
  722.          jmp    abort                   ;  Something is wrong, die
  723.         mov     filopn,1                ; Disk file is open
  724.         ret
  725. sini23: mov     ah,curchk               ; Restore desired checksum length
  726.         mov     trans.chklen,ah
  727.         jmp     updrtr                  ; Update retry counter and return
  728.  
  729. sinit4: cmp     ah,'M'                  ; Message packet?
  730.         jne     sinit4e                 ; ne = no
  731.         call    dodec                   ; decode it
  732.         jmp     error1                  ; display it and return
  733.  
  734. sinit4e:cmp     ah,'E'                  ; Is it an error packet
  735.         jne     sinit5
  736.         call    error
  737. sinit5: jmp     abort
  738.                                         ; say Mail not supported by host
  739. sinit6: test    flags.remflg,dquiet     ; quiet display mode?
  740.         jnz     sinit7                  ; nz = yes. Don't write to screen
  741.         call    erpos
  742. ;        mov     dx,offset erms25
  743.         mcmsg   erms25, cerms25
  744.  
  745.        mov     ah,prstr
  746.         int     dos                     ; Print an error message
  747. sinit7: mov     pack.state,'B'          ; go to EOT state
  748.         ret
  749. SINIT   ENDP
  750.  
  751. ;       Send file header
  752.  
  753. SFILE   PROC    NEAR
  754.         mov     dl,maxtry
  755.         cmp     pack.numtry,dl  ; Have we reached the maximum number of tries?
  756.         jl      sfile1                  ; l = no
  757.         test    flags.remflg,dquiet     ; quiet display mode?
  758.         jnz     sfile0                  ; nz = yes. Don't write to screen
  759.         call    erpos
  760. ;        mov     dx,offset erms14
  761.         mcmsg   erms14, cerms14
  762.  
  763.         mov     ah,prstr
  764.         int     dos                     ; Print an error message
  765. sfile0: 
  766. ;        mov     bx,offset erms21
  767.         mcmsgb   erms21, cerms21
  768.  
  769.         call    errpack                 ; Send error packet just in case
  770.         jmp     abort                   ; Change the state to abort
  771. sfile1: inc     pack.numtry             ; Increment it
  772.         mov     flags.cxzflg,0          ; Clear ^X,^Z flag.
  773.         mov     si,offset diskio.fname  ;addr of asciiz filename without paths
  774.         mov     di,offset data          ; destination
  775.         call    strcpy                  ; copy filename there
  776.         push    dx
  777.         mov     dx,offset data
  778.         call    strlen                  ; get length (w/o terminator) into cx
  779.         pop     dx
  780.         mov     ch,0
  781.         test    flags.remflg,dquiet     ; quiet display mode?
  782.         jnz     sfil13                  ; nz = yes, no printing
  783.         call    prtfn                   ; print filename in data
  784. sfil13: call    newfn           ; show possible new filename, put length in cx
  785.         call    doenc                   ; Do encoding; length is in cx
  786.         mov     ax,pack.pktnum          ; Get the packet number
  787.         mov     pack.seqnum,ax
  788.         mov     ah,'F'                  ; File header packet
  789.         cmp     flags.xflg,0            ; remote display requested?
  790.         je      sfl13y                  ; e = no
  791.         mov     ah,'X'                 ; use X rather than F packet for remote
  792. sfl13y: call    pktsize                 ; report packet size
  793.         call    sndpak                  ; send the packet
  794.         call    rcvpak                  ; Get a packet
  795.         call    acknak                  ; see what they had to say
  796.         cmp     al,0                    ; ack'd ok?
  797.         je      sfil14                  ; yes, on to next state
  798.         cmp     al,1                    ; maybe a nak?
  799.         jne     sfile3                  ; no, check for error
  800.         ret                             ; if nak, just return and try again
  801. sfil14: call    fackmsg                 ; get/show any embedded message
  802.         mov     pack.state,'a'          ; set file attributes as next state
  803.         ret
  804.  
  805. sfile3: cmp     ah,'M'                  ; Message packet?
  806.         jne     sfile4                  ; ne = no
  807.         call    dodec                   ; decode it
  808.         jmp     error1                  ; display it and return
  809.  
  810. sfile4: cmp     ah,'E'                  ; Is it an error packet
  811.         jne     sfile4                  ; ne = no
  812.         call    dodec                   ; Do all decoding
  813.         call    error
  814. sfile5: jmp     abort
  815. SFILE   ENDP
  816.  
  817. ; Send file attributes. Attributes: file size in bytes and kilobytes,
  818. ; file time and date, machine identification. [jrd]
  819.  
  820. SATTR   PROC    NEAR
  821.         cmp     flags.attflg,0          ; allowed to do file attributes?
  822.         je      satt0                   ; e = no
  823.         test    trans.capas,8           ; can we do file attributes?
  824.         jnz     satt1                   ; nz = yes
  825. satt0:  mov     pack.state,'d'          ; set the state to initiate send-data
  826.         ret
  827. satt1:  push    es                      ; save es around this work
  828.         push    ds
  829.         pop     es                      ; set es to datas segment for es:di
  830.         cld
  831.         mov     data,'1'                ; File length (Bytes) specifier
  832.         mov     dx,diskio.sizehi        ; high word of length
  833.         mov     ax,diskio.sizelo        ; low word of length
  834.         mov     di,offset data+2        ; where to store data (for lnout)
  835.         call    lnout                   ; convert file length, write to [di++]
  836.         mov     cx,di                   ; compute field length
  837.         sub     cx,offset data+2
  838.         add     cl,32                   ; field length to ascii
  839.         mov     data+1,cl               ; length. Done with File Size
  840.                                         ; Kilobyte attribute
  841.         mov     byte ptr[di],'!'        ; File length (Kilobytes) specifier
  842.         inc     di
  843.         mov     temp4,di                ; remember place for count field
  844.         inc     di                      ; data field
  845.         mov     dx,diskio.sizehi        ; high word of length, from file open
  846.         mov     ax,diskio.sizelo        ; low word of length
  847.         add     ax,1023                 ; add 1023 to round up
  848.         adc     dx,0
  849.         mov     al,ah                   ; do divide by 1024 bytes
  850.         mov     ah,dl
  851.         mov     dl,dh                   ; divide by 256 part
  852.         mov     dh,0
  853.         ror     dl,1                    ; low bit to carry flag
  854.         rcr     ax,1                    ; divide by two, with carry in
  855.         clc
  856.         ror     dl,1                    ; low bit to carry flag
  857.         rcr     ax,1                    ; divide by two, with carry in
  858.         and     dl,3fh                  ; keep low six bits
  859.         call    lnout                   ; convert file length
  860.         mov     cx,di                   ; compute field length
  861.         sub     cx,temp4                ; count field location
  862.         add     cl,32-1                 ; field length to ascii
  863.         push    di
  864.         mov     di,temp4                ; point at count field
  865.         mov     byte ptr[di],cl         ; store field length
  866.         pop     di                      ; Done with Kilobyte attribute
  867.                                         ; File date and time:
  868.         mov     al,'#'                  ; creation date/time specifier
  869.         stosb                           ; and point at field length
  870.         mov     al,17+32                ; length of date/time field, to ascii
  871.         stosb
  872.         mov     ah,0
  873.         mov     al,diskio.dta+25        ; yyyyyyym from DOS via file open
  874.         shr     al,1                    ; get year
  875.         add     ax,1980                 ; add bias
  876.         mov     dx,0
  877.         call    lnout                   ; put year (1988) in buffer
  878.         mov     ax,word ptr diskio.dta+24 ; yyyyyyyym mmmddddd  year+month+day
  879.         shr     ax,1                    ; month to al
  880.         mov     ah,0
  881.         mov     cl,4
  882.         shr     al,cl                   ; month to low nibble
  883.         mov     byte ptr[di],'0'        ; leading digit
  884.         inc     di
  885.         cmp     al,9                    ; more than one digit?
  886.         jbe     satt2                   ; be = no
  887.         mov     byte ptr[di-1],'1'      ; new leading digit
  888.         sub     al,10                   ; get remainder
  889. satt2:  add     al,'0'                  ; to ascii
  890.         stosb                           ; end of month
  891.         mov     al,diskio.dta+24        ; get day of month
  892.         and     al,1fh                  ; select day bits
  893.         mov     ah,0
  894.         mov     cl,10
  895.         div     cl                      ; quot = al, rem = ah
  896.         add     ax,'00'                 ; add ascii bias
  897.         stosw                           ; leading digit and end of date
  898.         mov     al,' '                  ; space separator
  899.         stosb
  900.         mov     al,diskio.dta+23        ; hours  hhhhhmmm
  901.         mov     cl,3
  902.         shr     al,cl                   ; move to low nibble
  903.         mov     ah,0
  904.         mov     cl,10
  905.         div     cl                      ; quot = al, rem = ah
  906.         add     ax,'00'                 ; add ascii bias
  907.         stosw                           ; store hours
  908.         mov     al,':'                  ; separator
  909.         stosb
  910.         mov     ax,word ptr diskio.dta+22 ; get minutes: hhhhhmmm mmmsssss
  911.         mov     cl,5
  912.         shr     ax,cl                   ; minutes to low byte
  913.         and     al,3fh                  ; six bits for minutes
  914.         mov     ah,0
  915.         mov     cl,10
  916.         div     cl
  917.         add     ax,'00'                 ; add ascii bias
  918.         stosw
  919.         mov     al,':'                  ; separator
  920.         stosb
  921.         mov     al,byte ptr diskio.dta+22 ; get seconds (double secs really)
  922.         and     al,1fh
  923.         shl     al,1                    ; DOS counts by two sec increments
  924.         mov     ah,0
  925.         mov     cl,10
  926.         div     cl
  927.         add     ax,'00'                 ; add ascii bias
  928.         stosw
  929.         mov     ax,'".'                 ; machine indicator(.), 2 data bytes
  930.         stosw
  931.         mov     ax,'8U'                 ; U8 = Portable O/S, MSDOS
  932.         stosw
  933.         pop     es                      ; recover es register
  934.         cmp     mailflg,0               ; Mailing?
  935.         je      satt3                   ; e = no
  936.         mov     byte ptr [di],'+'       ; Mail specification
  937.         inc     di
  938.         mov     si,offset sendas        ; user@host field
  939.         mov     dx,si
  940.         call    strlen                  ; get length into cl
  941.         push    cx                      ; save address length
  942.         inc     cl                      ; include M for disposition = mail
  943.         add     cl,' '                  ; add ascii bias
  944.         mov     [di],cl                 ; store in length field
  945.         inc     di
  946.         mov     byte ptr [di],'M'       ; mail the file
  947.         inc     di
  948.         pop     cx                      ; recover address length
  949.         jcxz    satt3                   ; z = empty field
  950.         push    es
  951.         push    ds
  952.         pop     es                      ; use es:di pointing to datas segment
  953.         cld
  954.         rep     movsb                   ; append address text to field
  955.         pop     es
  956. satt3:  sub     di,offset data          ; get length of data
  957.         mov     pack.datlen,di          ; data length for packet
  958.         call    pktsize                 ; report packet size
  959.         mov     ax,pack.pktnum          ; get the packet number
  960.         mov     pack.seqnum,ax
  961.         mov     ah,'A'                  ; Attributes packet
  962.         call    sndpak                  ; send the packet
  963.         call    rcvpak                  ; get response
  964.         call    acknak                  ; see what they had to say
  965.         cmp     al,0                    ; ack'd ok?
  966.         je      satt5                   ; e = yes, on to next state
  967.         cmp     al,1                    ; maybe a nak?
  968.         jne     satt6                   ; ne = no, check for error
  969.         ret                             ; if nak, just return and try again
  970. satt5:  cmp     pack.datlen,0           ; any data in the ACK?
  971.         je      satt5d                  ; e = no
  972.         cmp     data,'N'                ; are they refusing this file?
  973.         jne     satt5d                  ; ne = no
  974.         test    flags.remflg,dquiet     ; quiet display mode?
  975.         jnz     satt5a                  ; nz = yes. Don't write to screen
  976.         call    erpos                   ; Position the cursor
  977.         mov     ah,prstr
  978. ;        mov     dx,offset erms26        ; say host rejected the file
  979.         mcmsg   erms26, cerms26
  980.  
  981.         int     dos
  982. satt5a: mov     pack.state,'Z'          ; send EOF with Discard
  983.         mov     flags.cxzflg,'X'        ; simulate Control-X to discard
  984.         or      errlev,1                ; say send failed
  985.         or      fsta.xstatus,1+80h      ; set status, failed + intervention
  986.         mov     kstatus,1+80h           ; global status
  987.         ret
  988. satt5d: mov     pack.state,'d'          ; next state is initiate send-data
  989.         ret
  990. satt6:  cmp     ah,'E'                  ; Is it an error packet
  991.         jne     satt7                   ; ne = no
  992.         call    dodec                   ; Do all decoding
  993.         call    error
  994. satt7:  jmp     abort
  995. SATTR   ENDP
  996.  
  997. ;       Send data
  998. ;
  999. ; set up initial data buffer from file, 'd' state
  1000. sdatini proc    near
  1001.         mov     flags.filflg,0FFH       ; Indicate file buffer is empty
  1002.         mov     pack.state,'D'
  1003.         jmp     sdat23                  ; read first buffer from file
  1004. sdatini endp
  1005.  
  1006. ; Send main body of file, 'D' state
  1007.  
  1008. SDATA   PROC    NEAR
  1009.         cmp     flags.cxzflg,0          ; Have we seen ^X or ^Z?
  1010.         je      sdata2                  ; Nope, just continue
  1011.         cmp     flags.cxzflg,'C'        ; Stop it all?
  1012.         jne     sdata1                  ; It was a ^X or ^Z
  1013.         mov     pack.state,'A'          ; It was a ^C -- abort
  1014.         ret
  1015. sdata1: mov     pack.state,'Z'          ; Else, abort sending the file
  1016.         ret
  1017. sdata2: mov     dl,maxtry
  1018.         cmp     pack.numtry,dl  ; Have we reached the maximum number of tries?
  1019.         jl      sdata3                  ; l = no
  1020.         test    flags.remflg,dquiet     ; quiet display mode?
  1021.         jnz     sdat2a                  ; nz = yes. Don't write to screen
  1022.         call    erpos
  1023. ;       mov     dx,offset erms14
  1024.         mcmsg   erms14, cerms14
  1025.  
  1026.         mov     ah,prstr
  1027.         int     dos                     ; Print an error message
  1028. sdat2a: 
  1029. ;       mov     bx,offset erms22
  1030.         mcmsgb   erms22, cerms22
  1031.  
  1032.         call    errpack                 ; Send error packet just in case
  1033.         jmp     abort                   ; Change the state to abort
  1034. sdata3: inc     pack.numtry             ; Increment it
  1035.         mov     cx,siz                  ; number to transfer
  1036.         mov     pack.datlen,cx          ; length of data in packet
  1037.         call    movpak                  ; from filbuf to buffer data
  1038.         mov     ax,pack.pktnum          ; Get the packet number
  1039.         mov     pack.seqnum,ax          ; store in packet
  1040.         call    pktsize                 ; report packet size
  1041.         mov     ah,'D'                  ; Data packet
  1042.         call    sndpak                  ; send the packet
  1043.         call    rcvpak                  ; Get a packet
  1044.         call    acknak          ; see if ack or nak, check packet number
  1045.         cmp     al,0                    ; 0 => ack ok, go on
  1046.         je      sdat11                  ; ack, check for data in response
  1047.         cmp     al,1            ; 1 => nak, retry count incremented, try again
  1048.         jne     sdat15                  ; else look for other packet types
  1049.         ret                             ; else return
  1050.  
  1051. sdat11: cmp     pack.datlen,0           ; any data in ACK response?
  1052.         je      sdat23                  ; e = no
  1053.         call    dackmsg                 ; get/show any embedded message
  1054.         mov     bl,data                 ; get 1st byte
  1055.         cmp     bl,'X'                  ; someone typed control X?
  1056.         je      sdat24                  ; e = yes
  1057.         cmp     bl,'Z'          ; Control Z? Corrects earlier proto error
  1058.         jne     sdat23                  ; not X or Z, just keep going
  1059. sdat24: mov     flags.cxzflg,bl         ; set flag appropriately
  1060.         mov     pack.state,'Z'          ; simulate eof
  1061.         ret                             ; and return
  1062.  
  1063. SDAT23: call    gtchr                   ; fill buffer from file
  1064.          jmp    sdat12                  ; Error go see if its EOF
  1065.          nop                            ; make  three bytes
  1066.         mov     siz,ax                  ; Save the size of the data gotten
  1067.         ret
  1068. sdat12: cmp     ah,0FFH                 ; Is it EOF?
  1069.         je      sdat13                  ; e = yes
  1070.         jmp     abort                   ; If not give up
  1071. sdat13: mov     pack.state,'Z'          ; Set the state to EOF
  1072.         ret
  1073.  
  1074. sdat15: cmp     ah,'M'                  ; Message packet?
  1075.         jne     sdat16                  ; ne = no
  1076.         call    dodec                   ; decode it
  1077.         jmp     error1                  ; display it and return
  1078.  
  1079. sdat16: cmp     ah,'E'                  ; Is it an error packet
  1080.         jne     sdat17
  1081.         call    dodec                   ; Do all decoding
  1082.         call    error                   ; display and change state to Abort
  1083. sdat17: jmp     abort
  1084. SDATA   ENDP
  1085.  
  1086. ;       Send EOF
  1087.  
  1088. SEOF    PROC    NEAR
  1089.         mov     dl,maxtry
  1090.         cmp     pack.numtry,dl  ; Have we reached the maximum number of tries?
  1091.         jl      seof1                   ; l = no
  1092.         test    flags.remflg,dquiet; quiet display mode?
  1093.         jnz     seof0                   ; nz = yes. Don't write to screen
  1094.         call    erpos                   ; Position cursor
  1095. ;        mov     dx,offset erms14
  1096.         mcmsg   erms14, cerms14
  1097.  
  1098.         mov     ah,prstr
  1099.         int     dos                     ; Print an error message
  1100. seof0:  
  1101. ;       mov     bx,offset erms23
  1102.         mcmsgb   erms23, cerms23
  1103.  
  1104.         call    errpack                 ; Send error packet just in case
  1105.         jmp     abort                   ; Change the state to abort
  1106. seof1:  inc     pack.numtry             ; Increment it
  1107.         mov     ax,pack.pktnum          ; Get the packet number
  1108.         mov     pack.seqnum,ax
  1109.         mov     pack.datlen,0           ; No data
  1110.         cmp     flags.cxzflg,0          ; Seen a ^X or ^Z?
  1111.         je      seof11                  ; Nope, send normal EOF packet
  1112.         mov     data,'D'                ; Use "D" for discard
  1113.         mov     pack.datlen,1           ; Set data size to 1
  1114.         or      errlev,1                ; say send failed
  1115.         or      fsta.xstatus,1+80h      ; set status, failed + intervention
  1116.         mov     kstatus,1+80h           ; global status
  1117. seof11: mov     cx,pack.datlen          ; Put size in CX
  1118.         call    doenc                   ; Encode the packet
  1119.         call    pktsize                 ; report packet size
  1120.         mov     ah,'Z'                  ; EOF packet
  1121.         call    sndpak                  ; send the packet
  1122.         call    rcvpak                  ; Get a packet
  1123.         call    acknak                  ; see what they had to say
  1124.         cmp     al,0                    ; ack?
  1125.         je      seof12                  ; e = yes, go close file and proceed
  1126.         cmp     al,1                    ; maybe a nak?
  1127.         jne     seof3                   ; no, check for error packet
  1128.         ret                             ; if nak, just return
  1129.  
  1130. seof12: call    dackmsg                 ; get/show any embedded message
  1131.         mov     ah,close2               ; DOS 2.0 close file
  1132.         push    bx
  1133.         mov     bx,diskio.handle        ; file handle
  1134.         int     dos
  1135.         pop     bx
  1136.         call    GTNFIL                  ; Get the next file
  1137.          jmp    seof13                  ;  No more
  1138.          nop                            ; make three bytes
  1139.         mov     pack.state,'F'          ; Set the state to file send
  1140.         cmp     flags.cxzflg,'X'        ; Control-X seen?
  1141.         jne     seof14
  1142.         call    cxmsg                   ; Clear out the interrupt msg
  1143.         or      errlev,1                ; say send failed
  1144.         or      fsta.xstatus,1+80h      ; set status, failed + intervention
  1145.         mov     kstatus,1+80h           ; global status
  1146. seof14: mov     flags.cxzflg,0          ; Reset the flag
  1147.         ret
  1148. seof13: mov     pack.state,'B'          ; Set the state to EOT
  1149.         mov     filopn,0                ; No files open
  1150.         mov     difsiz,0                ; clear original filename
  1151.         mov     byte ptr sendas,0       ; clear sendas name
  1152.         ret
  1153. seof3:  cmp     ah,'E'                  ; Is it an error packet?
  1154.         jne     seof4
  1155.         call    dodec                   ; Decode packet
  1156.         call    error
  1157. seof4:  jmp     abort
  1158. SEOF    ENDP
  1159.  
  1160.  
  1161. ; Send EOT
  1162.  
  1163. SEOT    PROC    NEAR
  1164.         mov     dl,maxtry
  1165.         cmp     pack.numtry,dl  ; Have we reached the maximum number of tries?
  1166.         jl      seot1                   ; l = no
  1167.         test    flags.remflg,dquiet     ; quiet display mode?
  1168.         jnz     seot0                   ; nz = yes. Don't write to screen
  1169.         call    erpos                   ; Position cursor
  1170. ;        mov     dx,offset erms14
  1171.         mcmsg   erms14, cerms14
  1172.  
  1173.         mov     ah,prstr
  1174.         int     dos                     ; Print an error message
  1175. seot0:  
  1176. ;       mov     bx,offset erms24
  1177.         mcmsgb   erms24, cerms24
  1178.  
  1179.         call    errpack                 ; Send error packet just in case
  1180.         jmp     abort                   ; Change the state to abort
  1181. seot1:  inc     pack.numtry             ; Increment it
  1182.         mov     ax,pack.pktnum          ; Get the packet number
  1183.         mov     pack.seqnum,ax
  1184.         mov     pack.datlen,0           ; No data
  1185.         call    pktsize                 ; report packet size
  1186.         mov     ah,'B'                  ; End of Session packet
  1187.         call    sndpak                  ; send the packet
  1188.         call    rcvpak                  ; Get a packet
  1189.         call    acknak                  ; see if good ack or nak
  1190.         cmp     al,0                    ; ack'd ok?
  1191.         je      seot12                  ; e = yes, done with this
  1192.         cmp     al,1                    ; maybe a nak?
  1193.         jne     seot3                   ; ne = no, check for error
  1194.         ret                             ; else just return
  1195. seot12: call    fackmsg                 ; get/show any embedded message
  1196.         mov     pack.state,'C'          ; Set state to file completed
  1197.         ret
  1198.  
  1199. seot3:  cmp     ah,'E'                  ; Is it an error packet
  1200.         jne     seot4
  1201.         call    dodec                   ; Do all decoding
  1202.         call    error
  1203. seot4:  jmp     abort
  1204. SEOT    ENDP
  1205.  
  1206. sndpak  proc    near                    ; send packet with retries
  1207.         call    spack
  1208.          jmp    updrtr
  1209.          nop
  1210.         ret
  1211. sndpak  endp
  1212.  
  1213. rcvpak  proc    near                    ; receive packet with retries
  1214.         call    rpack                   ; Get a packet
  1215.          jmp    updrtr                  ;  Trashed packet, retry
  1216.          nop
  1217.         ret
  1218. rcvpak  endp
  1219.  
  1220. ; check the current packet for an ack or nak and handle it from any of
  1221. ; the send states.  Returns: 0 if an ack received with the correct expected
  1222. ; packet number, or if a nak received with the NEXT packet number (the
  1223. ; packet number is incremented, retry count reset); 1 if a nak or ack
  1224. ; with a bad packet number is received, retry count is updated and displayed.
  1225. ; A Timeout packet (type 'T') simply invokes a retry and a returned 1.
  1226. ; Finally, 2 is returned if anything else is seen
  1227. ;
  1228.  
  1229. ACKNAK  PROC    NEAR
  1230.         cmp     ah,'Y'                  ; ack packet?
  1231.         jne     ackna1                  ; ne = no, keep going
  1232.         mov     bx,pack.pktnum
  1233.         cmp     bx,pack.seqnum          ; is it what we were expecting?
  1234.         jne     ackna2                  ; no, update retries and punt
  1235.                                         ; packet ok, increment packet number
  1236. ackna0: mov     bx,pack.pktnum          ; reload packet number (!!!)
  1237.         inc     bx
  1238.         and     bx,03fh                 ; increment packet number
  1239.         mov     pack.pktnum,bx          ; store back
  1240.         inc     pack.numpkt             ; increment # of packets
  1241.         mov     pack.numtry,0
  1242.         mov     al,0                    ; ack'd ok
  1243.         ret
  1244.                                         ; not a 'Y'..
  1245. ackna1: cmp     ah,'N'                  ; a nak?
  1246.         je      ackna5                  ; yes, go on
  1247.         cmp     ah,'T'                  ; Timeout?
  1248.         je      ackna3                  ; e = yes, not a NAK but do a retry
  1249.         mov     al,2
  1250.         ret                             ; unknown packet type
  1251. ackna5: mov     bx,pack.pktnum
  1252.         inc     bx
  1253.         and     bx,3fh
  1254.         inc     fsta.nakrcnt            ; count received NAK for statistics
  1255.         cmp     bx,pack.seqnum          ; maybe a nak for pktnum+1?
  1256.         je      ackna0                  ; yes, treat as ack
  1257.         jne     ackna3
  1258.                                         ; nak or bad ack, update retry stuff
  1259. ackna2: inc     fsta.nakrcnt            ; count received NAK for statistics
  1260. ackna3: push    ax
  1261.         call    rtpos                   ; Position cursor
  1262.         inc     pack.numrtr             ; Increment the number of retries
  1263.         mov     ax,pack.numrtr
  1264.         call    nout                    ; Write the number of retries
  1265.         pop     ax
  1266.         mov     al,1                    ; nak code
  1267.         ret                             ; and return
  1268. ACKNAK  ENDP
  1269.  
  1270. ; Display message in ACK's to D packets. Requires a leading protocol char
  1271. ; and expects message to be encoded.
  1272. dackmsg proc near
  1273.         cmp     pack.datlen,1           ; any embedded message?
  1274.         jbe     dackmsgx                ; be = no (skip single char msgs)
  1275.         test    flags.remflg,dquiet     ; quiet display mode?
  1276.         jnz     dackmsgx                ; nz = yes, don't write to screen
  1277.         push    ax
  1278.         push    dx
  1279.         call    cxmsg                   ; clear message space in warning area
  1280.         call    dodec                   ; decode message, including X/Z/other
  1281.         mov     dx,offset data+1        ; point to asciiz message
  1282.         call    prtasz                  ; display it
  1283.         pop     dx
  1284.         pop     ax
  1285. dackmsgx:ret
  1286. dackmsg endp
  1287.  
  1288. ; Display messages in ACKs to F packets. Expects message to be not encoded.
  1289. fackmsg proc    near                    ; look for in ack
  1290.         cmp     pack.datlen,0           ; any embedded message?
  1291.         je      fackmsgx                ; e = no
  1292.         test    flags.remflg,dquiet     ; quiet display mode?
  1293.         jnz     fackmsgx                ; nz = yes, don't write to screen
  1294.         push    ax
  1295.         push    dx
  1296.         call    cxmsg                   ; clear message space in warning area
  1297.         call    dodec;;;decode
  1298.         mov     dx,offset data          ; point to asciiz message
  1299.         call    prtasz                  ; display it
  1300.         pop     dx
  1301.         pop     ax
  1302. fackmsgx:ret
  1303. fackmsg endp
  1304.  
  1305. ; newfn -- move replacement name from buffer sendas to buffer data
  1306. ; update cx to new filename length
  1307. newfn:  cmp     difsiz,0                ; Sending file under different name?
  1308.         je      newf4                   ; e = no, so don't give new name
  1309.         mov     si,offset sendas        ; source field
  1310.         mov     di,offset fsta.xname    ; statistics name area
  1311.         call    strcpy
  1312.         test    flags.remflg,dquiet     ; quiet display mode?
  1313.         jnz     newfa                   ; nz = yes. Don't write to screen
  1314.         mov     ah,prstr
  1315. ;        mov     dx,offset asmsg         ; display ' as '
  1316.         mcmsg   asmsg, casmsg
  1317.  
  1318.         cmp     mailflg,0               ; mail?
  1319.         je      newfn1                  ; e = no
  1320.         mov     dx,offset mailto        ; display ' To: '
  1321. newfn1: int     dos
  1322.         mov     ah,conout               ; use printable output
  1323.         cmp     mailflg,0               ; mail?
  1324.         je      newfa                   ; e = no
  1325.         mov     dx,offset sendas        ; get name
  1326.         call    prtasz                  ; print asciiz string
  1327.         jmp     newf4                   ; don't replace filename
  1328. newfa:  mov     si,offset sendas        ; Buffer where the name is
  1329.         mov     di,offset data
  1330.         mov     cx,difsiz               ; Length of name
  1331.         inc     cx                      ; plus null terminator
  1332. newf0:  lodsb                           ; Get a character into al
  1333.         stosb
  1334.         test    flags.remflg,dquiet    ; quiet display mode (should we print)?
  1335.         jnz     newf2                   ; nz = yes
  1336.         mov     dl,al                   ; set into dl for display
  1337.         int     dos                     ; Print them
  1338. newf2:  loop    newf0
  1339.         mov     cx,difsiz               ; Reset the length field
  1340. newf4:  test    flags.remflg,dserial    ; serial display mode?
  1341.         jz      newf5                   ; z = no
  1342.         mov     dx,offset crlf          ; start with cr/lf for serial display
  1343.         mov     ah,prstr
  1344.         int     dos
  1345. newf5:  ret
  1346.  
  1347. ; Do encoding.  Expect CX to be the data size
  1348.  
  1349. doenc:  jcxz    doen0
  1350.         mov     chrcnt,cx               ; Number of bytes of source data
  1351.         mov     bufpnt,offset data      ; Source of data
  1352.         mov     bx,offset nulref        ; Null routine for refilling buffer
  1353.         mov     ah,rptq
  1354.         mov     origr,ah                ; Save repeat prefix here
  1355.         mov     rptct,1                 ; Number of times char is repeated
  1356.         mov     rptval,0                ; Value of repeated char
  1357.         call    encode                  ; Make a packet with size in AX
  1358.          nop
  1359.          nop
  1360.          nop
  1361.         mov     pack.datlen,ax          ; Store length of data field
  1362.         mov     cx,ax
  1363.         call    movpak                  ; Move to data part of packet
  1364.         cmp     chrcnt,0                ; Did all chars fit into the buffer?
  1365.         jne     doen1                   ; ne = no, we have an error condition
  1366.         clc                             ; clear c bit for success
  1367. doen0:  ret
  1368. doen1:  stc                             ; set c bit for did not fit condition
  1369.         ret
  1370.  
  1371. ; CX is set before this is called
  1372. movpak: push    es
  1373.         mov     ax,ds
  1374.         mov     es,ax
  1375.         cld
  1376.         mov     si,offset filbuf        ; Move from here
  1377.         mov     di,offset data          ; to here
  1378.         shr     cx,1                    ; divide by two (words), set carry
  1379.         jnc     movpak1                 ; nc = even number of bytes
  1380.         movsb                           ; do the single move for odd count
  1381. movpak1:cmp     cx,0
  1382.         jle     movpak2
  1383.         rep     movsw
  1384. movpak2:pop     es
  1385.         ret
  1386.  
  1387. ; Dodecoding
  1388. dodec:
  1389.         push    ax                      ; Save packet size
  1390.         mov     bx,offset data          ; Address of data
  1391.         mov     ax,offset nulr          ; Routine to dump buffer (null)
  1392.         mov     bufpnt,offset decbuf    ; Where to put output
  1393.         mov     chrcnt,maxpack          ; Buffer size
  1394.         mov     cx,pack.datlen          ; Size of data
  1395.         jcxz    dodc0                   ; z = nothing to transfer
  1396.         call    decode
  1397.          nop
  1398.          nop
  1399.          nop
  1400. dodc0:  call    decmov          ; Move decoded data back to "data" buffer
  1401.         pop     ax
  1402.         ret
  1403.  
  1404. ; Move decoded data from decode buffer back to "data".
  1405. decmov: push    si
  1406.         push    di
  1407.         push    es
  1408.         mov     ax,ds
  1409.         mov     es,ax
  1410.         cld
  1411.         mov     cx,bufpnt               ; Last char we added
  1412.         sub     cx,offset decbuf        ; Get actual number of characters
  1413.         mov     pack.datlen,cx          ; Remember size of real data
  1414.         mov     si,offset decbuf        ; Data is here
  1415.         mov     di,offset data          ; Move to here
  1416.         shr     cx,1                    ; divide by two (words), set carry
  1417.         jnc     decmov1                 ; nc = even number of bytes
  1418.         movsb                           ; do single move
  1419. decmov1:cmp     cx,0
  1420.         jle     decmov2                 ; le = none to do
  1421.         rep     movsw                   ; Copy the data
  1422. decmov2:mov     al,0                    ; Null to end the string
  1423.         stosb
  1424.         pop     es
  1425.         pop     di
  1426.         pop     si
  1427.         ret
  1428.  
  1429. ;       Abort
  1430.  
  1431. ABORT   PROC    NEAR
  1432.         mov     difsiz,0                ; clear original filename
  1433.         mov     byte ptr sendas,0       ; clear sendas name
  1434.         mov     mailflg,0               ; clear Mail flag
  1435.         cmp     filopn,0                ; Any disk files open?
  1436.         je      abort0                  ; No so don't do a close
  1437.         mov     ah,close2               ; DOS 2.0 close file
  1438.         push    bx
  1439.         mov     bx,diskio.handle        ; file handle
  1440.         int     dos
  1441.         pop     bx
  1442.         mov     filopn,0                ; say file is closed now
  1443. abort0: mov     pack.state,'A'          ; Otherwise abort
  1444.         or      errlev,1                ; set DOS error level
  1445.         or      fsta.xstatus,1          ; set status
  1446.         mov     kstatus,1               ; global status
  1447.         ret
  1448. ABORT   ENDP
  1449.  
  1450. ; This is where we go if we get an error packet.  A call to ERROR
  1451. ; positions the cursor and prints the message.  A call to ERROR1
  1452. ; just prints a CRLF and then the message
  1453.  
  1454. ERROR   PROC    NEAR
  1455.         mov     pack.state,'A'          ; Set the state to abort
  1456.         test    flags.remflg,dquiet     ; quiet display mode?
  1457.         jnz     errorx                  ; nz = yes. Don't write to screen
  1458.         call    erpos                   ; Position the cursor
  1459.         jmp     error2
  1460. ERROR1: mov     ah,prstr                ; entry point for Server Generic cmds
  1461.         mov     dx,offset crlf
  1462.         int     dos
  1463. error2: mov     dx,offset data          ; error message string
  1464.         push    bx
  1465.         mov     bx,pack.datlen          ; Get the length of the data
  1466.         add     bx,dx                   ; Get to the end of the string
  1467.  
  1468.         mov     byte ptr [bx],0         ; terminate string
  1469.         pop     bx
  1470.         call    prtasz                  ; print asciiz string
  1471.         pop     bx
  1472. errorx: ret
  1473. ERROR   ENDP
  1474.  
  1475. ; Set the maximum send data packet size; modified for long packets
  1476. PACKLEN PROC    NEAR
  1477.         push    ax
  1478.         push    cx
  1479.         xor     ah,ah
  1480.         mov     al,trans.spsiz  ; Maximum send packet size for Regular pkts.
  1481.         cmp     ax,trans.slongp         ; negotiated long packet max size
  1482.         jae     pack2                   ; ae = use regular packets
  1483.         mov     ax,trans.slongp         ; else use long kind
  1484.         sub     ax,3                    ; minus extended count & checksum
  1485.         cmp     ax,(95*94-1-2)          ; longer than Long?
  1486.         jle     pack2                   ; le = no, Long will do
  1487.         dec     ax                      ; minus one more for extra long count
  1488. pack2:  sub     ax,2                    ; minus Sequence, Type
  1489.         sub     al,trans.chklen         ; And minus Checksum chars
  1490.         sbb     ah,0                    ; borrow propagate
  1491.         cmp     trans.ebquot,'N'        ; Doing 8-bit Quoting?
  1492.         je      pack0                   ; Nope so we've got our size
  1493.         cmp     trans.ebquot,'Y'
  1494.         je      pack0                   ; Not doing it in this case either
  1495.         dec     ax                      ; Another 1 for 8th-bit Quoting.
  1496. pack0:  cmp     rptq,0                  ; Doing repeat character Quoting?
  1497.         je      pack1                   ; Nope, so that's all for now
  1498.         dec     ax                      ; minus repeat prefix
  1499.         dec     ax                      ;  and repeat count
  1500. pack1:  dec     ax                  ; for last char might being a control code
  1501.         mov     trans.maxdat,ax         ; Save max length for data field
  1502.         pop     cx
  1503.         pop     ax
  1504.         ret
  1505. PACKLEN ENDP
  1506.  
  1507.  ; Print the number in AX on the screen in decimal rather that hex
  1508.  
  1509. NOUT    PROC    NEAR
  1510.         test    flags.remflg,dserial    ; serial display mode?
  1511.         jnz     pnout               ; nz = use "dot and plus" for serial mode
  1512.         test    flags.remflg,dquiet     ; quiet display mode?
  1513.         jnz     nout1                   ; nz = yes. Don't write to screen
  1514.         call    decout                  ; call standard decimal output routine
  1515. nout1:  ret
  1516.  
  1517.  
  1518. pnout:  or      ax,ax                   ; display packet in serial display mode
  1519.         jz      pnoutx                  ; z = nothing to display
  1520.         push    ax                      ; for serial mode display
  1521.         push    dx                      ; output .........+.........+  etc
  1522.         mov     temp,10
  1523.         mov     dx,0
  1524.         div     temp                    ; number/10. (AX=quo, DX=rem)
  1525.         push    ax                      ; save around printing
  1526.         push    dx                      ; save around initial printing
  1527.         cmp     dx,0                    ; remainder non-zero?
  1528.         jne     pnout1                  ; ne = yes
  1529.         mov     dl,'+'                  ; symbol plus for tens
  1530.         jmp     pnout2                  ; display it
  1531. pnout1: mov     dl,'.'                  ; symbol for between tens
  1532. pnout2: mov     ah,conout               ; output to console
  1533.         int     dos
  1534.         pop     dx                      ; recover remainder
  1535.         pop     ax                      ; recover quotient
  1536.         or      dx,dx           ; check for multiples of 70, to break lines
  1537.         jnz     pnout3                  ; nz = non-zero remainder, just exit
  1538.         mov     temp,7          ; divide ax by 7 (dx is zero by construction)
  1539.         div     temp                    ; ax = quotient, dx = remainder
  1540.         or      dx,dx                   ; zero remainder?
  1541.         jnz     pnout3                  ; nz = non-zero remainder, just exit
  1542.         mov     ah,prstr                ; output cr/lf after every 70th chars
  1543.         mov     dx,offset crlf
  1544.         int     dos
  1545. pnout3: pop     dx
  1546.         pop     ax
  1547. pnoutx: ret
  1548. NOUT    ENDP
  1549.  
  1550. ; Jumping to this location is like retskp.  It assumes the instruction
  1551. ;   after the call is a jmp addr
  1552.  
  1553. RSKP    PROC    NEAR
  1554.         pop     bp
  1555.         add     bp,3
  1556.         push    bp
  1557.         ret
  1558. RSKP    ENDP
  1559. code    ends
  1560.         end
  1561.