home *** CD-ROM | disk | FTP | other *** search
/ Netware Super Library / Netware Super Library.iso / drivers / nics / pktdrv7 / ipxpkt.asm < prev    next >
Encoding:
Assembly Source File  |  1990-07-27  |  35.6 KB  |  1,685 lines

  1. version    equ    2
  2.  
  3. ; Packet driver to simulate Ethernet on Novell IPX protocol.
  4. ;
  5. ; Paul Kranenburg
  6. ; Department of Computer Science
  7. ; University of Leiden
  8. ; Niels Bohrweg 1
  9. ; PO Box 9512
  10. ; 2300 RA Leiden
  11. ; The Netherlands
  12. ; e-mail: pk@cs.leidenuniv.nl
  13. ;
  14. ;
  15. ; File: ipxpkt.asm
  16. ;
  17. ;
  18. ; General description:
  19. ;
  20. ; Take destination from the Ethernet packet and feed it to IPX
  21. ; in the Event Control Block Immediate Address field.
  22. ;
  23. ; IPX packets are 576 bytes at most, 30 are needed for the IPX header
  24. ; leaving 546 bytes of user data. Another 4 bytes are used to describe
  25. ; fragments.
  26. ; If NO_OF_SND_BUFS is set to 1, this yields an MTU for this driver of 528.
  27. ; (546 - 4 - sizeof(Ether header)[=14]).
  28. ; If IPX trail is used another 32 bytes are lost (MTU = 496).
  29. ;
  30. ; If NO_OF_SND_BUFS is set to 3, the Ethernet packet is broken into at most
  31. ; 3 fragments. These are tagged with a Fragment id and shipped.
  32. ;
  33. ; On reception, fragments are kept on a linked list ordered by fragment number
  34. ; and keyed by source node address and fragment id.
  35. ; An IPX event is scheduled to allow for timeout of pending reassembly queues.
  36. ;
  37. ; If all fragments are reassembled, the client is called to provide a buffer for
  38. ; the packet.
  39. ;
  40. ; [ To save on buffer space, the driver could conceivably do with some minimum
  41. ;  number of buffers and call recv_find as soon as a fragment arrives, copy
  42. ;  the contents, and only call recv_copy when all fragments have arrived. However,
  43. ;  I don't think there is a way to notify the client in case a fragment gets lost.]
  44. ;
  45. ; In this code, the number of receive buffers (NO_OF_RCV_BUFS) has been set
  46. ; to 6 (a wild guess).
  47. ; This driver has yet to be tested in a gateway under heavy load. One probably
  48. ; needs more buffers in this case.
  49. ;
  50. ; Buffer space for the receive buffers is allocated after the "end_resident"
  51. ; label. There is a potential problem here: we start listening for packets
  52. ; using these buffers while still in the initialisation code, which is overlaid
  53. ; by the receive buffers. This is why interrupts are turned off wherever possible.
  54. ;
  55. ;
  56. ; CHANGELOG.
  57. ;
  58. ; 07/16/90
  59. ; Decoupled simulated ethernet address from IPX node address in routing tables,
  60. ; allowing for unique addresses in nets like ARCNET with only a one byte
  61. ; node address.
  62. ; Thanks to Robert Roll and Reid Sweatman of the University of Utah
  63. ; for pointing this out.
  64. ; IPXPKT accepts a command line option of the from `-n [<no_bytes>]' to specify
  65. ; the number of significant bytes in the IPX node address. If less then EADDR_LEN,
  66. ; the presented ethernet address will get supplemented with some bytes from
  67. ; the IPX node address to (hopefully) create a unique address among the nodes
  68. ; in the network involved.
  69. ; If `<no_bytes>' is omitted, the number of significant bytes will be set as the
  70. ; number of bytes left in the node address after stripping leading zeroes.
  71. ;
  72. ; 07/12/90
  73. ; Reorganize initialisation; do GET_ADDESS etc., before posting LISTEN's
  74. ;
  75. ; 05/16/90
  76. ; New socket number used when TRAIL enabled (0x6181).
  77. ; Enables co-existance of original and "TRAIL" versions of packet driver.
  78. ; You can start both an old and a new experimental driver in your gateway,
  79. ; but watch those IP subnet addresses.
  80. ;
  81. ; 05/15/90
  82. ; Corrected byte-order of IPX socket.
  83. ; Socket number NOT actually changed (== 0x6180, in dynamic range)
  84. ;
  85. ; 05/15/90
  86. ; Add dummy GET_LOCAL_TARGET call to force some traffic to IPX from a bridge.
  87. ; This will get the local net address to IPX. Define TRY_GET_LOCAL_TARGET if
  88. ; you want to use this.
  89. ;
  90. ; 05/07/90
  91. ; Add statistics gathering on various table lookups.
  92. ; Compile option STAT. Use ipxstat.c for display (derivative of version 5.0 `stat.c').
  93. ;
  94. ; 05/04/90
  95. ; Fixed case of register trashing in route code.
  96. ; Add IPX 32-byte trailer for bridge routing.
  97. ; Compile option TRAIL.
  98. ;
  99. ; 05/03/90
  100. ; Add routing table.
  101. ; Net/node addresses of incoming packets are put routing table along with
  102. ; immediate address field in ecb struct.
  103. ; Outgoing packets have the their net- and immediate address looked up
  104. ; in the table with the node address as key.
  105. ; Special case: broadcast packets are sent with packet type 0x14 through
  106. ; permanent entry in routing table.
  107. ;
  108. ; 05/03/90
  109. ; **REMOVED** 07/17/90
  110. ; Add compile option to declare receive buffer space at compile time.
  111. ; Compile option NOT_SO_SAVE; if defined, buffer space is allocated beginning at
  112. ; `end_resident' else space is reserved by compiler
  113. ;
  114. ; 05/02/90
  115. ; Merge `fill_ipxhdr' into `route'.
  116. ;
  117.  
  118. ;DEBUG            EQU    1
  119. TRY_GET_LOCAL_TARGET    EQU    1
  120. STAT            EQU    1
  121. TRAIL            EQU    1
  122. ETH_CONSTR        EQU    3
  123.  
  124.     include    defs.asm
  125.  
  126. MAX_IPX_LEN    =    576        ; Maximum packet size that can be
  127.                     ; shipped through IPX
  128. ifdef TRAIL
  129. IP_socket    =    08161h        ; Socket allocated for Blue book Ether
  130.                     ; on IPX with TRAILS (BYTE-SWAPPED)
  131. else
  132. IP_socket    =    08061h        ; Socket allocated for
  133.                     ; Blue book Ether on IPX (BYTE-SWAPPED)
  134. endif
  135.  
  136. PEP        =    4        ; Packet Exchange Packet (ipx_type)
  137. GBP        =    014h        ; Global Broadcast Packet (ipx_type)
  138. RTE_TICK    =    37        ; Interval between calls to rte_ticker,
  139.                     ; 37 is approx. 2 seconds
  140.  
  141. ipx_header    struc
  142. ipx_chksum    dw    ?        ; Checksum, network byte order
  143. ipx_len        dw    ?        ; Packet length,   "
  144. ipx_prot    db    ?        ; Transport protocol
  145. ipx_type    db    ?        ; Packet type
  146. ipx_destnet    db    4 dup(?)    ; Destination network
  147. ipx_destnode    db    6 dup(?)    ; Destination node
  148. ipx_destsock    dw    ?        ; Destination socket
  149. ipx_srcnet    db    4 dup(?)    ; Source network
  150. ipx_srcnode    db    6 dup(?)    ; Source node
  151. ipx_srcsock    dw    ?        ; Source socket
  152. ifdef TRAIL
  153. ipx_trail    db    8 * 4 dup(?)    ; IPX gateway trail
  154. endif
  155. ipx_header    ends
  156.  
  157.  
  158. frag_dscr    struc
  159. frag_addr    dd    ?        ; Fragment address
  160. frag_size    dw    ?        ; Fragment size
  161. frag_dscr    ends
  162.  
  163. ecb        struc
  164. ecb_link    dd    ?        ;
  165. ecb_esr        dd    ?        ; Event Service Routine
  166. ecb_inuse    db    ?        ; In Use field
  167. ecb_cmplt    db    ?        ; Completion Code
  168. ecb_sock    dw    ?        ; Socket Number
  169. ecb_ipxwork    db    4 dup (?)    ; IPX reserved workspace
  170. ecb_drvwork    db    12 dup (?)    ; Driver reserved workspace
  171. ecb_ia        db    6 dup (?)    ; Immediate Address
  172. ecb_fragcnt    dw    ?        ; Fragment count
  173. ;ecb_dscr    =    $        ; Start of Fragment descriptor list
  174. ecb        ends
  175.  
  176. aes_ecb        struc
  177. aes_link    dd    ?        ;
  178. aes_esr        dd    ?        ; Event Service Routine
  179. aes_inuse    db    ?        ; In Use field
  180. aes_work    db    5 dup (?)    ; Driver reserved workspace
  181. aes_ecb        ends
  182.  
  183.  
  184. ether_frag    struc
  185. ef_fragno    db    ?        ; This fragment number
  186. ef_fragtot    db    ?        ; Total number of fragments comprising the packet
  187. ef_fragid    dw    ?        ; Fragment Id
  188. ether_frag    ends
  189.  
  190. queue_entry    struc
  191. q_aes        db (size aes_ecb) dup(?); AES structure, used for reassembly timeouts
  192. q_filler    db    0
  193. q_count        db    0        ; Number of fragments currently queued here
  194. q_net        db    SIZE ipx_srcnet dup(?)
  195. q_node        db    SIZE ipx_srcnode dup(?)    ; Source node
  196. q_fragid    dw    ?            ; Fragment Id
  197. q_len        dw    ?            ; Total length of user data queued here
  198. q_ecb        dd    ?            ; Ecb pointer to fragment
  199. queue_entry    ends
  200.  
  201. u_buf        struc
  202. u_ecb        db (size ecb) dup(?)
  203. u_ipx_frag    db (size frag_dscr) dup(?)
  204. u_frag_frag    db (size frag_dscr) dup(?)
  205. u_data_frag    db (size frag_dscr) dup(?)
  206. u_ipx        db (size ipx_header) dup(?)
  207. u_ether_frag    db (size ether_frag) dup(?)
  208. ;u_data        LABEL        BYTE
  209. u_buf        ends
  210.  
  211. MAX_PAYLOAD    =    MAX_IPX_LEN - SIZE ipx_header - SIZE ether_frag
  212.  
  213. ;routing table entry
  214. rt_ent        struc
  215. rt_ether    db    EADDR_LEN dup(?)    ; Ethernet address of target: lookup key
  216. rt_net        db    SIZE ipx_srcnet dup(?)    ; Net address of target
  217. rt_node        db    SIZE ipx_srcnode dup(?)    ; Node address of target
  218. rt_gate        db    SIZE ecb_ia dup(?)    ; First hop on route to above
  219. rt_x_pkt    db    ?        ; IPX packet type to send packet with
  220. ;;rt_trail    db    ?        ; This node uses IPX trail
  221. rt_age        dw    ?        ; Usage indicator for this entry
  222. rt_ent        ends
  223.  
  224. print$        macro    string
  225. ;---------------------------------------;
  226. ;  sends $ terminated string to screen  ;
  227. ;---------------------------------------;
  228.         push    dx
  229.         mov     ah,9
  230.         mov     dx,offset &string&      ; print $ terminated string
  231.         int     21h
  232.         pop    dx
  233.         endm
  234.  
  235. ; ipx function numbers
  236. OPEN_SOCKET        =    0
  237. CLOSE_SOCKET        =    1
  238. GET_LOCAL_TARGET    =    2
  239. SEND_PACKET        =    3
  240. LISTEN            =    4
  241. SCHEDULE_EVENT        =    5
  242. CANCEL_EVENT        =    6
  243. SCHEDULE_SPECIAL_EVENT    =    7
  244. GET_NODE_ADDRESS    =    9
  245. RELINQUISH        =    0Ah
  246.  
  247. call_ipx    macro    opcode,reg1,reg2,reg3,reg4,reg5,reg6,reg7,reg8
  248.         irp N, <reg1,reg2,reg3,reg4,reg5,reg6,reg7,reg8>
  249.             ifnb <N>
  250.                 push    N
  251.             endif
  252.         endm
  253.         mov    bx, opcode
  254. ;better be save here and use Code segment explicitly
  255.         call    cs:IPXentry
  256.         irp N, <reg8,reg7,reg6,reg5,reg4,reg3,reg2,reg1>
  257.             ifnb <N>
  258.                 pop    N
  259.             endif
  260.         endm
  261.         endm
  262.  
  263. repmov        macro    count
  264.         rept    (count) / 2
  265.         movsw
  266.         endm
  267.         rept    (count) MOD 2
  268.         movsb
  269.         endm
  270.         endm
  271.  
  272. ifdef STAT
  273. statinc        macro    loc
  274.         local    a
  275. ;affects flags register (NOT carry)
  276.         inc    cs:loc
  277.         jnz    a
  278.         inc    cs:loc+2
  279.     a:
  280.         endm
  281. else
  282. statinc        macro    loc
  283.         endm
  284. endif
  285.  
  286. code    segment    word public
  287.     assume    cs:code, ds:code
  288.  
  289. IPXentry    dd    ?
  290. FragmentID    dw    ?
  291.  
  292. my_net_address    db    4 dup(?)    ; contiguous 10 byte addrss-area as IPX wants it
  293. my_node_address    db    6 dup(?)
  294.  
  295. no_bytes    dw    EADDR_LEN, 0    ; number of significant bytes in IPX node address
  296.                     ; set as command line option.
  297. init_cmplt    dw    0
  298.  
  299. dummy        db    10 dup(0ffh)    ; dummy addr/socket
  300.         dw    05104h        ; what socket to use (???)
  301.         db    6 dup(?)    ; returned address
  302.  
  303.  
  304. NO_OF_FRAGMENTS    =    3
  305. NO_OF_RCV_BUFS    =    6
  306. NO_OF_SND_BUFS    =    3
  307. NO_OF_QUEUES    =    NO_OF_RCV_BUFS        ; ????
  308. NO_OF_RTES    =    30
  309.  
  310. reass_queues    queue_entry    NO_OF_QUEUES dup(<>)
  311.  
  312. rcv_bufs    u_buf        NO_OF_RCV_BUFS dup(<>)
  313. snd_bufs    u_buf        NO_OF_SND_BUFS dup(<>)
  314.  
  315. ifdef STAT
  316. ;;        org    ($ + 1) and 0fffeh
  317. ; keep ID string an even number of bytes (including terminating zero and count)
  318.         db    "RTE_TABL", 0, NO_OF_RTES
  319. endif
  320. rte        rt_ent        NO_OF_RTES dup(<>)
  321. rte_end        dw        0
  322. rte_scache    dw        0
  323. rte_rcache    dw        0
  324. rte_aes        aes_ecb        <>
  325.  
  326. ifdef STAT
  327. ;;        org    ($ + 1) and 0fffeh
  328.         db    "IPXSTAT", 0    ; keep this string an even number of bytes
  329. queue_full    dw    2 dup(0)
  330. route_drop    dw    2 dup(0)
  331. scache_miss    dw    2 dup(0)
  332. rcache_miss    dw    2 dup(0)
  333. route_loops    dw    2 dup(0)
  334. route_lookups    dw    2 dup(0)
  335. endif
  336.  
  337. ;-------------------------------------------------------------------------------
  338. ;
  339. ; local functions
  340. ;
  341. ; A NOTE on style:
  342. ;
  343. ; the functions below seem to liberally load and reload pointers into
  344. ; a register pair involving the ds segment register.
  345. ; In fact, ds almost always contains the code segment as "assumed" above.
  346. ; Also, the distinction between pointers to ecb's and ubuf's / queue's is not made
  347. ; most of the time. This is alright as long as the ecb structures remain the first
  348. ; ones declared in u_buf and queue.
  349. ; Need to work out a consistent register usage some day...
  350. ;
  351.  
  352. find_queue    proc    near
  353.     ;
  354.     ; Find/allocate a queue-entry where an ether fragment can be stored.
  355.     ; On entry: es:di -> source node address.
  356.     ;           dx == fragment Id.
  357.     ; Out: si == 0 if no queue entry available,
  358.     ;      otherwise: (ds:)si -> allocated queue-entry.
  359.     ; Must be called with interrupts disabled.
  360.  
  361.     push    cx
  362.     push    bx
  363.     mov    cx, NO_OF_QUEUES
  364.     lea    si, reass_queues
  365.     mov    bx, 0
  366.  
  367. fq_loop:
  368.     mov    al, [si].q_count
  369.     or    al, al
  370.     jnz    fq_1
  371.     or    bx, bx        ;
  372.     jne    fq_2        ; remember first entry not in use
  373.     mov    bx, si        ;
  374.     jmp    short fq_2
  375.  
  376. fq_1:
  377.     mov    ax, cx        ; can use AX in stead of `push cx'
  378.     push    si
  379.     push    di
  380.     add    si, q_net
  381.     mov    cx, SIZE q_net + SIZE q_node
  382.     cld
  383.     repe    cmpsb
  384.     pop    di
  385.     pop    si
  386.     mov    cx, ax
  387.     jne    fq_2
  388.     cmp    dx, [si].q_fragid
  389.     jne    fq_2
  390.     jmp    short fq_x
  391.  
  392. fq_2:
  393.     add    si, SIZE queue_entry
  394.     loop    fq_loop
  395.  
  396.     mov    si, bx
  397.  
  398. ifdef STAT
  399.     or    si, si
  400.     jnz    fq_stat_1
  401.     statinc    queue_full
  402. fq_stat_1:
  403. endif
  404.  
  405. fq_x:
  406.     pop    bx
  407.     pop    cx
  408.     ret
  409. find_queue    endp
  410.  
  411. enqueue        proc    near
  412.     ; Queue an etherpacket fragment on appropriate queue
  413.     ; On entry: es:si -> received ecb.
  414.     ;           cx = length of data in this fragment
  415.     ; Out: carry set if no space available.
  416.     ;      zero flag set if packet on queue complete.
  417.     ;      ds:si -> queue_entry on which fragment was queued.
  418.  
  419.     push    si
  420.     push    es
  421.     mov    ax, 0
  422.     mov    es:[si].u_ecb.ecb_link.offs, ax        ; clear link-field
  423.     mov    es:[si].u_ecb.ecb_link.segm, ax
  424.     mov    di, si                    ; es:di -> ecb
  425.     mov    dx, es:[si].u_ether_frag.ef_fragid
  426.     push    di
  427. ;    lea    di, es:[si].u_ipx.ipx_srcnode
  428.     lea    di, es:[si].u_ipx.ipx_srcnet
  429.     call    find_queue
  430.     pop    di
  431.  
  432.     or    si, si
  433.     jnz    enq_0
  434.     add    sp, 4
  435.     stc
  436.     ret
  437.  
  438. enq_0:
  439.     mov    dl, es:[di].u_ether_frag.ef_fragno
  440.     mov    dh, es:[di].u_ether_frag.ef_fragtot
  441.     cmp    [si].q_count, 0
  442.     jne    enq_3
  443.  
  444. ;this is the first fragment we receive
  445.     call    rte_enter        ; record their route
  446.     pop    [si].q_ecb.segm
  447.     pop    [si].q_ecb.offs
  448.     mov    [si].q_len, cx
  449.     mov    [si].q_count, 1
  450.     cmp    dh, 1            ;
  451.     jne    enq_1            ; short cut if fragment count == 1.
  452.     clc
  453.     ret
  454.  
  455. ;initialise queue structure a bit more...
  456. enq_1:
  457.     mov    ax, es:[di].u_ether_frag.ef_fragid
  458.     mov    [si].q_fragid, ax
  459.  
  460. ;copy source node address
  461.     mov    bx, SIZE q_net + SIZE q_node - 1
  462.  
  463. enq_2:
  464. ;    mov    al, es:[di+bx].u_ipx.ipx_srcnode
  465.     mov    al, es:[di+bx].u_ipx.ipx_srcnet
  466.     mov    ds:[si+bx].q_net, al
  467.     sub    bx, 1
  468.     jnc    enq_2
  469.  
  470.     mov    ax, cs
  471.     mov    [si].q_aes.aes_esr.segm, ax
  472.     mov    [si].q_aes.aes_esr.offs, offset reass_timeout
  473.     mov    ax, ds
  474.     mov    es, ax
  475.     mov    ax, 2            ; two ticks to timeout
  476.     call_ipx    SCHEDULE_SPECIAL_EVENT,si,dx
  477.     cmp    dh, [si].q_count
  478.     clc
  479.     ret
  480.  
  481. ; add ecb to existing queue, keep list ordered by fragment number.
  482. enq_3:
  483.     lea    ax, [si].q_ecb
  484.     push    ax            ; put link field address on stack
  485.     push    ds
  486.     les    di, [si].q_ecb
  487.  
  488. enq_4:
  489.     mov    ax, es            ; are we at end of list?
  490.     or    ax, di
  491.     jz    enq_5
  492.     cmp    dl, es:[di].u_ether_frag.ef_fragno    ; link after this frag?
  493.     jb    enq_5
  494.     add    sp, 4
  495. ;    lea    ax, es:[di].u_ecb.ecb_link
  496. ;    push    ax
  497.     push    di                    ; push `prev'-link
  498.     push    es
  499.     les    di, es:[di].u_ecb.ecb_link        ; load `next'-link
  500.     jmp    enq_4
  501.  
  502. ; enter here with two addresses on the stack:
  503. ;    1) address of ecb to link in
  504. ;    2) address of link field after which to link
  505. ; es:di contains the "next" link.
  506.  
  507. enq_5:
  508.     mov    ax, es                    ; temp = next
  509.     mov    bx, di
  510.     pop    es                    ; get prev
  511.     pop    di
  512.     pop    es:[di].segm                ; prev->next = this one
  513.     pop    es:[di].offs
  514.     les    di, es:[di]                ; load `this one'
  515.     mov    es:[di].u_ecb.ecb_link.segm, ax        ; `this one'->next = temp
  516.     mov    es:[di].u_ecb.ecb_link.offs, bx
  517.     add    [si].q_len, cx                ; update total queued data
  518.     inc    [si].q_count                ; update fragcount
  519.     cmp    dh, [si].q_count            ; return `zero' if all there
  520.     clc
  521.     ret
  522.  
  523. enqueue        endp
  524.  
  525. dequeue        proc    near
  526.     ; Send reassembled packet to client and reschedule receive buffers.
  527.     ; On entry: ds:si -> queue.
  528.  
  529.     mov    cx, [si].q_len
  530.     les    di, [si].q_ecb
  531.     les    di, es:[di].u_data_frag.frag_addr
  532.     add    di, 2 * EADDR_LEN
  533.     push    si
  534.     call    recv_find
  535.     pop    si
  536.     mov    ax, es
  537.     or    ax, di
  538.     jz    deq_2
  539.  
  540.     mov    dh, [si].q_count
  541.     mov    cx, [si].q_len
  542.     push    si            ; save our queue address
  543.     push    ds
  544.     push    di            ; save their buffer address
  545.     push    es
  546.     push    cx
  547.     lds    si, ds:[si].q_ecb
  548.     cld
  549.  
  550. ;all set, es:di -> user buffer, ds:si -> first fragment
  551. ;??? save count and source pointer for call to recv_copy
  552.  
  553. deq_1:
  554.     mov    cx, ds:[si].u_ipx.ipx_len
  555.     xchg    cl, ch
  556.     sub    cx, (SIZE ipx_header + SIZE ether_frag)
  557.     push    si
  558.     push    ds
  559.     lds    si, ds:[si].u_data_frag.frag_addr
  560.     rep    movsb
  561.     pop    ds
  562.     pop    si
  563.     lds    si, ds:[si].u_ecb.ecb_link
  564.     dec    dh
  565.     jnz    deq_1
  566.  
  567.     pop    cx            ; recover packet length and address
  568.     pop    ds            ; for completion call
  569.     pop    si            ;
  570.     call    recv_copy
  571.  
  572.     pop    ds            ; recover queue address
  573.     pop    si            ;
  574.  
  575. deq_2:
  576.     mov    ax, ds
  577.     mov    es, ax
  578.     call_ipx    CANCEL_EVENT,si
  579.  
  580.     push    si
  581.     mov    dh, [si].q_count
  582.     les    si, ds:[si].q_ecb
  583.  
  584. deq_3:
  585.     mov    bx, es:[si].ecb_link.offs
  586.     mov    cx, es:[si].ecb_link.segm
  587.     call    listen_proc
  588.     mov    si, bx
  589.     mov    es, cx
  590. ;    les    si, es:[si].u_ecb.ecb_link
  591.     dec    dh
  592.     jnz    deq_3
  593.     pop    si
  594.     mov    [si].q_count, 0
  595.     ret
  596. dequeue        endp
  597.  
  598. reass_timeout    proc    far
  599.     ; Called by AES when reassembly timeout occurs.
  600.     ; On entry: es:si pointer to ecb.
  601.     ;
  602.  
  603.     push    ds
  604.     mov    ax, cs
  605.     mov    ds, ax
  606.     push    si
  607.     push    es
  608.     mov    dh, es:[si].q_count
  609.     les    si, es:[si].q_ecb
  610.  
  611. reass_to_3:
  612.     mov    bx, es:[si].ecb_link.offs
  613.     mov    cx, es:[si].ecb_link.segm
  614.     call    listen_proc
  615.     mov    si, bx
  616.     mov    es, cx
  617.     dec    dh
  618.     jnz    reass_to_3
  619.  
  620.     pop    es
  621.     pop    si
  622.     mov    es:[si].q_count, 0
  623.  
  624.     pop    ds
  625.     ret
  626. reass_timeout    endp
  627.  
  628. receiver    proc    far
  629.     ;
  630.     ; On entry: es:si -> ecb.
  631.     ;
  632.  
  633.     push    ds
  634.     mov    ax, cs
  635.     mov    ds, ax
  636.     mov    al, es:[si].u_ecb.ecb_cmplt
  637.     or    al, al
  638.     jnz    receiver_err
  639.  
  640.     cmp    es:[si].u_ecb.ecb_fragcnt, NO_OF_FRAGMENTS
  641.     jne    receiver_err
  642.  
  643. ;IPX receives its own broadcasts because
  644. ;source and destination sockets are the same
  645. ;if ours, ignore
  646.  
  647.     mov    ax, si
  648.     mov    di, si
  649.     add    di, ecb_ia
  650.     lea    si, my_node_address
  651.     mov    cx, SIZE my_node_address
  652.     cld
  653.     repe    cmpsb
  654.     mov    si, ax
  655.     jz    receiver_x
  656.  
  657.     mov    cx, es:[si].u_ipx.ipx_len
  658.     xchg    cl, ch
  659.     sub    cx, (SIZE ipx_header + SIZE ether_frag)
  660.     jbe    receiver_err
  661.  
  662.     call    enqueue
  663.     jc    receiver_err
  664.     jnz    rec_1
  665.     call    dequeue
  666.  
  667. rec_1:
  668.     pop    ds
  669.     cli
  670.     ret
  671.  
  672. receiver_err:
  673.     call    count_in_err
  674.  
  675. receiver_x:
  676.     call    listen_proc        ; post listen again
  677.     pop    ds
  678.     cli            ; must return with interrupts disabled, says Novell.
  679.     ret
  680. receiver    endp
  681.  
  682. listen_proc    proc    near
  683.     ;
  684.     ; Post to u_buf for reception.
  685.     ; On entry: es:si -> receive-ecb
  686.     ;
  687.  
  688.     push    bx
  689.  
  690. ;fill in ecb
  691.     mov    es:[si].u_ecb.ecb_esr.offs, offset receiver
  692.     mov    ax, cs
  693.     mov    word ptr es:[si].u_ecb.ecb_esr.segm, ax
  694.     mov    es:[si].u_ecb.ecb_sock, IP_socket
  695.     call_ipx    LISTEN,es,si,di,dx,cx
  696.  
  697.     pop    bx
  698.     ret
  699.  
  700. listen_proc    endp
  701.  
  702. rte_ticker    proc    far
  703.     ;
  704.     ; ESR service routine called by AES
  705.     ; Ages all entries in routing table
  706.     ; executes entirely with disabled interrupts
  707.     ;
  708.     ; On entry: es:si -> ecb
  709.  
  710.     push    ds
  711.     mov    ax, cs
  712.     mov    ds, ax
  713.  
  714.     mov    dx, rte_end
  715.     lea    di, rte
  716.  
  717. rtick_1:
  718.     add    di, SIZE rt_ent        ; leave broadcast entry alone
  719.     cmp    di, dx
  720.     jae    rtick_done
  721.     mov    ax, [di].rt_age
  722.     add    ax, 1
  723.     jo    rtick_1
  724.     mov    [di].rt_age, ax
  725.     jmp    rtick_1
  726.  
  727. ;re-schedule ecb
  728. rtick_done:
  729.     mov    ax, RTE_TICK
  730.     call_ipx    SCHEDULE_SPECIAL_EVENT
  731.     pop    ds
  732.     ret
  733. rte_ticker    endp
  734.  
  735. rte_enter    proc    near
  736.     ;
  737.     ; Enter route to table
  738.     ; On entry: es:di -> ecb
  739.     ;
  740.     assume    ds:nothing, es:nothing
  741.  
  742.     push    bx
  743.     push    cx
  744.     push    dx
  745.     push    si
  746.     push    ds
  747.     push    di
  748.     push    es
  749.  
  750.     les    di, es:[di].u_data_frag.frag_addr
  751.     add    di, EADDR_LEN            ; es:di -> src node address
  752.     mov    bx, rte_rcache            ; global, last succesful entry
  753.     call    rte_find            ; will set DS to code seg
  754.     jc    ert_1
  755.  
  756. ifdef STAT
  757.     cmp    rte_rcache, si
  758.     je    ert_stat_1
  759.     statinc    rcache_miss
  760. ert_stat_1:
  761. endif
  762.  
  763.     mov    rte_rcache, si
  764.     jmp    ert_x                ; we already have route to src
  765.  
  766. ert_1:
  767. ;not in table, find free entry
  768. ;    mov    ax, cs
  769. ;    mov    ds, ax                ; ds == code
  770.  
  771.     lea    dx, rte
  772.     add    dx, SIZE rte            ; dx -> beyond rte
  773.     mov    bx, rte_end            ;
  774.     add    rte_end, SIZE rt_ent        ; take the chance
  775.     cmp    bx, dx                ; check whether table full
  776.     jb    ert_2
  777.     sub    rte_end, SIZE rt_ent        ; undo guess
  778.     call    kill_route            ; will leave freed entry in bx
  779.  
  780. ert_2:
  781. ;insert addresses in ecb and ipx into routing table
  782. ; $%#@, must swap registers for string operations
  783. ;
  784.     cld
  785.     mov    ax, ds
  786.     mov    cx, es
  787.     mov    es, ax
  788.     mov    ds, cx
  789.     mov    si, di                ; ds:si -> source address
  790.     mov    di, bx                ; es:di -> rt_ent
  791.  
  792.     add    di, rt_ether
  793.  
  794.     repmov  <SIZE rt_ether>
  795.  
  796.     pop     ds                ; recover ecb from stack
  797.     pop     si                ;
  798.     push    si
  799.     push    ds
  800.  
  801.     mov    ax, si
  802.  
  803.     add    si, u_ipx + ipx_srcnet        ; record source net+node
  804.     repmov    <SIZE rt_net + SIZE rt_node>
  805.  
  806.     mov    si, ax                ; restore si to start of ecb
  807.     add    si, ecb_ia            ; record immediate address
  808.     repmov    <SIZE rt_gate>
  809.  
  810.     mov    es:[bx].rt_x_pkt, PEP        ; packet type
  811.     mov    es:[bx].rt_age, 10        ; usage count (XXX)
  812.  
  813. ert_x:
  814.     pop    es
  815.     pop    di
  816.     pop    ds
  817.     pop    si
  818.     pop    dx
  819.     pop    cx
  820.     pop    bx
  821.     ret
  822. rte_enter    endp
  823.  
  824. kill_route    proc    near
  825.     ;
  826.     ; Delete entry in route table with highest rt_age field
  827.     ; Out: bx -> route entry freed
  828.     ;
  829.  
  830.     push    cx
  831.     push    dx
  832.     push    di
  833.     lea    di, rte
  834.     add    di, SIZE rt_ent        ; leave broadcast entry alone
  835.  
  836.     mov    dx, rte_end
  837.     mov    bx, di
  838.     mov    cx, 0
  839.  
  840. krt_1:
  841.     cmp    di, dx
  842.     je    krt_done
  843.     mov    ax, [di].rt_age
  844.     cmp    ax, cx
  845.     jbe    krt_2
  846.     mov    cx, ax
  847.     mov    bx, di
  848.  
  849. krt_2:
  850.     add    di, SIZE rt_ent
  851.     jmp    krt_1
  852.  
  853. krt_done:
  854. ifdef STAT
  855.     statinc    route_drop
  856. endif
  857.     pop    di
  858.     pop    dx
  859.     pop    cx
  860.     ret
  861. kill_route    endp
  862.  
  863.  
  864. rte_find    proc    near
  865.     ;
  866.     ; Find a route for address
  867.     ; On entry: es:di -> target address we are looking for (EADDR_LEN bytes)
  868.     ;           bx -> entry to start search
  869.     ; Out: ds:si -> route table entry, or carry set if not found
  870.     ;
  871.     assume    ds:code, es:nothing
  872.  
  873.     push    bx
  874.     push    dx
  875.     mov    ax, cs        ; cs == code segment
  876.     mov    ds, ax
  877.  
  878.     mov    dx, rte_end    ; global, points to first invalid rte entry
  879.     mov    ax, bx        ; ax == stopper
  880.     cld
  881.  
  882. frt_1:
  883. ifdef STAT
  884.     statinc    route_loops
  885. endif
  886.     mov    si, bx        ;
  887.     add    si, rt_ether    ; si -> rt_ether field (= key) of current rte-entry
  888.     push    di        ; save di
  889.     mov    cx, SIZE rt_ether
  890.     repe    cmpsb
  891.     pop    di
  892.     je    frt_x        ; compare ok, report succes
  893.     add    bx, SIZE rt_ent    ; next rte-entry
  894.     cmp    bx, dx
  895.     jb    frt_2
  896.     lea    bx, rte        ; end of valid entries, wrap around
  897. frt_2:
  898.     cmp    bx, ax
  899.     jne    frt_1
  900.     stc            ; back where we started, report failure
  901.  
  902. ;found, update cache and prepare output
  903. frt_x:
  904.     mov    si, bx
  905.  
  906. ifdef STAT
  907.     statinc    route_lookups
  908. endif
  909.     pop    dx
  910.     pop    bx
  911.     ret
  912. rte_find    endp
  913.  
  914. route    proc    near
  915.     ;
  916.     ; Determine where to send the packet
  917.     ; On entry: ds:si -> user data, es:di -> ecb
  918.     ;
  919.     assume    ds:nothing, es:nothing
  920.  
  921.     push    bx
  922.     push    cx
  923.     push    ds
  924.     push    si
  925.  
  926. ;find entry in routing table, in: es:di, out: ds:si -> pointer to table entry
  927.  
  928.     push    di
  929.     push    es
  930.     mov    ax, ds            ;
  931.     mov    es, ax            ; es:di -> target address
  932.     mov    di, si            ;
  933.  
  934.     mov    bx, rte_scache        ; global, last succesful entry
  935.     call    rte_find
  936.     pop    es
  937.     pop    di
  938.     jc    route_x
  939.  
  940. ifdef STAT
  941.     cmp    rte_scache, si
  942.     je    route_stat_1
  943.     statinc    scache_miss
  944. route_stat_1:
  945. endif
  946.  
  947.     mov    rte_scache, si        ; remember this entry
  948.     mov    ax, ds:[si].rt_age    ;
  949.     sub    ax, 1            ; decrement usage
  950.     jc    route_1
  951.     mov    ds:[si].rt_age, ax
  952.  
  953. route_1:
  954.     cld
  955.     mov    bx, di            ; remember ecb in BX
  956.  
  957. ifdef TRAIL
  958. ;clear ipx trail fields
  959.     add    di, u_ipx.ipx_trail
  960.     mov    ax, 0
  961.     mov    cx, (SIZE ipx_trail) / 2
  962.     rep    stosw
  963.  
  964.     mov    di, bx
  965. endif
  966.  
  967. ;fill in packet type and destination socket
  968.     mov    al, ds:[si].rt_x_pkt
  969.     mov    es:[di].u_ipx.ipx_type, al    ;PEP
  970.     mov    es:[di].u_ipx.ipx_destsock, IP_socket
  971.  
  972. ;fill in full destination adress
  973.     mov    ax, si            ; save si
  974.     add    si, rt_net
  975.     add    di, u_ipx.ipx_destnet
  976.     repmov    <SIZE ipx_destnet + SIZE ipx_destnode>
  977.     mov    si, ax            ; restore si, di
  978.     mov    di, bx            ;
  979.  
  980. ;fill in immediate address
  981.     add    di, ecb_ia
  982.     add    si, rt_gate
  983.     repmov    <SIZE ecb_ia>
  984.     mov    di, bx            ;
  985.  
  986. route_x:
  987.     pop    si
  988.     pop    ds
  989.     pop    cx
  990.     pop    bx
  991.     ret
  992. route    endp
  993.  
  994.     public    int_no
  995. int_no    db    0,0,0,0            ;must be four bytes long for get_number.
  996.  
  997.     public    driver_class, driver_type, driver_name, driver_function, parameter_list
  998. driver_class    db    1        ;from the packet spec
  999. driver_type    db    1        ;from the packet spec
  1000. driver_name    db    'IPX',0        ;name of the driver.
  1001. driver_function    db    2
  1002. parameter_list    label    byte
  1003.     db    1    ;major rev of packet driver
  1004.     db    9    ;minor rev of packet driver
  1005.     db    14    ;length of parameter list
  1006.     db    EADDR_LEN    ;length of MAC-layer address
  1007.   if NO_OF_SND_BUFS eq 1
  1008.     dw    MAX_PAY_LOAD - 2 * EADDR_LEN - 2    ;MTU, including MAC headers
  1009.   else
  1010.     dw    GIANT    ;MTU, including MAC headers
  1011.   endif
  1012.     dw    MAX_MULTICAST * EADDR_LEN    ;buffer size of multicast addrs
  1013.     dw    0    ;(# of back-to-back MTU rcvs) - 1
  1014.     dw    0    ;(# of successive xmits) - 1
  1015. int_num    dw    0    ;Interrupt # to hook for post-EOI
  1016.             ;processing, 0 == none,
  1017.  
  1018.     public    rcv_modes
  1019. rcv_modes    dw    4        ;number of receive modes in our table.
  1020.         dw    0,0,0,rcv_mode_3
  1021.  
  1022.     public    as_send_pkt
  1023. ; The Asynchronous Transmit Packet routine.
  1024. ; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
  1025. ;   interrupts possibly enabled.
  1026. ; Exit with nc if ok, or else cy if error, dh set to error number.
  1027. ;   es:di and interrupt enable flag preserved on exit.
  1028. as_send_pkt:
  1029.     ret
  1030.  
  1031.     public    drop_pkt
  1032. ; Drop a packet from the queue.
  1033. ; Enter with es:di -> iocb.
  1034. drop_pkt:
  1035.     assume    ds:nothing
  1036.     ret
  1037.  
  1038.     public    xmit
  1039. ; Process a transmit interrupt with the least possible latency to achieve
  1040. ;   back-to-back packet transmissions.
  1041. ; May only use ax and dx.
  1042. xmit:
  1043.     assume    ds:nothing
  1044.     ret
  1045.  
  1046.  
  1047.     public    send_pkt
  1048. send_pkt:
  1049. ;enter with ds:si -> packet, cx = packet length.
  1050. ;exit with nc if ok, or else cy if error, dh set to error number.
  1051.     assume    ds:nothing
  1052.     push    es
  1053.     push    di
  1054.     mov    ax, cs
  1055.     mov    es, ax
  1056.  
  1057. ;first, compute number of fragments needed, keep in dx
  1058.     mov    dx, 0
  1059.     mov    ax, cx
  1060.  
  1061. snd_1:
  1062.     inc    dx
  1063.     sub    ax, MAX_PAYLOAD
  1064.     jnc    snd_1
  1065.  
  1066. ;can we handle this amount?
  1067.     cmp    dx, NO_OF_SND_BUFS
  1068.     jbe    snd_frags_ok
  1069.  
  1070. snd_err:
  1071.     call    count_out_err
  1072.     pop    di
  1073.     pop    es
  1074.     mov    dh, CANT_SEND
  1075.     stc
  1076.     ret
  1077.  
  1078. snd_frags_ok:
  1079.     lea    di, snd_bufs
  1080.     push    cx
  1081.     mov    cx, dx
  1082.     mov    bx, 0
  1083.     mov    al, 0
  1084.  
  1085. snd_free_chk:
  1086.     or    al, es:[di+bx].u_ecb.ecb_inuse
  1087.     add    bx, SIZE u_buf
  1088.     loop    snd_free_chk
  1089.  
  1090.     pop    cx
  1091.     or    al, al
  1092.     jnz    snd_err
  1093.  
  1094.     mov    dh, dl
  1095.     mov    dl, 1
  1096.     mov    bx, 0
  1097.     inc    FragmentID
  1098.     push    di
  1099.  
  1100. snd_next_frag:
  1101. ;
  1102. ; dh = total number of fragments to send
  1103. ; dl = current fragment
  1104. ; bx = offset into client buffer (ds:si) for this fragment
  1105. ; cx = bytes to go
  1106. ; es:di = address of current fragment's ecb
  1107. ;
  1108.  
  1109. ;determine next hop
  1110.  
  1111.     call    route        ; XXX, should be done for first fragment only!
  1112.     jnc    snd_frag1
  1113.     pop    di
  1114.     jmp    snd_err
  1115.  
  1116. snd_frag1:
  1117. ;fill in ecb
  1118.     mov    ax, 0
  1119.     mov    es:[di].u_ecb.ecb_esr.offs, ax
  1120.     mov    es:[di].u_ecb.ecb_esr.segm, ax
  1121.  
  1122.     mov    es:[di].u_ecb.ecb_sock, IP_socket
  1123.  
  1124.     mov    es:[di].u_ether_frag.ef_fragtot, dh
  1125.     mov    es:[di].u_ether_frag.ef_fragno, dl
  1126.     mov    ax, FragmentID
  1127.     mov    es:[di].u_ether_frag.ef_fragid, ax
  1128.  
  1129.     mov    ax, ds
  1130.     mov    es:[di].u_data_frag.frag_addr.segm, ax
  1131.  
  1132.     mov    ax, MAX_PAYLOAD
  1133.     sub    cx, ax
  1134.     jnc    snd_frag2
  1135.     add    ax, cx
  1136.  
  1137. snd_frag2:
  1138.     mov    es:[di].u_data_frag.frag_size, ax
  1139.     push    si
  1140.     add    si, bx
  1141.     mov    es:[di].u_data_frag.frag_addr.offs, si
  1142.     add    bx, ax
  1143.  
  1144. ;
  1145. ; es:si -> ecb to ship
  1146. ;
  1147.  
  1148.     mov    si, di
  1149.     call_ipx    SEND_PACKET,es,di,dx,cx,bx
  1150.     pop    si    ; ds:si -> client buffer once more
  1151.  
  1152.     add    di, SIZE u_buf    ; next send buffer
  1153.     inc    dl
  1154.     cmp    dl, dh
  1155.     jbe    snd_next_frag
  1156.  
  1157.     pop    di
  1158.  
  1159. ;simple timeout on sends
  1160.     mov    cx, 0ffffh
  1161.  
  1162. snd_wait:
  1163. ;wait until sends are done
  1164.     sti
  1165.     mov    bx, 0
  1166.     push    cx
  1167.     mov    ch, 0
  1168.     mov    cl, dh        ; dh still has fragment count
  1169.     mov    al, 0
  1170. snd_wait1:
  1171.     or    al, es:[di+bx].u_ecb.ecb_inuse
  1172.     add    bx, SIZE u_buf
  1173.     loop    snd_wait1
  1174.     pop    cx
  1175.  
  1176.     or    al, al
  1177.     jz    snd_done
  1178.     call_ipx    RELINQUISH,es,di,dx,cx
  1179.     loop    snd_wait
  1180.  
  1181. ;arrive here on timeout, cancel IPX sends
  1182.     mov    ch, 0
  1183.     mov    cl, dh
  1184.     mov    si, di
  1185.  
  1186. snd_cancel:
  1187.     call_ipx    CANCEL_EVENT,es,si,cx
  1188.     add    si, SIZE u_buf
  1189.     loop    snd_cancel
  1190.     jmp    snd_err
  1191.  
  1192. snd_done:
  1193. ;check completion status of send buffers
  1194.     mov    bx, 0
  1195.     mov    ch, 0
  1196.     mov    cl, dh
  1197.     mov    al, 0
  1198. snd_done1:
  1199.     or    al, es:[di+bx].u_ecb.ecb_cmplt
  1200.     add    bx, SIZE u_buf
  1201.     loop    snd_done1
  1202.  
  1203.     or    al, al
  1204.     jz    snd_ok
  1205.     jmp    snd_err
  1206.  
  1207. snd_ok:
  1208.     pop    di
  1209.     pop    es
  1210.     ret
  1211.  
  1212.     public    get_address
  1213. get_address:
  1214. ;get the address of the interface.
  1215. ;enter with es:di -> place to get the address, cx = size of address buffer.
  1216. ;exit with nc, cx = actual size of address, or cy if buffer not big enough.
  1217.     assume    ds:code
  1218.     cmp    cx, EADDR_LEN
  1219.     jnb    ga_0
  1220.  
  1221.     stc
  1222.     ret
  1223.  
  1224. ga_0:
  1225.     push    si
  1226.     cmp    init_cmplt, 1
  1227.     jnc    ga_1
  1228.     call    listen_init
  1229.     mov    init_cmplt, 1
  1230.  
  1231. ga_1:
  1232.     lea    si, my_node_address    ; first copy IPX node address
  1233.     cld
  1234.     repmov    <EADDR_LEN>
  1235.  
  1236.     mov    cx, EADDR_LEN        ; if not significant enough,
  1237.     cmp    no_bytes, 0ffffh    ; magic in no_bytes?
  1238.     jne    ga_3
  1239.     lea    bx, my_node_address    ; they don't know
  1240.     mov    dx, bx            ; figure out something for no_bytes
  1241.     add    dx, SIZE my_node_address    ; savety
  1242.     mov    no_bytes, EADDR_LEN
  1243.  
  1244. ga_2:
  1245.     cmp    word ptr [bx], 0
  1246.     jnz    ga_3
  1247.     dec    no_bytes
  1248.     inc    bx
  1249.     cmp    bx, dx
  1250.     je    ga_3
  1251.     jmp    ga_2
  1252.  
  1253. ga_3:
  1254.     sub    cx, no_bytes        ; copy some of IPX net address
  1255.     jcxz    get_addr_x
  1256.     cmp    cx, SIZE my_net_address
  1257.     jbe    ga_4
  1258.     mov    cx, SIZE my_net_address
  1259. ga_4:
  1260.     lea    si, my_net_address
  1261.     sub    di, EADDR_LEN
  1262.  
  1263. ;next are three different methods for constructing the reported ethernet address
  1264. ;from a less-than-6-bytes-long IPX node adress and the IPX net address.
  1265. if ETH_CONSTR eq 1
  1266.  
  1267.     rep    movsb
  1268.  
  1269. elseif ETH_CONSTR eq 2
  1270.  
  1271. ga_21:
  1272.     mov    bx, SIZE my_net_address - 1
  1273.     mov    al, [si+bx]
  1274.     stosb
  1275.     dec    bx
  1276.     loop    ga_21
  1277.  
  1278. elseif ETH_CONSTR eq 3
  1279.  
  1280.     push    di
  1281.     push    si
  1282.     push    es
  1283.     mov    ax, ds
  1284.     mov    es, ax
  1285.     lea    di, dummy
  1286.     repmov    <SIZE my_net_address>
  1287.     pop    es
  1288.     pop    si
  1289.     pop    di
  1290.  
  1291.     lea    si, dummy
  1292.  
  1293. ga_3_loop:
  1294.     mov    bx, 0
  1295.     mov    dx, 0
  1296.     mov    al, 0
  1297. ga_31:
  1298.     cmp    bx, SIZE my_net_address
  1299.     jz    ga_33
  1300.     mov    ah, [si+bx]
  1301.     cmp    al, ah
  1302.     jnc    ga_32
  1303.     mov    al, ah
  1304.     mov    dx, bx
  1305. ga_32:
  1306.     inc    bx
  1307.     jmp    ga_31
  1308. ga_33:
  1309.     stosb
  1310.     mov    bx, dx
  1311.     mov    word ptr [si+bx], 0
  1312.     loop    ga_3_loop
  1313.     
  1314. endif
  1315.  
  1316.  
  1317. get_addr_x:
  1318.     mov    cx, EADDR_LEN
  1319.     pop    si
  1320.     clc
  1321.     ret
  1322.  
  1323. listen_init    proc    near
  1324. ;and start listening on each receive buffer
  1325.     push    si
  1326.     push    cx
  1327.     push    es
  1328.     mov    ax, cs
  1329.     mov    es, ax
  1330.     mov    cx, NO_OF_RCV_BUFS
  1331.     lea    si, rcv_bufs
  1332. li_1:
  1333.     call    listen_proc
  1334.     add    si, SIZE u_buf
  1335.     loop    li_1
  1336.     pop    es
  1337.     pop    cx
  1338.     pop    si
  1339.     ret
  1340. listen_init    endp
  1341.  
  1342.  
  1343.     public    set_address
  1344. set_address:
  1345. ;enter with ds:si -> Ethernet address, CX = length of address.
  1346. ;exit with nc if okay, or cy, dh=error if any errors.
  1347.     assume    ds:nothing
  1348.     ret
  1349.  
  1350.  
  1351. rcv_mode_3:
  1352. ;receive mode 3 is the only one we support, so we don't have to do anything.
  1353.     ret
  1354.  
  1355.  
  1356.     public    set_multicast_list
  1357. set_multicast_list:
  1358. ;enter with es:di ->list of multicast addresses, cx = number of bytes.
  1359. ;return nc if we set all of them, or cy,dh=error if we didn't.
  1360.     mov    dh,NO_MULTICAST
  1361.     stc
  1362.     ret
  1363.  
  1364.  
  1365.     public    get_multicast_list
  1366. get_multicast_list:
  1367. ;return with nc, es:di ->list of multicast addresses, cx = number of bytes.
  1368. ;return cy, NO_ERROR if we don't remember all of the addresses ourselves.
  1369. ;return cy, NO_MULTICAST if we don't implement multicast.
  1370.     mov    dh,NO_MULTICAST
  1371.     stc
  1372.     ret
  1373.  
  1374.  
  1375.     public    reset_interface
  1376. reset_interface:
  1377. ;reset the interface.
  1378.     assume    ds:code
  1379.     ret
  1380.  
  1381.  
  1382. ;called when we want to determine what to do with a received packet.
  1383. ;enter with cx = packet length, es:di -> packet type.
  1384.     extrn    recv_find: near
  1385.  
  1386. ;called after we have copied the packet into the buffer.
  1387. ;enter with ds:si ->the packet, cx = length of the packet.
  1388.     extrn    recv_copy: near
  1389.  
  1390.     extrn    count_in_err: near
  1391.     extrn    count_out_err: near
  1392.  
  1393.     public    recv
  1394. recv:
  1395. ;called from the recv isr.  All registers have been saved, and ds=cs.
  1396. ;Upon exit, the interrupt will be acknowledged.
  1397.     assume    ds:code
  1398.     ret
  1399.  
  1400.  
  1401.     public    recv_exiting
  1402. recv_exiting:
  1403. ;called from the recv isr after interrupts have been acknowledged.
  1404. ;Only ds and ax have been saved.
  1405.     assume    ds:nothing
  1406.     ret
  1407.  
  1408.     public    terminate
  1409. terminate:
  1410. ;called when this driver should cease operation.
  1411.     assume    ds:nothing
  1412.  
  1413. ;close socket, outstanding listens should be cancelled automatically
  1414.     mov    dx, IP_socket
  1415.     call_ipx    CLOSE_SOCKET
  1416.  
  1417. ;    mov    ax, cs
  1418. ;    mov    es, ax
  1419. ;    mov    cx, NO_OF_RCV_BUFS
  1420. ;    lea    si, rcv_bufs
  1421. ;
  1422. ;terminate_1:
  1423. ;    call_ipx    CANCEL_EVENT,es,si,cx
  1424. ;    add    si, SIZE u_buf
  1425. ;    loop    terminate_1
  1426.  
  1427.     ret
  1428.  
  1429.  
  1430. ;any code after this will not be kept after initialization.
  1431. end_resident    label    byte
  1432.  
  1433.  
  1434.     public    usage_msg
  1435. usage_msg    db    "usage: ipx_pkt [-n] [-d] [-w] <packet_int_no>",CR,LF,'$'
  1436.  
  1437.     public    copyright_msg
  1438. copyright_msg    db    "Packet driver for IPX, version ",'0'+version,CR,LF
  1439.         db    "Portions Copyright 1990, P. Kranenburg",CR,LF,'$'
  1440.  
  1441. no_ipx_msg    db    "IPX not present",CR,LF, '$'
  1442. wrong_sock_msg    db    "IPX has no good socket",CR,LF, '$'
  1443. ifdef DEBUG
  1444. debugmsg1    db    "Doing a GET TARGET",CR,LF, '$'
  1445. debugmsg2    db    "Past GET TARGET",CR,LF, '$'
  1446. debugmsg3    db    "Past GET ADDRESS",CR,LF, '$'
  1447. endif
  1448.  
  1449.     extrn    set_recv_isr: near
  1450.  
  1451. ;enter with si -> argument string, di -> word to store.
  1452. ;if there is no number, don't change the number.
  1453.     extrn    get_number: near
  1454.     extrn    skip_blanks: near
  1455.  
  1456.     public    parse_args
  1457. parse_args:
  1458.  
  1459.     call    skip_blanks
  1460.     cmp    byte ptr [si], CR
  1461.     je    parse_args_x
  1462.     cmp    byte ptr [si], '-'
  1463.     jne    parse_err
  1464.     cmp    byte ptr [si+1],'n'    ; '-n'?
  1465.     je    parse_args_1
  1466.  
  1467. parse_err:
  1468.     stc                ;no, must be an error.
  1469.     ret
  1470.  
  1471. parse_args_1:
  1472.     add    si, 2
  1473.     call    skip_blanks
  1474.     cmp    byte ptr [si], CR
  1475.     jne    parse_args_2
  1476.     mov    no_bytes, 0ffffh    ;try heuristic
  1477.     jmp    parse_args_x
  1478.  
  1479. parse_args_2:
  1480.     mov    di,offset no_bytes
  1481.     call    get_number
  1482.     cmp    no_bytes, 0
  1483.     jb    parse_err
  1484.     cmp    no_bytes, EADDR_LEN
  1485.     ja    parse_err
  1486.  
  1487. parse_args_x:
  1488.     clc
  1489.     ret
  1490.  
  1491.  
  1492.     public    etopen
  1493. etopen:
  1494.  
  1495. ;first see if IPX is there
  1496.     mov    ax, 07A00h
  1497.     int    2fh
  1498.     cmp    al, 0ffh
  1499.     je    ipx_is_here
  1500.     print$    no_ipx_msg
  1501.     stc
  1502.     ret
  1503.  
  1504. ipx_is_here:
  1505.     mov    ax, es
  1506.     mov    IPXentry.offs, di
  1507.     mov    IPXentry.segm, ax
  1508.  
  1509. ;close socket first, since "head" won't notify us on termination
  1510.     mov    dx, IP_socket
  1511.     call_ipx    CLOSE_SOCKET
  1512.  
  1513. ;next open socket
  1514.     mov    al, 0ffh        ; stay open until explicitly closed
  1515.     mov    dx, IP_socket
  1516.     call_ipx    OPEN_SOCKET
  1517.     or    al, 0
  1518.     jnz    wrong_socket
  1519.     cmp    dx, IP_socket
  1520.     je    good_socket
  1521.  
  1522. ;close socket and exit
  1523. wrong_socket:
  1524.     call_ipx    CLOSE_SOCKET
  1525.     print$    wrong_sock_msg
  1526.     stc
  1527.     ret
  1528.  
  1529. good_socket:
  1530. ;init send buffer fragment list
  1531.     mov    ax, cs
  1532.     mov    es, ax
  1533.  
  1534.     mov    cx, NO_OF_SND_BUFS
  1535.     lea    si, snd_bufs
  1536.  
  1537. et_1:
  1538.     mov    es:[si].u_ecb.ecb_fragcnt, NO_OF_FRAGMENTS
  1539.  
  1540.     mov    bx, si            ; bx = offset ipx_header
  1541.     add    bx, u_ipx
  1542.     mov    es:[si].u_ipx_frag.frag_addr.offs, bx
  1543.     mov    es:[si].u_ipx_frag.frag_addr.segm, ax
  1544.     mov    es:[si].u_ipx_frag.frag_size, SIZE ipx_header
  1545.  
  1546.     mov    bx, si            ; bx = offset ether_frag
  1547.     add    bx, u_ether_frag
  1548.     mov    es:[si].u_frag_frag.frag_addr.offs, bx
  1549.     mov    es:[si].u_frag_frag.frag_addr.segm, ax
  1550.     mov    es:[si].u_frag_frag.frag_size, SIZE ether_frag
  1551. ;
  1552. ; NOTE: u_data_frag is initialised send_pkt with address of user data buffer
  1553. ;
  1554.     add    si, SIZE u_buf
  1555.     loop    et_1
  1556.  
  1557. ;initialise receive buffer ipx data fragment addresses
  1558. ;and start listening on each
  1559.     mov    cx, NO_OF_RCV_BUFS
  1560.     lea    si, rcv_bufs
  1561.     lea    di, end_resident
  1562.  
  1563. et_2:
  1564.     mov    es:[si].u_ecb.ecb_fragcnt, NO_OF_FRAGMENTS
  1565.  
  1566.     mov    bx, si
  1567.     add    bx, u_ipx
  1568.     mov    es:[si].u_ipx_frag.frag_addr.offs, bx
  1569.     mov    es:[si].u_ipx_frag.frag_addr.segm, ax
  1570.     mov    es:[si].u_ipx_frag.frag_size, SIZE ipx_header
  1571.  
  1572.     mov    bx, si
  1573.     add    bx, u_ether_frag
  1574.     mov    es:[si].u_frag_frag.frag_addr.offs, bx
  1575.     mov    es:[si].u_frag_frag.frag_addr.segm, ax
  1576.     mov    es:[si].u_frag_frag.frag_size, SIZE ether_frag
  1577.  
  1578.     mov    es:[si].u_data_frag.frag_addr.offs, di        ; di = offset data buffer
  1579.     mov    es:[si].u_data_frag.frag_addr.segm, ax
  1580.     mov    es:[si].u_data_frag.frag_size, MAX_PAYLOAD
  1581.  
  1582.     add    si, SIZE u_buf
  1583.     add    di, MAX_PAYLOAD
  1584.     loop    et_2
  1585.  
  1586. ;heuristic to force network traffic which seems to necessary before
  1587. ;the GET_NODE_ADDRESS call will work properly (boo,hiss)
  1588.  
  1589. ;get our address
  1590.     sti
  1591.     lea    si, my_net_address
  1592.     call_ipx    GET_NODE_ADDRESS,es,si
  1593.     lea    si, my_net_address
  1594.     cmp    byte ptr es:[si], 0
  1595.     jne    et_3
  1596.     cmp    byte ptr es:[si+1], 0
  1597.     jne    et_3
  1598.     cmp    byte ptr es:[si+2], 0
  1599.     jne    et_3
  1600.     cmp    byte ptr es:[si+3], 0
  1601.     jne    et_3
  1602.  
  1603. ifdef TRY_GET_LOCAL_TARGET
  1604. ifdef DEBUG
  1605.     push    es
  1606.     print$    debugmsg1
  1607.     pop    es
  1608. endif
  1609.     lea    si, dummy
  1610.     mov    di, si
  1611.     add    di, 12
  1612.     call_ipx    GET_LOCAL_TARGET,es
  1613. ifdef DEBUG
  1614.     push    es
  1615.     print$    debugmsg2
  1616.     pop    es
  1617. endif
  1618.  
  1619. ;get our address
  1620.     lea    si, my_net_address
  1621.     call_ipx    GET_NODE_ADDRESS,es,si
  1622. ifdef DEBUG
  1623.     push    es
  1624.     push    si
  1625.     print$    debugmsg3
  1626.     pop    si
  1627.     pop    es
  1628. endif
  1629. endif
  1630.  
  1631. et_3:
  1632. ;initialise routing table with the broadcast address
  1633.     cld
  1634.     lea    di, rte
  1635.     mov    rte_end, di            ; rte_end = second entry
  1636.     add    rte_end, SIZE rt_ent        ;
  1637.     mov    rte_scache, di            ; rte_cache = first (and only) entry
  1638.     mov    rte_rcache, di            ; 
  1639.     mov    es:[di].rt_age, 0
  1640.     mov    es:[di].rt_x_pkt, GBP        ; broadcast packet type
  1641. ;;    mov    es:[di].rt_trail, 1        ; uses trail
  1642.  
  1643.     add    di, rt_ether            ; broadcast address
  1644.     mov    al, 0ffh
  1645.     mov    cx, SIZE rt_ether
  1646.     rep    stosb
  1647.  
  1648.     push    ds
  1649.     mov    ax, cs
  1650.     mov    ds, ax
  1651.     mov    cx, SIZE rt_net
  1652.     rep    movsb                ; net field set from my_net_address
  1653.     pop    ds
  1654.  
  1655.     mov    al, 0ffh
  1656.     mov    cx, SIZE rt_node + SIZE rt_gate
  1657.     rep    stosb                ; all FF's
  1658.  
  1659. ;schedule periodic aging of route table entries
  1660.     lea    si, rte_aes
  1661.     mov    ax, cs
  1662.     mov    es:[si].aes_esr.segm, ax
  1663.     mov    es:[si].aes_esr.offs, offset rte_ticker
  1664.     mov    ax, RTE_TICK
  1665.     call_ipx    SCHEDULE_SPECIAL_EVENT, es
  1666.  
  1667.  
  1668. ;if all is okay,
  1669.     mov    dx,offset end_resident
  1670.     add    dx, NO_OF_RCV_BUFS * MAX_PAYLOAD
  1671.     clc
  1672.     ret
  1673.  
  1674. ;if we got an error,
  1675.     stc
  1676.     ret
  1677.  
  1678.     public    print_parameters
  1679. print_parameters:
  1680.     ret
  1681.  
  1682. code    ends
  1683.  
  1684.     end
  1685.