home *** CD-ROM | disk | FTP | other *** search
/ Stars of Shareware: Programmierung / SOURCE.mdf / programm / msdos / asm / drivers1 / ipxpkt.asm < prev    next >
Assembly Source File  |  1992-01-23  |  38KB  |  1,726 lines

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