home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / mskermit / msscom.asm < prev    next >
Assembly Source File  |  2020-01-01  |  63KB  |  1,865 lines

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