home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / msr313src.zip / msssen.asm < prev    next >
Assembly Source File  |  1993-07-12  |  70KB  |  2,089 lines

  1.     NAME    msssen
  2. ; File MSSSEN.ASM
  3.     include mssdef.h
  4.  
  5. ;  Copyright (C) 1985, 1993, Trustees of Columbia University in the 
  6. ;  City of New York.  Permission is granted to any individual or institution
  7. ;  to use this software as long as it is not sold for profit.  This copyright
  8. ;  notice must be retained.  This software may not be included in commercial
  9. ;  products without written permission of Columbia University.
  10. ;
  11. ; Edit history:
  12. ; 27 August 1992 version 3.13
  13. ; 6 Sept 1991 version 3.11
  14. ; Last edit 15 May 1993
  15. ; Sliding Windows
  16.  
  17.     public    spar, rpar, nout, send, flags, trans, dtrans, packlen
  18.     public    send10, mail, newfn, errpack, sstate, response, ackmsg
  19.     public    pktnum, numpkt, cntretry, sndpak, sparmax, remprn
  20.  
  21. spmin    equ    20        ; Minimum packet size
  22. spmax    equ    94        ; Maximum packet size
  23.  
  24. data     segment
  25.     extrn    fsta:word, auxfile:byte, encbuf:byte, decbuf:byte, maxtry:byte
  26.     extrn    errlev:byte, kstatus:word, diskio:byte, rpacket:byte
  27.     extrn    windflag:byte, rstate:byte, fmtdsp:byte, windlow:byte
  28.     extrn    charids:word, portval:word, chkparflg:byte, maxbufparas:word
  29.     extrn    protlist:byte, flowon:byte, flowoff:byte
  30.  
  31. flags    flginfo    <>
  32. trans    trinfo    <>        ; negotiated trans information
  33. dtrans    trinfo    <>        ; default trans information
  34. crlf    db    cr,lf,'$'
  35. ender    db    bell,bell,'$'
  36. cemsg    db    'User intervention',0
  37. erms14  db    'No response from the host',0
  38. erms15    db    'File not found',0
  39. erms17    db    'Too many retries',0
  40. erms24    db    'Unable to send packet',0
  41. erms25    db    'Host does not support Kermit MAIL command',0
  42. erms26    db    'File rejected by host',0
  43. erms27    db    'Error. No buffers in send routine',0
  44. erms30    db    ': File size',0
  45. erms31    db    ': Date/time',0
  46. erms32    db    ': Mailing refused',0
  47. erms33    db    ': File Type',0
  48. erms34    db    ': Transfer Char-set',0
  49. erms36    db    ': Unknown reason',0
  50. infms1  db    cr,'             Sending: In progress',cr,lf,'$'
  51. infms2  db    cr,'             Mailing: In progress',cr,lf,'$'
  52. infms3  db    'Completed',cr,lf,'$'
  53. infms4  db    'Failed',cr,lf,'$'
  54. infms5    db    'Remote name is ',0
  55. filhlp  db      ' A filename (possibly wild)$'
  56. filmsg    db    ' Local Source File  or press ENTER for prompts$'
  57. remfnm    db    ' Remote Destination File: ',0    ; asciiz
  58. lclfnm    db    ' Local Source File: ',0    ; asciiz
  59. mailhlp    db    ' Filename  mail-address  or press ENTER for prompts$'
  60. mailto    db    ' To: ',0            ; asciiz
  61. mailtohlp db    ' mail address (eg, user@host or host::user)$'
  62. mailflg    db    0        ; 1 if Mail, 0 if Send command
  63. printhlp db    ' Filename and any extra host''s printer paramters$'
  64. printpmt db    ' Host printer parameters: ',0    ; asciiz 
  65. printas    db    ' host''s printer parameters, such as /COPIES=2/QUE=HPLJ$'
  66. asmsg    db    ' as ',0
  67. sstate    db    0        ; current automata state
  68. pktnum    db    0        ; packet number
  69. sndcnt    db    0        ; retry counter for sndpak, internal
  70. filopn    db    0        ; 1 if disk file is open for reading
  71. tempseq    db    0        ; target sequence number for responses
  72. retry    db    0        ; current retry threshold
  73.     even
  74. current_max dw    40        ; current max D packet length
  75. attlist    dw    sat5t,sat1t,sat2t,sat3t,sat4t,sat6t,sat7t,0 ;attrib procedures
  76. attptr    dw    0        ; pointer to items in attlist
  77. numpkt    dw    0        ; number of packets for file group
  78. temp    dw    0
  79. temp4    dw    0
  80. ninefive dw    95        ; constant word for long packets
  81. data    ends
  82.  
  83. code1    segment
  84.     extrn bufclr:far, pakptr:far, bufrel:far, makebuf:far, chkwind:far
  85.     extrn getbuf:far, rpack:far, spack:far, fcsrtype:far, peekreply:far
  86. code1    ends
  87.  
  88. code    segment
  89.     extrn serini:near, comnd:near, init:near
  90.     extrn gtnfil:near, gtchr:near, clrbuf:near, filekind:near
  91.     extrn getfil:near, rprpos:near, prtasz:near, cxerr:near
  92.     extrn ermsg:near, rtmsg:near, cxmsg:near, stpos:near, decout:near
  93.     extrn doenc:near, dodec:near, lnout:near, winpr:near
  94.     extrn prompt:near, intmsg:near, msgmsg:near
  95.     extrn strcpy:near, strlen:near, pktsize:near, isfile:near
  96.     extrn pcwait:near, ihostr:near, begtim:near, endtim:near, filecps:near
  97.  
  98.     assume    cs:code, ds:data, es:nothing
  99.  
  100. ; Data structures comments.
  101. ; Sent raw text material (typically rpar and filenames) is placed in encbuf,
  102. ; which may be encoded by doenc. doenc needs an output buffer provided as
  103. ; a pointer generated here via procedure buflist. encbuf is 512 bytes long.
  104. ; Sent packet material is placed in buffers pointed at by buflist. These
  105. ; buffers are subdivisions of one large buffer bufbuf (private to msscom).
  106. ; Proceedure makebuf does the subdivision and initialization of contents.
  107. ; Received material is directed to buffer rbuf which is part of structure
  108. ; rpacket; rbuf is 128 bytes long.
  109. ; Rpack and Spack expect a pointer in SI to the packet data field, done in a
  110. ; pktinfo format.
  111.  
  112. ;    SEND filespec
  113. ;    MAIL filspec user@node
  114. ;    REMOTE PRINT filespec parameters
  115.  
  116. SEND    PROC    NEAR
  117.     mov    mailflg,0        ; Send command, not Mail command
  118.     mov    temp,0
  119.     jmp    short send1        ; join common code
  120.  
  121. MAIL:    mov    mailflg,1        ; set flag for Mail command vs Send
  122.     mov    temp,1            ; temp copy of mailflag
  123.     jmp    short send1
  124.  
  125. REMPRN:    mov    mailflg,2        ; REMOTE PRINT entry point
  126.     mov    temp,2
  127.  
  128. send1:    mov    auxfile,0        ; clear send-as name (in case none)
  129.     mov    dx,offset diskio.string ; address of filename string
  130.     mov    bx,offset filmsg    ; help message
  131.     cmp    mailflg,0        ; Mail command?
  132.     je    send2            ; e = no
  133.     mov    mailflg,0        ; clear in case error exit
  134.     mov    bx,offset mailhlp    ; help message
  135.     cmp    temp,2            ; REMOTE PRINT?
  136.     jne    send2            ; ne = no
  137.     mov    bx,offset printhlp    ; help message
  138. send2:    mov    ah,cmword        ; get input file spec
  139.     call    comnd
  140.     jnc    send2a            ; nc = success
  141.     ret                ; failure
  142. send2a:    cmp    diskio.string,'#'    ; first char a replacement for '?'?
  143.     jne    send2b            ; ne = no
  144.     mov    diskio.string,'?'    ; yes. Change '#' for '?'
  145. send2b:    or    ax,ax            ; any text given?
  146.     jz    send3            ; z = no, prompt
  147.     cmp    temp,0            ; Mail or REMOTE PRINT command?
  148.     jne    send5            ; ne = yes, require address etc
  149.     mov    bx,offset auxfile         ; send file under different name?
  150.     mov    dx,offset filhlp    ; help
  151.     mov    ah,cmline        ; allow embedded white space
  152.     call    comnd
  153.     jnc    send2c            ; nc = success
  154.     ret                ; failure
  155. send2c:    cmp    auxfile,'#'        ; first char a replacement for '?'?
  156.     jne    send2d            ; ne = no
  157.     mov    auxfile,'?'        ; change '#' to '?'
  158. send2d:    jmp    send6            ; join common completion code
  159.  
  160. send3:    mov    dx,offset lclfnm    ; prompt for local filename
  161.     call    prompt
  162.     mov    dx,offset diskio.string ; reload destination of user's text
  163.     mov    bx,offset filhlp    ; help
  164.     mov    ah,cmword        ; get filename
  165.     call    comnd            ; try again for a local filename
  166.     jnc    send3a            ; nc = success
  167.     ret                ; failure
  168. send3a:    cmp    diskio.string,'#'    ; first char a replacement for '?'?
  169.     jne    send3b            ; ne = no
  170.     mov    diskio.string,'?'    ; yes. Change '#' for '?'
  171. send3b:    push    ax
  172.     mov    ah,cmeol        ; get the terminating CR
  173.     call    comnd
  174.     pop    ax
  175.     jnc    send3c            ; nc = success
  176.     ret                ; failure
  177. send3c:    or    ax,ax            ; user's byte count
  178.     jz    send3            ; z = nothing was typed, get some
  179.  
  180. send4:    mov    dx,offset remfnm    ; ask for remote name first
  181.     cmp    temp,0            ; Mail command?
  182.     je    send4a            ; e = no
  183.     mov    dx,offset mailto    ; ask for name@host
  184.     cmp    temp,2            ; REMOTE PRINT?
  185.     jne    send4a            ; ne = no
  186.     mov    dx,offset printpmt    ; ask for host print parameters
  187. send4a:    call    prompt
  188. send5:    mov    bx,offset auxfile         ; send file under different name?
  189.     mov    dx,offset filhlp    ; help
  190.     cmp    temp,0            ; Mail command?
  191.     je    send5a            ; e = no
  192.     mov    dx,offset mailtohlp    ; help
  193.     cmp    temp,2            ; REMOTE PRINT?
  194.     jne    send5a            ; ne = no
  195.     mov    dx,offset printas    ; help for printer parameters
  196. send5a:    mov    ah,cmline        ; allow embedded white space
  197.     call    comnd
  198.     jnc    send5b            ; nc = success
  199.     ret                ; failure
  200. send5b:    cmp    temp,2            ; REM Print cmd?
  201.     je    send6            ; e = yes, allow no parameters
  202.     or    ax,ax            ; text entered?
  203.     jz    send4            ; z = no, get some
  204.  
  205. send6:    mov    flags.xflg,0        ; reset flag for normal file send[mtd]
  206.     mov    sstate,0        ; dummy state, must be illegal
  207.     mov    ax,temp            ; get temp mailflag
  208.     mov    mailflg,al        ; store in secure area for later
  209.     mov    ah,trans.sdelay        ; seconds to delay before sending
  210.     shl    ah,1            ; times 4*256 to get millisec
  211.     shl    ah,1            ;  for pcwait
  212.     mov    al,1            ; set low byte to 1 for no delay case
  213.     call    pcwait            ; wait number of millisec in ax    
  214. SEND10:                ; SEND10 is an entry point for REMote cmds
  215.     mov    kstatus,kssuc        ; global status, success
  216.     mov    windflag,0        ; init windows in use display flag
  217.     mov    trans.windo,1        ; one window slot before negotiations
  218.     mov    cx,dspsiz        ; default send pkt length (94)
  219.     call    makebuf            ; make some packet buffers
  220.     call    clrbuf            ; clear port buffer of old NAKs
  221.     call    packlen            ; compute packet length
  222.     call    cxerr            ; clear Last Error line
  223.     call    cxmsg            ; clear Last Message line
  224.     mov    ax,offset diskio.string    ; filename to send, can be wild
  225.     call    isfile            ; does file exist?
  226.     jnc    send12            ; carry reset = yes, file found
  227.     cmp    sstate,'S'        ; was this from a remote GET?
  228.     jne    send11            ; ne = no, print error and continue
  229.     mov    dx,offset erms15    ; file not found
  230.     mov    trans.chklen,1        ; send init checksum is always 1 char
  231.     call    ermsg
  232.     mov    bx,dx
  233.     call    errpack            ; go complain
  234.     mov    sstate,'A'        ; abort
  235.     ret
  236.  
  237. send11:    mov    ah,prstr
  238.     mov    dx,offset crlf
  239.     int    dos
  240.     mov    dx,offset erms15    ; 'file not found'
  241.     call    prtasz
  242.     or    errlev,kssend        ; set DOS error level
  243.     or    fsta.xstatus,kssend    ; set status
  244.     mov    kstatus,kssend        ; global status
  245.     mov    mailflg,0        ; clear Mail flag
  246.     clc                ; pretend successful completion
  247.     ret
  248.  
  249. send12:    call    serini            ; initialize serial port
  250.     jnc    send13            ; nc = success
  251.     or    errlev,kssend        ; say send failed
  252.     or    fsta.xstatus,kssend    ; set status
  253.     mov    kstatus,kssend        ; global status
  254.     mov    dx,offset erms14    ; no response from host
  255.     call    ermsg            ; show message
  256.     stc                ; return failure
  257.     ret
  258.  
  259. send13:    xor    ax,ax
  260.     mov    pktnum,al        ; set the packet number to zero
  261.     mov    fsta.pretry,ax         ; set the number of retries to zero
  262.     mov    numpkt,ax        ; number pkts send in this file group
  263.     mov    flags.cxzflg,al
  264.     mov    sstate,'S'        ; set the state to send initiate
  265.     mov    chkparflg,1        ; check for unexpected parity
  266.     call    ihostr            ; initialize the host (clear NAKs)
  267.     call    init            ; clear screen and initialize buffers
  268.     call    clrbuf            ; clear serial port buffer of junk
  269.     test    flags.remflg,dquiet+dserial ; quiet or serial display mode?
  270.     jnz    send15            ; nz = yes, suppress msgs
  271.     call    stpos            ; show status of file transfer
  272.     mov    dx,offset infms1    ; Sending in progress message
  273.     cmp    mailflg,0        ; Sending, vs Mailing?
  274.     je    send14            ; e = yes, sending
  275.     mov    dx,offset infms2    ; Mailing in progress message
  276. send14:    mov    ah,prstr
  277.     int    dos
  278. send15:    jmp    short dispatch        ; sstate has initial state ('S')
  279. SEND    ENDP
  280.  
  281. dispatch proc    near            ; Dispatch on state variable sstate
  282.     mov    ah,sstate
  283.     cmp    ah,'S'            ; send initiate state?
  284.     jne    dispat2            ; ne = no
  285.     call    sinit            ; negotiate
  286.     jmp    short dispatch
  287.  
  288. dispat2:cmp    ah,'F'            ; file header state?
  289.     jne    dispat3            ; ne = no
  290.     call    sfile            ; send file header
  291.     jmp    short dispatch
  292.  
  293. dispat3:cmp    ah,'a'            ; send attributes state?
  294.     jne    dispat4            ; ne = no
  295.     call    sattr            ; send attributes
  296.     jmp    short dispatch
  297.  
  298. dispat4:cmp    ah,'D'            ; data send state?
  299.     jne    dispat5            ; ne = no
  300.     call    sdata            ; send data
  301.     jmp    short dispatch
  302.  
  303. dispat5:cmp    ah,'Z'            ; EOF state?
  304.     jne    dispat6
  305.     call    seof            ; do EOF processing
  306.     jmp    short dispatch
  307.  
  308. dispat6:cmp    ah,'B'            ; end of file group state?
  309.     jne    dispat7
  310.     call    seot
  311.     jmp    short dispatch
  312.  
  313. dispat7:cmp    ah,'E'            ; user intervention ^C or ^E?
  314.     jne    dispat8            ; ne = no
  315.     mov    bx,offset cemsg        ; user intervention message
  316.     call    errpack            ; send error message
  317.     call    intmsg            ; show interrupt msg for Control-C-E
  318.  
  319. dispat8:push    ax                ; 'A' abort or 'C' completion
  320.     pop    ax
  321.     mov    mailflg,0        ; clear Mail flag
  322.     call    bufclr            ; release all buffers
  323.     mov    windlow,0
  324.     mov    pktnum,0
  325.     call    stpos            ; show status of file transfer
  326.     mov    dx,offset infms3    ; Completed message
  327.     cmp    sstate,'C'        ; send complete state?
  328.     je    dispat9            ; e = yes, else failure
  329.     mov    dx,offset infms4    ; Failed message
  330.     or    errlev,kssend        ; say send failed
  331.     or    fsta.xstatus,kssend    ; set status
  332.     mov    kstatus,kssend        ; global status
  333.  
  334. dispat9:test    flags.remflg,dquiet+dserial ; quiet or serial display mode?
  335.     jnz    dispa9a            ; nz = yes, keep going
  336.     mov    ah,prstr        ; show completed/failed message
  337.     int    dos
  338.     mov    al,1            ; underline cursor
  339.     call    fcsrtype        ; set IBM-PC cursor to underline
  340. dispa9a:cmp    flags.cxzflg,0        ; completed normally?
  341.     je    dispa10            ; e = yes
  342.     or    errlev,kssend        ; say send failed
  343.     or    fsta.xstatus,kssend+ksuser ; set status, failed + intervention
  344.     mov    kstatus,kssend+ksuser    ; global status
  345. dispa10:mov    ax,1        ; tell statistics this was a send operation
  346.     call    endtim            ; stop statistics counter
  347.     test    flags.remflg,dquiet    ; quiet display mode?
  348.     jnz    dispa13            ; nz = yes, no talking
  349.     call    filecps            ; show file chars/sec
  350.     call    intmsg            ; show any interruption
  351.     cmp    flags.belflg,0        ; Bell desired?
  352.     je    dispa13            ; e = no
  353.     mov    ah,prstr
  354.     mov    dx,offset ender        ; ring bells
  355.     int    dos
  356. dispa13:call    rprpos            ; position cursor
  357.     xor    al,al
  358.     mov    flags.cxzflg,al        ; clear flag for next command
  359.     mov    auxfile,al        ; clear send-as filename buffer
  360.     mov    flags.xflg,al        ; clear to-screen flag
  361.     mov    diskio.string,al    ; clear active filename buffer
  362.     mov    fsta.xname,al        ; clear statistics external name
  363.     clc
  364.     ret                ; return to main command parser
  365. dispatch endp
  366.  
  367. ; Enter with filespec in diskio.string, external name/mail address in auxfile
  368.  
  369. ; Send Initiate packet
  370. SINIT    PROC    NEAR
  371.     mov    al,1            ; say starting send operation
  372.     mov    trans.windo,1        ; one window slot before negotiations
  373.     mov    cx,dspsiz        ; default send pkt length (94)
  374.     call    makebuf            ; remake buffers for new windowing
  375.     call    packlen            ; compute packet length
  376.     xor    ax,ax
  377.     mov    windlow,al        ; window lower border
  378.     mov    pktnum,al        ; sequence number to use
  379.     mov    windflag,al        ; windows in use display flag
  380.     mov    al,dtrans.xchset    ; reset Transmission char set
  381.     mov    trans.xchset,al        ;  to the current user default
  382.     mov    al,dtrans.xtype        ; ditto for File Type
  383.     mov    trans.xtype,al
  384.     call    getbuf            ; get a buffer address into si
  385.     call    sparmax            ; set up our maximum capabilities
  386.     call    rpar            ; put them into the packet
  387.     mov    trans.chklen,1        ; Send init checksum is always 1 char
  388.     mov    [si].pktype,'S'        ; send-initiate packet
  389.     call    sndpak            ; send the packet
  390.     jnc    sinit2            ; nc = successful send
  391.     ret                ; failure, change state
  392.  
  393. sinit2:    mov    al,pktnum        ; packet just sent
  394.     mov    ah,maxtry        ; normal retry threshold
  395.     add    ah,ah
  396.     add    ah,maxtry        ; triple the normal threshold
  397.     call    response        ; get response
  398.     jnc    sinit3            ; nc = success
  399.     ret
  400.  
  401. sinit3:    push    si            ; ACK in window
  402.     mov    si,offset rpacket    ; point to packet for spar
  403.     call    spar            ; parse the received data
  404.     pop    si
  405.     mov    cx,trans.slong        ; negotiated send packet size
  406.     call    makebuf            ; remake buffers for new windowing
  407.     call    packlen            ; update max send packet size
  408.     mov    pktnum,1        ; next sequence number after 'S' pkts
  409.     mov    windlow,1        ; lowest acceptable sequence received
  410.     cmp    mailflg,0        ; non-zero to do Mail command
  411.     je    sinit4            ; e = send, not mail command
  412.     cmp    flags.attflg,0        ; file attributes disabled?
  413.     je    sinit5            ; e = yes, so no Mail
  414.     test    trans.capas,8        ; can they do file attributes?
  415.     jz    sinit5            ; z = no, so cannot do Mail
  416. sinit4:    call    getfil            ; open the file
  417.     jc    sinit4a            ; c = error
  418.     mov    filopn,1        ; disk file is open
  419.     mov    sstate,'F'        ; set the state to file send
  420.     ret
  421.  
  422. sinit4a:mov    dx,offset erms15    ; file not found
  423.     jmp    giveup            ;  something is wrong, quit w/msgs
  424.  
  425.                     ; say Mail not supported by host
  426. sinit5:    mov    dx,offset erms25    ; say no mail today
  427.     call    ermsg            ; tell both sides
  428.     mov    sstate,'B'        ; go to EOT state
  429.     ret
  430. SINIT    ENDP
  431.  
  432. ; Send file header
  433. ; Enter with pktnum set for the next transmission, no buffer
  434.  
  435. SFILE    PROC    NEAR
  436.     mov    al,1            ; say starting send operation
  437.     call    begtim            ; start statistics
  438.     cmp    filopn,1        ; is file open already?
  439.     je    sfile1            ; e = yes
  440.     call    bufrel            ; release buffer for SI
  441.     mov    dx,offset erms24    ; cannot send packet
  442.     jmp    giveup            ; something is wrong, quit
  443.  
  444. sfile1:    mov    flags.cxzflg,0        ; clear ^C, ^E, ^X, ^Z flag 
  445.     call    getbuf            ; get a packet buffer, address in si
  446.     jnc    sfile2            ; nc = got one
  447.     mov    dx,offset erms27    ; no buffers
  448.     jmp    giveup            ; tell both sides and fail
  449.  
  450. sfile2:    mov    dx,offset encbuf    ; destination = encode source buffer
  451.     call    strlen            ; get length (w/o terminator) into cx
  452.     call    doenc            ; do encoding; length is in cx
  453.     mov    [si].pktype,'F'        ; File header packet
  454.     cmp    flags.xflg,0        ; REMOTE command? (goes to screen)
  455.     je    sfile3            ; e = no
  456.     mov    [si].pktype,'X'        ; use X rather than F for REMOTE cmds
  457. sfile3:    call    sndpak            ; send the packet
  458.     jnc    sfile4            ; nc = success
  459.     ret
  460.  
  461. sfile4:    mov    al,pktnum        ; want response for packet just sent
  462.     mov    ah,maxtry        ; retry threshold
  463.     call    response        ; get response
  464.     jnc    sfile5            ; nc = success (got an ACK)
  465.     ret
  466.  
  467. sfile5:    call    ackmsg            ; show any message in ACK
  468.     inc    pktnum            ; next pkt to send/rcv, from ackpak
  469.     and    pktnum,3fh        ; modulo 64
  470.     call    filekind        ; report file type, char set
  471.     push    trans.maxdat        ; negotiated max packet length
  472.     pop    current_max        ; set current D packet length
  473.     mov    sstate,'D'        ; send data as the next state
  474.     test    trans.capas,8        ; can they do file attributes?
  475.     jz    sfile6            ; z = no, so cannot do attributes
  476.     mov    sstate,'a'        ; set file attributes as next state
  477. sfile6:    ret                ; return to do next state
  478. SFILE    ENDP
  479.  
  480. ; Send file attributes. Attributes: file size in bytes and kilobytes,
  481. ; file time and date, machine identification.
  482. ; Writes output to buffer encbuf.
  483. SATTR    PROC    NEAR
  484.     cmp    flags.attflg,0        ; allowed to do file attributes?
  485.     je    satt0            ; e = no
  486.     mov    attptr,offset attlist    ; point at list of attributes procs
  487.     test    trans.capas,8        ; can we do file attributes?
  488.     jnz    satt1            ; nz = yes
  489. satt0:    mov    sstate,'D'        ; set the state to send-data
  490.     ret
  491.  
  492. satt1:    mov    bx,attptr        ; ptr to list of attributes procedures
  493.     cmp    word ptr [bx],0        ; at end of list?
  494.     je    satt0            ; e = yes, do next state
  495.     call    getbuf            ; get buffer for sending
  496.     jnc    satt2            ; nc = got one
  497.     mov    dx,offset erms27    ; no buffers
  498.     jmp    giveup            ; tell both sides and fail
  499.  
  500. satt2:    mov    di,offset encbuf    ; address of a temp data buffer
  501.     mov    byte ptr [di],0        ; start with null terminator
  502.     push    es            ; save es around this work
  503.     push    ds
  504.     pop    es            ; set es to data segment for es:di
  505.     push    si
  506.     mov    bx,attptr        ; ptr to list of attributes routines
  507.     mov    bx,[bx]            ; de-reference one level
  508.     call    bx            ; do it
  509.     pop    si
  510.     pop    es
  511.     mov    byte ptr [di],0        ; insert null terminator in temp buf
  512.     mov    dx,offset encbuf
  513.     call    strlen            ; get length of this attribute to CX
  514.     cmp    cx,trans.maxdat        ; longer than any packet?
  515.     jbe    satt3            ; be = no, proceed
  516.     call    bufrel            ; release this buffer
  517.     mov    dx,offset erms24    ; "Unable to send packet"
  518.     call    ermsg            ; position cursor, display asciiz msg
  519.     mov    flags.cxzflg,'X'    ; say skip this file
  520.     mov    sstate,'Z'        ; move to EOF state
  521.     ret
  522.  
  523. satt3:    mov    ax,[si].datlen        ; data length for packet, so far
  524.     add    ax,cx            ; plus, maybe, this contribution
  525.     cmp    ax,trans.maxdat        ; new info fits into packet?
  526.     jg    satt4            ; g = no, send what we have
  527.     push    si            ; preserve packet structure pointer
  528.     push    es
  529.     les    di,[si].datadr        ; packet buffer beginning
  530.     add    di,[si].datlen        ; plus current contents
  531.     add    [si].datlen,cx        ; say adding this many bytes
  532.     mov    si,offset encbuf    ; temp buffer is source of new info
  533.     cld                ; packet buffer is destination
  534.     rep    movsb            ; copy new material to packet buffer
  535.     pop    es
  536.     pop    si
  537.     add    attptr,2        ; next attributes procedure address
  538.     mov    bx,attptr
  539.     cmp    word ptr [bx],0        ; at end of list?
  540.     jne    satt2            ; ne = no, do next attribute proc
  541.     cmp    [si].datlen,0        ; any data to send?
  542.     jne    satt4            ; ne = yes
  543.     call    bufrel            ; release the unused buffer
  544.     mov    sstate,'D'        ; set the state to send-data
  545.     ret
  546.  
  547. satt4:    call    sndatt            ; send attributes packet, get response
  548.     jc    satt5            ; c = failure
  549.     jmp    satt1            ; get new buffer, do more attributes
  550. satt5:    ret                ; failure, change state
  551.  
  552.                     ; Send Attributes packet, local worker
  553. sndatt:    mov    [si].pktype,'A'        ; Attributes packet
  554.     call    sndpak            ; send the packet
  555.     jnc    sndat1            ; nc = success
  556.     ret
  557. sndat1:    mov    al,pktnum        ; get response for packet just sent
  558.     mov    ah,maxtry        ; retry threshold
  559.     call    response        ; get response
  560.     jnc    sndat2            ; nc = success
  561.     ret
  562.  
  563. sndat2:    inc    pktnum            ; sent and ack'd, next seqnum to use
  564.     and    pktnum,3fh
  565.     cmp    rpacket.datlen,0    ; any data in the ACK?
  566.     je    sndat3            ; e = no
  567.     push    es
  568.     les    bx,rpacket.datadr    ; received data field
  569.     mov    al,es:[bx]        ; response
  570.     inc    bx            ; point to next field
  571.     cmp    al,'N'            ; are they refusing this file?
  572.     pop    es
  573.     je    sndat4            ; e = yes, 'N' = refusing the file
  574. sndat3:    clc                ; say success
  575.     ret
  576.                     ; display file refusal reason
  577. sndat4:    test    flags.remflg,dquiet    ; quiet display?
  578.     jnz    sndat7            ; nz = yes
  579.     mov    dx,offset erms26    ; say host rejected the file 
  580.     call    ermsg
  581.     mov    cx,rpacket.datlen    ; display all reasons
  582. sndat5:    dec    cx            ; next byte
  583.     cmp    cx,0            ; anything there?
  584.     jle    sndat7            ; b = no
  585.     push    es
  586.     mov    es,word ptr rpacket.datadr+2    ; received data field seg
  587.     mov    ah,es:[bx]        ; reason code
  588.     inc    bx            ; point to next field
  589.     pop    es
  590.     mov    dx,offset erms30    ; ah has reason code from above
  591.     cmp    ah,'1'            ; Byte count?
  592.     je    sndat6            ; e = yes
  593.     cmp    ah,'!'            ; Kilobyte count?
  594.     je    sndat6
  595.     mov    dx,offset erms31
  596.     cmp    ah,'#'            ; Date and Time?
  597.     je    sndat6            ; e = yes
  598.     mov    dx,offset erms32
  599.     cmp    ah,'+'            ; Mail?
  600.     je    sndat6            ; e = yes
  601.     mov    dx,offset erms33
  602.     cmp    ah,'"'            ; File Type?
  603.     je    sndat6
  604.     mov    dx,offset erms34
  605.     cmp    ah,'*'            ; Transfer Char-set?
  606.     je    sndat6
  607.     mov    dx,offset erms36    ; unknown reason
  608. sndat6:    call    prtasz            ; display reason
  609.     jmp    short sndat5        ; do any other reasons
  610. sndat7:    mov    flags.cxzflg,'X'    ; simulate Control-X to discard
  611.     mov    sstate,'Z'        ; send EOF with Discard
  612.     stc
  613.     ret
  614.  
  615. ; Individual attribute routines. Each expects DI to point to a free storage
  616. ; byte in an output buffer and it updates DI to the next free byte. Expects
  617. ; ES to be pointing at the data segment. OK to clobber SI here.
  618.  
  619. sat1t:    test    flags.attflg,attlen    ; can we send length attribute?
  620.     jz    sat1tx            ; z = no
  621.     mov    si,di            ; remember starting location
  622.     mov    byte ptr [di],'1'    ; file length (Bytes) specifier
  623.     mov    dx,diskio.sizehi    ; high word of length
  624.     mov    ax,diskio.sizelo    ; low word of length
  625.     add    di,2
  626.     call    lnout            ; convert file length, write to [di++]
  627.     mov    cx,di            ; compute field length
  628.     sub    cx,si
  629.     sub    cx,2
  630.     add    cl,32            ; field length to ascii
  631.     mov    [si+1],cl        ; length. Done with File Size
  632. sat1tx:    ret
  633.                     ; Kilobyte attribute
  634. sat2t:    test    flags.attflg,attlen    ; can we send length attribute?
  635.     jz    sat2tx            ; z = no
  636.     mov    byte ptr[di],'!'    ; file length (Kilobytes) specifier
  637.     inc    di
  638.     mov    temp4,di        ; remember place for count field
  639.     inc    di            ; data field
  640.     mov    dx,diskio.sizehi    ; high word of length, from file open
  641.     mov    ax,diskio.sizelo    ; low word of length
  642.     add    ax,1023            ; add 1023 to round up
  643.     adc    dx,0
  644.     mov    al,ah            ; do divide by 1024 bytes
  645.     mov    ah,dl
  646.     mov    dl,dh            ; divide by 256 part
  647.     xor    dh,dh
  648.     ror    dl,1            ; low bit to carry flag
  649.     rcr    ax,1            ; divide by two, with carry in
  650.     clc
  651.     ror    dl,1            ; low bit to carry flag
  652.     rcr    ax,1            ; divide by two, with carry in
  653.     and     dl,3fh            ; keep low six bits
  654.     call    lnout            ; convert file length
  655.     mov    cx,di            ; compute field length
  656.     sub    cx,temp4        ; count field location
  657.     add    cl,32-1            ; field length to ascii
  658.     push    di
  659.     mov    di,temp4        ; point at count field
  660.     mov    [di],cl            ; store field length
  661.     pop    di            ; Done with Kilobyte attribute
  662. sat2tx:    ret
  663.  
  664. sat3t:    test    flags.attflg,attdate    ; can we send file date and time?
  665.     jnz    sat3t1            ; nz = yes
  666.     ret
  667. sat3t1:    cld                ; file Date and Time
  668.     mov    al,'#'            ; creation date/time specifier
  669.     stosb                ; and point at field length
  670.     mov    al,17+32        ; length of date/time field, to ascii
  671.     stosb
  672.     xor    ah,ah
  673.     mov    al,diskio.dta+25    ; yyyyyyym from DOS via file open
  674.     shr    al,1            ; get year
  675.     add    ax,1980            ; add bias
  676.     xor    dx,dx
  677.     call    lnout            ; put year (1990) in buffer
  678.     mov    ax,word ptr diskio.dta+24 ; yyyyyyyym mmmddddd  year+month+day
  679.     shr    ax,1            ; month to al
  680.     xor    ah,ah
  681.     mov    cl,4
  682.     shr    al,cl            ; month to low nibble
  683.     mov    byte ptr[di],'0'    ; leading digit
  684.     inc    di
  685.     cmp    al,9            ; more than one digit?
  686.     jbe    sat3t2            ; be = no
  687.     mov    byte ptr[di-1],'1'    ; new leading digit
  688.     sub    al,10            ; get remainder
  689. sat3t2:    add    al,'0'            ; to ascii
  690.     stosb                ; end of month
  691.     mov    al,diskio.dta+24    ; get day of month
  692.     and    al,1fh            ; select day bits
  693.     xor    ah,ah
  694.     mov    cl,10
  695.     div    cl            ; quot = al, rem = ah
  696.     add    ax,'00'            ; add ascii bias
  697.     stosw                ; leading digit and end of date
  698.     mov    al,' '            ; space separator
  699.     stosb
  700.     mov    al,diskio.dta+23    ; hours  hhhhhmmm
  701.     mov    cl,3
  702.     shr    al,cl            ; move to low nibble
  703.     xor    ah,ah
  704.     mov    cl,10
  705.     div    cl            ; quot = al, rem = ah
  706.     add    ax,'00'            ; add ascii bias
  707.     stosw                ; store hours
  708.     mov    al,':'            ; separator
  709.     stosb
  710.     mov    ax,word ptr diskio.dta+22 ; get minutes: hhhhhmmm mmmsssss
  711.     mov    cl,5
  712.     shr    ax,cl            ; minutes to low byte
  713.     and    al,3fh            ; six bits for minutes
  714.     xor    ah,ah
  715.     mov    cl,10
  716.     div    cl
  717.     add    ax,'00'            ; add ascii bias
  718.     stosw
  719.     mov    al,':'            ; separator
  720.     stosb
  721.     mov    al,byte ptr diskio.dta+22 ; get seconds (double secs really)
  722.     and    al,1fh
  723.     shl    al,1            ; DOS counts by two sec increments
  724.     xor    ah,ah
  725.     mov    cl,10
  726.     div    cl
  727.     add    ax,'00'            ; add ascii bias
  728.     stosw
  729.     ret
  730.  
  731. sat4t:    mov    ax,'".'            ; machine indicator(.), 2 data bytes
  732.     cld
  733.     stosw
  734.     mov    ax,'8U'            ; U8 = Portable O/S, MSDOS
  735.     stosw
  736.     ret
  737.  
  738.  
  739. sat5t:    cmp    mailflg,0        ; Mailing or REMOTE PRINTing?
  740.     jne    sat5t1            ; ne = yes
  741.     ret
  742. sat5t1:    mov    byte ptr [di],'+'    ; Disposition specification
  743.     inc    di
  744.     mov    dx,offset auxfile    ; user@host or print param field
  745.     call    strlen            ; get length into cl
  746.     push    cx            ; save address length
  747.     inc    cl            ; include disposition letter M or P
  748.     add    cl,' '            ; add ascii bias
  749.     mov    [di],cl            ; store in length field
  750.     inc    di
  751.     mov    byte ptr [di],'M'    ; mail the file
  752.     cmp    mailflg,2        ; REMOTE PRINT?
  753.     jne    sat5t2            ; ne = no
  754.     mov    byte ptr [di],'P'    ; say disposition is Print
  755. sat5t2:    inc    di
  756.     pop    cx            ; recover address length
  757.     jcxz    sat5tx            ; z = empty field
  758.     mov    si,dx            ; parameter field
  759.     cld
  760.     rep    movsb            ; append address text to field
  761. sat5tx:    ret
  762.  
  763. sat6t:    test    flags.attflg,atttype    ; can we send File Type attribute?
  764.     jz    sat6tx            ; z = no
  765.     mov    al,'"'            ; File Type attribute (")
  766.     cld
  767.     stosb
  768.     cmp    dtrans.xtype,0        ; Text?
  769.     jne    sat6t1            ; ne = no, likely Binary
  770.     mov    al,3+20h        ; three bytes follow
  771.     stosb
  772.     mov    al,'A'            ; A for ascii
  773.     stosb
  774.     mov    ax,'JM'            ; using Control-M and Control-J
  775.     stosw                ; as line delimiters
  776.     ret
  777. sat6t1:    mov    al,2+20h        ; two bytes follow
  778.     stosb
  779.     mov    ax,'8B'            ; "B8" = Binary, 8-bit byte literals
  780.     stosw
  781. sat6tx:    ret
  782.  
  783. sat7t:    test    flags.attflg,attchr    ; Character-set allowed?
  784.     jz    sat7tx            ; z = no
  785.     cmp    dtrans.xtype,1        ; Binary?
  786.     je    sat7tx            ; e = yes, no char-set stuff
  787.     mov    al,'*'            ; Encoding strategy
  788.     cld
  789.     stosb
  790.     mov    al,1+20h        ; length following, say one char
  791.     stosb
  792.     mov    al,'A'            ; assume normal Transparent
  793.     stosb
  794.     cmp    dtrans.xchset,xfr_xparent ; is it transparent?
  795.     je    sat7tx            ; e = yes
  796.     mov    al,'C'            ; say transfer char-set encoding
  797.     dec    di            ; replace 'A' with 'C'
  798.     stosb
  799.     push    bx
  800.     mov    bl,dtrans.xchset    ; get def char set index
  801.     xor    bh,bh
  802.     shl    bx,1            ; count words
  803.     mov    bx,charids[bx+2]    ; bx points at set [length, string]
  804.     mov    al,[bx]            ; get length of ident string
  805.     mov    cl,al            ; copy to loop counter
  806.     xor    ch,ch
  807.     inc    al            ; add 'C' in attribute
  808.     add    al,20h            ; length of string + ascii bias
  809.     mov    byte ptr [di-2],al    ; length of attribute
  810.     push    si
  811.     mov    si,bx            ; ident string length byte
  812.     inc    si            ; text of ident string
  813.     cld
  814.     rep    movsb            ; copy to destination
  815.     pop    si
  816.     pop    bx
  817. sat7tx:    ret
  818.  
  819. SATTR    ENDP
  820.  
  821. ;    Send data
  822. ; Send main body of file, 'D' state
  823.  
  824. SDATA    PROC    NEAR
  825.     cmp    flags.cxzflg,0        ; interrupted?
  826.     je    sdata1            ; e = no
  827.     mov    sstate,'Z'        ; declare EOF, analyze interrupt there
  828.     ret
  829.  
  830. sdata1:
  831. ;sdata2a:call    peekresponse        ; look for responses between pkts
  832. ;    jnc    sdata2a            ; nc = got a response, get another
  833.     call    getbuf            ; get a buffer for sending
  834.     jnc    sdata2            ; nc = success
  835.     mov    al,windlow        ; earliest sequence number sent
  836.     mov    ah,maxtry        ; retry threshold
  837.     jmp    response        ; can't send, try getting responses
  838.  
  839. sdata2:    mov    [si].pktype,'D'        ; send Data packet
  840.     push    trans.maxdat        ; negotiated max packet length
  841.     mov    ax,current_max        ; running max
  842.     mov    trans.maxdat,ax        ; for encoder
  843.     call    gtchr            ; fill buffer from file and encode
  844.     pop    trans.maxdat        ; recover negotiated max pkt
  845.     jc    sdata3            ; c = failure (no data/EOF, other)
  846.     cmp    [si].datlen,0        ; read any data?
  847.     je    sdata3            ; e = end of data, send 'Z' for EOF
  848.     call    sndpak            ; send the packet
  849.     inc    pktnum            ; next sequence number to send
  850.     and    pktnum,3fh        ; modulo 64
  851. sdata2a:call    peekresponse        ; look for responses between pkts
  852.     jnc    sdata2a            ; nc = got a response, get another
  853.     clc                ; return successful
  854.     ret
  855.  
  856. sdata3:    call    bufrel            ; release unused buffer
  857.     mov    sstate,'Z'        ; at End of File, change to EOF state
  858.     ret
  859. SDATA    ENDP
  860.  
  861. ; Send EOF, 'Z' state
  862. SEOF    PROC    NEAR
  863.     call    getbuf            ; get a buffer for EOF packet
  864.     jnc    seof1            ; nc = got one, send 'Z' packet
  865.     mov    al,pktnum        ; seqnum of next packet to be used
  866.     dec    al            ; back up to last used
  867.     and    al,3fh            ;  sequence number of last sent pkt
  868.     mov    ah,maxtry        ; retry threshold
  869.     jmp    response        ; get responses to earlier packets
  870.  
  871. seof1:    xor    cx,cx            ; assume no data
  872.     cmp    flags.cxzflg,0        ; interrupted?
  873.     je    seof3            ; e = no, send normal EOF packet
  874.     call    intmsg            ; say interrupted
  875.     mov    encbuf,'D'        ; Use "D" for discard
  876.     mov    cx,1            ; set data size to 1
  877.     or    errlev,kssend        ; say send failed
  878.     or    fsta.xstatus,kssend+ksuser ; set status, failed + intervention
  879.     mov    kstatus,kssend+ksuser    ; global status
  880. seof3:    call    doenc            ; encode the packet (cx = count)
  881.     mov    [si].pktype,'Z'        ; EOF packet
  882.     call    sndpak            ; send the packet
  883.     jnc    seof6            ; nc = success
  884.     ret
  885.  
  886. seof6:    mov    al,[si].seqnum        ; packet just sent
  887.     mov    ah,maxtry        ; retry threshold
  888.     call    response        ; get reponse
  889.     jnc    seof7            ; nc = success
  890.     ret
  891.  
  892. seof7:    call    ackmsg            ; ACK, get/show any embedded message
  893.     inc    pktnum            ; next sequence number to send
  894.     and    pktnum,3fh        ; modulo 64
  895.                     ; Heuristic: ACK to 'Z' implies
  896.     mov    al,windlow        ;  ACKs to all previous packets
  897.     mov    ah,pktnum        ; loop limit, next packet
  898. seof8:    cmp    al,ah            ; done all "previous" packets?
  899.     je    seof8b            ; e = yes
  900.     call    pakptr            ; access packet for seqnum in AL
  901.     jc    seof8a            ; c = not in use
  902.     mov    si,bx            ; point to it
  903.     call    bufrel            ; release old buffer (synthetic ACK)
  904. seof8a:    inc    al            ; next slot
  905.     and    al,3fh
  906.     jmp    short seof8
  907. seof8b:                    ; end of Heuristic
  908.     mov    al,pktnum
  909.     mov    windlow,al        ; update windlow to next use
  910.     mov    ah,close2        ; close file
  911.     mov    bx,diskio.handle    ; file handle
  912.     int    dos
  913.     mov    filopn,0        ; no files open
  914.     cmp    flags.cxzflg,0        ; interrupted?
  915.     je    seof9            ; e = no
  916.     or    errlev,kssend        ; say send failed
  917.     or    fsta.xstatus,kssend+ksuser ; set status, failed + intervention
  918.     mov    kstatus,kssend+ksuser    ; global status
  919.     cmp    flags.cxzflg,'Z'    ; Control-Z seen?
  920.     jne    seof9            ; ne = no
  921.     mov    ax,1            ; tell statistics this was a send
  922.     call    endtim
  923.     mov    flags.cxzflg,0        ; clear the Control-Z
  924.     mov    auxfile,0        ; clear send-as/mail-address buffer
  925.     mov    sstate,'B'        ; file group complete state
  926.     ret
  927. seof9:    cmp    flags.cxzflg,0        ; interrupted?
  928.     je    seof10            ; e = no
  929.     cmp    flags.cxzflg,'X'    ; was Control-X signaled?
  930.     je    seof10            ; e = yes
  931.     mov    sstate,'E'         ; not ^X/^Z, must be ^C/^E
  932.     ret
  933. seof10:    mov    flags.cxzflg,0        ; clear the Control-X
  934.     cmp    mailflg,0        ; mail?
  935.     jne    seof11            ; e = yes, retain address in auxfile
  936.     mov    auxfile,0        ; clear send-as name
  937. seof11:    call    GTNFIL            ; get the next file
  938.     jc    seof12            ; c = no more files, do end of group
  939.     mov    filopn,1        ; file opened by gtnfil
  940.     mov    sstate,'F'        ; set state to file header send
  941.     ret
  942. seof12:    mov    sstate,'B'        ; set state to file group completed
  943.     ret
  944. SEOF    ENDP
  945.  
  946.  
  947. ; Send EOT
  948.  
  949. SEOT    PROC    NEAR
  950.     call    getbuf            ; get a buffer for sending
  951.     jnc    seot1            ; nc = got one
  952.     mov    al,pktnum        ; next sequence number to use
  953.     dec    al            ; back up to last used
  954.     and    al,3fh            ; get response to what was just sent
  955.     mov    ah,maxtry        ; retry threshold
  956.     jmp    response        ; get response, stay in this state
  957.  
  958. seot1:    mov    [si].pktype,'B'        ; End of Session packet
  959.     call    sndpak            ; send the packet
  960.     jnc    seot2            ; nc = sucess
  961.     ret
  962. seot2:    mov    al,pktnum        ; sequence number just sent
  963.     mov    ah,maxtry        ; retry threshold
  964.     call    response        ; get a response to it
  965.     jnc    seot3            ; nc = success
  966.     ret
  967. seot3:    call    ackmsg            ; get/show any embedded message
  968.     inc    pktnum            ; next sequence number to use
  969.     and    pktnum,3fh        ; modulo 64
  970.     mov    sstate,'C'        ; set state to file completed
  971.     ret
  972. SEOT    ENDP
  973.  
  974. ; Look for a returned response, but don't block on packet reader timeouts
  975. ; while waiting for a packet to start. Either reads and processes the packet
  976. ; or changes state on quit signal or does nothing. Returns carry set if
  977. ; no packet or error state, else returns what "response" does.
  978. peekresponse proc near
  979.     push    si            ; preserve regular packet pointer
  980.     mov    si,offset rpacket    ; address of receive packet structure
  981.     call    peekreply        ; see if there is a reply
  982.     pop    si
  983.     jc    peekres1        ; c = no packet
  984.     mov    tempseq,-1        ; target sequence number, -1=any
  985.     mov    al,maxtry        ; retry threshold
  986.     mov    retry,al
  987.     jmp    resp4            ; nc = have packet, go analyze
  988.  
  989. peekres1:cmp    flags.cxzflg,'C'    ; Control-C typed?
  990.     je    peekres2        ; e = yes, quit
  991.     cmp    flags.cxzflg,'E'    ; Control-E typed?
  992.     jne    peekres3        ; ne = no
  993. peekres2:mov    sstate,'E'        ; change to Error state
  994. peekres3:stc                ; return saying no packet
  995.     ret
  996. peekresponse endp
  997.  
  998. ; Get response to seqnum in AL, retry AH times if necessary.
  999. ; Success: return carry clear and response data in rpacket
  1000. ; Failure: return carry set and new state in sstate, will send Error packet
  1001. ; Changes AX, BX, CX
  1002. response proc    near
  1003.     mov    tempseq,al        ; target sequence number
  1004.     mov    retry,ah        ; retry threshold
  1005.     mov    rpacket.numtry,0    ; no receive retries yet
  1006. resp1:    mov    ah,rpacket.numtry    ; number of attempts in this routine
  1007.     cmp    ah,retry        ; done enough?
  1008.     ja    resp3            ; yes, feign a timeout
  1009.     push    si            ; preserve regular packet pointer
  1010.     mov    si,offset rpacket    ; address of receive packet structure
  1011.     call    rpack            ; get a packet
  1012.     pop    si
  1013.     jnc    resp4            ; nc = success
  1014.     cmp    flags.cxzflg,'C'    ; Control-C typed?
  1015.     je    resp2            ; e = yes, quit
  1016.     cmp    flags.cxzflg,'E'    ; Control-E typed?
  1017.     jne    resp3            ; ne = no
  1018. resp2:    mov    sstate,'E'        ; change to Error state
  1019.     stc                ; return failure
  1020.     ret
  1021.  
  1022. resp3:    cmp    tempseq,-1        ; just shopping (accept any, no retry)?
  1023.     jne    resp3e            ; ne = no
  1024.     stc                ; say failed to receive, don't retry
  1025.     ret
  1026. resp3e:    inc    rpacket.numtry        ; no packet received, resend oldest
  1027.     call    shrink            ; shrink new packet size
  1028.     mov    al,windlow        ; get oldest sequence number
  1029. resp3a:    call    pakptr            ; get packet pointer to seqnum in AL
  1030.     jnc    resp3b            ; nc = ok, sequence number is in use
  1031.     clc                ; packet not in use, simulate success
  1032.     ret
  1033.  
  1034. resp3b:    push    si            ; resend oldest packet
  1035.     dec    numpkt            ; a retry is not a new packet sent
  1036.     mov    si,bx            ; packet pointer, from pakptr
  1037.     call    cntretry        ; count retries, sense ^C/^E
  1038.     jnc    resp3c            ; nc = ok to continue
  1039.     pop    si            ; clean stack
  1040.     ret                ; ^C/^E encountered, change states
  1041.  
  1042. resp3c:    mov    al,[si].numtry        ; times this packet was retried
  1043.     cmp    al,retry        ; reached the limit?
  1044.     jbe    resp3d            ; be = no, can do more sends
  1045.     pop    si            ; clean stack
  1046.     mov    dx,offset erms17    ; to many retries
  1047.     jmp    giveup            ; abort with msgs to local and remote
  1048.  
  1049. resp3d:    mov    rpacket.numtry,0
  1050.     call    sndpak            ; resend the packet
  1051.     pop    si            ; clean stack
  1052.     jnc    resp1            ; nc = success, retry getting response
  1053.     ret
  1054.                 ; this point is also called by peekresponse
  1055. RESP4:    call    acknak            ; got packet, get kind of response
  1056.     or    al,al            ; ACK in window?
  1057.     jz    resp8            ; z = yes
  1058.     cmp    al,1            ; NAK in window?
  1059.     je    resp6            ; e = yes, repeat pkt
  1060.     cmp    al,3            ; NAK out of window?
  1061.     je    resp5            ; e = yes, repeat packet
  1062.     cmp    al,4            ; ACK to inactive packet?
  1063.     jne    resp4e            ; ne = no
  1064. ;;
  1065.     jmp    resp1            ; old ACK, ignore it
  1066. ; the above is to accomodate "book Kermits", not a good strategy, but...
  1067. ;;    inc    rpacket.numtry        ; count tries on reception
  1068. ;;    jmp    resp1            ; e = yes, retry reception
  1069. resp4e:    cmp    al,5            ; NAK to inactive packet?
  1070.     jne    resp4a            ; ne = no, leaves "other" types
  1071.     jmp    resp1            ; ignore NAK, try again
  1072.                     ; other packet types
  1073. resp4a:    cmp    rpacket.pktype,'M'    ; Message packet?
  1074.     jne    resp4b            ; ne = no
  1075.     push    si
  1076.     mov    si,offset rpacket
  1077.     call    msgmsg            ; display it and discard
  1078.     pop    si
  1079.     jmp    resp1            ; retry getting an ACK
  1080.  
  1081. resp4b:    cmp    rpacket.pktype,'E'    ; Error packet?
  1082.     jne    resp4c            ; ne = no
  1083.     jmp    error
  1084.  
  1085. resp4c:    jmp    resp1            ; Unknown packet type, ignore it
  1086.  
  1087. resp5:    cmp    trans.windo,1        ; is windowing off?
  1088.     je    resp5a            ; e = yes, use old heuristic
  1089.     call    shrink            ; shrink new packet size
  1090.     call    nakout            ; NAK rcvd outside window, resend all
  1091.     inc    rpacket.numtry        ; count this as an internal retry
  1092.     jmp    resp1            ; get more responses
  1093.     
  1094. resp5a:    mov    al,rpacket.seqnum    ; NAK rcvd outside window, say NAK is
  1095.     dec    al            ;  ACK for preceeding pkt to satisfy
  1096.     and    al,3fh            ;  non-windowing Kermits
  1097.     mov    rpacket.seqnum,al    ; force seqnum to preceed this NAK
  1098.     mov    rpacket.pktype,'Y'    ; force packet to look like an ACK
  1099.     jmp    short resp4        ; reanalyze our status
  1100.  
  1101. resp6:    mov    al,rpacket.seqnum    ; single sequence number being NAK'd
  1102.     inc    rpacket.numtry        ; count this as an internal retry
  1103.     call    shrink            ; shrink new packet size
  1104.     jmp    resp3a            ; repeat that packet
  1105.  
  1106.                     ; ACK in window
  1107. resp8:    call    grow            ; grow new packet size
  1108.     mov    al,windlow        ; try to purge all ack'd packets
  1109.     call    pakptr            ; get buffer pointer for it into BX
  1110.     jc    resp8a            ; c = buffer not in use
  1111.     cmp    [bx].ackdone,0        ; ack'd yet?
  1112.     je    resp8a            ; e = no, stop here
  1113.     mov    si,bx
  1114.     call    bufrel            ; ack'd active buffer, release si
  1115.     inc    al            ; rotate window
  1116.     and    al,3fh
  1117.     mov    windlow,al
  1118.     jmp    short resp8        ; keep purging
  1119.  
  1120. resp8a:    mov    al,tempseq        ; check for our desired seqnum
  1121.     mov    rpacket.numtry,0
  1122.     cmp    al,-1            ; do not check for match?
  1123.     je    resp8b            ; e = yes
  1124.     cmp    al,rpacket.seqnum    ; is this the desired object?
  1125.     je    resp8b            ; e = yes
  1126.     jmp    resp1            ; no, read another response
  1127. resp8b:    clc                ; return success
  1128.     ret
  1129. response endp
  1130.  
  1131. ; Send packet, with retries
  1132. ; Enter with SI pointing to packet structure. Success returns carry clear.
  1133. ; Failure, after retries, returns carry set and perhaps a new sstate.
  1134. sndpak    proc    near            
  1135.     inc    numpkt            ; number packets sent
  1136.     call    pktsize            ; report packet qty and size
  1137.     mov    sndcnt,0        ; send retry counter, internal
  1138.     cmp    [si].pktype,'I'        ; do not show windows for I/S
  1139.     je    sndpa3
  1140.     cmp    [si].pktype,'S'
  1141.     je    sndpa3
  1142.     call    winpr            ; show windows in use
  1143.  
  1144. sndpa3:    call    spack            ; send the packet
  1145.     jc    sndpa4            ; nc = failure
  1146.     ret                ; return success
  1147.  
  1148. sndpa4:    push    ax            ; failure, do several retries
  1149.     mov    ax,100            ; wait 0.1 seconds
  1150.     call    pcwait
  1151.     call    cntretry        ; show retries on screen
  1152.     inc    sndcnt            ; internal retry counter
  1153.     mov    al,sndcnt
  1154.     cmp    al,maxtry        ; reached retry limit?
  1155.     pop    ax
  1156.     jbe    sndpa3            ; be = no, can do more retries
  1157.     mov    dx,offset erms24    ; cannot send packet
  1158.     jmp    giveup            ; set carry, change state
  1159. sndpak    endp
  1160.  
  1161. ; Check the packet rpacket for an ACK, NAK, or other.
  1162. ; Returns in CX:
  1163. ; 0 for ACK to active packet (marks buffer as ACK'd, may rotate window)
  1164. ; 1 for NAK to active packet
  1165. ; 2 for an unknown packet type
  1166. ; 3 for NAK outside the window
  1167. ; 4 for other ACKs (out of window, to inactive packet)
  1168. ; 5 for NAKs in window to inactive packets.
  1169. ; Timeout packet (type 'T') is regarded as a NAK out of the window.
  1170. ; Marks pkts ACK'd but clears packets only when they rotate below the window.
  1171. ; Uses registers AX ,BX, and CX.
  1172. ACKNAK    PROC    NEAR
  1173.     mov    al,rpacket.seqnum    ; this packet's sequence number
  1174.     mov    ah,rpacket.pktype    ; and packet type
  1175.     cmp    ah,'Y'            ; ack packet?
  1176.     jne    ackna2            ; ne = no
  1177.     call    pakptr            ; is it for an active buffer?
  1178.     jnc    ackna1            ; nc = yes
  1179.     mov    al,4            ; say ACK for inactive pkt
  1180.     clc
  1181.     ret
  1182. ackna1:    mov    [bx].ackdone,1        ; say packet has been acked
  1183.     cmp    al,windlow        ; ok to rotate window?
  1184.     jne    ackna1a            ; ne = no
  1185.     inc    windlow            ; rotate window one slot
  1186.     and    windlow,3fh
  1187.     push    si            ; save pointer
  1188.     mov    si,bx            ; packet pointer from pakptr
  1189.     call    bufrel            ; release buffer for SI
  1190.     pop    si
  1191. ackna1a:cmp    rpacket.datlen,0    ; any data in the ACK?
  1192.     je    ackna1c            ; e = no
  1193.     cmp    sstate,'F'        ; in file header state?
  1194.     je    ackna1c            ; e = yes, no protocol char
  1195.     push    es
  1196.     les    bx,rpacket.datadr    ; look for data in the ACK
  1197.     mov    ah,es:[bx]
  1198.     pop    es
  1199.     cmp    ah,'C'            ; Control-C message?
  1200.     je    ackna1b            ; e = yes
  1201.     cmp    ah,'X'            ; quit this file?
  1202.     je    ackna1b            ; e = yes
  1203.     cmp    ah,'Z'            ; quit this file group?
  1204.     jne    ackna1c
  1205. ackna1b:mov    flags.cxzflg,ah        ; store here
  1206.     mov    sstate,'Z'        ; move to end of file state
  1207.     mov    rstate,'Z'
  1208. ackna1c:xor    al,al            ; ack'd ok
  1209.     clc
  1210.     ret
  1211.                     ; not an ACK
  1212. ackna2:    cmp    ah,'N'            ; NAK?
  1213.     je    ackna3            ; e = yes
  1214.     cmp    ah,'T'            ; Timeout?
  1215.     je    ackna3a            ; e = yes, same as NAK out of window
  1216.     mov    al,2            ; else say unknown type
  1217.     clc
  1218.     ret
  1219.  
  1220. ackna3:    inc    fsta.nakrcnt        ; count received NAK for statistics
  1221.     push    si
  1222.     mov    si,offset rpacket
  1223.     call    chkwind            ; check if seqnum is in window
  1224.     pop    si
  1225.     jcxz    ackna3b            ; z = in window
  1226. ackna3a:mov    al,3            ; say NAK out of window
  1227.     clc
  1228.     ret
  1229.  
  1230. ackna3b:push    bx            ; NAK in window, is pkt still active?
  1231.     call    pakptr            ; seqnum for an active packet?
  1232.     pop    bx
  1233.     jc    ackna4            ; c = no, ignore NAK as "dead NAK" 
  1234.     mov    al,1            ; say NAK for active packet
  1235.     clc
  1236.     ret
  1237.  
  1238. ackna4:    mov    al,5            ; dead NAKs
  1239.     clc
  1240.     ret
  1241. ACKNAK    ENDP
  1242.  
  1243. nakout    proc    near            ; NAK out of window, resend all pkts
  1244.     mov    al,windlow        ; start here
  1245.     mov    ah,al
  1246.     add    ah,trans.windo
  1247.     and    ah,3fh            ; top of window+1
  1248. nakout1:call    pakptr            ; get pkt pointer for seqnum in al
  1249.     jc    nakout4            ; c = slot not in use
  1250.     mov    si,bx            ; bx is packet pointer from pakptr
  1251.     cmp    [si].ackdone,0        ; has packet has been acked?
  1252.     jne    nakout4            ; ne = yes
  1253.     cmp    al,windlow        ; count retries only for windlow
  1254.     jne    nakout2            ; ne = not windlow
  1255.     call    cntretry        ; count retries
  1256.     jc    nakout3            ; c = quit now
  1257.     mov    cl,[si].numtry
  1258.     cmp    cl,retry        ; reached the limit yet?
  1259.     ja    nakout3            ; a = yes, quit
  1260. nakout2:push    ax
  1261.     call    pktsize            ; report packet size
  1262.     call    sndpak            ; resend the packet
  1263.     pop    ax
  1264.     jmp    short nakout4        ; do next packet
  1265.  
  1266. nakout3:mov    dx,offset erms17    ; error exit, too many retries
  1267.     jmp    giveup
  1268.  
  1269. nakout4:inc    al            ; next sequence number
  1270.     and    al,3fh
  1271.     cmp    al,ah            ; sent all packets?
  1272.     jne    nakout1            ; ne = no, repeat more packets
  1273.     ret                ; return to do another data send
  1274. nakout    endp
  1275.  
  1276. cntretry proc    near            ; count retries, sense user exit
  1277.     cmp    flags.cxzflg,'C'    ; user wants abrupt exit?
  1278.     je    cntre2            ; e = yes
  1279.     cmp    flags.cxzflg,'E'    ; Error type exit?
  1280.     je    cntre2            ; e = yes
  1281.     inc    fsta.pretry        ; increment the number of retries
  1282.     inc    [si].numtry        ; for this packet too
  1283.     test    flags.remflg,dserver    ; server mode?
  1284.     jnz    cntre3            ; nz = yes, writing to their screen
  1285.     cmp    flags.xflg,1        ; writing to screen?
  1286.     je    cntre1            ; e = yes, skip this
  1287. cntre3:    cmp    fmtdsp,0        ; formatted display?
  1288.     je    cntre1            ; e = no
  1289.     call    rtmsg            ; display retries
  1290. cntre1:    clc
  1291.     ret
  1292. cntre2:    mov    sstate,'E'        ; abort, shared by send and receive
  1293.     mov    rstate,'E'        ; abort
  1294.     stc
  1295.     ret
  1296. cntretry endp
  1297.  
  1298. ; Display message in ACK's to F and D packets. Requires a leading protocol
  1299. ; char for D packets and expects message to be encoded.
  1300. ackmsg    proc near
  1301.     test    flags.remflg,dquiet    ; quiet display mode?
  1302.     jnz    ackmsgx            ; nz = yes, don't write to screen
  1303.     cmp    rpacket.datlen,0    ; any embedded message?
  1304.     je    ackmsgx            ; e = no
  1305.     cmp    sstate,'F'        ; file header state?
  1306.     je    ackmsg1            ; e = yes, no leading protocol char
  1307.     cmp    rpacket.datlen,1    ; D packet, skip protocol char
  1308.     je    ackmsgx            ; e = no displayable information
  1309. ackmsg1:push    si
  1310.     push    ax
  1311.     push    dx
  1312.     call    cxmsg            ; clear message space in warning area
  1313.     mov    word ptr decbuf,0    ; clear two bytes
  1314.     mov    si,offset rpacket    ; source address
  1315.     call    dodec            ; decode message, including X/Z/other
  1316.     mov    dx,offset decbuf+1    ; decoded data
  1317.     cmp    sstate,'F'        ; file header state?
  1318.     jne    ackmsg3            ; ne = no
  1319.     push    dx
  1320.     mov    dx,offset infms5    ; give a leader msg
  1321.     call    prtasz
  1322.     pop    dx
  1323.     dec    dx            ; start with first message char
  1324. ackmsg2:mov    bx,dx
  1325.     cmp    byte ptr [bx],' '    ; space?
  1326.     jne    ackmsg3            ; ne = no
  1327.     inc    dx            ; next msg char
  1328.     jmp    short ackmsg2        ; continue stripping off spaces
  1329. ackmsg3:call    prtasz            ; display message
  1330.     mov    ah,prstr
  1331.     mov    dx,offset crlf
  1332.     int    dos
  1333.     pop    dx
  1334.     pop    ax
  1335.     pop    si
  1336. ackmsgx:mov    rpacket.datlen,0
  1337.     ret
  1338. ackmsg    endp
  1339.  
  1340. ; Shrink transmitted D packets by 1/2 to accomodate comms failures
  1341. shrink    proc    near
  1342.     push    ax
  1343.     mov    ax,current_max        ; current max packet length
  1344.     shr    ax,1            ; divide by two
  1345.     cmp    ax,40            ; too small?
  1346.     jae    shrink1            ; ae = no
  1347.     mov    ax,40            ; smallest size
  1348. shrink1:mov    current_max,ax        ; new max packet length
  1349.     pop    ax
  1350.     ret
  1351. shrink    endp
  1352.  
  1353. ; Enlarge transmitted D packets by 5/4 to recover from comms failures
  1354. grow    proc    near
  1355.     push    ax
  1356.     mov    ax,current_max        ; current max packet length
  1357.     shr    ax,1
  1358.     shr    ax,1
  1359.     add    ax,current_max        ; 5/4 * current max
  1360.     cmp    ax,trans.maxdat        ; max negotiated
  1361.     jbe    grow1            ; be = not exceeded
  1362.     mov    ax,trans.maxdat        ; limit to negotiated
  1363. grow1:    mov    current_max,ax
  1364.     pop    ax
  1365.     ret
  1366. grow    endp
  1367.  
  1368. ; newfn    -- move replacement name from buffer auxfile to buffer encbuf
  1369.  
  1370. newfn    proc    near
  1371.     push    si
  1372.     push    di
  1373.     cmp    auxfile,0        ; sending file under different name?
  1374.     je    newfn4            ; e = no, so don't give new name
  1375.     mov    si,offset auxfile    ; source field
  1376.     mov    di,offset fsta.xname    ; statistics external name area
  1377.     call    strcpy
  1378.     test    flags.remflg,dquiet    ; quiet display mode?
  1379.     jnz    newfn2            ; nz = yes, do not write to screen
  1380.     mov    dx,offset asmsg        ; display ' as '
  1381.     cmp    mailflg,0        ; mail?
  1382.     je    newfn1            ; e = no
  1383.     mov    dx,offset mailto    ; display ' To: '
  1384. newfn1:    call    prtasz            ; display asciiz msg
  1385.     cmp    mailflg,0        ; mail?
  1386.     je    newfn2            ; e = no
  1387.     mov    dx,offset auxfile    ; get name
  1388.     call    prtasz            ; display asciiz string
  1389.     jmp    short newfn4        ; don't replace filename
  1390. newfn2:    mov    si,offset auxfile    ; external name
  1391.     mov    di,offset encbuf
  1392.     call    strcpy            ; put into encoder buffer
  1393.     test    flags.remflg,dquiet    ; quiet display mode (should we print)?
  1394.     jnz    newfn5            ; nz = yes
  1395.     mov    dx,si
  1396.     call    prtasz            ; display external name
  1397. newfn4:    test    flags.remflg,dserial    ; serial display mode?
  1398.     jz    newfn5            ; z = no
  1399.     mov    dx,offset crlf        ; start with cr/lf for serial display
  1400.     mov    ah,prstr
  1401.     int    dos
  1402. newfn5:    pop    di
  1403.     pop    si
  1404.     ret
  1405. newfn    endp
  1406.  
  1407. ; This routine sets up the data for the response to an Init packet
  1408. ; trans.rxxx are items which have been negotiated.
  1409. ; Lines marked ;M energize the second CAPAS byte. Leave them as comments
  1410. ; because earlier versions of MS Kermit (and C Kermit) are confused by it
  1411. ; (failed to decode bit saying second CAPAS byte follows and thus lose sync).
  1412. ; Enter with SI = pktinfo pointer
  1413. ; Returns [si].datlen = length of data field, with packet buffer filled in.
  1414. RPAR    PROC    NEAR
  1415.     push    ax
  1416.     push    di
  1417.     push    es
  1418.     cld
  1419.     les    di,[si].datadr        ; data field address to es:di
  1420.     mov    al,trans.rpsiz        ; receive packet size
  1421.     add    al,' '            ; add a space to make it printable
  1422.     stosb                ; 1, put it in the packet
  1423.     mov    al,trans.rtime        ; receive packet time out
  1424.     add    al,' '
  1425.     stosb                ; 2
  1426.     mov    al,trans.rpad        ; number of padding chars
  1427.     add    al,' '
  1428.     stosb                ; 3
  1429.     mov    al,trans.rpadch        ; padding char
  1430.     add    al,40h            ; Uncontrol it
  1431.     and    al,7FH
  1432.     stosb                ; 4
  1433.     mov    al,trans.reol        ; EOL char
  1434.     add    al,' '
  1435.     stosb                ; 5
  1436.     mov    al,trans.rquote        ; quote char
  1437.     stosb                ; 6
  1438.     mov    al,trans.ebquot        ; 8-bit quote char
  1439.     stosb                ; 7
  1440.     mov    al,trans.chklen        ; Length of checksum
  1441.     add    al,'0'            ; make into a real digit
  1442.     stosb                ; 8
  1443.     mov    al,trans.rptq        ; repeat quote char
  1444.     or    al,al            ; null means none
  1445.     jnz    rpar0
  1446.     mov    al,' '            ; send a blank instead
  1447. rpar0:    stosb                ; 9
  1448.     mov    al,2            ; CAPAS, bit1 = can do long packets
  1449.     cmp    flags.attflg,0        ; allowing attributes packets?
  1450.     je    rpar1            ; e = no
  1451.     or    al,8            ; bit #3, can do file attributes
  1452. rpar1:    or    al,4            ; bit #4 can do windows
  1453.     cmp    dtrans.lshift,lock_disable ; locking shift disabled?
  1454.     je    rpar2            ; e = yes
  1455.     or    al,20h            ; set locking shift cap (bit #1)
  1456. rpar2:    add    al,20h            ; apply tochar() to byte
  1457. ;M    or    al,1            ; say second CAPAS byte follows
  1458.     stosb                ; 10
  1459.                     ; additional CAPAS go in here
  1460. ;M    mov    al,20h+20h    ; Allow M (message) pkts, (#6, bit5)
  1461. ;M    stosb                ; 11
  1462.     mov    al,trans.windo        ; number of active window slots (1-31)
  1463.     add    al,20h            ; apply tochar()
  1464.     stosb
  1465.     push    dx            ; save reg
  1466.     mov    ax,trans.rlong        ; longest packet which we can receive
  1467.     xor    dx,dx            ; clear extended part for division
  1468.     div    ninefive        ; divide by 95. quo = ax, rem = dx
  1469.     add    al,20h            ; apply tochar() to quotient
  1470.     stosb
  1471.     add    dl,20h            ; apply tochar() to remainder
  1472.     mov    al,dl
  1473.     pop    dx            ; restore regs
  1474.     stosb
  1475.     sub    di,word ptr [si].datadr    ; end minus beginning = length
  1476.     mov    [si].datlen,di        ; length of rpar data in packet
  1477.     pop    es
  1478.     pop    di
  1479.     pop    ax
  1480.     ret
  1481. RPAR    ENDP
  1482.  
  1483. ; Set maximum capabilities
  1484. ; dtrans are the defaults (which the user can modify), trans are negotiated
  1485. ; and active values.
  1486. SPARMAX    PROC    NEAR
  1487.     push    ax
  1488.     mov    al,dtrans.spsiz        ; [1] regular packet size MAXL
  1489.     mov    trans.spsiz,al
  1490.     mov    al,dtrans.stime        ; [2] send timeout value TIME
  1491.     mov    trans.rtime,al
  1492.     mov    al,dtrans.spad        ; [3] send padding count NPAD
  1493.     mov    trans.spad,al
  1494.     mov    al,dtrans.spadch    ; [4] send padding character PADC
  1495.     mov    trans.spadch,al
  1496.     mov    al,dtrans.seol        ; [5] EOL character EOL
  1497.     mov    trans.seol,al
  1498.     mov    al,dtrans.squote    ; [6] control quote character QCTL
  1499.     mov    trans.squote,al
  1500.     push    bx            ; [7] 8-bit quote character QBIN
  1501.     mov    bx,portval
  1502.     mov    ah,[bx].parflg        ; get our parity flag
  1503.     pop    bx
  1504.     mov    al,'Y'            ; will quote upon request
  1505.     cmp    dtrans.lshift,lock_force ; locking shift forced?
  1506.     jne    spmax1            ; ne = no
  1507.     mov    al,'N'            ; ignore 8-bit quote
  1508.     jmp    short spmax1a        ; for any parity
  1509. spmax1:    cmp    ah,parnon        ; parity of none?
  1510.     je    spmax1a            ; e = yes, reset 8 bit quote character
  1511.     mov    al,dqbin        ; we want quoting, active
  1512. spmax1a:mov    trans.ebquot,al        ; save quoting state
  1513.     mov    dtrans.ebquot,al
  1514.     mov    al,dtrans.chklen    ; [8] initial checksum type CHKT
  1515.     mov    trans.chklen,al
  1516.     mov    al,dtrans.rptq        ; [9] repeat prefix character REPT
  1517.     mov    trans.rptq,al
  1518.     mov    al,8+4            ; [10] capas bitmap CAPAS
  1519.     cmp    dtrans.lshift,lock_disable ; locking shift disabled?
  1520.     je    spmax2            ; e = yes
  1521.     or    al,20h            ; locking shift capa
  1522. spmax2:    mov    trans.capas,al        ; [10] capas bitmap CAPAS
  1523.     mov    trans.capas+1,20h    ; [11] CAPAS+1, Message pkts
  1524.     mov    al,dtrans.windo        ; [12] window size WINDO
  1525.     mov    trans.windo,al
  1526.     mov    ax,dtrans.slong        ; [13-14] long packet send length,
  1527.     mov    trans.slong,ax        ;  MAXLX1 and MAXLX2
  1528.                     ;
  1529.     mov    al,dtrans.rpsiz        ; max regular packet we can receive
  1530.     mov    trans.rpsiz,al        ;  for window of one slot
  1531.     mov    ax,dtrans.rlong        ; max long packet we can receive
  1532.     mov    trans.rlong,ax        ;  for window of one slot
  1533.     pop    ax
  1534.     ret
  1535. SPARMAX    ENDP
  1536.  
  1537. ; This routine reads in all the send init packet information
  1538. ; Enter with SI = pktinfo address, [si].datlen = packet length
  1539. ; All regs preserved except AX. 
  1540. ; dtrans.xxx are the default parameters if the other side says nothing
  1541. ; trans.sxxx are the active negotiated parameters we will use.
  1542. SPAR    PROC    NEAR
  1543.     push    es
  1544.     push    ax            ; set min defaults for no host data
  1545.     mov    trans.spsiz,80        ; [1] regular packet size MAXL
  1546.     mov    al,dtrans.stime        ; get user selected stime
  1547.     mov    trans.stime,al        ; [2] send timeout value TIME
  1548.     mov    trans.spad,0        ; [3] send padding count NPAD
  1549.     mov    al,dtrans.spadch    ; [4] send padding character PADC
  1550.     mov    trans.spadch,al
  1551.     mov    trans.seol,CR        ; [5] EOL character EOL
  1552.     mov    trans.squote,'#'    ; [6] control quote character QCTL
  1553.     push    bx            ; [7] 8-bit quote character QBIN
  1554.     mov    bx,portval        ; current port settings
  1555.     mov    ah,[bx].parflg        ; get our parity flag
  1556.     pop    bx
  1557.     mov    al,'Y'            ; say will quote upon request
  1558.     cmp    dtrans.lshift,lock_force ; locking shift forced?
  1559.     jne    spar0a            ; ne = no
  1560.     mov    al,'N'            ; ignore 8-bit quotes
  1561.     jmp    short spar0b        ; regardless of parity
  1562. spar0a:    cmp    ah,parnon        ; parity of none?
  1563.     je    spar0b            ; e = yes
  1564.     mov    al,dqbin        ; we want quoting, active
  1565. spar0b:    mov    trans.ebquot,al        ; use proper quote char
  1566.     mov    dtrans.ebquot,al
  1567.     mov    trans.chklen,1        ; [8] initial checksum type CHKT
  1568.     xor    ax,ax
  1569.     mov    trans.rptq,al        ; [9] repeat prefix character REPT
  1570.     mov    trans.capas,al        ; [10-11] capas bitmap CAPAS
  1571.     mov    trans.capas+1,al
  1572.     mov    trans.lshift,lock_disable ; init locking shift to disabled
  1573.     mov    trans.windo,1        ; [12] window size WINDO
  1574.     mov    al,trans.spsiz
  1575.     mov    trans.slong,ax        ; [13-14] long packet send length,
  1576.                     ;  MAXLX1 and MAXLX2
  1577.                     ; start negotiations
  1578.     push    si            ; pktinfo structure pointer
  1579.     mov    ax,[si].datlen        ; length of received data
  1580.     mov    ah,al            ; number of args is now in ah
  1581.     les    si,[si].datadr        ; es:si is pointer to received data
  1582.     cld
  1583.     or    ah,ah            ; [1] MAXL  any data?
  1584.     jg    spar1a            ; g = yes
  1585.     jmp    sparx1
  1586. spar1a:    mov    al,es:[si]        ; get the max regular packet size
  1587.     inc    si
  1588.     dec    ah            ; ah = bytes remaining to be examined
  1589.     sub    al,' '            ; subtract ascii bias
  1590.     jnc    spar1b            ; c = old C Kermit error
  1591.     mov    al,spmax
  1592. spar1b:    cmp    al,dtrans.spsiz        ; user limit is less?
  1593.     jbe    spar1c            ; be = no
  1594.     mov    al,dtrans.spsiz        ; replace with our lower limit
  1595. spar1c:    cmp    al,spmin        ; below the minimum?
  1596.     jge    spar1d            ; ge = no
  1597.     mov    al,spmin
  1598. spar1d:    cmp    al,spmax        ; or above the maximum?
  1599.     jle    spar1e            ; le = no
  1600.     mov    al,spmax
  1601. spar1e:    mov    trans.spsiz,al        ; save it
  1602.     push    ax
  1603.     xor    ah,ah            ; set long packet to regular size
  1604.     mov    trans.slong,ax
  1605.     pop    ax
  1606.  
  1607.     or    ah,ah            ; [2] TIME  more data?
  1608.     jg    spar2a            ; g = yes
  1609.     jmp    sparx1
  1610. spar2a:    mov    al,es:[si]        ; get the timeout value
  1611.     inc    si
  1612.     dec    ah
  1613.     sub    al,' '            ; subtract a space
  1614.     jge    spar2b            ; must be non-negative
  1615.     xor    al,al            ; negative, so use zero
  1616. spar2b:    cmp    al,trans.rtime        ; same as other side's timeout
  1617.     jne    spar2c            ; ne = no
  1618.     inc    al            ; yes, but make it a little different
  1619. spar2c:    cmp    dtrans.stime,dstime    ; is current value the default?
  1620.     je    spar2d            ; e = yes, else user value overrides
  1621.     mov    al,dtrans.stime        ; get user selected stime
  1622. spar2d:    mov    trans.stime,al        ; save it
  1623.                     ;
  1624.     or    ah,ah            ; [3] NPAD  more data?
  1625.     jg    spar3a            ; g = yes
  1626.     jmp    sparx1
  1627. spar3a:    mov    al,es:[si]        ; get the number of padding chars
  1628.     inc    si
  1629.     dec    ah
  1630.     sub    al,' '
  1631.     jge    spar3b            ; must be non-negative
  1632.     xor    al,al
  1633. spar3b:    mov    trans.spad,al        ; number of padding chars to send
  1634.                     ;
  1635.     or    ah,ah            ; [4] PADC  more data?
  1636.     jg    spar4a            ; g = yes
  1637.     jmp    sparx1
  1638. spar4a:    mov    al,es:[si]        ; get the padding char
  1639.     inc    si
  1640.     dec    ah
  1641.     add    al,40h            ; remove ascii bias
  1642.     and    al,7FH
  1643.     cmp    al,del            ; Delete?
  1644.     je    spar4b            ; e = yes, then it's OK
  1645.     cmp    al,31            ; control char?
  1646.     jbe    spar4b            ; be = yes, then OK
  1647.     xor    al,al            ; no, use null
  1648. spar4b:    mov    trans.spadch,al
  1649.                     ;
  1650.     or    ah,ah            ; [5] EOL  more data?
  1651.     jg    spar5a            ; g = yes
  1652.     jmp    sparx1
  1653. spar5a:    mov    al,es:[si]        ; get the EOL char
  1654.     inc    si
  1655.     dec    ah
  1656.     sub    al,' '
  1657.     cmp    al,31            ; control char?
  1658.     jbe    spar5b            ; le = yes, then use it
  1659.     mov    al,cr            ; else use the default
  1660. spar5b:    mov    trans.seol,al        ; EOL char to be used
  1661.                     ;
  1662.     or    ah,ah            ; [6] QCTL  more data?
  1663.     jg    spar6a            ; g = yes
  1664.     jmp    sparx1
  1665. spar6a:    mov    al,es:[si]        ; get the quote char
  1666.     inc    si
  1667.     dec    ah
  1668.     cmp    al,' '            ; less than a space?
  1669.     jge    spar6b            ; ge = no
  1670.     mov    al,dsquot        ; yes, use default
  1671. spar6b:    cmp    al,7eh            ; must also be less than a tilde
  1672.     jbe    spar6c            ; be = is ok
  1673.     mov    al,dsquot        ; else use default
  1674. spar6c:    mov    trans.squote,al
  1675.                     ;
  1676.     or    ah,ah            ; [7] QBIN  more data?
  1677.     jg    spar7a            ; g = yes
  1678.     jmp    sparx1
  1679. spar7a:    mov    al,es:[si]        ; get other side's 8-bit quote request
  1680.     inc    si
  1681.     dec    ah
  1682.     call    doquo            ; and set quote char
  1683.                     ;
  1684.     or    ah,ah            ; [8] CHKT  more data?
  1685.     jg    spar8a            ; a = yes
  1686.     jmp    sparx1
  1687. spar8a:    mov    al,es:[si]        ; get other side's checksum length
  1688.     inc    si
  1689.     dec    ah
  1690.     call    dochk            ; determine what size to use
  1691.                     ;
  1692.     or    ah,ah            ; [9] REPT  more data?
  1693.     jg    spar9a            ; g = yes
  1694.     jmp    sparx1
  1695. spar9a:    mov    al,es:[si]        ; get other side's repeat count prefix
  1696.     inc    si
  1697.     dec    ah
  1698.     call    dorpt            ; negotiate prefix into trans.rptq
  1699.                     ;
  1700.     or    ah,ah            ; [10] CAPAS  more data?
  1701.     ja    spar10a            ; g = yes
  1702.     jmp    sparx1
  1703. spar10a:mov    al,es:[si]        ; get CAPAS bitmap from other side
  1704.     inc    si
  1705.     dec    ah
  1706.     and    al,not (1)        ; remove least significant bit
  1707.     sub    al,20h            ; apply unchar()
  1708.     mov    trans.capas,al        ; store result in active byte
  1709.     test    al,20h            ; locking shift proposed?
  1710.     jz    spar11            ; z = no
  1711.     cmp    dtrans.lshift,lock_enable ; have we enabled its negotiation?
  1712.     jb    spar11            ; b = no, (0) disabled
  1713.     ja    spar10b            ; a = (2) forced
  1714.     mov    al,trans.ebquot        ; get negotiated 8-bit quote char
  1715.     cmp    al,'N'            ; did 8-bit quote negotiation fail?
  1716.     je    spar11            ; e = yes, no locking shift agreement
  1717.     cmp    al,'Y'            ; did 8-bit quote negotiation fail?
  1718.     je    spar11            ; e = yes, no locking shift agreement
  1719.     mov    trans.lshift,lock_enable ; set state of locking shift
  1720.     jmp    short spar11
  1721. spar10b:mov    trans.ebquot,'N'    ; ignore 8-bit quote prefix
  1722.     mov    trans.lshift,lock_force    ; and activate working copy
  1723. spar11:    or    ah,ah            ; [11] CAPAS+  more data?
  1724.     ja    spar11a            ; a = yes
  1725.     jmp    sparx1
  1726. spar11a:test    byte ptr [si-1],1    ; is CAPAS byte continued to another?
  1727.     jz    spar12            ; z = no
  1728.     mov    al,es:[si]        ; get 2nd CAPAS bitmap from other side
  1729.     inc    si
  1730.     dec    ah
  1731.     and    al,not (1)        ; remove least significant bit
  1732.     sub    al,20h            ; apply unchar(). Store nothing
  1733.     mov    trans.capas+1,al    ; keep second CAPAS byte
  1734.  
  1735. spar11c:or    ah,ah            ; [11] CAPAS++  more data?
  1736.     ja    spar11d            ; a = yes
  1737.     jmp    sparx1
  1738. spar11d:test    byte ptr es:[si-1],1    ; is CAPAS byte continued to another?
  1739.     jz    spar12            ; z = no
  1740.     mov    al,es:[si]        ; 3rd et seq CAPAS bitmaps
  1741.     inc    si
  1742.     dec    ah
  1743.     and    al,not (1)        ; remove least significant bit
  1744.     sub    al,20h            ; apply unchar(). Store nothing
  1745.     jmp    short spar11c        ; seek more CAPAS bytes
  1746.                     ;
  1747. spar12:    or    ah,ah            ; [12] WINDO  more data?
  1748.     jg    spar12a            ; g = yes
  1749.     jmp    sparx1            ; exit spar
  1750. spar12a:mov    al,es:[si]        ; get other side's window size
  1751.     inc    si
  1752.     dec    ah
  1753.     sub    al,20h            ; apply unchar()
  1754.     call    dewind            ; negotiate window size
  1755.                     ;
  1756.     cmp    ah,2            ; [13-14] MAXL (long packet needs 2)
  1757.     jge    spar13a            ; ge = enough data to look at
  1758.     push    ax            ; make long same size as regular
  1759.     xor    ah,ah
  1760.     mov    al,trans.spsiz        ; normal packet size
  1761.     mov    trans.slong,ax        ; assume not using long packets
  1762.     pop    ax            ; recover ah
  1763.     jmp    short sparx1        ; do final checks on packet length
  1764. spar13a:test    trans.capas,2        ; do they have long packet capability?
  1765.     jnz    spar13b            ; nz = yes
  1766.     jmp    short sparx1        ; no, skip following l-pkt len fields
  1767. spar13b:mov    al,es:[si]        ; long pkt length, high order byte
  1768.     inc    si
  1769.     push    ax            ; save ah
  1770.     sub    al,20h            ; apply unchar()
  1771.     xor    ah,ah
  1772.     mul    ninefive        ; times 95 to dx (high), ax (low)
  1773.     mov    trans.slong,ax        ; store high order part
  1774.     mov    al,es:[si]        ; long pkt length, low order byte
  1775.     inc    si
  1776.     sub    al,20h            ; apply unchar()
  1777.     xor    ah,ah
  1778.     add    ax,trans.slong        ; plus high order part
  1779.     mov    trans.slong,ax        ; store it
  1780.     or    ax,ax            ; if result is 0 then use regular pkts
  1781.     jnz    spar13c            ; non-zero, use what they want
  1782.     mov    al,trans.spsiz        ; else default to regular packet size
  1783.     xor    ah,ah
  1784.     mov    trans.slong,ax    ;  and ignore the CAPAS bit (no def 500 bytes)
  1785. spar13c:cmp    ax,dtrans.slong        ; longer than we want to do?
  1786.     jbe    spar13d            ; be = no
  1787.     mov    ax,dtrans.slong        ; limit to our longest sending size
  1788.     mov    trans.slong,ax        ; and use it
  1789. spar13d:push    dx
  1790.     mov    dl,trans.spsiz        ; regular pkt length
  1791.     xor    dh,dh
  1792.     cmp    ax,dx            ; long pkt shorter than regular?
  1793.     jae    spar13f            ; ae = no
  1794.     cmp    al,spmin        ; below the minimum allowed length?
  1795.     jae    spar13e            ; ae = yes
  1796.     mov    al,spmin        ; drop down to minimum
  1797.     mov    trans.slong,ax        ; update long pkt too
  1798. spar13e:mov    trans.spsiz,al        ; use small long packet length
  1799. spar13f:pop    dx
  1800.     pop    ax            ; recover ah
  1801.                     ; Windowing can further shrink pkts
  1802. sparx1:    push    cx            ; final packet size negotiations
  1803.     push    dx
  1804.     mov    ax,maxbufparas        ; our max buffer size, paragraphs
  1805.     mov    cl,trans.windo        ; number of active window slots
  1806.     xor    ch,ch
  1807.     sub    ax,cx            ; minus null byte and rounding/slot
  1808.     jcxz    sparx2            ; 0 means 1, for safety here
  1809.     xor    dx,dx            ; whole buffer / # window slots
  1810.     div    cx            ; ax = longest windowed pkt possible
  1811.                     ; transmitter packet size
  1812. sparx2:    cmp    ax,(9024/16)        ; longest packet, in paragraphs
  1813.     jbe    sparx2a            ; be = less than eq longest
  1814.     mov    ax,(9024/16)
  1815. sparx2a:mov    cl,4            ; convert to bytes
  1816.     shl    ax,cl            ; ax has bytes per buffer
  1817.     cmp    ax,trans.slong        ; our slots can be longer than theirs?
  1818.     ja    sparx2b            ; a = yes, use their shorter length
  1819.     mov    trans.slong,ax        ; no, use our shorter length
  1820. sparx2b:mov    cl,trans.spsiz        ; current regular pkt size
  1821.     xor    ch,ch
  1822.     cmp    cx,ax            ; is regular longer than window slot?
  1823.     jbe    sparx3            ; be = no
  1824.     mov    trans.spsiz,al        ; shrink regular to windowed size
  1825.                     ; receiver packet size
  1826. sparx3:    cmp    ax,dtrans.rlong        ; slot shorter than longest allowed?
  1827.     jae    sparx5            ; ae = no
  1828.     mov    trans.rlong,ax        ; long size we want to receive
  1829.     mov    cl,dtrans.rpsiz        ; regular packet size user limit
  1830.     xor    ch,ch
  1831.     cmp    cx,ax            ; is regular longer than window too?
  1832.     jbe    sparx4            ; be = no
  1833.     mov    cl,al            ; shrink regular to windowed size
  1834. sparx4:    mov    trans.rpsiz,cl        ; regular size we want
  1835. sparx5:
  1836.     push    bx            ; list of protected control codes
  1837.     xor    bh,bh
  1838.     mov    bl,flowon        ; flow control
  1839.     or    bl,bl            ; any?
  1840.     jz    sparx5a            ; z = none
  1841.     and    protlist[bx],not 1    ; if active
  1842.     mov    bl,flowoff
  1843.     and    protlist[bx],not 1
  1844. sparx5a:cmp    flags.comflg,'t'    ; doing internal TCP?
  1845.     jne    sparx6            ; ne = no
  1846.     mov    protlist[127],0        ; protect DEL and IAC
  1847. sparx6:    pop    bx
  1848.  
  1849.     pop    dx
  1850.     pop    cx
  1851.     pop    si            ; saved at start of spar
  1852.     pop    ax
  1853.     pop    es
  1854.     ret
  1855. SPAR    ENDP
  1856.  
  1857. ; Set 8-bit quote character based on my capabilities and the other
  1858. ; Kermit's request. Quote if one side says Y and the other a legal char
  1859. ; or if both sides say the same legal char. Enter with AL = their quote char
  1860. DOQUO    PROC    NEAR
  1861.     cmp    dtrans.lshift,lock_force ; forcing locking shift?
  1862.     je    dq3            ; e = yes, ignore 8-bit quoting
  1863.     cmp    dtrans.ebquot,'N'    ; we refuse to do 8-bit quoting?
  1864.     je    dq3            ; e = yes, do not quote
  1865.     cmp    al,'N'            ; 'N' = they refuse quoting?
  1866.     je    dq3            ; e = yes, do not quote
  1867.     cmp    dtrans.ebquot,'Y'    ; can we do it if requested?
  1868.     je    dq2            ; e = yes, use their char in al
  1869.     cmp    al,'Y'            ; 'Y' = they can quote if req'd?
  1870.     jne    dq1            ; ne = no, use their particular char
  1871.     mov    al,dtrans.ebquot    ; we want to use a particular char
  1872.     call    prechk            ;  and they said 'Y', check ours
  1873.     jc    dq3            ; c = ours is out of range, no quoting
  1874. dq1:    cmp    al,dtrans.ebquot    ; active quote vs ours, must match
  1875.     jne    dq3            ; ne = mismatch, no quoting
  1876. dq2:    mov    trans.ebquot,al        ; get active char, ours or theirs
  1877.     cmp    al,'Y'
  1878.     je    dq4    
  1879.     call    prechk            ; in range 33-62, 96-126?
  1880.     jc    dq3            ; c = out of range, do not quote
  1881. dq4:    cmp    al,trans.rquote        ; same prefix as control-quote?
  1882.     je    dq3            ; e = yes, don't do 8-bit quote
  1883.     cmp    al,trans.squote        ; same prefix control-quote?
  1884.     je    dq3            ; this is illegal too
  1885.     mov    trans.ebquot,al        ; remember what we decided on
  1886.     ret
  1887. dq3:    mov    trans.ebquot,'N'    ; quoting will not be done
  1888.     ret
  1889. DOQUO    ENDP
  1890.  
  1891. ; Check if prefix in AL is in the proper range: 33-62, 96-126. 
  1892. ; Return carry clear if in range, else return carry set.
  1893. prechk:    cmp    al,33
  1894.     jb    prechk2            ; b = out of range
  1895.     cmp    al,62
  1896.     jbe    prechk1            ; be = in range 33-62
  1897.     cmp    al,96
  1898.     jb    prechk2            ; b = out of range
  1899.     cmp    al,126
  1900.     ja    prechk2            ; a = out of range 96-126
  1901. prechk1:clc                ; carry clear for in range
  1902.     ret
  1903. prechk2:stc                ; carry set for out of range
  1904.     ret
  1905.  
  1906. ; Set checksum length. AL holds their checksum request.
  1907. dochk:    cmp    al,'1'            ; Must be '1', '2', '3', or 'B'
  1908.     jb    doc1            ; b = not '1' to '3'
  1909.     cmp    al,'3'
  1910.     jbe    doc2            ; be = ok
  1911.     cmp    al,'B'            ; special non-blank 2 byte?
  1912.     je    doc2            ; e = yes
  1913. doc1:    mov    al,'1'            ; else use default of '1'
  1914. doc2:    sub    al,'0'            ; remove ascii bias
  1915.     mov    trans.chklen,al        ; other side's request is do-able here
  1916.     ret
  1917.  
  1918. ; Set repeat count quote character.  The one used must be different than
  1919. ; the control and eight-bit quote characters.  Also, both sides must 
  1920. ; use the same character
  1921. dorpt:    mov    trans.rptq,0        ; assume will not repeat prefix
  1922.     call    prechk            ; is it in the valid range?
  1923.     jnc    dorpt1            ; nc = in range
  1924.     xor    al,al            ; don't use their value 
  1925. dorpt1:    cmp    al,trans.squote        ; same as the control quote char?
  1926.     je    dorpt2            ; e = yes, that's illegal, no repeats
  1927.     cmp    al,trans.rquote        ; this too?
  1928.     je    dorpt2            ; e = no good either
  1929.     cmp    al,trans.ebquot        ; same as eight bit quote char?
  1930.     je    dorpt2            ; e = yes, illegal too, no repeats
  1931.     cmp    al,dtrans.rptq        ; both sides using same char?
  1932.     jne    dorpt2            ; ne = no, that's no good either
  1933.     mov    trans.rptq,al        ; use repeat quote char now
  1934. dorpt2:    ret
  1935.  
  1936.                     ; negotiate window size in al
  1937. dewind:    cmp    al,dtrans.windo        ; their (al) vs our max window size
  1938.     jbe    dewind1            ; be = they want less than we can do
  1939.     mov    al,dtrans.windo        ; limit to our max size
  1940. dewind1:or    al,al
  1941.     jnz    dewind2
  1942.     inc    al            ; use 1 if 0
  1943. dewind2:mov    trans.windo,al        ; store active window size
  1944.     ret
  1945.  
  1946.  
  1947. ; Set the maximum send data packet size; modified for long packets
  1948. PACKLEN    PROC    NEAR
  1949.     push    ax
  1950.     xor    ah,ah
  1951.     mov    al,trans.spsiz    ; Maximum send packet size for Regular pkts. 
  1952.     cmp    ax,trans.slong        ; negotiated long packet max size
  1953.     jae    packle2            ; ae = use regular packets
  1954.     mov    ax,trans.slong        ; else use long kind
  1955.     sub    ax,3            ; minus extended count & checksum
  1956.     cmp    ax,(95*94-1-2)        ; longer than Long?
  1957.     jle    packle2            ; le = no, Long will do
  1958.     dec    ax            ; minus one more for extra long count
  1959. packle2:sub    ax,2            ; minus Sequence, Type
  1960.     cmp    trans.chklen,'B'-'0'    ; special 'B'?
  1961.     jne    packle3            ; ne = no
  1962.     sub    al,2            ; 'B' is two byte kind
  1963.     jmp    short packle3a
  1964. packle3:sub    al,trans.chklen        ; and minus Checksum chars
  1965. packle3a:sbb    ah,0            ; borrow propagate
  1966.     cmp    trans.ebquot,'N'    ; doing 8-bit Quoting?
  1967.     je    packle0            ; e = no, so we've got our size
  1968.     cmp    trans.ebquot,'Y'
  1969.     je    packle0            ; e = not doing it in this case either
  1970.     dec    ax            ; another 1 for 8th-bit Quoting. 
  1971. packle0:cmp    trans.rptq,0        ; doing repeat character Quoting?
  1972.     je    packle1            ; e = no, so that's all for now
  1973.     dec    ax            ; minus repeat prefix
  1974.     dec    ax            ;  and repeat count
  1975. packle1:dec    ax            ; for last char might being a control code
  1976.     mov    trans.maxdat,ax        ; save max length for data field
  1977.     pop    ax
  1978.     ret
  1979. PACKLEN    ENDP
  1980.  
  1981.  ; Print the number in AX on the screen in decimal rather that hex
  1982.  
  1983. NOUT     PROC    NEAR
  1984.     test    flags.remflg,dserver    ; server mode?
  1985.     jnz    nout2            ; nz = yes, writing to their screen
  1986.     cmp    flags.xflg,0        ; receiving to screen?
  1987.     jne    nout1            ; ne = yes
  1988. nout2:    test    flags.remflg,dserial     ; serial display mode?
  1989.     jnz    pnout            ; nz = use "dot and plus" for serial mode
  1990.     test    flags.remflg,dquiet    ; quiet display mode?
  1991.     jnz    nout1            ; nz = yes. Don't write to screen
  1992.     call    decout            ; call standard decimal output routine
  1993. nout1:    ret
  1994.  
  1995. pnout:    or    ax,ax            ; display packet in serial display mode
  1996.     jz    pnoutx            ; z = nothing to display
  1997.     push    ax            ; for serial mode display
  1998.     push    cx
  1999.     push    dx            ; output .........+.........+  etc
  2000.     xor    dx,dx            ; extended numerator
  2001.     mov    cx,10
  2002.     div    cx            ; number/10. (AX=quo, DX=rem)
  2003.     or    dx,dx            ; remainder non-zero?
  2004.     jnz    pnout1            ; nz = yes
  2005.     mov    dl,'+'            ; symbol plus for tens
  2006.     jmp    short pnout2        ; display it
  2007. pnout1:    mov    dl,'.'            ; symbol for between tens
  2008. pnout2:    mov    ah,conout        ; output to console
  2009.     int    dos
  2010.     pop    dx
  2011.     pop    cx
  2012.     pop    ax
  2013. pnoutx:    ret
  2014. NOUT    ENDP
  2015.  
  2016. ; Decode and display Error packet message. 
  2017. ERROR    PROC    NEAR
  2018.     mov    sstate,'A'        ; Set the state to abort
  2019.     push    si
  2020.     mov    si,offset rpacket    ; source address
  2021.     call    dodec            ; decode to decbuf
  2022.     mov    dx,offset decbuf    ; where msg got decoded, asciiz
  2023.     call    ermsg            ; display string
  2024.     pop    si
  2025.     stc                ; set carry for failure state
  2026.     ret
  2027. ERROR    ENDP
  2028.  
  2029. ; General routine for sending an error packet.  Register BX should
  2030. ; point to the text of the message being sent in the packet
  2031.  
  2032. ERRPACK    PROC    NEAR
  2033.     push    cx
  2034.     push    di
  2035.     mov    di,offset encbuf    ; Where to put the message
  2036.     xor    cx,cx
  2037. errpa1:    mov    al,[bx]
  2038.     inc    bx
  2039.     cmp    al,'$'            ; at end of message?
  2040.     je    errpa2            ; e = terminator
  2041.     or    al,al            ; this kind of terminator too?
  2042.     jz    errpa2            ; z = terminator
  2043.     inc    cx            ; count number of chars in msg
  2044.     mov    [di],al            ; copy message
  2045.     inc    di
  2046.     jmp    short errpa1
  2047. errpa2:    push    si
  2048.     mov    si,offset rpacket    ; use response buffer
  2049.     mov    al,pktnum
  2050.     mov    rpacket.seqnum,al
  2051.     call    doenc
  2052.     call    pktsize            ; report packet size
  2053.     mov    rpacket.pktype,'E'    ; send an error packet
  2054.     call    spack
  2055.     mov    rpacket.datlen,0    ; clear response buffer
  2056.     pop    si
  2057.     pop    di
  2058.     pop    cx
  2059.     ret
  2060. ERRPACK    ENDP
  2061.  
  2062. ; Enter with dx pointing to asciiz error message to be sent
  2063. giveup    proc near
  2064.     call    bufclr            ; release all buffers
  2065.     call    ermsg            ; position cursor, display asciiz msg
  2066.     mov    bx,dx
  2067.     call    errpack            ; send error packet
  2068.     xor    ax,ax
  2069.     mov    auxfile,al        ; clear send-as/mail-to buffer
  2070.     mov    mailflg,al        ; clear Mail flag
  2071.     cmp    filopn,al        ; disk files open?
  2072.     je    giveu2            ; e = no so don't do a close
  2073.     mov    ah,close2        ; close file
  2074.     push    bx
  2075.     mov    bx,diskio.handle    ; file handle
  2076.     int    dos
  2077.     pop    bx
  2078.     mov    filopn,0        ; say file is closed now
  2079. giveu2:    mov    sstate,'A'        ; abort state
  2080.     or    errlev,kssend        ; set DOS error level
  2081.     or    fsta.xstatus,kssend    ; set status
  2082.     mov    kstatus,kssend        ; global status
  2083.     stc                ; set carry for failure status
  2084.     ret
  2085. giveup    endp
  2086.  
  2087. code    ends 
  2088.     end
  2089.