home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / extra / nyenhuis3.arc / MSSCOM.ASM < prev    next >
Assembly Source File  |  1990-01-14  |  56KB  |  1,610 lines

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