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

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