home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / msk316src.zip / MSSCOM.ASM < prev    next >
Assembly Source File  |  1999-04-24  |  66KB  |  1,978 lines

  1.     NAME    msscom
  2. ; File MSSCOM.ASM
  3.     include mssdef.h
  4. ;    Copyright (C) 1982, 1999, Trustees of Columbia University in the 
  5. ;    City of New York.  The MS-DOS Kermit software may not be, in whole 
  6. ;    or in part, licensed or sold for profit as a software product itself,
  7. ;    nor may it be included in or distributed with commercial products
  8. ;    or otherwise distributed by commercial concerns to their clients 
  9. ;    or customers without written permission of the Office of Kermit 
  10. ;    Development and Distribution, Columbia University.  This copyright 
  11. ;    notice must not be removed, altered, or obscured.
  12. ;
  13. ; Edit history
  14. ; 12 Jan 1995 version 3.14
  15. ; Last edit
  16. ; 12 Jan 1995
  17. ; 8 Dec 1992 version 3.13
  18. ; 6 Sept 1991 version 3.11
  19. ; 2 March 1991 version 3.10
  20.  
  21.     public    spack, rpack, spause, bufclr, pakptr, bufrel
  22.     public    makebuf, getbuf, pakdup, chkwind, firstfree, windused
  23.     public    rpacket, windlow, chkparflg, maxbufparas, peekreply
  24.     public    winusedmax, krto, k_sa, k_sd, k_rto, tcp_rto, windgrow
  25.     public    windshrink, cwindow, ssthresh
  26.  
  27. stat_suc equ    0        ; success
  28. stat_tmo equ    1        ; timeout
  29. stat_chk equ    2        ; checksum mismatch
  30. stat_ptl equ    4        ; packet too long
  31. stat_int equ    8        ; user interrupt
  32. stat_eol equ    10h        ; eol char seen
  33. stat_echo equ    20h        ; echo of sent packet
  34. stat_bad equ    80h        ; packet is bad (premature EOL)
  35. BIOSCLK    equ    046ch
  36. HI_MAX    equ    018h
  37. LO_MAX    equ     0b0h
  38.  
  39. data    segment
  40.     extrn    flags:byte, trans:byte, fsta:word, ssta:word, fmtdsp:byte
  41.     extrn    pktnum:byte, portval:word, denyflg:word, cardet:byte
  42.     extrn    parmsk:byte
  43.  
  44. badpflag db    0        ; flag to say have shown bad parity message
  45. spmes    db    'Spack: $'
  46. rpmes     db    'Rpack: $'
  47. crlf    db      cr,lf,'$'
  48. msgstl    db    'Internal Error: send packet is too long',cr,lf,0,'$'
  49. msgheader db    '<$'
  50. msgtmo    db    'Timeout$'
  51. msgchk    db    'Bad checksum $'
  52. msgint    db    'Interrupted$'
  53. msgptl    db    'Pkt too long$'
  54. msgbad    db    'Early EOL$'
  55. msgtail    db    '>',cr,lf,'$'
  56. msgecho    db    cr,lf,'<Echo of sent packet>',cr,lf,'$'
  57. msgbadsnd db    cr,lf,'<Error sending packet bytes>',cr,lf,'$'
  58. msgbadpare db    'Unexpected Parity from host! Changing Parity to EVEN'
  59.     db    cr,lf,0
  60. msgbadparo db    'Unexpected Parity from host! Changing Parity to ODD'
  61.     db    cr,lf,0
  62. msgbadparm db    'Unexpected Parity from host! Changing Parity to MARK'
  63.     db    cr,lf,0
  64. tmp    db    0
  65. spause    dw    0        ; # millisec to wait before sending pkt
  66. timeval    db    0        ; active receive timeout value, seconds
  67. prvtyp  db      0        ; Type of last packet sent
  68. prvlen    dw    0        ; bytes sent, for echo suppression
  69. portcount dw    0        ; bytes in prtchr ready-to-read buffer
  70. chkparflg db    0        ; non-zero to check parity on received pkts
  71. chklength db    1        ; active checksum length
  72. prevchar db    0        ; previous char from comms line (for ^C exit)
  73. SOHchar    db    0        ; start of packet char, local copy
  74. lentyp    db    0        ; packet length type, 3, 0, 1
  75. debflg    db    0        ; debug display, send/receive flag
  76. timeit    db    0        ; arm timeout counter
  77. tcp_rto    dw    0        ; rto (Bios ticks) from internal TCP/IP stack
  78. k_rto    dw    0        ; Kermit round trip timeout, Bios ticks
  79. k_sa    dw    0        ; Kermit smoothed avg round trip time
  80. k_sd    dw    0        ; Kermit std deviation of round trip time
  81.  
  82.                 ; sliding windows data structures
  83. windlow    db    0        ; lower border of window
  84. windused db    0        ; number of window slots in use
  85. winusedmax db    0        ; max used window slots
  86. cwindow    db    0,0        ; congestion window, fractions of slot
  87. ssthresh db    0        ; congestion threshold slot count
  88. thirtytwo db    32        ; divisor for window congestion counting
  89. prolog  db    10 dup (0)    ; prolog: SOH, LEN, SEQ, TYPE, xlen,...,null
  90. epilog    db    10 dup (0)    ; epilog: checksum, eol, handshake + null term
  91. rbuf    db    128 dup (0)    ; static packet buffer for replies
  92.     even
  93. pbufseg    dw    0        ; segment of packet buffer memory block
  94. maxbufparas dw    0        ; paragraphs available in free memory
  95. bufnum    dw    0        ; number of buffers available now
  96. buflist dw    maxwind dup (0) ; pointers to packet structures in pktlist
  97. bufuse    dw    maxwind dup (0) ; in-use flag (0 = not in use)
  98. pktlist    pktinfo maxwind dup (<>) ; pktinfo structured members (private)
  99. rpacket    pktinfo <rbuf,0,length rbuf,0,0> ; reply pktinfo
  100.     even
  101. rtemp    dw    0        ; address of pktinfo structure for rpack
  102. stemp    dw    0        ; address of pktinfo structure for spack
  103. linecnt    db    0        ; debug line width counter
  104. colcnt    db    0
  105. chksum    dw    0        ; running checksum (two char)
  106. chrcnt    dw    0        ; number of bytes in data field of a packet
  107. pktcnt    dw    0        ; number of bytes sent/rcvd in this packet
  108. status    dw    0        ; status of packet receiver (0 = ok)
  109. rptim    db    4 dup (0)    ; read packet timeout slots
  110. ninefive dw    95        ; for mult/div with long packets
  111. bias    db    ' '        ; ascii bias for checksum calculations
  112. prolog_len dw    0        ; length of prolog information, for crc
  113. temp    dw    0
  114. data    ends
  115.  
  116. code1    segment
  117.     extrn    strlen:far, isdev:far, decout:far, dec2di:far, sndblk:far
  118.     assume    cs:code1
  119. code1    ends
  120.  
  121. code    segment
  122.     extrn    prtchr:near, outchr:near, prtblk:far
  123.     extrn    sppos:near, ermsg:near, clearl:near, rppos:near
  124.     extrn    pktcpt:near, pcwait:far, peekcom:far
  125.  
  126.     assume     cs:code, ds:data, es:nothing
  127. prtchr1    proc    far        ; near-far interface routines for code1 seg
  128.     call    prtchr
  129.     ret
  130. prtchr1    endp
  131. foutchr    proc    far
  132.     call    outchr
  133.     ret
  134. foutchr    endp
  135. rppos1    proc    far
  136.     call    rppos
  137.     ret
  138. rppos1    endp
  139. sppos1    proc    far
  140.     call    sppos
  141.     ret
  142. sppos1    endp
  143. ermsg1    proc    far
  144.     call    ermsg
  145.     ret
  146. ermsg1    endp
  147. clearl1    proc    far
  148.     call    clearl
  149.     ret
  150. clearl1    endp
  151. fpktcpt    proc    far
  152.     call    pktcpt
  153.     ret
  154. fpktcpt    endp
  155. code    ends
  156.  
  157. code1    segment
  158.     assume     cs:code1, ds:data, es:nothing
  159.  
  160. ; Send_Packet
  161. ; This routine assembles a packet from the arguments given and sends it
  162. ; to the host.
  163. ;
  164. ; Expects the following:
  165. ;    SI = pointer to pktinfo structure, as
  166. ;    [SI].PKTYPE - Packet type letter
  167. ;    [SI].SEQNUM - Packet sequence number
  168. ;    [SI].DATLEN - Number of data characters
  169. ;    [SI].DATADR - DWord Address of data field for packet
  170. ; Returns: carry clear if success, carry set if failure.
  171. ; Packet construction areas:
  172. ;     Prolog (8 bytes)            Data     null  Epilog
  173. ;+----------------------------------------+---------------+---------------+
  174. ;| SOH,LEN,SEQ,TYPE,Xlen(2-3),Xlen chksum | packet's data | chksum,EOL,HS |
  175. ;+----------------------------------------+---------------+---------------+
  176. ; where Xlen is 2 byte (Long) or 3 byte (Extra Long) count of bytes to follow.
  177. ;
  178. SPACK    PROC    FAR
  179.     mov    stemp,si    ; save pkt pointer
  180.     mov    ah,[si].pktype
  181.     mov    prvtyp,ah    ; remember packet type
  182.     mov    pktcnt,0    ; number of bytes sent in this packet
  183.     mov    prolog_len,0
  184.     mov    ax,spause    ; wait spause milliseconds before sending pkt
  185.     or    ax,ax        ; zero?
  186.     jz    spac1        ; z = yes
  187.     call    pcwait        ;   to let other side get ready
  188. spac1:    mov    cl,trans.spad    ; get the number of padding chars
  189.     xor    ch,ch
  190.     jcxz    spac4        ; z = none
  191.     xor    al,al
  192.     xchg    al,trans.sdbl    ; doubling char, stash and clear it
  193.     push    ax
  194.     mov    al,trans.spadch    ; get padding char
  195. spac2:    call    spkout        ; send padding char
  196.     jnc    spac3        ; nc = success
  197.     ret            ; failed
  198. spac3:    loop    spac2
  199.     pop    ax        ; recover doubling char
  200.     xchg    trans.sdbl,al
  201.  
  202. spac4:    call    snddeb        ; do debug display (while it's still our turn)
  203.     mov    al,trans.ssoh    ; get the start of header char
  204.     mov    prolog,al    ; put SOH in the packet
  205.     mov    si,stemp    ; address of send pktinfo
  206.     mov    al,[si].seqnum    ; SEQ
  207.     add    al,20h        ; ascii bias
  208.     mov    prolog+2,al    ; store SEQ in packet
  209.     xor    ah,ah
  210.     mov    chksum,ax    ; start checksum
  211.     mov    al,prvtyp    ; TYPE
  212.     mov    word ptr prolog+3,ax ; store TYPE (and null terminator)
  213.     add    chksum,ax    ; add to checksum
  214. ;
  215. ; packet length type is directly governed here by length of header plus data
  216. ; field, [si].datlen, plus chksum: regular <= 94, long <= 9024, else X long.
  217. ;
  218.     mov    ax,[si].datlen    ; DATA length
  219.     add    ax,2        ; add SEQ, TYPE lengths
  220.     cmp    trans.chklen,'B'-'0' ; B blank-free checksum?
  221.     jne    spac12        ; ne = no
  222.     add    al,2        ; B is a special two byte checksum
  223.     jmp    short spac13
  224. spac12:    add    al,trans.chklen    ; add checksum length at the end
  225. spac13:    adc    ah,0        ; propagate carry, yields overall new length
  226.     cmp    ax,[si].datsize    ; too big?
  227.     jle    spac14        ; le = ok
  228.     push    ax        ; for carry-on-regardless after error
  229.     push    dx        ; tell user an internal error has occurred
  230.     mov    dx,offset msgstl ; packet is too long
  231.     call    ermsg1        ; display message on error line
  232.     call    captdol        ; put into packet log
  233.     pop    dx
  234.     pop    ax
  235.     jmp    short spac14    ; carry-on-regardless
  236.  
  237. spac14:    mov    lentyp,3    ; assume regular packet
  238.     cmp    ax,94        ; longer than a regular?
  239.     ja    spac15        ; a = use Long
  240.     add    al,20h        ; convert length to ascii
  241.     mov    prolog+1,al    ; store LEN
  242.     xor    ah,ah
  243.     add    chksum,ax    ; add LEN to checksum
  244.     mov    bx,offset prolog+4 ; look at data field
  245.     jmp    spac19        ; do regular
  246.  
  247.                 ; use Long packets (type 0)
  248. spac15:    sub    ax,2        ; deduct SEQ and TYPE from above = data+chksum
  249.     mov    lentyp,0    ; assume type 0 packet
  250.     cmp    ax,(95*95-1)    ; longest type 0 Long packet (9024)
  251.     jbe    spac16        ; be = type 0
  252.     mov    lentyp,1    ; type 1 packet, Extra Long
  253. spac16:    mov    cl,lentyp    ; add new LEN field to checksum
  254.     add    cl,20h        ; ascii bias, tochar()
  255.     xor    ch,ch
  256.     add    chksum,cx    ; add to running checksum
  257.     mov    prolog+1,cl    ; put LEN into packet
  258.     mov    bx,offset prolog+4
  259.     mov    cx,1        ; a counter
  260.     xor    dx,dx        ; high order numerator of length
  261. spac17:    div    ninefive    ; divide ax by 95. quo = ax, rem = dx
  262.     push    dx        ; push remainder
  263.     inc    cx        ; count push depth
  264.     cmp    ax,95        ; quotient >= 95?
  265.     jae    spac17        ; ae = yes, recurse
  266.     push    ax        ; push for pop below
  267. spac18:    pop    ax        ; get a digit
  268.     add    al,20h        ; apply tochar()
  269.     mov    [bx],al        ; store in data field
  270.     add    chksum,ax    ; accumulate checksum for header
  271.     inc    bx        ; point to next data field byte
  272.     loop    spac18        ; get the rest
  273.                 ;
  274.     mov    ax,chksum    ; current checksum
  275.     shl    ax,1        ; put two highest bits of al into ah
  276.     shl    ax,1
  277.     and    ah,3        ; want just those two bits
  278.     shr    al,1        ; put al back in place
  279.     shr    al,1
  280.     add    al,ah        ; add two high bits to earlier checksum
  281.     and    al,03fh        ; chop to lower 6 bits (mod 64)
  282.     add    al,20h        ; apply tochar()
  283.     mov    [bx],al        ; store that in length's header checksum
  284.     inc    bx
  285.     xor    ah,ah
  286.     add    chksum,ax    ; add that byte to running checksum
  287.                 ; end of inserting Long pkt info
  288.  
  289. spac19:    mov    cx,bx        ; where we stopped+1
  290.     mov    bx,offset prolog ; place where prolog section starts
  291.     sub    cx,bx
  292.     mov    prolog_len,cx
  293.     dec    prolog_len
  294.     jcxz    spac22        ; nothing
  295.     mov    prvlen,cx    ; count sent bytes for echo suppression
  296.     sub    prvlen,3    ;  minus LEN, SEQ, TYPE
  297.     test    cardet,1    ; Carrier detect, was on and is now off?
  298.     jnz    spac33        ; nz = yes, fail
  299.     add    pktcnt,cx    ; count number of bytes sent in this packet
  300.     push    es
  301.     push    ds
  302.     pop    es
  303.     call    sndblk        ; block send cx bytes from es:bx
  304.     pop    es
  305.     jc    spac33        ; c = bad send
  306.  
  307. spac22:    test    cardet,1    ; Carrier detect, was on and is now off?
  308.     jnz    spac33        ; nz = yes, fail
  309.     push    es
  310.     mov    si,stemp    ; address of pktinfo
  311.     les    bx,[si].datadr    ; select from given data buffer
  312.     mov    cx,[si].datlen    ; get the number of data bytes in packet
  313.     add    prvlen,cx    ; count bytes sent
  314.     add    pktcnt,cx    ; count number of bytes sent in this packet
  315.     push    cx
  316.     call    sndblk        ; block send cx bytes from es:bx
  317.     pop    cx
  318.     jnc    spac23        ; nc = success
  319.     pop    es        ; clean stack
  320.     jmp    spac33        ; bad send
  321.  
  322. spac23:    or    cx,cx        ; any data chars remaining?
  323.     jle    spac25        ; le = no, finish up
  324.     cmp    trans.chklen,2    ; what kind of checksum are we using?
  325.     jg    spac25        ; g = 3 characters, skip linear checksum
  326.     xor    ah,ah
  327.     mov    dx,chksum
  328. spac24:    mov    al,es:[bx]    ; get a data char
  329.     inc    bx        ; point to next char
  330.     add    dx,ax        ; add the char to the checksum [umd]
  331.     loop    spac24
  332.     and    dx,0fffh    ; keep only low order 12 bits
  333.     mov    chksum,dx
  334.  
  335. spac25:    pop    es
  336.     mov    bx,offset epilog ; area for epilog
  337.     mov    cx,chksum
  338.     mov    bias,' '+1    ; bias for checksum char (+1 for non-blank)
  339.     cmp    trans.chklen,'B'-'0'; special non-blank checksum?
  340.     je    spac27        ; e = yes
  341.     dec    bias        ; use ' ' for regular packets
  342.     cmp    trans.chklen,2    ; what kind of checksum are we using?
  343.     je    spac27        ; e = 2 characters
  344.     jg    spac26        ; g = 3 characters
  345.     mov    al,cl        ; 1 char: get the character total
  346.     mov    ch,cl        ; save here too (need 'cl' for shift)
  347.     and    al,0C0H        ; turn off all but the two high order bits
  348.     mov    cl,6
  349.     shr    al,cl        ; shift them into the low order position
  350.     mov    cl,ch
  351.     add    al,cl        ; add it to the old bits
  352.     and    al,3FH        ; turn off the two high order bits.  (MOD 64)
  353.     add    al,' '        ; add a space so the number is printable
  354.     mov    [bx],al        ; put in the packet
  355.     inc    bx        ; point to next char
  356.     jmp    short spac30
  357.  
  358. spac26:    push    bx        ; don't lose our place
  359.     push    es
  360.     mov    bx,ds        ; set up es for crcclc
  361.     mov    es,bx        ; es:[bx] is src of data for crcclc
  362.     mov    bx,offset prolog+1 ; first checksummed char, skip SOH
  363.     mov    cx,prolog_len
  364.     xor    dx,dx        ; initial CRC value is 0
  365.     call    crcclc        ; calculate the CRC of prolog part, to cx
  366.     mov    dx,cx        ; first part of CRC returned in cx
  367.     mov    si,stemp    ; address of pktinfo
  368.     mov    cx,[si].datlen
  369.     les    bx,[si].datadr    ; address of data
  370.     call    crcclc        ; do CRC of data, using current CRC in dx
  371.     pop    es
  372.     pop    bx        ; recover place to store more debug info
  373.     push    cx        ; save the crc
  374.     mov    ax,cx        ; manipulate it here
  375.     and    ax,0F000H    ; get 4 highest bits
  376.     mov    cl,4
  377.     shr    ah,cl        ; shift over 4 bits
  378.     add    ah,' '        ; make printable
  379.     mov    [bx],ah        ; add to buffer
  380.     inc    bx
  381.     pop    cx        ; get back checksum value
  382. spac27:    push    cx        ; save it for now
  383.     and    cx,0FC0H    ; get bits 6-11
  384.     mov    ax,cx
  385.     mov    cl,6
  386.     shr    ax,cl        ; shift them bits over
  387.     add    al,bias        ; make printable
  388.     mov    [bx],al        ; add to buffer
  389.     inc    bx
  390.     pop    cx        ; get back the original
  391.     and    cx,003FH    ; get bits 0-5
  392.     add    cl,bias        ; make printable
  393.     mov    [bx],cl        ; add to buffer
  394.     inc    bx
  395.  
  396. spac30:    mov    al,trans.seol    ; get the EOL the other host wants
  397.     xor    ah,ah
  398.     mov    [bx],ax        ; put eol and terminator in buffer
  399.     inc    bx
  400.     xor    ch,ch
  401.     mov    cl,trans.chklen    ; checksum length
  402.     cmp    cl,'B'-'0'    ; special non-blank checksum?
  403.     jne    spac32        ; ne = no
  404.     mov    cl,2        ; Blank checksum is a two byte affair
  405. spac32:    add    prvlen,cx    ; bytes sent
  406.     inc    cx        ; plus EOL char
  407.     add    pktcnt,cx    ; count number of bytes sent in this packet
  408.     push    cx
  409.     push    es
  410.     mov    ax,seg epilog
  411.     mov    es,ax
  412.     mov    bx,offset epilog; where to find data
  413.     call    sndblk        ; block send cx bytes from es:bx
  414.     pop    es
  415.     pop    cx        ; epilog byte count
  416.        
  417. spac33:    pushf            ; save carry of how got here
  418.     cmp    debflg,0    ; recording material?
  419.     je    spac34        ; e = no
  420.     call    showsend    ; enter with CX holding epilog byte count
  421.  
  422. spac34:    mov    ax,pktcnt    ; number of bytes sent in this packet
  423.     add    fsta.psbyte,ax    ; file total bytes sent
  424.     adc    fsta.psbyte+2,0    ; propagate carry to high word
  425.     add    fsta.pspkt,1    ; statistics, count a packet being sent
  426.     adc    fsta.pspkt+2,0    ;  ripple carry
  427.     call    chkcon        ; check console for user interrupts
  428.     mov    si,stemp    ; restore pkt pointer
  429.     call    getbtime    ; get Bios time of day to dx:ax
  430.     mov    word ptr [si].sndtime,ax ; low order sent time
  431.     mov    word ptr [si].sndtime+2,dx ; high order
  432.     popf            ; restore carry from spac33
  433.     jc    spac35        ; c = failure to send
  434.     clc            ; carry clear for success
  435.     ret            ; return successfully
  436. spac35:    mov    dx,offset msgbadsnd ; say sending error in log
  437.     call    captdol
  438.     stc            ; carry set for failure
  439.     ret            ; bad send
  440. SPACK    ENDP 
  441.  
  442. spkout    proc    near
  443.     test    cardet,1    ; Carrier detect, was on and is now off?
  444.     jz    spkou4        ; z = no
  445.     stc            ; fail now
  446.     ret
  447. spkou4:    cmp    al,255        ; possible Telnet IAC char?
  448.     jne    spkou2        ; ne = no
  449.     cmp    flags.comflg,'t' ; internal Telnet?
  450.     je    spkou3        ; e = yes, double the char
  451. spkou2:    cmp    al,trans.sdbl    ; double this char?
  452.     jne    spkou1        ; ne = no
  453. spkou3:    call    spkou1        ; do it once here and again via fall through
  454.     jnc    spkou1        ;  but again only if no failure
  455.     ret            ; return failure
  456. spkou1:    push    bx        ; send char in al out the serial port
  457.     push    cx        ; return carry clear if success
  458.     push    dx
  459.     push    es
  460.     cmp    debflg,0    ; recording material?
  461.     je    spkour        ; e = no
  462.     call    captchr        ; record in debugging log
  463. spkour:    mov    ah,al        ; foutchr wants char in ah
  464.     inc    pktcnt        ; count number of bytes sent in this packet
  465.     call    foutchr        ; serial port transmitter procedure
  466.     pop    es
  467.     pop    dx
  468.     pop    cx
  469.     pop    bx        ; carry set by foutchr if failure to send
  470.     ret
  471. spkout    endp
  472.  
  473. ; Log sent packet to debug area
  474. showsend proc    near
  475.     push    cx        ; save epilog byte count
  476.  
  477.     mov    bx,offset prolog ; place where prolog section starts
  478.     mov    cx,prolog_len
  479.     inc    cx        ; include SOH
  480.     jz    showp        ; should never be zero
  481. showp:    mov    al,[bx]
  482.     inc    bx
  483.     push    bx
  484.     push    cx
  485.     call    captchr
  486.     pop    cx
  487.     pop    bx
  488.     loop    showp
  489.  
  490. showp1:    push    es
  491.     mov    si,stemp    ; offset of given packet structure
  492.     les    bx,[si].datadr    ; select from given data buffer
  493.     mov    cx,[si].datlen    ; get the number of data bytes in packet
  494.     jcxz    showd1        ; z = none
  495. showd:    mov    al,es:[bx]    ; get a data char
  496.     inc    bx        ; point to next char
  497.     push    bx
  498.     push    cx
  499.     call    captchr
  500.     pop    cx
  501.     pop    bx
  502.     loop    showd
  503. showd1:    pop    es
  504.  
  505.     pop    cx        ; recover epilog byte count
  506.     jcxz    showe1        ; should never be zero
  507.     mov    bx,offset epilog
  508. showe:    mov    al,[bx]
  509.     inc    bx
  510.     push    bx
  511.     push    cx
  512.     call    captchr        ; record in debugging log
  513.     pop    cx
  514.     pop    bx
  515.     loop    showe
  516.     mov    dx,offset crlf
  517.     call    captdol
  518. showe1:    ret
  519. showsend    endp
  520.      
  521. ; Calculate the CRC of the string whose address is in ES:BX, length CX bytes.
  522. ; Returns the CRC in CX.  Destroys BX and AX.
  523. ; The CRC is based on the SDLC polynomial: x**16 + x**12 + x**5 + 1.
  524. ; Original by Edgar Butt  28 Oct 1987 [ebb].
  525. ; Enter with initial CRC in DX (normally 0).
  526. crcclc: push    dx
  527.     jcxz    crc1
  528. crc0:    push    cx
  529.     mov    ah,es:[bx]        ; get the next char of the string
  530.         inc    bx
  531.         xor    dl,ah            ; XOR input with lo order byte of CRC
  532.         mov    ah,dl            ; copy it
  533.     mov    cl,4            ; load shift count
  534.         shl    ah,cl            ; shift copy
  535.         xor    ah,dl            ; XOR to get quotient byte in ah
  536.         mov    dl,dh            ; high byte of CRC becomes low byte
  537.         mov    dh,ah            ; initialize high byte with quotient
  538.         xor    al,al
  539.         shr    ax,cl            ; shift quotient byte
  540.         xor    dl,ah            ; XOR (part of) it with CRC
  541.         shr    ax,1            ; shift it again
  542.         xor    dx,ax            ; XOR it again to finish up
  543.     pop    cx
  544.     loop    crc0
  545. crc1:   mov    cx,dx            ; return CRC in CX
  546.         pop    dx
  547.         ret
  548.  
  549. ; Receive_Packet
  550. ; This routine waits for a packet arrive from the host. Two Control-C's in a
  551. ; row from the comms line will cause a Control-C interruption exit.
  552. ; Returns
  553. ;    SI = pointer to pktinfo structure, as
  554. ;    [SI].SEQNUM - Packet sequence number
  555. ;    [SI].DATLEN - Number of data characters
  556. ;    [SI].DATADR - Address of data field for packet
  557. ; Returns AH -  packet type (letter code)
  558. ; Returns: carry clear if success, carry set if failure.
  559. ; Packet construction areas:
  560. ;     Prolog (8 bytes+2 nulls)    null    Data    null  Epilog   null
  561. ;+----------------------------------------+---------------+---------------+
  562. ;| SOH,LEN,SEQ,TYPE,Xlen(2-3),Xlen chksum | packet's data | chksum,EOL,HS |
  563. ;+----------------------------------------+---------------+---------------+
  564. ; where Xlen is 2 byte (Long) or 3 byte (Extra Long) count of bytes to follow.
  565.  
  566. RPACK    PROC    FAR
  567.     mov    rtemp,si        ; save pkt structure address
  568.     xor    ax,ax            ; get a zero
  569.     mov    badpflag,al        ; bad parity flag, clear it
  570.     mov    prevchar,al        ; clear previous recv'd char area
  571.     mov    bias,al            ; assume not using special B chksum
  572.     mov    [si].pktype,'T'        ; assume 'T' type packet (timeout)
  573.     mov    [si].datlen,ax        ; init to empty buffer
  574.     cmp    flags.comflg,'t'    ; internal Telnet?
  575.     jne    rpack11            ; ne = no
  576.     mov    trans.stime,0        ; no timeouts
  577.     jmp    rpack10
  578. rpack11:mov    ax,word ptr [si].sndtime ; time at which pkt was sent
  579.     or    ax,word ptr [si].sndtime+2
  580.     jnz    rpack10            ; nz = have a send time
  581.     cmp    flags.timflg,0        ; are timeouts turned off?
  582.     je    rpack10            ; e = yes, just check for more input
  583.     cmp    trans.stime,0        ; doing time outs?
  584.     je    rpack10            ; e = no, go check for more input
  585.     call    getbtime        ; get Bios time of day to dx:ax
  586.     sub    ax,2            ; make two bios ticks ago
  587.     sbb    dx,0
  588.     mov    word ptr [si].sndtime,ax ; low order sent time
  589.     mov    word ptr [si].sndtime+2,dx ; high order
  590. rpack10:xor    ax,ax
  591.     push    es
  592.     les    bx,[si].datadr        ; caller's data buffer
  593.     mov    word ptr es:[bx],ax    ; clear storage areas (asciiz)
  594.     pop    es
  595.     mov    word ptr prolog,ax
  596.     mov    prolog_len,ax
  597.     mov    word ptr epilog,ax
  598.     mov    bx,offset prolog    ; bx is buffer offset pointer
  599.     mov    pktcnt,ax        ; number of bytes rcvd in packet
  600.     mov    al,trans.rsoh        ; start of packet char
  601.     mov    SOHchar,al        ; save, local copy is modified
  602.     call    rcvdeb            ; setup debug display
  603.     mov    bx,seg prolog
  604.     mov    es,bx
  605.     mov    bx,offset prolog    ; set es:bx for logging
  606.     jmp    rpack0a
  607.  
  608. ; Get here with unexpected char (SOH or before) and echos with printable SOH
  609. rpack0:    push    ax            ; save char which got us here
  610.     mov    bx,rtemp        ; pktinfo address
  611.     mov    [bx].datlen,0        ; say no data yet
  612.     mov    [bx].seqnum,0ffh    ; illegal value
  613.     mov    [bx].pktype,0        ; illegal value
  614.     xor    ax,ax            ; get a zero
  615.     push    es
  616.     les    bx,[bx].datadr         ; point to data buffer
  617.     mov    word ptr es:[bx],ax    ; clear start of that buffer
  618.     pop    es
  619.     mov    bx,seg prolog
  620.     mov    es,bx            ; set buffer pointer
  621.     mov    bx,offset prolog
  622.     mov    word ptr prolog,ax    ; clear prolog field
  623.     mov    word ptr epilog,ax    ; clear epilog field
  624.     mov    pktcnt,ax        ; count of chars
  625.     mov    al,trans.rsoh        ; start of packet char
  626.     mov    SOHchar,al        ; save, local copy is modified
  627.     mov    al,trans.stime        ; time to wait for start of packet
  628.     mov    timeval,al        ; local timer value, seconds
  629.     pop    ax
  630.     test    status,stat_int        ; interrupted?
  631.     jz    rpack0d            ; z = no
  632.     jmp    rpack60            ; yes, exit now
  633.  
  634. rpack0d:mov    cx,status        ; current status
  635.     mov    status,stat_suc        ; presume success
  636.     test    cx,stat_echo        ; doing echo processing?
  637.     jnz    rpack0h            ; nz = yes
  638.     cmp    al,SOHchar        ; read SOH?
  639.     jne    rpack0a            ; ne = no, get it
  640.     jmp    rpack1            ; go read LEN
  641.  
  642. rpack0h:cmp    trans.rsoh,' '        ; printable SOH (special case)?
  643.     jb    rpack0a            ; b = no, normal, get normal SOH
  644.     ; For printable SOH we need to step over possible repetitions used in
  645.     ; the general data stream. Thus we wait the length of the sent pkt.
  646.     ; This will hopefully gobble up echos of the last sent packet.
  647.     mov    cx,prvlen        ; LEN field of last sent packet
  648.     jcxz    rpack0a            ; in case zero by some accident
  649.     cmp    cx,256            ; keep waiting bounded
  650.     jbe    rpack0f            ; be = not very large
  651.     mov    cx,256            ; this should be enough bytes to wait
  652. rpack0f:push    cx
  653.     call    inchr            ; get and discard cx bytes
  654.     pop    cx
  655.     test    status,stat_tmo+stat_int ; timeout or user intervention?
  656.     jz    rpack0g            ; z = no
  657.     jmp    rpack60            ; timeout or user intervention, quit
  658. rpack0g:loop    rpack0f            ; keep discarding bytes
  659.     mov    status,stat_suc        ; assume success
  660.     mov    cl,trans.stime        ; time to wait for start of packet
  661.     mov    timeval,cl        ; local timer value, seconds
  662.     call    inchr            ; this reads the EOP byte echo, maybe
  663.     jnc    rpack0b            ; nc = not EOP, consider as SOP
  664.                     ; else discard echoed EOP
  665. rpack0a:mov    status,stat_suc        ; assume success
  666.     mov    cl,trans.stime        ; time to wait for start of packet
  667.     mov    timeval,cl        ; local timer value, seconds
  668.     call    inchr            ; get a character. SOH
  669.     jnc    rpack0b            ; nc = got one
  670.     test    status,stat_eol        ; hit eol from prev packet?
  671.     jnz    rpack0            ; nz = yes, restart
  672.     jmp    rpack60            ; timeout or user intervention
  673.  
  674. rpack0b:mov    ah,al            ; copy the char
  675.     and    ah,7fh            ; strip any parity bit, regardless
  676.     cmp    ah,SOHchar        ; start of header char?
  677.     je    rpack0c            ; e = yes, SOH
  678.     jmp    rpack0            ; ne = no, go until it is
  679. rpack0c:xor    ah,ah            ; clear the terminator byte
  680.     cmp    SOHchar,' '        ; printable SOHchar?
  681.     jb    rpack1            ; b = no (else start crippled mode)
  682.     mov    SOHchar,ah        ; yes, set it to null for no matches
  683.  
  684. rpack1:    cmp    flags.timflg,0        ; are timeouts turned off?
  685.     je    rpack1h            ; e = yes
  686.     cmp    flags.comflg,'t'    ; internal Telnet?
  687.     je    rpack1h            ; e = yes, use TCP timing info
  688.     mov    timeval,2        ; reduce local timer value to 2 secs
  689. rpack1h:call    inchr            ; get a character. LEN
  690.     jc    rpack1a            ; failure
  691.     and    al,7fh            ; strip any parity bit
  692.     cmp    al,SOHchar        ; start of header char?
  693.     jne    rpack1b            ; ne = no
  694. rpack1a:jmp    rpack0            ; yes, start over (common jmp point)
  695.  
  696. rpack1b:mov    chksum,ax        ; start the checksum
  697.     sub    al,20h            ; unchar(LEN) to binary
  698.     jnc    rpack1e            ; nc = legal (printable)
  699.     or    status,stat_ptl        ; set bad length status
  700.     jmp    rpack40            ; and quit
  701. rpack1e:mov    si,rtemp
  702.     mov    [si].datlen,ax        ; save the data count (byte)
  703.     call    inchr            ; get a character. SEQ
  704.     jc    rpack1a            ; c = failure
  705.     and    al,7fh            ; strip any parity bit
  706.     cmp    al,SOHchar        ; SOH?
  707.     je    rpack1a            ; e = yes, then go start over
  708.     add    chksum,ax
  709.     sub    al,' '            ; get the real packet number
  710.     jnc    rpack1f            ; nc = no overflow
  711.     or    status,stat_ptl        ; say bad status
  712.     jmp    rpack40            ; and exit now
  713. rpack1f:mov    si,rtemp
  714.     mov    [si].seqnum,al        ; save the packet number. SEQ
  715.     call    inchr            ; get a character. TYPE
  716.     jc    rpack1a            ; c = failure
  717.     and    al,7fh            ; strip any parity bit
  718.     cmp    al,SOHchar        ; SOH?
  719.     je    rpack1a            ; e = yes, then go start over
  720.     mov    [si].pktype,al        ; save the message type
  721.     cmp    al,prvtyp        ; echo of sent packet?
  722.     jne    rpack1g            ; ne = no
  723.     or    status,stat_echo    ; status is echo processing
  724.     mov    dx,offset msgecho     ; say echo in log
  725.     call    captdol
  726.     jmp    rpack0            ; start over
  727.  
  728. rpack1g:add    chksum,ax        ; add it to the checksum
  729.     call    parchk            ; check parity on protocol characters
  730.     call    getlen        ; get complicated data length (reg, lp, elp)
  731.                 ; into [si].datlen and kind into byte lentyp. carry set if error
  732.     jnc    rpack1c            ; nc = packet is ok so far
  733.     jmp    rpack40            ; failure
  734. rpack1c:
  735. ; Start of change.
  736. ; Now determine block check type for this packet.  Here we violate the layered
  737. ; nature of the protocol by inspecting the packet type in order to detect when
  738. ; the two sides get out of sync.  Two heuristics allow us to resync here:
  739. ;   a. I and S packets always have a type 1 checksum.
  740. ;   b. A NAK never contains data, so its block check type is len - 1.
  741.     mov    si,rtemp        ; pktinfo address
  742.     mov    ch,trans.chklen        ; current checksum length
  743.     cmp    ch,'B'-'0'        ; special non-blank kind?
  744.     jne    rpk4            ; ne = no
  745.     mov    ch,2            ; yes, it's a special 2-byte flavor
  746. rpk4:    mov    ax,[si].datlen        ; length of packet information
  747.     mov    cl,[si].pktype        ; packet type byte itself
  748.     cmp    cl,'S'            ; "S" packet?
  749.     jne    rpk0            ; ne = no
  750.     mov    ch,1            ; S packets use one byte checksums
  751.     jmp    short rpk3
  752. rpk0:    cmp    cl,'I'            ; I packets are like S packets
  753.     jne    rpk1
  754.     mov    ch,1            ; I packets use one byte checksums
  755.     jmp    short rpk3
  756. rpk1:    cmp    cl,'N'            ; NAK?
  757.     jne    rpk3            ; ne = no
  758.     cmp    ax,1            ; NAK, get length of data + chklen
  759.     jb    rpk1a            ; b = impossible length
  760.     cmp    ax,3            ; longest NAK (3 char checksum)
  761.     jbe    rpk2            ; be = possible
  762. rpk1a:    or    status,stat_ptl        ; status = bad length
  763.     jmp    rpack40            ; return on impossible length
  764. rpk2:    mov    trans.chklen,al        ; remainder must be checksum type
  765.     mov    ch,al
  766. rpk3:    sub    al,ch            ; minus checksum length, for all pkts
  767.     sbb    ah,0            ; propagate borrow
  768.     mov    [si].datlen,ax        ; store apparent length of data field
  769.     mov    chklength,ch        ; remember for checking below
  770. ; End of change.
  771. ; For long packets we start the real data (after the extended byte
  772. ; count 3 or 4 bytes).
  773.     sub    bx,offset prolog+1    ; compute length of prolog (skip SOH)
  774.     mov    prolog_len,bx
  775.  
  776.     mov    si,rtemp
  777.     mov    dx,[si].datlen        ; length of data field, excl LP header
  778.     mov    chrcnt,dx
  779.     cmp    dx,[si].datsize        ; material longer than data buffer?
  780.     jbe    rpk8c            ; be = no
  781. rpk8b:    or    status,stat_ptl        ; failure status, packet too long
  782.     jmp    rpack40            ; too big, quit now
  783. rpk8c:    les    bx,[si].datadr         ; point to offset of data buffer
  784.     mov    word ptr es:[bx],0    ; clear start of that buffer
  785.  
  786.                     ; get DATA field characters
  787. rpack2:    push    chrcnt            ; save data length to be examined
  788.     xor    cl,cl
  789.     xchg    debflg,cl        ; debugging done in checksum section
  790.     push    cx            ;  for data field bytes
  791. rpak21:    cmp    chrcnt,0        ; done all?
  792.     jle    rpak25            ; le = yes
  793.     cmp    portcount,0        ; any bytes ready to read?
  794.     je    rpak22            ; e = no, do timed read single byte
  795.     mov    cx,chrcnt        ; bytes wanted
  796.     call    prtblk            ; do block read of cx bytes to es:[bx]
  797.     mov    portcount,dx        ; available bytes
  798.     jc    rpak22            ; c = failed
  799.     add    pktcnt,cx        ; count bytes received
  800.     sub    chrcnt,cx        ; needed minus supplied = to be read
  801.     jmp    short rpak21        ; try to read more
  802.  
  803.                     ; do 1 byte timed read and check
  804. rpak22:    call    inchr            ; get a character into al. DATA
  805.     jc    rpak23            ; c = Control-C, timeout, eol
  806.     dec    chrcnt            ; count byte read
  807.     cmp    al,SOHchar        ; start of header char?
  808.     jne    rpak21            ; ne = no, read more data
  809.     jmp    short rpak24        ; yes, then go start over
  810. rpak23:    test    status,stat_eol        ; bare EOL in data part?
  811.     jz    rpak24            ; z = no, must be bad news, quit
  812.     and    status,not stat_eol    ; turn off status bit
  813.     dec    chrcnt            ; accept byte
  814.     jmp    short rpak21        ;  and carry on regardless
  815.  
  816. rpak24:    pop    cx            ; failure
  817.     xchg    debflg,cl        ; restore debugging
  818.     mov    cx,chrcnt        ; bytes remaining to be read
  819.     pop    chrcnt            ; recover length of data field
  820.     sub    chrcnt,cx        ; reduce length available to log
  821.     cmp    debflg,0        ; recording material?
  822.     je    rpak24b            ; e = no
  823.     call    rlogdata        ; log data bytes
  824. rpak24b:jmp    rpack40            ; failed
  825.  
  826. rpak25:    pop    cx
  827.     xchg    debflg,cl        ; restore debugging
  828.     pop    chrcnt            ; recover length of data field
  829.     cmp    debflg,0        ; recording material?
  830.     je    rpak26            ; e = no
  831.     call    rlogdata        ; log data field bytes
  832. rpak25a:cmp    chklength,2        ; which checksum length is in use?
  833.     ja    rpack3            ; a = Three char CRC, skip chksum
  834. rpak26:    mov    cx,chrcnt        ; length of data field
  835.     jcxz    rpack3            ; z = empty, nothing to do
  836.     mov    dx,chksum        ; linear checksum thus far
  837.     push    si
  838.     push    ds
  839.     lds    si,[si].datadr         ; point to offset of data buffer
  840.     xor    ah,ah
  841.     cld
  842. rpak27:    lodsb
  843.     add    dx,ax            ; add bytes to checksum
  844.     loop    rpak27
  845.     pop    ds
  846.     pop    si
  847.     mov    chksum,dx
  848.  
  849. rpack3:    and    chksum,0fffh    ; keep only lower 12 bits of current checksum
  850.     mov    bx,offset epilog    ; record debugging in epilog buffer
  851.     mov    ax,seg epilog
  852.     mov    es,ax
  853.     mov    word ptr es:[bx],0
  854.     call    inchr            ; start Checksum bytes
  855.     jc    rpack3b            ; failed
  856.     mov    ah,al
  857.     and    ah,7fh            ; strip high bit
  858.     cmp    ah,SOHchar        ; start of header char?
  859.     jne    rpack3a            ; ne = no
  860.     jmp    rpack0            ; yes, then go start over
  861. rpack3a:sub    al,' '            ; unchar() back to binary
  862.     mov    cx,chksum        ; current checksum
  863.     cmp    chklength,2        ; which checksum length is in use?
  864.     je    rpack5            ; e = Two character checksum
  865.     ja    rpack4            ; a = Three char CRC, else one char
  866.     shl    cx,1            ; put two highest digits of al into ah
  867.     shl    cx,1
  868.     and    ch,3            ; want just those two bits
  869.     shr    cl,1            ; put al back in place
  870.     shr    cl,1
  871.     add    cl,ch            ;add two high bits to earlier checksum
  872.     and    cl,03fh            ; chop to lower 6 bits (mod 64)
  873.     cmp    cl,al        ; computed vs received checksum byte (binary)
  874.     je    rpack3b            ; e = equal, so finish up
  875.     or    status,stat_chk        ; say checksum failure
  876. rpack3b:jmp    rpack40
  877.  
  878. rpack4:    mov    tmp,al            ; save value from packet here
  879.     push    bx            ; three character CRC
  880.     push    es
  881.     mov    bx,seg prolog
  882.     mov    es,bx
  883.     mov    bx,offset prolog+1    ; where data for CRC is, skipping SOH
  884.     mov    cx,prolog_len        ; length of prolog field
  885.     xor    dx,dx            ; initial CRC is zero
  886.     call    crcclc            ; calculate the CRC and put into CX
  887.     mov    dx,cx            ; previous CRC
  888.     mov    bx,rtemp
  889.     mov    cx,[bx].datlen        ; length of data field
  890.     les    bx,[bx].datadr        ; data field segment
  891.     call    crcclc            ; final CRC is in CX
  892.     mov    chksum,cx        ; save computed checksum
  893.     pop    es
  894.     pop    bx
  895.     mov    ah,ch            ; cx = 16 bit binary CRC of rcv'd data
  896.     and    ah,0f0h            ; manipulate it here
  897.     shr    ah,1
  898.     shr    ah,1            ; get 4 highest bits
  899.     shr    ah,1
  900.     shr    ah,1            ; shift right 4 bits
  901.     cmp    ah,tmp            ; is what we got == calculated?
  902.     je    rpack4a            ; e = yes
  903.     or    status,stat_chk        ; checksum failure
  904. rpack4a:call    inchr            ; get next character of checksum
  905.     jc    rpack40            ; c = failed
  906.     and    al,7fh            ; strip high bit
  907.     cmp    al,SOHchar        ; SOH?
  908.     jne    rpack4b            ; ne = no
  909.     jmp    rpack0            ; start over
  910. rpack4b:sub    al,' '            ; get back real value
  911.                     ; two character checksum + CRC
  912. rpack5:    mov    ch,al            ; save last char here for now
  913.     mov    ax,chksum
  914.     and    ax,0fc0h        ; get bits 11..6
  915.     mov    cl,6
  916.     shr    ax,cl            ; shift bits
  917.     cmp    al,ch            ; equal?
  918.     je    rpack5a            ; e = yes
  919.     mov    bias,1            ; try adding bias
  920.     inc    al            ; try 'B' method of +1 on bias
  921.     cmp    al,ch            ; same?
  922.     je    rpack5a            ; matched
  923.     or    status,stat_chk        ; checksum failure
  924. rpack5a:call    inchr            ; get last character of checksum
  925.     jc    rpack40            ; c = failed
  926.     and    al,7fh            ; strip high bit
  927.     cmp    al,SOHchar        ; SOH?
  928.     jne    rpack5b            ; ne = no
  929.     jmp    rpack0            ; e = yes
  930. rpack5b:sub    al,' '            ; get back real value
  931.     mov    cx,chksum
  932.     and    cl,3FH            ; get bits 0-5
  933.     add    cl,bias            ; try 'B' method of +1 on bias
  934.     cmp    al,cl            ; do the last chars match?
  935.     je    rpack40            ; e = yes
  936.     or    status,stat_chk        ; say checksum failure
  937.  
  938. rpack40:test    status,stat_tmo        ; timeout?
  939.     jz    rpack41            ; z = no
  940.     jmp    rpack60            ; nz = yes
  941. rpack41:test    status,stat_eol        ; premature eol?
  942.     jz    rpack42            ; z = no
  943.     or    status,stat_bad        ; say bad packet overall
  944.     jmp    short rpack45        ; now try for handshake
  945.  
  946. rpack42:call    inchr            ; get eol char
  947.     jnc    rpack43            ; nc = got regular character
  948.     test    status,stat_int        ; interrupted?
  949.     jnz    rpack60            ; nz = yes
  950. rpack43:and    status,not stat_tmo    ; ignore timeouts on EOL character
  951.     test    status,stat_eol        ; eol char?
  952.     jnz    rpack44            ; nz = yes, got the EOL char
  953.     and    al,7fh            ; strip high bit
  954.     cmp    al,SOHchar        ; SOH already?
  955.     jne    rpack44            ; ne = no
  956.     jmp    rpack0            ; yes, start over
  957.  
  958. rpack44:and    status,not stat_eol     ; desired eol is not an error
  959.                     ; test for line turn char
  960. rpack45:mov    bx,portval        ;   if doing handshaking
  961.     cmp    [bx].hndflg,0        ; doing half duplex handshaking?
  962.     je    rpack60            ; e = no
  963.     mov    ah,[bx].hands        ; get desired handshake char
  964.     mov    tmp,ah            ; keep handshake char here
  965.     mov    bx,seg epilog        ; where to store character
  966.     mov    es,bx
  967.     mov    bx,offset epilog
  968. rpack45b:call    inchr            ; get handshake char
  969.     jnc    rpack46            ; nc = regular character
  970.     test    status,stat_eol        ; EOL char?
  971.     jnz    rpack46            ; nz = yes
  972.     jmp    short rpack48        ; timeout or user intervention
  973. rpack46:and    status,not stat_eol    ; ignore unexpected eol status here
  974.     and    al,7fh            ; strip high bit
  975.     cmp    al,SOHchar        ; SOH already?
  976.     jne    rpack47            ; ne = no
  977.     jmp    rpack0            ; yes, start over
  978. rpack47:cmp    al,tmp            ; compare received char with handshake
  979.     jne    rpack45        ; ne = not handshake, try again til timeout
  980. rpack48:and    status,not stat_tmo    ; ignore timeouts on handshake char
  981.  
  982.                     ; Perform logging and debugging now
  983. rpack60:call    chkcon            ; check console for user interrupt
  984.     cmp    debflg,0        ; recording packets?
  985.     je    rpack66            ; e = no
  986.     mov    dx,offset crlf
  987.     call    captdol            ; end current display line
  988.     cmp    status,stat_suc        ; success?
  989.     je    rpack66            ; e = yes
  990.     mov    dx,offset msgheader    ; starting "<"
  991.     call    captdol
  992.     test    status,stat_tmo        ; timeout?
  993.     jz    rpack61            ; no
  994.     mov    dx,offset msgtmo     ; say timeout in log
  995.     call    captdol
  996.     mov    si,rtemp
  997.     mov    ah,'T'            ; return packet type in ah
  998.     mov    [si].pktype,ah        ; say 'T' type packet (timeout)
  999. rpack61:test    status,stat_chk        ; checksum bad?
  1000.     jz    rpack62            ; z = no
  1001.     mov    dx,offset msgchk
  1002.     call    captdol
  1003. rpack62:test    status,stat_ptl        ; packet too long?
  1004.     jz    rpack63            ; z = no
  1005.     mov    dx,offset msgptl
  1006.     call    captdol
  1007. rpack63:test    status,stat_int        ; user interruption?
  1008.     jz    rpack64            ; z = no
  1009.     mov    dx,offset msgint
  1010.     call    captdol
  1011. rpack64:test    status,stat_bad        ; premature EOL?
  1012.     jz    rpack65            ; z = no
  1013.     mov    dx,offset msgbad
  1014.     call    captdol
  1015. rpack65:mov    dx,offset msgtail    ; end of error cause field
  1016.     call    captdol
  1017.  
  1018. rpack66:mov    ax,pktcnt        ; number of bytes received in packet
  1019.     add    fsta.prbyte,ax        ; file total received pkt bytes
  1020.     adc    fsta.prbyte+2,0        ; propagate carry to high word
  1021.     add    fsta.prpkt,1        ; file received packets
  1022.     adc    fsta.prpkt+2,0        ;  ripple carry
  1023.     mov    si,rtemp        ; restore pkt pointer
  1024.     mov    ah,[si].pktype        ; return packet TYPE in ah
  1025.     cmp    status,stat_suc        ; successful so far?
  1026.     jne    rpack72            ; ne = no
  1027.     cmp    chkparflg,0        ; do parity checking?
  1028.     je    rpack71            ; e = no
  1029.     mov    chkparflg,0        ; do only once
  1030.     test    badpflag,80h        ; get parity error flagging bit
  1031.     jz    rpack71            ; z = no parity error
  1032.     mov    bx,portval
  1033.     mov    cl,badpflag        ; get new parity plus flagging bit
  1034.     and    cl,7fh            ; strip flagging bit
  1035.     mov    [bx].parflg,cl        ; force new parity
  1036. rpack71:clc                ; carry clear for success
  1037.     ret
  1038. rpack72:stc                ; carry set for failure
  1039.     ret                ; failure exit
  1040. RPACK    ENDP
  1041.  
  1042. ; Get packet if enough bytes for minimal pkt and SOP has been seen. Returns
  1043. ; carry set if unable, else use Rpack to deliver the packet. Call the same
  1044. ; as Rpack.
  1045. peekreply proc    far
  1046.     call    peekcom            ; get comms SOP count
  1047.     jnc    peekrp1            ; nc = have some bytes
  1048.     ret                ; return carry set for nothing to do
  1049. peekrp1:cmp    cx,6            ; enough for basic NAK?
  1050.     jb    peekrp2            ; be = no
  1051.     mov    si,offset rpacket    ; reset pointer needed at start
  1052.     jmp    rpack            ; go decode the packet
  1053. peekrp2:stc                ; say nothing to do
  1054.     ret
  1055. peekreply endp
  1056.  
  1057. ; Check Console (keyboard). Return carry setif "action" chars: cr for forced
  1058. ; timeout, Control-E for force out Error packet, Control-C for quit work now.
  1059. ; Return carry clear on Control-X and Control-Z as these are acted upon by
  1060. ; higher layers. Consume and ignore anything else.
  1061. chkcon:    call    isdev        ; is stdin a device and not a disk file?
  1062.     jnc    chkco5        ; nc = no, a disk file so do not read here
  1063.     mov    dl,0ffh
  1064.     mov    ah,dconio        ; read console
  1065.     int    dos
  1066.     jz    chkco5            ; z = nothing there
  1067.     and    al,1fh            ; make char a control code
  1068.     cmp    al,CR            ; carriage return?
  1069.     je    chkco3            ; e = yes, simulate timeout
  1070.     cmp    al,'C'-40h        ; Control-C?
  1071.     je    chkco1            ; e = yes
  1072.     cmp    al,'E'-40h        ; Control-E?
  1073.     je    chkco1            ; e = yes
  1074.     cmp    al,'X'-40h        ; Control-X?
  1075.     je    chkco4            ; e = yes
  1076.     cmp    al,'Z'-40h        ; Control-Z?
  1077.     je    chkco4        ; record it, take no immmediate action here
  1078.     cmp    al,'Q'-40h        ; Control-Q?
  1079.     je    chkco6            ; e = yes
  1080.     or    al,al            ; scan code being returned?
  1081.     jnz    chkco5            ; nz = no, ignore ascii char
  1082.     mov    ah,dconio        ; read and discard second byte
  1083.     mov    dl,0ffh
  1084.     int    dos
  1085.     jmp    short chkco5        ; else unknown, ignore
  1086. chkco1:    or    al,40h            ; make Control-C-E printable
  1087.     mov    flags.cxzflg,al        ; remember what we saw
  1088. chkco2:    or    status,stat_int        ; interrupted
  1089.     stc
  1090.     ret                ; act now
  1091. chkco3:    or    status,stat_tmo        ; CR simulates timeout
  1092.     stc
  1093.     ret                ; act now
  1094. chkco4:    or    al,40h            ; make control-X-Z printable
  1095.     mov    flags.cxzflg,al        ; put into flags
  1096.     clc                ; do not act on them here
  1097.     ret
  1098. chkco5:    cmp    flags.cxzflg,'C'    ; control-C intercepted elsewhere?
  1099.     je    chkco2            ; e = yes
  1100.     clc                ; else say no immediate action needed
  1101.     ret
  1102. chkco6:    xchg    ah,al            ; put Control-Q in AH for transmission
  1103.     call    spkout            ; send it now
  1104.     jmp    short chkco5
  1105.  
  1106. getlen    proc    near        ; compute packet length for short & long types
  1107.                 ; returns length in [si].datlen and
  1108.                 ; length type (0, 1, 3) in local byte lentyp
  1109.                 ; returns length of  data + checksum
  1110.     mov     si,rtemp
  1111.     mov    ax,[si].datlen    ; get LEN byte value
  1112.     and    ax,7fh        ; clear unused high byte and parity bit
  1113.  
  1114.     cmp    al,3        ; regular packet has 3 or larger here
  1115.     jb    getln1        ; b = long packet
  1116.     sub    [si].datlen,2    ; minus SEQ and TYPE = DATA + CHKSUM
  1117.     mov    lentyp,3    ; store assumed length type (3 = regular)
  1118.     clc            ; clear carry for success
  1119.     ret
  1120.  
  1121. getln1:    push    cx        ; counter for number of length bytes
  1122.     mov    lentyp,0    ; store assumed length type 0 (long)
  1123.     mov    cx,2        ; two base-95 digits
  1124.     or    al,al        ; is this a type 0 (long packet)?
  1125.     jz    getln2        ; z = yes, go find & check length data
  1126.     mov    lentyp,1    ; store length type (1 = extra long)
  1127.     inc    cx        ; three base 95 digits
  1128.     cmp    al,1        ; is this a type 1 (extra long packet)?
  1129.     je    getln2        ; e = yes, go find & check length data
  1130.     pop    cx
  1131.     or    status,stat_ptl    ; say packet too long (an unknown len code)
  1132.     stc            ; set carry bit to say error
  1133.     ret
  1134. getln2:                ; chk header chksum and recover binary length
  1135.     push    dx        ; save working reg
  1136.     xor    ax,ax        ; clear length accumulator, low part
  1137.     mov    [si].datlen,ax    ; clear final length too
  1138. getln3:    xor    dx,dx        ; ditto, high part
  1139.     mov    ax,[si].datlen    ; length to date
  1140.     mul    ninefive    ; multiply accumulation (in ax) by 95
  1141.     mov    [si].datlen,ax    ; save results
  1142.     push    cx
  1143.     call    inchr        ; read another serial port char into al
  1144.     pop    cx
  1145.     jc    getln4        ; c = failure
  1146.     add    chksum,ax
  1147.     sub    al,20h        ; subtract space, apply unchar()
  1148.     js    getln4        ; sign set is failure
  1149.     mov    si,rtemp
  1150.     add    [si].datlen,ax    ; add to overall length count
  1151.     loop    getln3        ; cx preset earlier for type 0 or type 1
  1152.     mov    dx,chksum    ; get running checksum
  1153.     shl    dx,1        ; get two high order bits into dh
  1154.     shl    dx,1
  1155.     and    dh,3        ; want just these two bits
  1156.     shr    dl,1        ; put low order part back
  1157.     shr    dl,1
  1158.     add    dl,dh        ; add low order byte to two high order bits
  1159.     and    dl,03fh        ; chop to lower 6 bits (mod 64)
  1160.     add    dl,20h        ; apply tochar()
  1161.     push    dx
  1162.     call    inchr        ; read another serial port char
  1163.     pop    dx
  1164.     jc    getln4        ; c = failure
  1165.     add    chksum,ax
  1166.     cmp    dl,al        ; our vs their checksum, same?
  1167.     je    getln5        ; e = checksums match, success
  1168. getln4:    or    status,stat_chk    ; checksum failure
  1169.     pop    dx        ; unsave regs (preserves flags)
  1170.     pop    cx
  1171.     stc            ; else return carry set for error
  1172.     ret
  1173. getln5:    pop    dx        ; unsave regs (preserves flags)
  1174.     pop    cx
  1175.     clc            ; clear carry (say success)
  1176.     ret
  1177. getlen    endp
  1178.  
  1179. ; Get char from serial port into al, with timeout and console check.
  1180. ; Return carry set if timeout or console char or EOL seen,
  1181. ; return carry clear and char in AL for other characters.
  1182. ; Sets status of stat_eol if EOL seen.
  1183. inchr    proc    near
  1184.     mov    timeit,0    ; reset timeout flag (do each char separately)
  1185.     push    es        ; save debug buffer pointer es:bx
  1186.     push    bx
  1187. inchr1:    call    prtchr1        ; read a serial port character
  1188.     mov    portcount,dx    ; bytes remaining in ready-to-read buffer
  1189.     jc    inchr2        ; c = nothing there
  1190.     pop    bx        ; here with char in al from port
  1191.     pop    es        ; debug buffer pointer
  1192.     mov    ah,al        ; copy char to temp place AH
  1193.     and    ah,7fh        ; strip parity bit from work copy
  1194.     and    al,parmsk    ; apply 7/8 bit parity mask
  1195.     cmp    debflg,0    ; recording material?
  1196.     je    inchr1a        ; e = no
  1197.     call    captchr        ; log char in al
  1198. inchr1a:inc    pktcnt        ; count received byte
  1199.     test    flags.remflg,dserver ; acting as a server?
  1200.     jz    inchr6        ; z = no
  1201.     cmp    ah,'C'-40h    ; Control-C from comms line?
  1202.     jne    inchr6        ; ne = no
  1203.     cmp    ah,prevchar    ; was previous char also Control-C?
  1204.     jne    inchr6        ; ne = no
  1205.     cmp    ah,SOHchar    ; could Control-C also be an SOH?
  1206.     je    inchr6        ; e = yes, do not exit
  1207.     cmp    ah,trans.reol    ; could Control-C also be an EOL?
  1208.     je    inchr6        ; e = yes
  1209.     test    denyflg,finflg    ; is FIN enabled?
  1210.     jnz    inchr6        ; nz = no, ignore server exit cmd
  1211.     mov    flags.cxzflg,'C'; set Control-C flag
  1212.     or    status,stat_int+stat_eol ; say interrupted and End of Line
  1213.     mov    al,ah        ; use non-parity version
  1214.     xor    ah,ah        ; always return with high byte clear
  1215.     mov    es:[bx],ax    ; store char and null in debugging buffer
  1216.     inc    bx
  1217.     stc            ; exit failure
  1218.     ret
  1219. inchr6:    mov    prevchar,ah    ; remember current as previous char
  1220.     cmp    ah,trans.reol    ; eol char we want?
  1221.     je    inchr7        ; e = yes, ret with carry set
  1222.     xor    ah,ah        ; always return with high byte clear
  1223.     mov    es:[bx],ax    ; store char and null in buffer
  1224.     inc    bx
  1225.     clc            ; char is in al
  1226.     ret
  1227.  
  1228. inchr7:    or    status,stat_eol    ; set status appropriately
  1229.     xor    ah,ah        ; always return with high byte clear
  1230.     mov    es:[bx],ax    ; store char and null in buffer
  1231.     inc    bx
  1232.     stc            ; set carry to say eol seen
  1233.     ret            ; and return qualified failure
  1234.     
  1235. inchr2:    mov    bx,portval
  1236.     cmp    [bx].portrdy,0    ; is port not-ready?
  1237.     jne    inchr2c        ; ne = no, port is ready, just no char
  1238.     or    status,stat_int    ; interrupted
  1239.     stc            ; return failure (interruption)
  1240.     pop    bx
  1241.     pop    es
  1242.     ret
  1243.  
  1244. inchr2c:call    chkcon        ; check console (about 250 microseconds)
  1245.     jnc    inchr2a        ; nc = nothing to interrupt us
  1246.     pop    bx        ; clean stack
  1247.     pop    es
  1248.     ret            ; return failure for interruption
  1249.  
  1250. inchr2a:cmp    flags.timflg,0    ; are timeouts turned off?
  1251.     je    inchr1        ; e = yes, just check for more input
  1252.     cmp    timeval,0    ; turned off running timeouts on receive?
  1253.     je    inchr1        ; e = yes
  1254.     cmp    trans.stime,0    ; doing time outs?
  1255.     je    inchr1        ; e = no, go check for more input
  1256.     push    cx        ; save regs
  1257.     push    dx
  1258.     cmp    timeit,0    ; have we gotten time of day for first fail?
  1259.     jne    inchr4        ; ne = yes, just compare times
  1260.     push    ax
  1261.     push    dx
  1262.     call    krto        ; get current round trip timeout value
  1263.     call    getbtime    ; get Bios time to dx:ax
  1264.     add    ax,k_rto    ; add Bios ticks of timeout interval
  1265.     adc    dx,0
  1266.     mov    word ptr rptim,ax
  1267.     mov    word ptr rptim+2,dx
  1268.     pop    dx
  1269.     pop    ax
  1270.     mov    timeit,1    ; say have tod of timeout
  1271.     jmp    short inchr4d
  1272.  
  1273. inchr4:    call    getbtime    ; get Bios time
  1274.     cmp    dx,word ptr rptim+2 ; high order word
  1275.     ja    inchr4c        ; a = we are late
  1276.     jb    inchr4d        ; b = we are early
  1277.     cmp    ax,word ptr rptim ; low order word
  1278.     ja    inchr4c        ; a = we are late
  1279.     jmp    inchr4d        ; not timed out yet
  1280.  
  1281. inchr4c:or    status,stat_tmo    ; say timeout
  1282.     pop    dx
  1283.     pop    cx
  1284.     pop    bx
  1285.     pop    es
  1286.     stc            ; set carry bit
  1287.     ret            ; failure
  1288. inchr4d:pop    dx
  1289.     pop    cx
  1290.     jmp    inchr1        ; not timed out yet
  1291. inchr    endp
  1292.  
  1293.                 ; Packet Debug display routines
  1294. rcvdeb:    test    flags.debug,logpkt ; In debug mode?
  1295.     jnz    rcvde1        ; nz = yes
  1296.     test    flags.capflg,logpkt ; log packets?
  1297.     jnz    rcvde1        ; nz = yes
  1298.     ret            ; no
  1299. rcvde1:    mov    debflg,'R'    ; say receiving
  1300.     jmp    short deb1
  1301.  
  1302. snddeb:    test    flags.debug,logpkt ; In debug mode?
  1303.     jnz    sndde1        ; nz = yes
  1304.     test    flags.capflg,logpkt ; log packets?
  1305.     jnz    sndde1        ; yes
  1306.     ret            ; no
  1307. sndde1:    mov    debflg,'S'    ; say sending
  1308.  
  1309. deb1:    push    di        ; Debug. Packet display
  1310.     test    flags.debug,logpkt    ; is debug active (vs just logging)?
  1311.     jz    deb1e        ; z = no, just logging
  1312.     cmp    fmtdsp,0    ; non-formatted display?
  1313.     je    deb1d        ; e = yes, skip extra line clearing
  1314.     cmp    debflg,'R'    ; receiving?
  1315.     je    deb1a        ; e = yes
  1316.     call    sppos1        ; spack: cursor position
  1317.     jmp    short deb1b
  1318. deb1a:    call    rppos1        ; rpack: cursor position
  1319. deb1b:    mov    cx,4        ; clear 4 lines
  1320. deb1c:    push    cx
  1321.     call    clearl1        ; clear the line
  1322.         mov    dx,offset crlf
  1323.         mov    ah,prstr    ; display
  1324.         int    dos
  1325.     pop    cx
  1326.     loop    deb1c
  1327.     cmp    debflg,'R'    ; receiving?
  1328.     je    deb1d        ; e = yes
  1329.     call    sppos1        ; reposition cursor for spack:
  1330.     jmp    short deb1e
  1331. deb1d:    call    rppos1        ; reposition cursor for rpack:
  1332. deb1e:    mov    dx,offset spmes    ; spack: message
  1333.     cmp    debflg,'R'
  1334.     jne    deb2        ; ne = sending
  1335.     mov    dx,offset rpmes    ; rpack: message
  1336. deb2:    mov    colcnt,0    ; number of columns used so far
  1337.     mov    linecnt,0    ; no lines completed yet
  1338.     call    captdol        ; record dollar terminated string in Log file
  1339.     pop    di
  1340.     ret
  1341.  
  1342.  
  1343. captdol    proc    near        ; write dollar sign terminated string in dx
  1344.                 ; to the capture file (Log file).
  1345.     push    ax        ; save regs
  1346.     push    si
  1347.     push    es
  1348.     mov    si,dx        ; point to start of string
  1349.     cld
  1350. captdo1:lodsb            ; get a byte into al
  1351.     cmp    al,'$'        ; at the end yet?
  1352.     je    captdo3        ; e = yes
  1353.     or    al,al        ; asciiz?
  1354.     jz    captdo3        ; z = yes, this is also the end
  1355.     inc    colcnt
  1356.     cmp    al,lf        ; new line?
  1357.     jne    captdo4        ; ne = no
  1358.     inc    linecnt        ; count displayed lines
  1359.     mov    colcnt,0    ; start of new line
  1360. captdo4:test    flags.debug,logpkt ; debug display active?
  1361.     jz    captdo2        ; z = no
  1362.     cmp    linecnt,4    ; four lines used on screen?
  1363.     jae    captdo2        ; ae = yes, omit more lines
  1364.     push    ax
  1365.     mov    dl,al
  1366.     mov    ah,conout
  1367.     int    dos        ; display char in dl
  1368.     pop    ax
  1369. captdo2:test    flags.capflg,logpkt ; logging active?
  1370.     jz    captdo1        ; z = no
  1371.     call    fpktcpt        ; record the char, pktcpt is in msster.asm
  1372.     jmp    short captdo1    ; repeat until dollar sign is encountered
  1373. captdo3:pop    es
  1374.     pop    si
  1375.     pop    ax
  1376.     ret
  1377. captdol    endp
  1378.  
  1379. captchr    proc    near        ; record char in AL into the Log file
  1380.     test    flags.debug,logpkt ; debug display active?
  1381.     jnz    captch1        ; nz = yes
  1382.     test    flags.capflg,logpkt ; logging active?
  1383.     jnz    captch1        ; nz = yes
  1384.     ret
  1385. captch1:push    ax
  1386.     push    es
  1387.     test    al,80h        ; high bit set?
  1388.     jz    captch2        ; z = no
  1389.     push    ax
  1390.     mov    al,'~'
  1391.     call    captworker    ; record the char
  1392.     pop    ax
  1393.     and    al,not 80h
  1394. captch2:cmp    al,DEL        ; DEL?
  1395.     je    captch2a    ; e = yes
  1396.     cmp    al,' '        ; control char?
  1397.     jae    captch4        ; ae = no
  1398. captch2a:add    al,40h        ; uncontrollify the char
  1399.     push    ax        ; save char in dl
  1400.     mov    al,5eh        ; show caret before control code
  1401.     call    captworker    ; record the char
  1402.     cmp    colcnt,70    ; exhausted line count yet?
  1403.     jb    captch3        ; b = not yet
  1404.     mov    al,CR
  1405.     call    captworker
  1406.     mov    al,LF
  1407.     call    captworker
  1408. captch3:pop    ax        ; recover char in dl
  1409. captch4:call    captworker    ; record the char
  1410.     cmp    colcnt,70
  1411.          jb    captch5        ; b = not yet
  1412.     mov    al,CR
  1413.     call    captworker
  1414.     mov    al,LF
  1415.     call    captworker
  1416. captch5:pop    es
  1417.     pop    ax
  1418.     ret
  1419. captchr    endp
  1420.  
  1421. captworker proc    near
  1422.     inc    colcnt        ; count new character
  1423.     cmp    al,LF        ; new line?
  1424.     jne    captw3        ; ne = no
  1425.     inc    linecnt        ; count displayed lines
  1426.     mov    colcnt,0    ; start of new line
  1427. captw3:    test    flags.debug,logpkt ; debug display active?
  1428.     jz    captw1        ; z = no
  1429.     cmp    linecnt,4    ; four lines used on screen?
  1430.     jae    captw1        ; ae = yes, omit more lines
  1431.     push    ax
  1432.     mov    dl,al
  1433.     mov    ah,conout
  1434.     int    dos
  1435.     pop    ax
  1436. captw1:    test    flags.capflg,logpkt ; logging active?
  1437.     jz    captw2        ; z = no
  1438.     call    fpktcpt        ; log to file
  1439. captw2:    ret
  1440. captworker endp
  1441.  
  1442. ; Log bytes received in data 
  1443. rlogdata proc    near
  1444.     push    cx
  1445.     mov    cx,chrcnt        ; bytes in data field
  1446.     jcxz    rlogda2            ; z = none
  1447.     push    si            ; log data field
  1448.     push    es
  1449.     les    si,[si].datadr         ; point to offset of data buffer
  1450.     xor    ah,ah
  1451.     cld
  1452. rlogda1:mov    al,es:[si]
  1453.     inc    si
  1454.     push    cx
  1455.     push    si
  1456.     call    captchr            ; log char in al
  1457.     pop    si
  1458.     pop    cx
  1459.     loop    rlogda1
  1460.     pop    es
  1461.     pop    si
  1462. rlogda2:pop    cx
  1463.     ret
  1464. rlogdata endp
  1465.  
  1466. parchk    proc    near            ; check parity of pkt prolog chars
  1467.     cmp    chkparflg,0        ; ok to check parity?
  1468.     jne    parchk0            ; ne = yes
  1469.     ret
  1470. parchk0:push    ax
  1471.     push    bx
  1472.     push    cx
  1473.     push    dx
  1474.     mov    chkparflg,0        ; don't check again until asked
  1475.     mov    ax,word ptr prolog    ; first two prolog chars
  1476.     or    ax,word ptr prolog+2    ; next two
  1477.     test    ax,8080h        ; parity bit set?
  1478.     jz    parchk7            ; z = no
  1479.     mov    parmsk,7fh        ; set parity mask for 7 bits
  1480.     cmp    badpflag,0        ; said bad parity once this packet?
  1481.     jne    parchk7            ; ne = yes
  1482.     mov    cx,4            ; do all four protocol characters
  1483.     xor    dx,dx            ; dl=even parity cntr, dh=odd parity
  1484.     mov    bx,offset prolog
  1485. parchk1:mov    al,[bx]            ; get a char
  1486.     inc    bx            ; point to next char
  1487.     or    al,al            ; sense parity
  1488.     jpo    parchk2            ; po = odd parity
  1489.     inc    dl            ; count even parity
  1490.     jmp    short parchk3
  1491. parchk2:inc    dh            ; count odd parity
  1492. parchk3:loop    parchk1            ; do all four chars
  1493.     cmp    dl,4            ; got four even parity chars?
  1494.     jne    parchk4            ; ne = no
  1495.     mov    badpflag,parevn+80h    ; say even parity and flagging bit
  1496.     mov    dx,offset msgbadpare    ; say using even parity
  1497.     jmp    short parchk6
  1498. parchk4:cmp    dh,4            ; got four odd parity chars?
  1499.     jne    parchk5            ; ne = no
  1500.     mov    badpflag,parodd+80h    ; say odd parity and flagging bit
  1501.     mov    dx,offset msgbadparo    ; say using odd parity
  1502.     jmp    short parchk6
  1503. parchk5:mov    badpflag,parmrk+80h    ; say mark parity and flagging bit
  1504.     mov    dx,offset msgbadparm    ; say using mark parity
  1505. parchk6:call    ermsg1
  1506.     call    captdol            ; write in log file too
  1507. parchk7:pop    dx
  1508.     pop    cx
  1509.     pop    bx
  1510.     pop    ax
  1511.     ret
  1512. parchk    endp
  1513.  
  1514. ; General packet buffer structure manipulation routines. The packet buffers
  1515. ; consist of a arrays of words, bufuse and buflist, an array of pktinfo
  1516. ; structure packet descriptors, and a malloc'd main buffer.
  1517. ; Each pktinfo member describes a packet by holding the address (seg:offset 
  1518. ; within segment data) of the data field of a packet (datadr), the length of 
  1519. ; that field in bytes (datsize), the number of bytes currently occupying that 
  1520. ; field (datlen), the packet sequence number, an ack-done flag byte, and the 
  1521. ; number of retries of the packet.
  1522. ; The data field requires a null terminator byte in packet routines rpack and
  1523. ; spack. Trans.windo buffers are constructed by procedure makebuf.
  1524. ; Bufuse is an array holding an in-use flag for each pktinfo member; 0 means
  1525. ; the member is free, otherwise a caller has allocated the member via getbuf.
  1526. ; Buflist holds the address (offset in segment 'data') of each pktinfo member,
  1527. ; for rapid list searching.
  1528. ;
  1529. ; Packet structures are constructed and initialized by procedure makebuf.
  1530. ; Other procedures below access the members in various ways. Details of
  1531. ; buffer construction should remain local to these routines.
  1532. ; Generally, SI is used to point to a pktinfo member and AL holds a packet
  1533. ; sequence number (0 - 63 binary). BX and CX are used for some status reports.
  1534. ;
  1535. ;  bufuse    buflist            pktlist (group of pktinfo members)
  1536. ;  -------    -------        -------------------------------------------
  1537. ; 0 for unused              | datadr,datlen,datsize,seqnum,ackdone,numtry |
  1538. ;            pointers to ->+ datadr,datlen,datsize,seqnum,ackdone,numtry |
  1539. ; 1 for used              | datadr,datlen,datsize,seqnum,ackdone,numtry |
  1540. ;                        etc
  1541. ;
  1542. ; Construct new buffers, cleared, by computing the amount of DOS free memory,
  1543. ; allocating as much as (window slots * desired packet length). When there
  1544. ; is not enough memory shorten the packet length to yield the desired number
  1545. ; of window slots. 
  1546. ; This is called two ways: a protocol initialization stage with one normal
  1547. ; length packet and a post negotiation stage with a stated length of packet.
  1548. ; The first state finds the maximum available memory for packet buffers as
  1549. ; well as allocating one regular packet from it. The second stage always
  1550. ; follows spar() and that routine needs the maximum memory figure.
  1551. ; Enter with trans.windo equal to the number of buffer slots and CX equal
  1552. ; to the length of each buffer (we add one byte for null terminator here).
  1553. ; Return word maxbufparas as number of paragraphs in free memory before
  1554. ; allocations are done here.
  1555. makebuf    proc    far
  1556.     push    ax
  1557.     push    bx
  1558.     push    cx
  1559.     push    dx
  1560.     push    si
  1561.     push    di
  1562.     mov    di,cx            ; save real packet length
  1563.     mov    ax,pbufseg        ; current buffer segment
  1564.     or    ax,ax            ; segment allocated already?
  1565.     jz    makebu1            ; z = no
  1566.     push    es
  1567.     mov    es,ax            ; allocated segment
  1568.     mov    ah,freemem        ; free it
  1569.     int    dos
  1570.     pop    es
  1571. makebu1:mov    bx,0ffffh        ; allocate biggest chunk of memory
  1572.     mov    ah,alloc
  1573.     int    dos            ; must fail, bx gets number paras free
  1574.     mov    maxbufparas,bx        ; report max paragraphs available
  1575.     mov    al,trans.windo        ; number of window slots
  1576.     xor    ah,ah
  1577.     or    ax,ax            ; zero?
  1578.     jnz    makebu2            ; nz = no
  1579.     inc    ax
  1580.     mov    trans.windo,al        ; zero means one slot
  1581. makebu2:add    cx,15            ; round, space for null at end of pkt
  1582.     and    cx,0fff0h        ; and truncate
  1583.     mul    cx            ; times pkt size per window slot
  1584.     mov    cx,4
  1585. makebu3:shr    dx,1            ; divide double word by 16, to paras
  1586.     rcr    ax,1
  1587.     loop    makebu3            ; ax gets paragraphs wanted
  1588.     cmp    ax,bx            ; wanted versus available
  1589.     jae    makebu4            ; ae = want more than available
  1590.     mov    bx,ax            ; set bx to qty paragraphs wanted
  1591. makebu4:mov    cx,bx            ; remember desired paragraphs
  1592.     mov    ah,alloc        ; allocate a memory block
  1593.     int    dos
  1594.     mov    pbufseg,ax        ; seg of memory area
  1595.     mov    ax,bx            ; number paragraphs allocated
  1596.     mov    cl,trans.windo        ; number of window slots
  1597.     xor    ch,ch
  1598.     mov    bufnum,cx        ; number of buffers = window slots
  1599.     cmp    cx,1            ; just one window slot?
  1600.     je    makebu5            ; e = yes, save division
  1601.     xor    dx,dx
  1602.     div    cx            ; paras per window slot to ax
  1603. makebu5:mov    dx,ax            ; keep paragraphs per buffer in dx
  1604. ;    mov    di,ax            ; paragraphs per buffer
  1605. ;    mov    cl,4
  1606. ;    shl    di,cl            ; bytes per buffer (9040 max)
  1607.     mov    cx,bufnum        ; number of buffers wanted
  1608.     mov    ax,pbufseg        ; seg where buffer starts
  1609.     mov    si,offset pktlist    ; where pktinfo group starts
  1610.     xor    bx,bx            ; index (words)
  1611. makebu6:mov    bufuse[bx],0        ; say buffer slot is not used yet
  1612.     mov    buflist[bx],si        ; pointer to pktinfo member
  1613.     mov    word ptr [si].datadr,0     ; offset of data field
  1614.     mov    word ptr [si].datadr+2,ax ; segment of data field
  1615.     mov    [si].datsize,di        ; data buffer size, bytes
  1616.     mov    [si].numtry,0        ; clear number tries for this buffer
  1617.     mov    [si].ackdone,0        ; not acked yet
  1618.     mov    [si].seqnum,0        ; a dummy sequence number
  1619.     add    si,size pktinfo        ; next pktinfo member
  1620.     add    ax,dx            ; pointer to next buffer segment
  1621.     add    bx,2            ; next buflist slot
  1622.     loop    makebu6            ; make another structure member
  1623.     mov    windused,0        ; no slots used yet
  1624.     mov    winusedmax,0        ; max slots used
  1625.     mov    word ptr cwindow,1    ; initial congestion window
  1626.     mov    ax,bufnum        ; max slots availble
  1627.     mov    ssthresh,al        ; save as congestion threshold
  1628.     clc                ; success
  1629.     pop    di
  1630.     pop    si
  1631.     pop    dx
  1632.     pop    cx
  1633.     pop    bx
  1634.     pop    ax
  1635.     ret
  1636. makebuf    endp
  1637.  
  1638. ; Allocate a buffer. Return carry clear and SI pointing at fresh pktinfo
  1639. ; structure, or if failure return carry set and all regs preserved.
  1640. getbuf    proc    far
  1641.     push    ax
  1642.     push    cx
  1643.     push    si
  1644.     xor    si,si            ; index
  1645.     mov    cx,bufnum        ; number of buffers
  1646.     jcxz    getbuf2            ; 0 means none, error
  1647.     mov    al,windused        ; window slots in use now
  1648.     cmp    al,cwindow        ; max slots allowed at this time
  1649.     jae    getbuf2            ; ae = all in use, sorry
  1650.  
  1651. getbuf1:cmp    bufuse[si],0        ; is this slot in use?
  1652.     je    getbuf3            ; e = no, grab it
  1653.     add    si,2            ; try next slot
  1654.     loop    getbuf1            ; fall through on no free buffers
  1655. getbuf2:pop    si            ; get here if all are in use
  1656.     pop    cx
  1657.     pop    ax
  1658.     stc                ; return failure, si preserved
  1659.     ret
  1660.  
  1661. getbuf3:mov    bufuse[si],1        ; mark buffer as being in use
  1662.     inc    windused        ; one more slot in use
  1663.     mov    si,buflist[si]        ; address of pktinfo member
  1664.     mov    al,pktnum        ; next sequence number to be used
  1665.     mov    [si].seqnum,al        ; use it as sequence number
  1666.     mov    [si].datlen,0        ; no data in packet
  1667.     mov    [si].numtry,0        ; clear number tries for this buffer
  1668.     mov    [si].ackdone,0        ; not acked yet
  1669.     pop    cx            ; discard originally saved si
  1670.     pop    cx
  1671.     pop    ax
  1672.     clc                ; return success, buffer ptr in si
  1673.     ret
  1674. getbuf    endp
  1675.  
  1676. ; Release all buffers (marks them as free, releases buffer memory).
  1677.  
  1678. bufclr    proc    far
  1679.     push    ax
  1680.     push    cx
  1681.     push    di
  1682.     push    es
  1683.     push    ds
  1684.     pop    es
  1685.     mov    cx,maxwind        ; max number of buffers
  1686.     xor    ax,ax
  1687.     mov    di,offset bufuse    ; buffer in-use list
  1688.     cld
  1689.     rep    stosw            ; store zeros to clear the buffers
  1690.     mov    windused,0        ; number now used (none)
  1691.     mov    ax,pbufseg        ; segment of memory
  1692.     mov    es,ax            ; es = segment affected
  1693.     mov    ah,freemem        ; free it
  1694.     int    dos
  1695.     xor    ax,ax
  1696.     mov    pbufseg,ax        ; say no buffer segment
  1697.     pop    es
  1698.     pop    di
  1699.     pop    cx
  1700.     pop    ax
  1701.     ret
  1702. bufclr    endp
  1703.  
  1704. ; Release buffer whose pktinfo pointer is in SI.
  1705. ; Return carry clear if success, or carry set if failure.
  1706. bufrel    proc    far
  1707.     push    bx
  1708.     push    cx
  1709.     mov    cx,bufnum        ; number of buffers
  1710.     xor    bx,bx
  1711. bufrel1:cmp    buflist[bx],si        ; compare addresses, match?
  1712.     je    bufrel2            ; e = yes, found it
  1713.     add    bx,2
  1714.     loop    bufrel1
  1715.     pop    cx
  1716.     pop    bx
  1717.     stc                ; no such buffer
  1718.     ret
  1719. bufrel2:mov    bufuse[bx],0        ; say buffer is no longer in use
  1720.     dec    windused        ; one less used buffer
  1721.     pop    cx
  1722.     pop    bx
  1723.     clc
  1724.     ret
  1725. bufrel    endp
  1726.  
  1727. ; Grow sliding window slot count in accordance with Van Jacobson's
  1728. ; TCP/IP congestion avoidance paper.
  1729. ; An ACK has been received for a packet in the window.
  1730. ; Inc cwindow by 1 if cwindow is below ssthresh, else inc by 1/cwindow.
  1731. ; Keep fractions of a slot in byte cwindow+1.
  1732. ; Limit cwindow to max slots in system, bufnum.
  1733. windgrow proc    far
  1734.     push    ax
  1735.     push    cx
  1736.     xor    ah,ah
  1737.     mov    al,cwindow    ; congestion window, slots
  1738.     cmp    al,ssthresh     ; above congestion avoidance threshold?
  1739.     jae    windgr1        ; ae = yes, grow slowly
  1740.     inc    cwindow        ; fast opening, add one window slot
  1741.     jmp    short windgr2
  1742. windgr1:mov    al,thirtytwo    ; congestion avoidance slow growth
  1743.     xor    ah,ah
  1744.     div    cwindow     ; 32 / cwindow, al=quo, ah=rem
  1745.     add    cwindow+1,al     ; increment qty of 32nds of slots
  1746.     mov    al,cwindow+1
  1747.     mov    cl,5        ; divide cwindow+1 by 32
  1748.     shr    al,cl        ; get whole number of window slots
  1749.     jz    windgr2        ; z = none
  1750.     inc    cwindow        ; increment cwindow
  1751.     mov    cwindow+1,0    ; clear fraction
  1752. windgr2:mov    al,cwindow    ; limit cwindow to max slots (bufnum)
  1753.     cmp    al,byte ptr bufnum ; exceeds max number of window slots?
  1754.     jbe    windgr3        ; be = no
  1755.     mov    al,byte ptr bufnum ; limit
  1756. windgr3:mov    cwindow,al    ; max window slots allowed at this time
  1757.     pop    cx
  1758.     pop    ax
  1759.     ret
  1760. windgrow endp
  1761.  
  1762. ; Shrink sliding window slots. ssthresh <- cwindow / 2, cwindow <- 1.
  1763. windshrink proc far
  1764.     push    ax
  1765.     mov    al,cwindow    ; current congestion window, slots
  1766.     shr    al,1        ; divide by two
  1767.     jnz    windsh1        ; nz = not too small
  1768.     mov    al,1        ; must have at least one slot available
  1769. windsh1:mov    ssthresh,al    ; new congestion threshold
  1770.     mov    word ptr cwindow,1 ; back to slow start
  1771.     pop    ax
  1772.     ret
  1773. windshrink endp
  1774.  
  1775. ; Returns in BX the "packet pointer" for the buffer with the same seqnum as
  1776. ; provided in AL. Returns carry set if no match found. Modifies BX.
  1777. pakptr    proc    far
  1778.     push    cx
  1779.     push    di
  1780.     mov    cx,bufnum        ; number of buffers
  1781.     xor    di,di            ; buffer index for tests
  1782. pakptr1:cmp    bufuse[di],0        ; is buffer vacant?
  1783.     je    pakptr2            ; e = yes, ignore
  1784.     mov    bx,buflist[di]        ; bx = address of pktinfo member
  1785.     cmp    al,[bx].seqnum        ; is this the desired sequence number?
  1786.     je    pakptr3            ; e = yes
  1787. pakptr2:add    di,2            ; next buffer index
  1788.     loop    pakptr1            ; do next test
  1789.     xor    bx,bx            ; say no pointer
  1790.     stc                ; set carry for failure
  1791.     pop    di
  1792.     pop    cx
  1793.     ret
  1794. pakptr3:clc                ; success, BX has buffer pointer
  1795.     pop    di
  1796.     pop    cx
  1797.     ret
  1798. pakptr    endp
  1799.  
  1800. ; Returns in AH count of packets with a given sequence number supplied in AL
  1801. ; and returns in BX the packet pointer of the last matching entry.
  1802. ; Used to detect duplicated packets.
  1803. pakdup    proc    far
  1804.     push    cx
  1805.     push    dx
  1806.     push    di
  1807.     mov    cx,bufnum        ; number of buffers
  1808.     xor    di,di            ; buffer index for tests
  1809.     xor    ah,ah            ; number of pkts with seqnum in al
  1810.     mov    dx,-1            ; a bad pointer
  1811. pakdup1:cmp    bufuse[di],0        ; is buffer vacant?
  1812.     je    pakdup2            ; e = yes, ignore
  1813.     mov    bx,buflist[di]        ; bx = address of pktinfo member
  1814.     cmp    al,[bx].seqnum        ; is this the desired sequence number?
  1815.     jne    pakdup2            ; ne = no
  1816.     mov    dx,bx            ; yes, remember last pointer
  1817.     inc    ah            ; count a found packet
  1818. pakdup2:add    di,2            ; next buffer index
  1819.     loop    pakdup1            ; do next test
  1820.     mov    bx,dx            ; return last matching member's ptr
  1821.     pop    di
  1822.     pop    dx
  1823.     pop    cx
  1824.     or    ah,ah            ; any found?
  1825.     jz    pakdup3            ; z = no
  1826.     clc                ; return success
  1827.     ret
  1828. pakdup3:stc                ; return failure
  1829.     ret
  1830. pakdup    endp
  1831.     
  1832. ; Find sequence number of first free window slot and return it in AL,
  1833. ; Return carry set and al = windlow if window is full (no free slots).
  1834. firstfree proc    far
  1835.     mov    al,windlow        ; start looking at windlow
  1836.     mov    ah,al
  1837.     add    ah,trans.windo
  1838.     and    ah,3fh            ; ah = 1+top window seq number, mod 64
  1839. firstf1:push    bx
  1840.     call    pakptr            ; buffer in use for seqnum in AL?
  1841.     pop    bx
  1842.     jc    firstf2            ; c = no, seq number in not in use
  1843.     inc    al            ; next sequence number
  1844.     and    al,3fh            ; modulo 64
  1845.     cmp    al,ah            ; done all yet?
  1846.     jne    firstf1            ; ne = no, do more
  1847.     mov    al,windlow        ; a safety measure
  1848.     stc                ; carry set to say no free slots
  1849.     ret
  1850. firstf2:clc                ; success, al has first free seqnum
  1851.     ret
  1852. firstfree endp
  1853.  
  1854. ; Check sequence number for lying in the current or previous window or
  1855. ; outside either window.
  1856. ; Enter with sequence number of received packet in [si].seqnum.
  1857. ; Returns:
  1858. ;    carry clear and cx =  0 if [si].seqnum is within the current window,
  1859. ;    carry set   and cx = -1 if [si].seqnum is inside previous window,
  1860. ;    carry set   and cx = +1 if [si].seqnum is outside any window.
  1861. chkwind    proc    far
  1862.     mov    ch,[si].seqnum        ; current packet sequence number
  1863.     mov    cl,trans.windo        ; number of window slots
  1864.     sub    ch,windlow        ; ch = distance from windlow
  1865.     jc    chkwin1            ; c = negative result
  1866.     cmp    ch,cl            ; span greater than # window slots?
  1867.     jb    chkwinz            ; b = no, in current window
  1868.     sub    ch,64            ; distance measured the other way
  1869.     neg    ch
  1870.     cmp    ch,cl            ; more than window size?
  1871.     ja    chkwinp            ; a = yes, outside any window
  1872.     jmp    short chkwinm        ; else in previous window
  1873.  
  1874.                     ; sequence number less than windlow
  1875. chkwin1:neg    ch            ; distance, positive, cl >= ch
  1876.     cmp    ch,cl            ; more than window size?
  1877.     ja    chkwin2            ; a = yes, maybe this window
  1878.     jmp    short chkwinm        ; no, in previous window
  1879.  
  1880. chkwin2:sub    ch,64            ; distance measured the other way
  1881.     neg    ch
  1882.     cmp    ch,cl            ; greater than window size?
  1883.     jb    chkwinz            ; b = no, in current window
  1884.                     ; else outside any window
  1885.  
  1886. chkwinp:mov    cx,1            ; outside any window
  1887.     stc                ; carry set for outside current window
  1888.     ret
  1889. chkwinz:xor    cx,cx            ; inside current window
  1890.     clc                ; carry clear, inside current window
  1891.     ret
  1892. chkwinm:mov    cx,-1            ; in previous window
  1893.     stc                ; carry set for outside current window
  1894.     ret
  1895. chkwind    endp
  1896.  
  1897. ; Return Bios time of day in dx:ax
  1898. getbtime proc    far
  1899.     push    es
  1900.     push    bx
  1901.     push    cx
  1902.     xor     ax,ax
  1903.         mov     es,ax
  1904. getbt1:    mov    cx,es:[biosclk+0]
  1905.     mov    dx,es:[biosclk+2]
  1906.     in    al,61h            ; pause
  1907.     in    al,61h
  1908.     mov    ax,es:[biosclk+0]
  1909.     mov    bx,es:[biosclk+2]
  1910.     cmp    ax,cx
  1911.     jne    getbt1            ; ne = time jumped
  1912.     cmp    bx,dx
  1913.     jne    getbt1            ; ne = time jumped
  1914.     mov    ax,[bp+4+0]
  1915.     cwd                ; sign extend ax to dx
  1916.         add     ax,cx
  1917.         adc     dx,bx
  1918.     pop    cx            ; end critical section
  1919.     pop    bx
  1920.     pop    es
  1921.     ret
  1922. getbtime endp
  1923.  
  1924. ; Compute Kermit round trip timeout, k_rto, in Bios clock ticks.
  1925. ; Enter with SI pointing to packet structure of sent packet
  1926. krto    proc    far
  1927.     cmp    flags.comflg,'t'    ; internal Telnet?
  1928.     je    krto5            ; e = yes, get rto from it
  1929.     push    ax
  1930.     push    dx
  1931.     call    getbtime        ; get current Bios time to dx:ax
  1932.     sub    ax,word ptr [si].sndtime ; minus time at which pkt was sent
  1933.     sbb    dx,word ptr [si].sndtime+2
  1934.     jc    krto4            ; c = negative elapsed time (midnight)
  1935.     ; assume rtt Bios ticks fits into 13 bits (in AX), 450 minutes
  1936.     shl    ax,1
  1937.     shl    ax,1
  1938.     shl    ax,1            ; rtt * 8
  1939.     sub    ax,k_sa            ; minus 8 * smoothed average rtt
  1940.     mov    dx,ax            ; dx = 8 * rtt_error
  1941.     sar    ax,1
  1942.     sar    ax,1
  1943.     sar    ax,1            ; ax = rtt_error
  1944.     add    k_sa,ax            ; k_sa += rtt_error
  1945.     or    ax,ax            ; negative?
  1946.     jge    krto1            ; ge = no
  1947.     neg    ax            ; make error positve
  1948. krto1:    mov    dx,k_sd            ; 8 * std dev
  1949.     shr    dx,1
  1950.     shr    dx,1            ; k_sd >> 2
  1951.     sub    ax,dx            ; rtt_error -= (k_sd >> 2)
  1952.     add    k_sd,ax            ; k_sd += rtt_error
  1953.     mov    ax,k_sa
  1954.     shr    ax,1
  1955.     shr    ax,1
  1956.     add    ax,k_sd
  1957.     shr    ax,1            ; k_rto = ((k_sa >> 2) + k_sd) >> 1
  1958.     cmp    ax,60 * 18 * 3        ; more than 3 * 60 seconds?
  1959.     jle    krto2            ; le = no
  1960.     mov    ax,60 * 18 * 3        ; clamp at 3 * 60 seconds
  1961. krto2:    cmp    ax,9            ; below floor of 9 Bios clock ticks
  1962.     jge    krto3            ; ge = no
  1963.     mov    ax,9            ; set floor
  1964. krto3:    mov    k_rto,ax        ; predicted round trip time out, ticks
  1965. krto4:    pop    dx
  1966.     pop    ax
  1967.     ret
  1968. krto5:    push    ax            ; Internal TCP/IP stack provides rto
  1969.     mov    ax,tcp_rto        ; Bios ticks from TCP/IP stack
  1970.     add    ax,18 * 2        ; bias up by two seconds
  1971.     mov    k_rto,ax
  1972.     pop    ax
  1973.     mov    timeit,0        ; cancel previous timeout
  1974.     ret
  1975. krto    endp
  1976. code1    ends
  1977.     end
  1978.