home *** CD-ROM | disk | FTP | other *** search
/ Netware Super Library / Netware Super Library.iso / drivers / nics / pktdrv7 / wd8003e.asm < prev    next >
Encoding:
Assembly Source File  |  1990-08-22  |  58.8 KB  |  1,865 lines

  1. version    equ    6
  2.  
  3.     include    defs.asm
  4.  
  5. ;   PC/FTP Packet Driver source, conforming to version 1.05 of the spec
  6. ;   Updated to version 1.08 Feb. 17, 1989 by Russell Nelson.
  7. ;   Robert C Clements, K1BC,  August 19, 1988
  8. ;   Portions (C) Copyright 1988 Robert C Clements
  9.  
  10. ;   Version 3 updated by Jan Engvald LDC to handle WD8003ET/A (micro channel
  11. ;   card) and to utilize all 32 kbyte memory on the WD8003EBT card.
  12.  
  13. ;   Version 10 updated by Drew D. Perkins at Carnegie Mellon University to
  14. ;   handle WD8013EBT 16-bit card, to implement a transmit ring buffer, and
  15. ;   to implement the High Performance Option.
  16.  
  17. ;   This program is free software; you can redistribute it and/or modify
  18. ;   it under the terms of the GNU General Public License as published by
  19. ;   the Free Software Foundation, version 1.
  20. ;
  21. ;   This program is distributed in the hope that it will be useful,
  22. ;   but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. ;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  24. ;   GNU General Public License for more details.
  25. ;
  26. ;   You should have received a copy of the GNU General Public License
  27. ;   along with this program; if not, write to the Free Software
  28. ;   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  29.  
  30. code    segment    word public
  31.     assume    cs:code, ds:code
  32.  
  33. ; Stuff specific to the Western Digital WD003E Ethernet controller board
  34. ; C version by Bob Clements, K1BC, May 1988 for the KA9Q TCP/IP package
  35. ; Symbol prefix "EN" is for Ethernet, Western-digital card
  36.  
  37. ; The EN registers - First, the board registers
  38.  
  39. EN_CMD        equ    000h    ; Board's command register
  40. EN_REG1        equ    001h    ; 8013 bus size register
  41. EN_REG5        equ    005h    ; New command register (REGISTER 5)
  42. EN_SAPROM    equ    008h    ; Window on station addr prom
  43. EN_REGE        equ    00eh    ; Board Id (code) byte
  44.  
  45. ; The EN registers - Next, the DS8390 chip registers
  46. ; There are two (really 3) pages of registers in the chip. You select
  47. ; which page you want, then address them at offsets 10-1F from base.
  48. ; The chip command register (EN_CCMD) appears in both pages.
  49.  
  50. EN_CCMD        equ    010h    ; Chip's command register
  51.  
  52. ; Page 0
  53.  
  54. EN0_STARTPG    equ    011h    ; Starting page of ring bfr
  55. EN0_STOPPG    equ    012h    ; Ending page +1 of ring bfr
  56. EN0_BOUNDARY    equ    013h    ; Boundary page of ring bfr
  57. EN0_TSR        equ    014h    ; Transmit status reg
  58. EN0_TPSR    equ    014h    ; Transmit starting page
  59. EN0_TCNTLO    equ    015h    ; Low  byte of tx byte count
  60. EN0_TCNTHI    equ    016h    ; High byte of tx byte count
  61. EN0_ISR        equ    017h    ; Interrupt status reg
  62. EN0_RCNTLO    equ    01ah    ; Remote byte count reg
  63. EN0_RCNTHI    equ    01bh    ; Remote byte count reg
  64. EN0_RXCR    equ    01ch    ; RX control reg
  65. EN0_TXCR    equ    01dh    ; TX control reg
  66. EN0_COUNTER0    equ    01dh    ; Rcv alignment error counter
  67. EN0_DCFG    equ    01eh    ; Data configuration reg
  68. EN0_COUNTER1    equ    01eh    ; Rcv CRC error counter
  69. EN0_IMR        equ    01fh    ; Interrupt mask reg
  70. EN0_COUNTER2    equ    01fh    ; Rcv missed frame error counter
  71.  
  72. ; Page 1
  73.  
  74. EN1_PHYS    equ    011h    ; This board's physical enet addr
  75. EN1_CURPAG    equ    017h    ; Current memory page
  76. EN1_MULT    equ    018h    ; Desired multicast addr
  77.  
  78.  
  79. ; Board commands in EN_CMD
  80. EN_RESET    equ    080h    ; Reset the board
  81. EN_MEMEN    equ    040h    ; Enable the shared memory
  82. EN_MEM_MASK    equ    03fh    ; B18-B13 of address of the shared memory
  83.  
  84. ; Bits in REG1
  85. ENR1_BUS16BIT    equ    001h    ; Bus is 16 bits
  86.  
  87. ; Commands for REG5 register
  88. ENR5_MEM16EN    equ    080h    ; Enable 16 bit memory access from bus (8013)
  89. ENR5_LAN16EN    equ    040h    ; Enable 16 bit memory access from chip (8013)
  90. ENR5_MEM_MASK    equ    01fh    ; B23-B19 of address of the memory (8013)
  91. ENR5_LA19    equ    001h    ; B19 of address of the memory (8013)
  92. ENR5_EIL    equ    004h    ; Enable 8390 interrupts to bus (microchannel)
  93.  
  94. ; Bits in the REGE register
  95. ENRE_MICROCHANEL equ    080h    ; Microchannel bus (vs. PC/AT)
  96. ENRE_LARGERAM    equ    040h    ; Large RAM
  97. ENRE_SOFTCONFIG    equ    020h    ; Soft config
  98. ENRE_REVMASK    equ    01eh    ; Revision mask
  99. ENRE_ETHERNET    equ    001h    ; Ethernet (vs. Starlan)
  100.  
  101. ; Chip commands in EN_CCMD
  102. ENC_STOP    equ    001h    ; Stop the chip
  103. ENC_START    equ    002h    ; Start the chip
  104. ENC_TRANS    equ    004h    ; Transmit a frame
  105. ENC_NODMA    equ    020h    ; No remote DMA used on this card
  106. ENC_PAGE0    equ    000h    ; Select page 0 of chip registers
  107. ENC_PAGE1    equ    040h    ; Select page 1 of chip registers
  108.  
  109. ; Commands for RX control reg
  110. ENRXCR_MON    equ    020h    ; Monitor mode
  111. ENRXCR_RUNT    equ    002h    ; accept runt packets
  112. ENRXCR_BCST    equ    004h    ; Accept broadcasts
  113. ENRXCR_MULTI    equ    008h    ; Accept multicasts
  114. ENRXCR_PROMP    equ    010h    ; physical promiscuous mode
  115.  
  116. ; Commands for TX control reg
  117. ENTXCR_LOOP    equ    002h    ; Set loopback mode
  118.  
  119. ; Bits in EN0_DCFG - Data config register
  120. ENDCFG_BM8    equ    048h    ; Set burst mode, 8 deep FIFO
  121. ENDCFG_WTS    equ    1    ; Word Transfer Select
  122.  
  123. ; Bits in EN0_ISR - Interrupt status register
  124. ENISR_RX    equ    001h    ; Receiver, no error
  125. ENISR_TX    equ    002h    ; Transmitter, no error
  126. ENISR_RX_ERR    equ    004h    ; Receiver, with error
  127. ENISR_TX_ERR    equ    008h    ; Transmitter, with error
  128. ENISR_OVER    equ    010h    ; Receiver overwrote the ring
  129. ENISR_COUNTERS    equ    020h    ; Counters need emptying
  130. ENISR_RESET    equ    080h    ; Reset completed
  131. ENISR_ALL    equ    01fh    ; Interrupts we will enable
  132.  
  133. ; Bits in received packet status byte and EN0_RSR
  134. ENPS_RXOK    equ    001h    ; Received a good packet
  135.  
  136. ; Bits in TX status reg
  137.  
  138. ENTSR_COLL    equ    004h    ; Collided at least once
  139. ENTSR_COLL16    equ    008h    ; Collided 16 times and was dropped
  140. ENTSR_FU    equ    020h    ; TX FIFO Underrun
  141.  
  142. ; Shared memory management parameters
  143.  
  144. ; The transmit process now implements a transmit ring buffer.  The 8390
  145. ; chip divides transmit buffer memory into 256 byte pages.  On a card
  146. ; with only 8 Kbytes of total buffer memory, we allocate 6 pages for
  147. ; transmit--enough for one 1514 byte packet--leaving 6.5 Kbytes for
  148. ; packet reception.  On cards with more memory, we allocate 18 pages for
  149. ; transmit--enough for three 1514 byte packets--leaving 11.5 Kbytes (on
  150. ; a 16 Kbyte card) or 27.5 Kbytes (on a 32 Kbyte card) for packet
  151. ; reception.
  152.  
  153. ; The ring buffer code views the allocated transmit buffer as a ring
  154. ; with one small kink thrown in.  Unfortunately, the 8390 doesn't
  155. ; actually implement a ring itself for transmit.  You can only tell it
  156. ; the starting page of a packet and the length of the packet.  The
  157. ; packet has to be entirely contiguous in the buffer.  Therefore, our
  158. ; ring has to prematurely wrap from the end back to the beginning
  159. ; whenever normal placement of a packet would cause it to straddle the
  160. ; transmit/receive buffer boundary.  When the ring wraps, the remaining
  161. ; space at the end of the buffer is wasted.
  162.  
  163. ; The ring buffer code uses four variables: sm_tcur, sm_tboundary,
  164. ; sm_rstart_pg and sm_tlen. These first two variables parallel the
  165. ; curpage and boundary page registers that the chip uses for the receive
  166. ; buffer.  sm_tcur always indicates the next place that a packet will be
  167. ; added to the ring buffer (the tail of the queue).  sm_tboundary
  168. ; indicates the next place a packet will be removed from ring buffer
  169. ; (the head of the queue).  When the ring buffer is completely empty
  170. ; completely full, sm_tcur equals sm_tboundary.  sm_rstart_pg indicates
  171. ; the start page of the receive ring buffer, and hence the stop page of
  172. ; the transmit ring buffer.  The transmit ring buffer always starts at
  173. ; page zero.  sm_tlen is an array of packet lengths which shadows the
  174. ; actual ring buffer; there is one element in the array for each buffer
  175. ; page.  Each element of the array stores the length of a packet which
  176. ; starts on the corresponding page, or zero if there is no packet
  177. ; starting at that page.  A zero is also stored at the page indicated by
  178. ; sm_tcur except when the ring buffer is completely full, in which case
  179. ; sm_tcur equals sm_tboundary and sm_tlen[sm_tcur] is not zero.
  180.  
  181. ; The transmit process also implements a very low latency transmit
  182. ; interrupt scheme which uses three other important variables: sm_tpsr,
  183. ; sm_tcnt, and sm_tnum.  These variables contain the pre-calculated
  184. ; values that the interrupt handler should hand the chip in order to
  185. ; initiate the next transmission as soon as possible.  sm_tpsr indicates
  186. ; the starting page of the next packet that the transmit interrupt code
  187. ; should initiate transmission of.  sm_tcnt indicates its length.
  188. ; sm_tnum always indicates the number of packets in the ring buffer
  189. ; which are completely ready to be transmitted, not how many packets are
  190. ; allocated in the ring buffer.  That is, sm_tnum is not incremented
  191. ; until the packet has been copied into the ring buffer, potentially a
  192. ; long time after space for the packet has been allocated.  sm_tnum is
  193. ; not decremented until the packet has actually been transmitted.
  194. ; Therefore, at interrupt time sm_tnum will have to be 2 or greater to
  195. ; indicate that there is actually another packet waiting to go.
  196.  
  197. ; Since sm_tpsr at interrupt time always indicates the next page to be
  198. ; transmitted, it also contains the value that sm_tboundary should be updated
  199. ; to in order to free the associated buffer space.  This fact is used to avoid
  200. ; recomputation of sm_tboundary from the normal ring buffer data structure
  201. ; (specifically sm_tboundary and sm_tlen).  Unfortunately, using this scheme
  202. ; also added alot of hair (as you will see) to the code to make sure that
  203. ; sm_tpsr and sm_tboundary always tracked each other appropriately in all
  204. ; boundary conditions.  If I was going to do this again, I would probably
  205. ; reevaluate this design decision and do things a bit differently (probably
  206. ; I would use another array to indicate the next sm_tboundary value).
  207.  
  208.     public    sm_tcur, sm_tboundary, sm_tlen, sm_tnum, sm_tcnt, sm_tpsr
  209. SM_TSTART_PG    equ    0    ; First page of TX buffer
  210. SM_TMAX_PG    equ    18    ; 3 MTU-sized packets
  211. sm_tcur        dw    0    ; Offset of current page of TX buffer
  212. sm_tboundary    dw    0    ; Offset of boundary page of TX buffer
  213. sm_tlen        dw    SM_TMAX_PG dup (0) ; Length of packets in TX buffer
  214. sm_tnum        dw    0    ; Number of packets in TX buffer
  215. sm_tcnt        dw    0    ; Length of next packet in TX buffer
  216. sm_tpsr        db    0    ; Starting page of next packet in TX buffer
  217. sm_rstart_pg    db    6    ; Starting page of ring
  218. sm_rstop_pg    db    32    ; Last page +1 of ring
  219.  
  220. xmit_isr    db    0    ; ISR shared between xmit and recv
  221. xmit_tsr    db    0    ; TSR shared between xmit and recv
  222.  
  223. board_features    db    0    ; Board features
  224. BF_EIL        equ    1
  225. BF_WTS        equ    2    ; 16-bit board, enable Word Transfer
  226. BF_MEM16EN    equ    4    ; 16-bit board, enable 16-bit memory
  227. BF_16K        equ    8    ; Board has 16 KB or shared memory
  228.  
  229. ; Description of header of each packet in receive area of shared memory
  230.  
  231. EN_RBUF_STAT    equ    0    ; Received frame status
  232. EN_RBUF_NXT_PG    equ    1    ; Page after this frame
  233. EN_RBUF_SIZE    equ    2    ; Length of this frame (word access)
  234. EN_RBUF_SIZE_LO    equ    2    ; Length of this frame
  235. EN_RBUF_SIZE_HI    equ    3    ; Length of this frame
  236. EN_RBUF_NHDR    equ    4    ; Length of above header area
  237.  
  238. ; End of WD8003E parameter definitions
  239.  
  240. ; The following three values may be overridden from the command line.
  241. ; If they are omitted from the command line, these defaults are used.
  242.  
  243.     public    int_no, io_addr, mem_base
  244. int_no        db    3,0,0,0        ; Interrupt level
  245. io_addr        dw    0280h,0        ; I/O address for card (jumpers)
  246. mem_base    dw    0d000h,0    ; Shared memory addr (software)
  247.  
  248.     public    driver_class, driver_type, driver_name, driver_function, parameter_list
  249. driver_class    db    1        ;from the packet spec
  250. driver_type    db    14        ;from the packet spec
  251. driver_name    db    'WD8003E',0    ;name of the driver.
  252. driver_function    db    6        ;basic, high-performance, extended
  253. parameter_list    label    byte
  254.         db    1    ;major rev of packet driver
  255.         db    10    ;minor rev of packet driver
  256.         db    14    ;length of parameter list
  257.         db    EADDR_LEN    ;length of MAC-layer address
  258.         dw    GIANT    ;MTU, including MAC headers
  259.         dw    MAX_MULTICAST * EADDR_LEN ;buffer size of mcast addrs
  260. rcv_bufs    dw    4    ;(# of back-to-back MTU rcvs) - 1
  261. xmt_bufs    dw    0    ;(# of successive xmits) - 1
  262. int_num        dw    0    ;Interrupt # to hook for post-EOI
  263.             ;processing, 0 == none,
  264.  
  265. mcast_list_bits db      0,0,0,0,0,0,0,0 ;Bit mask from last set_multicast_list
  266. mcast_all_flag  db      0               ;Non-zero if hware should have all
  267.                     ; ones in mask rather than this list.
  268.  
  269.     public    rcv_modes
  270. rcv_modes    dw    7        ;number of receive modes in our table.
  271.         dw    0        ;there is no mode 1.
  272.         dw    rcv_mode_1
  273.         dw    rcv_mode_2
  274.         dw    rcv_mode_3
  275.         dw    rcv_mode_4
  276.         dw    rcv_mode_5
  277.         dw    rcv_mode_6
  278. rxcr_bits       db      ENRXCR_BCST     ; Default to ours plus multicast
  279.  
  280.     extrn    sys_features: byte
  281.  
  282.     extrn    send_head: dword    ; head of transmit queue
  283.     extrn    send_tail: dword    ; tail of transmit queue
  284.  
  285.     extrn    send_queue: near
  286. ; Called when we want to queue an iocb.
  287. ; Enter with es:di -> iocb.
  288.  
  289.     extrn    send_dequeue: near
  290. ; Called to dequeue an iocb and possibly call its upcall.
  291. ; Enter with interrupts disabled.
  292. ; Destroys ds:si.
  293.  
  294.  
  295. ifdef    debug            ; Include a very useful logging mechanism.  
  296.  
  297. ; The log entry structure.  Log entries include useful data such as
  298. ; a type (each place a log entry is made uses a different type), various
  299. ; chip status, ring buffer status, log entry dependent data, and optionally
  300. ; 8259 interrupt controller status.
  301. logentry    struc
  302. le_type        db    0    ; Log entry type
  303. le_ccmd        db    ?    ; Value of CCMD register
  304. le_isr        db    ?    ; Value of ISR register
  305. le_tsr        db    ?    ; Value of TSR register
  306. le_tcur        dw    ?    ; Value of sm_tcur
  307. le_tboundary    dw    ?    ; Value of sm_tboundary
  308. le_tnum        dw    ?    ; Value of sm_tnum
  309. le_dw        dw    ?    ; Log type specific dw data
  310. ifndef    mkle8259        ; Log 8259 status?
  311. le_dd        dd    ?    ; Log type specific dd data
  312. else
  313. le_irr1        db    ?    ; Value of 8259-1 IRR register
  314. le_isr1        db    ?    ; Value of 8259-1 ISR register
  315. le_irr2        db    ?    ; Value of 8259-2 IRR register
  316. le_isr2        db    ?    ; Value of 8259-2 ISR register
  317. endif
  318. logentry    ends
  319.  
  320. ; The types of log entries.
  321. LE_SP_E        equ    0    ; send_pkt entry
  322. LE_SP_X        equ    1    ; send_pkt exit
  323. LE_ASP_E    equ    2    ; as_send_pkt entry
  324. LE_ASP_X    equ    3    ; as_send_pkt exit
  325. LE_RBALLOC_E    equ    4    ; tx_rballoc entry
  326. LE_RBALLOC_X    equ    5    ; tx_rballoc exit
  327. LE_COPY_E    equ    6    ; sm_copy entry
  328. LE_COPY_X    equ    7    ; sm_copy exit
  329. LE_START_E    equ    8    ; tx_start entry
  330. LE_START_X    equ    9    ; tx_start exit
  331. LE_XMIT_E    equ    0ah    ; xmit entry
  332. LE_XMIT_X    equ    0bh    ; xmit exit
  333. LE_TXISR_E    equ    0ch    ; txisr entry
  334. LE_TXISR_X    equ    0dh    ; txisr exit
  335. LE_RECV_E    equ    0eh    ; recv entry
  336. LE_RECV_X    equ    0fh    ; recv exit
  337. LE_RCVFRM_E    equ    10h    ; rcv_frm entry
  338. LE_RCVFRM_X    equ    11h    ; rcv_frm exit
  339. LE_COPY_L    equ    12h    ; sm_copy loop
  340. LE_TIMER_E    equ    13h    ; timer entry
  341. LE_TIMER_X    equ    14h    ; timer exit
  342.  
  343.     public    log, log_index
  344. log        logentry 256 dup (<>) ; The log itself
  345. log_index    db    0    ; Index to current log entry
  346.  
  347. ; The macro used to create log entries.
  348. mkle    macro    letype, ledw, ledd, ledd2 ; Make an entry in the log
  349.     pushf            ; Save interrupt enable state
  350.     cli            ; Disable interrupts
  351.     push    dx        ; Save registers
  352.     push    bx
  353.     push    ax
  354.     mov bl,    log_index    ; Get current log_index
  355.     xor bh,    bh        ; Clear high byte
  356.     shl bx,    1        ; Multiply by sixteen
  357.     shl bx,    1
  358.     shl bx,    1
  359.     shl bx,    1
  360.     mov log[bx].le_type, letype ; Store log entry type
  361.     loadport        ; Base of device
  362.     setport EN_CCMD    ; Point at chip command register
  363.     in al,    dx        ; Get chip command state
  364.     mov log[bx].le_ccmd, al    ; Store CCMD value
  365.     setport EN0_ISR        ; Point at chip command register
  366.     in al,    dx        ; Get chip command state
  367.     mov log[bx].le_isr, al    ; Store ISR value
  368.     setport EN0_TSR        ; Point at chip command register
  369.     in al,    dx        ; Get chip command state
  370.     mov log[bx].le_tsr, al    ; Store TSR value
  371.     mov ax,    sm_tcur        ; Get current sm_tcur
  372.     mov log[bx].le_tcur, ax    ; Store sm_tcur value
  373.     mov ax,    sm_tboundary    ; Get current sm_tboundary
  374.     mov log[bx].le_tboundary, ax ; Store sm_tboundary value
  375.     mov ax,    sm_tnum        ; Get current sm_tnum
  376.     mov log[bx].le_tnum, ax    ; Store sm_tnum value
  377.     mov log[bx].le_dw, ledw    ; Store log entry dw
  378. ifndef    mkle8259        ; Include extra per-type data
  379.     mov word ptr log[bx].le_dd, ledd ; Store low word of log entry dd
  380.     mov word ptr log[bx].le_dd+2, ledd2 ; Store high word of log entry dd
  381. else                ; Include 8259 status
  382.     mov    al,0ah        ; read request register from
  383.     out    0a0h,al        ; secondary 8259
  384.     nop            ; settling delay
  385.     nop
  386.     nop
  387.     in    al,0a0h        ; get it
  388.     mov log[bx].le_irr2, al
  389.     mov    al,0bh        ; read in-service register from
  390.     out    0a0h,al        ; secondary 8259
  391.     nop            ; settling delay
  392.     nop
  393.     nop
  394.     in    al,0a0h        ; get it
  395.     mov log[bx].le_isr2, al
  396.     mov    al,0ah        ; read request register from
  397.     out    020h,al        ; primary 8259
  398.     nop            ; settling delay
  399.     nop
  400.     nop
  401.     in    al,020h        ; get it
  402.     mov log[bx].le_irr1, al
  403.     mov    al,0bh        ; read in-service register from
  404.     out    020h,al        ; primary 8259
  405.     nop            ; settling delay
  406.     nop
  407.     nop
  408.     in    al,020h        ; get it
  409.     mov log[bx].le_isr1, al
  410. endif
  411. ifdef    screenlog        ; Log the entry type to the screen too
  412.     push    es
  413.     mov ax,    0b800h        ; Color screen only...
  414.     mov es,    ax
  415.     mov bl,    log_index    ; Get current log_index
  416.     xor bh,    bh        ; Clear high byte
  417.     shl bx,    1        ; Multiply by sixteen
  418.     add bx,    3360
  419.     mov byte ptr es:[bx-1], 07h
  420.     mov byte ptr es:[bx], letype+30h
  421.     mov byte ptr es:[bx+1], 70h
  422.     pop    es
  423. endif
  424.     inc    log_index    ;
  425.     pop    ax        ; Restore registers
  426.     pop    bx
  427.     pop    dx
  428.     popf            ; Restore interrupt enable state
  429.     endm
  430. else
  431. mkle    macro    letype, ledw, ledd, ledd2 ; Define an empty macro
  432.     endm
  433. endif
  434.  
  435.     public    send_pkt
  436. ; The Transmit Packet routine.
  437. ; Enter with ds:si -> packet, cx = packet length,
  438. ; Exit with nc if ok, or else cy if error, dh set to error number.
  439. send_pkt:
  440.     assume    ds:nothing
  441.     mkle LE_SP_E, cx, si, ds
  442.  
  443.     call    re_enable_interrupts
  444.  
  445. ; Convert synchronous call to asynchronous call.
  446.     add sp,    -(size iocb)    ; Allocate iocb on stack
  447.     mov di,    sp        ; Get iob offset
  448.     mov ax,    ss        ; Get iocb segment
  449.     mov es,    ax
  450.     mov word ptr es:[di].buffer, si        ; Set buffer offset
  451.     mov word ptr es:[di].buffer+2, ds    ; Set buffer segment
  452.     mov es:[di].len, cx    ; Set buffer length
  453.     mov es:[di].flags, 0    ; Clear all flags
  454.     call    as_send_pkt    ; Call asynchronous send (es:di preserved)
  455. ifndef    debug
  456.     jc    sp_error    ; Error, return immediately
  457. else
  458.     jnc    sp_loop
  459.     jmp    sp_error
  460. endif
  461.  
  462. ; Check if asynchronous send has completed.
  463. sp_loop:
  464.     mov cx,    8000h        ; Avoid infinite loop
  465. sp_chkdone:
  466.     test es:[di].flags, DONE ; Transmission complete?
  467. ifndef    debug
  468.     jz    sp_notdone    ; No
  469. else
  470.     jnz    sp_done
  471.     jmp    sp_notdone
  472. sp_done:
  473. endif
  474.     mov dh,    es:[di].ret_code    ; Get return code
  475.     cmp dh,    0        ; Error?
  476. ifndef    debug
  477.     jne    sp_error    ; Yes
  478. else
  479.     je    sp_success
  480.     jmp    sp_error
  481. sp_success:
  482. endif
  483.  
  484. ; Send has completed successfully.
  485.     add sp,    size iocb    ; Free iocb on stack
  486.     mkle LE_SP_X, cx, 1, 0
  487.     clc            ; No error, clear carry
  488.     ret
  489.  
  490. ; Send has completed unsuccessfully.
  491. sp_error:
  492.     add sp,    size iocb    ; Free iocb on stack
  493.     mkle LE_SP_X, cx, 0, 0
  494.     stc            ; popf may have cleared carry
  495.     ret
  496.  
  497. sp_notdone:
  498.     pushf            ; Make interrupt enable bit accessible
  499.     pop    bx        ;  in bx
  500.     test bx, EI        ; Were interrupts enabled on pkt driver entry?
  501.     jnz    sp_deccnt    ; Yes
  502.  
  503. ; Interrupts are disabled, poll chip to determine when the transmit completes.
  504.     loadport        ; Base of device
  505.     setport    EN0_ISR        ; Point at interrupt status register
  506.     in al,    dx        ; Get pending interrupts
  507.     test al,ENISR_ALL    ; Any interrupts?
  508.     jz    sp_deccnt    ; No
  509.     pushf            ; Push flags
  510.     call    xmit        ; Fake first half of transmit interrupt
  511.     jmp    sp_fakeint    ; Fake second half of transmit interrupt
  512.  
  513. ; Decrement the loop counter
  514. sp_deccnt:
  515.     dec    cx        ; Reached zero?
  516. ifndef    debug
  517.     jnz    sp_chkdone    ; No, loop again
  518. else
  519.     jz    sp_txstuck
  520.     jmp    sp_chkdone
  521. sp_txstuck:
  522. endif
  523.  
  524. ; TX is stuck.  Fall thru into sp_fakeint code after setting xmit variables.
  525.     pushf            ; Make sure interrupts are disabled
  526.     cli
  527.     mov xmit_isr, ENISR_TX_ERR ; Frame transmitted error
  528.     mov xmit_tsr, 0        ; Unknown error
  529.     call    count_out_err    ; Count the anomaly
  530.     call    xmit_fake    ; Fake first half of transmit interrupt
  531. sp_fakeint:
  532.     push    es        ; Push iocb segment
  533.     push    di        ; Push iocb offset
  534.     mov dx,    cs        ; Set up ds=cs
  535.     mov ds,    dx
  536.     call    recv        ; Let recv do the hard work
  537.     pop    di        ; Pop iocb offset
  538.     pop    es        ; Pop iocb segment
  539.     popf            ; Restore interrupt enable bit
  540.     jmp    sp_loop        ; Restart loop (check the DONE bit again)
  541.  
  542.  
  543.     public    as_send_pkt
  544. ; The Asynchronous Transmit Packet routine.
  545. ; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
  546. ;   interrupts possibly enabled.
  547. ; Exit with nc if ok, or else cy if error, dh set to error number.
  548. ;   es:di and interrupt enable flag preserved on exit.
  549. as_send_pkt:
  550.     assume    ds:nothing
  551.     mkle LE_ASP_E, cx, di, es
  552.  
  553. ; Check if this packet is too large.
  554.     cmp cx,    GIANT        ; Bigger than this?
  555.     jbe    tx_oklen1    ; No
  556.     mov dh,    CANT_SEND    ; Yes, return error
  557.     mov es:[di].ret_code, dh
  558.     or es:[di].flags, DONE
  559.     stc
  560.     ret
  561.  
  562. ; Frame is small enough, check if it's large enough.
  563. tx_oklen1:
  564.     cmp cx,    RUNT        ; Smaller than this?
  565.     jnb    tx_oklen2    ; No
  566.     mov cx,    RUNT        ; Yes, stretch frame to minimum allowed
  567.     mov es:[di].len, cx    ; Update iocb too
  568.  
  569. ; Frame is an acceptable size.
  570. tx_oklen2:
  571.     push    es        ; Push iocb segment
  572.     push    di        ;  and offset
  573.  
  574. ; If queue is empty and have ring buffer space, allocate it.  Else, queue iocb.
  575.     pushf            ; Push original interrupt enable flag
  576.     cli            ; Disable interrupts
  577.     send_queueempty        ; Queue empty?
  578. ifndef    debug
  579.     jnz    tx_queue    ; No, queue this one too
  580. else
  581.     jz    tx_alloc
  582.     jmp    tx_queue
  583. tx_alloc:
  584. endif
  585.     mov ax,    cx        ; Move length to ax
  586.     call    tx_rballoc    ; Allocate space in the ring buffer
  587. ifndef    debug
  588.     jc    tx_nospace    ; No space right now, queue the packet
  589. else
  590.     jnc    tx_space
  591.     jmp    tx_nospace
  592. tx_space:
  593. endif
  594.     popf            ; Pop original interrupt enable flag
  595.  
  596. ; Do the actual copy of the packet into the ring buffer.
  597.     call    sm_copy
  598.  
  599. ; Start the packet transmission.
  600.     pushf            ; Push original interrupt enable flag
  601.     cli            ; Disable interrupts
  602.     call    tx_start    ; Set up hints and start transmission
  603.     popf            ; Pop original interrupt enable flag
  604.  
  605.     pop    di        ; Pop iocb segment
  606.     pop    es        ;  and offset
  607.     or es:flags[di], DONE    ; Mark done
  608.     mov es:ret_code[di], 0    ; Success
  609.     mkle LE_ASP_X, 0, 0, 0
  610.     clc
  611.     ret            ; End of send_pkt routine
  612.  
  613. ; No space in ring buffer, get the iocb from the stack and queue it.
  614. tx_nospace:
  615.     mov bx,    sp        ; Need es:di -> iocb, but can't pop flags.
  616.     mov di,    ss:[bx+2]    ; Get iocb offset
  617.     mov es,    ss:[bx+4]    ; Get iocb segment
  618.  
  619. ; Queue the iocb and return.
  620. tx_queue:
  621.     call    send_queue
  622.     popf            ; Pop original interrupt enable flag
  623.     pop    di        ; Pop iocb segment
  624.     pop    es        ;  and offset
  625.     mkle LE_ASP_X, 1, 0, 0
  626.     clc
  627.     ret
  628.  
  629.  
  630.     public    tx_rballoc
  631. ; Allocate some space in the transmit ring buffer.
  632. ; Enter with ds:si -> buffer source, ax = length, interrupts disabled.
  633. ; If not enough space, exit with: cy, ds:si -> source, ax = length.
  634. ; Else, nc, ds:si -> buffer source, es:di -> buffer destination, ax = length,
  635. ;    bl = transmit starting page.
  636. tx_rballoc:
  637.     assume    ds:nothing
  638.     mkle LE_RBALLOC_E, ax, 0, 0
  639.  
  640. ; First, compute likely destination, index, and new sm_tcur (may have to wrap)
  641.     mov es,    mem_base    ; Paragraph of the TX buffer
  642.     mov di,    sm_tcur        ; Offset of current TX buffer page
  643.     mov bx,    ax        ; Length of packet
  644.     lea dx, [bx+255][di]    ; Compute packet end + 255
  645.     xor dl,    dl        ; Only want the page number
  646.  
  647. ; Second, make sure ring buffer isn't completely full (sm_tlen[sm_tcur] != 0)
  648.     mov bx,    di        ; Compute index into length table
  649.     mov cl, 7        ; Index is offset shifted right seven bits
  650.     shr bx,    cl
  651. tx_chkfull:
  652.     cmp sm_tlen[bx], 0    ; Current sm_tlen must equal zero
  653. ifndef    debug
  654.     jne    tx_noroom    ; TX buffer full
  655. else
  656.     je    tx_room
  657.     jmp    tx_noroom
  658. tx_room:
  659. endif
  660.  
  661. ; Third, check if packet will fit in ring buffer at this position
  662.     cmp di,    sm_tboundary    ; Has sm_tcur wrapped?
  663. ifndef    debug
  664.     jae    tx_notwrapped    ; No
  665. else
  666.     jb    tx_wrapped
  667.     jmp    tx_notwrapped
  668. tx_wrapped:
  669. endif
  670.     cmp dx,    sm_tboundary     ; Will packet fit between sm_tcur/sm_tboundary?
  671. ifndef    debug
  672.     ja    tx_noroom    ; No
  673. else
  674.     jbe    tx_willfit
  675.     jmp    tx_noroom
  676. endif
  677. tx_willfit:            ; Yes, packet will fit
  678.     mov sm_tlen[bx], ax    ; Copy length to sm_tlen
  679.     mov sm_tcur, dx        ; Update current TX buffer offset
  680.     shr bx,    1        ; Convert length table index to tpsr
  681.     mkle LE_RBALLOC_X, bx, di, es
  682.     clc            ; Clear carry
  683.     ret            ; Return success
  684.  
  685. tx_notwrapped:            ; sm_tcur >= sm_tboundary
  686.     cmp dh,    sm_rstart_pg    ; Will packet fit between sm_tcur/sm_rstart_pg?
  687. ifndef    debug
  688.     jb    tx_willfit    ; Yes
  689. else
  690.     jae    tx_mightfit
  691.     jmp    tx_willfit
  692. tx_mightfit:
  693. endif
  694.     jne    tx_wrapcur    ; No
  695.     xor dx,    dx        ; Packet fit exactly, but wrap sm_tcur now
  696.     jmp    tx_willfit
  697.  
  698. ; Packet won't fit in remaining TX ring buffer, wrap sm_tcur.
  699. ; If ring buffer is completely empty, wrap sm_tboundary as well.
  700. ; If ring buffer is not empty, sm_tpsr will need updated if sm_tnum is 1.
  701. tx_wrapcur:
  702.     sub dx,    di        ; Compute next sm_tcur (offset zero + length)
  703.     mov di,    sm_tboundary    ; Get sm_tboundary (don't need di for a bit)
  704.     mov cl,    7        ; Convert it to an index into length table
  705.     shr di,    cl
  706.     xor cx,    cx        ; Put a zero value in a register
  707.     cmp sm_tlen[di], cx    ; Is the buffer completely empty?
  708.     mov di,    cx        ; Wrap sm_tcur to zero
  709.     jne    tx_notempty    ; No
  710.     mov sm_tboundary, cx    ; Wrap sm_tboundary
  711.     mov bx,    cx        ; Wrap index
  712.     jmp    tx_notwrapped    ; Check new position
  713.  
  714. tx_notempty:
  715.     mov sm_tlen[bx], cx    ; Insert 0 length pkt to skip remaining buffer
  716.     mov sm_tcur, cx        ; Interrupt handler may need this to be updated
  717.     mov bx,    cx        ; Wrap index
  718.     cmp sm_tnum, 1        ; Will sm_tpsr be corrupted?
  719.     jne    tx_tpsrok    ; No, its ok
  720.     mov sm_tpsr, cl        ; Wrap sm_tpsr as well
  721. tx_tpsrok:
  722.     jmp    tx_chkfull    ; Check packet fit again
  723.  
  724. ; Packet won't fit now, return failure.
  725. tx_noroom:
  726.     mkle LE_RBALLOC_X, 0, 0, 0
  727.     stc            ; Set carry
  728.     ret            ; Return failure
  729.  
  730.  
  731.     public    sm_copy
  732. ; Copy a packet from the receive ring buffer.
  733. ; Enter with ds:si -> source, es:di -> destination, ax = length.
  734. ; Exits with ds:si updated, es:di updated, ax and bx preserved,
  735. ;   cx, and dx destroyed.
  736. sm_copy:
  737.     assume    ds:nothing
  738.     mkle LE_COPY_E, ax, di, es
  739.  
  740.     push    ax        ; Preserve ax
  741.     push    bx        ; Preserve bx
  742.     mov cx,    ax        ; Copy length to cx
  743.     shr cx,    1        ; Convert bytes to words
  744.     pushf            ; Save carry and interrupt enable bits
  745.     test board_features, BF_MEM16EN    ; Is this a WD8013?
  746. ifndef    debug
  747.     jne    sm_wd8013    ; yes
  748. else
  749.     je    sm_wd8003
  750.     jmp    sm_wd8013
  751. sm_wd8003:
  752. endif
  753.     rep    movsw        ; Copy a word at a time
  754. sm_copied:
  755.     mkle LE_COPY_L, 0, 0, 0
  756.     popf            ; Restore carry bit
  757.     jnc    sm_even        ; odd byte left over?
  758.     lodsw            ;   yes, word fetch
  759.     stosb            ;   and byte store
  760. sm_even:
  761.     pop    bx        ; Get back bx
  762.     pop    ax        ; Get back ax
  763.     mkle LE_COPY_X, 0, 0, 0
  764.     ret            ; Return success
  765.  
  766. sm_wd8013:            ; Board is a WD8013
  767.     cli            ; Disable interrupts
  768.     loadport        ; Base of device
  769.     setport    EN_REG5        ; Enable 16-bit access
  770.     mov al,    ENR5_MEM16EN+ENR5_LAN16EN+ENR5_LA19
  771.     out dx,    al
  772.     mov bx,    sp
  773.     test    ss:[bx], EI    ; Were interrupts enabled on entry?
  774.     jne    sm_piecemeal    ; Yes, copy piecemeal
  775. sm_allatonce:            ; No, copy all at once
  776.     rep    movsw        ; Copy packet
  777. sm_disable:
  778.     mov al,    ENR5_LAN16EN+ENR5_LA19 ; Disable 16-bit access to WD8013
  779.     out dx,    al
  780.     jmp    sm_copied
  781.  
  782. ; Do copy a piece at a time, enabling and disabling interrupts as we go,
  783. ;  in order to guarantee reasonable interrupt latency
  784. sm_piecemeal:
  785.     mov ax,    cx        ; Copy length to ax
  786.     mov bx,    cx        ;  and bx
  787.     add bx,    63
  788.     mov cx,    6        ; Divide length by 64 to get number of loops
  789.     shr bx,    cl
  790.     mov cx,    ax        ; Copy length back to cx
  791.     and cx,    63        ; Do up to 63 copies first time
  792.     jne    sm_copypiece    ; cx was not zero, do cx copies
  793. sm_loop:
  794.     mov cx,    64        ; Do 64 copies
  795. sm_copypiece:
  796.     rep    movsw        ; Copy a word at a time
  797.     dec    bx        ; Decrement loop counter
  798.     je    sm_disable    ; All done
  799.     mov al,    ENR5_LAN16EN+ENR5_LA19 ; Disable 16-bit access to WD8013
  800.     out dx,    al
  801.     mkle LE_COPY_L, bx, di, es
  802.     sti            ; Enable interrupts for a moment
  803.     cli            ; Disable interrupts again
  804.     mov al,    ENR5_MEM16EN+ENR5_LAN16EN+ENR5_LA19
  805.     out dx,    al
  806.     jmp    sm_loop        ; Do more copies
  807.  
  808.  
  809.     public    tx_start
  810. ; Start the transmitter and manage the low-latency transmitter hints.
  811. ; Enters with ax = length, bl = transmit starting page.
  812. tx_start:
  813.     assume    ds:nothing
  814.     mkle LE_START_E, ax, bx, 0
  815.  
  816. ; First, increment the count of packets in the ring buffer and figure out what
  817. ;   needs to be done.
  818.     inc    sm_tnum        ; Increment count of packets (pkt now queued)
  819.     cmp sm_tnum, 3        ; Already have a second packet set up?
  820.     jae    tx_running    ; Yes.
  821.     cmp sm_tnum, 2        ; Transmitter already running?
  822.     jae    tx_setnext    ; Yes
  823.  
  824. ; Start the transmitter running.
  825.     loadport        ; Base of device
  826.     setport    EN0_TCNTLO    ; Low byte of TX count
  827.     out dx,    al        ; Tell card the low byte
  828.     setport    EN0_TCNTHI    ; High byte of TX length
  829.     mov al,    ah        ; Get the high byte into al
  830.     out dx,    al        ; Tell card the high byte
  831.     setport    EN0_TPSR    ; Transmit Page Start Register
  832.     mov al,    bl        ; Get the starting page
  833.     out dx,    al        ; Tell card the starting page
  834.     setport    EN_CCMD        ; Chip command reg
  835.     mov al,    ENC_TRANS+ENC_NODMA
  836.     out dx,    al        ; Start the transmitter
  837.  
  838. ; Set new sm_tboundary.  If we wrapped sm_tcur in sm_rballoc AND the last
  839. ; interrupt came in during the sm_copy then sm_tboundary will not have
  840. ; wrapped and will now be corrupt.
  841.     mov byte ptr sm_tboundary+1, bl
  842.     mov bl,    byte ptr sm_tcur+1 ; Get sm_tcur page number (to set sm_tpsr)
  843.  
  844. ; Set the low-latency transmitter hints.
  845. tx_setnext:
  846.     mov sm_tcnt, ax        ; Set up next transmit count
  847.     mov sm_tpsr, bl        ; Set up next transmit starting page
  848.  
  849. tx_running:
  850.     mkle LE_START_X, ax, bx, 0
  851.     ret
  852.  
  853.  
  854.     public    drop_pkt
  855. ; Drop a packet from the queue.
  856. ; Enter with es:di -> iocb.
  857. drop_pkt:
  858.     assume    ds:nothing
  859.  
  860.     ret
  861.  
  862.  
  863.     public    get_address
  864. ; Get the address of the interface.
  865. ; Enter with es:di -> place to get the address, cx = size of address buffer.
  866. ; Exit with nc, cx = actual size of address, or cy if buffer not big enough.
  867. get_address:
  868.     assume ds:code
  869.  
  870.     cmp cx,    EADDR_LEN    ; Caller wants a reasonable length?
  871.     jb    get_addr_x    ; No, fail.
  872.     mov cx,    EADDR_LEN    ; Yes. Set count for loop
  873.     loadport        ; Base of device
  874.     setport    EN_SAPROM    ; Where the address prom is
  875.     cld            ; Make sure string mode is right
  876. get_addr_loop:
  877.     in al,    dx        ; Get a byte of address
  878.     stosb            ; Feed it to caller
  879.     inc    dx        ; Next byte at next I/O port
  880.     loop    get_addr_loop    ; Loop over six bytes
  881.     mov cx,    EADDR_LEN    ; Tell caller how many bytes we fed him
  882.     clc            ; Carry off says success
  883.     ret
  884. get_addr_x:
  885.     stc            ; Tell caller our addr is too big for him
  886.     ret
  887.  
  888.  
  889.     public    set_address
  890. ; Enter with ds:si -> Ethernet address, CX = length of address.
  891. ; Exit with nc if okay, or cy, dh=error if any errors.
  892. set_address:
  893.     assume    ds:nothing
  894.  
  895.     cmp    cx,EADDR_LEN        ;ensure that their address is okay.
  896.     je    set_address_4
  897.     mov    dh,BAD_ADDRESS
  898.     stc
  899.     jmp    short set_address_done
  900. set_address_4:
  901.  
  902.     loadport        ; Base of device
  903.     setport    EN1_PHYS
  904. set_address_1:
  905.     lodsb
  906.     out    dx,al
  907.     inc    dx
  908.     loop    set_address_1
  909.  
  910. set_address_okay:
  911.     mov    cx,EADDR_LEN        ;return their address length.
  912.     clc
  913. set_address_done:
  914.     push    cs
  915.     pop    ds
  916.     assume    ds:code
  917.     ret
  918.  
  919.  
  920. ; Routines to set address filtering modes in the DS8390
  921. rcv_mode_1:     ; Turn off receiver
  922.     mov al,    ENRXCR_MON      ; Set to monitor for counts but accept none
  923.     jmp short rcv_mode_set
  924. rcv_mode_2:     ; Receive only packets to this interface
  925.     mov al, 0               ; Set for only our packets
  926.     jmp short rcv_mode_set
  927. rcv_mode_3:     ; Mode 2 plus broadcast packets (This is the default)
  928.     mov al,    ENRXCR_BCST     ; Set four ours plus broadcasts
  929.     jmp short rcv_mode_set
  930. rcv_mode_4:     ; Mode 3 plus selected multicast packets
  931.     mov al,    ENRXCR_BCST+ENRXCR_MULTI ; Ours, bcst, and filtered multicasts
  932.     mov     mcast_all_flag,0
  933.     jmp short rcv_mode_set
  934. rcv_mode_5:     ; Mode 3 plus ALL multicast packets
  935.     mov al,    ENRXCR_BCST+ENRXCR_MULTI ; Ours, bcst, and filtered multicasts
  936.     mov     mcast_all_flag,1
  937.     jmp short rcv_mode_set
  938. rcv_mode_6:     ; Receive all packets (Promiscuous physical plus all multi)
  939.     mov al,    ENRXCR_BCST+ENRXCR_MULTI+ENRXCR_PROMP
  940.     mov     mcast_all_flag,1
  941. rcv_mode_set:
  942.     push    ax              ; Hold mode until masks are right
  943.     call    set_8390_multi  ; Set the multicast mask bits in chip
  944.     pop     ax
  945.     loadport        ; Base of device
  946.     setport    EN0_RXCR    ; Set receiver to selected mode
  947.     out dx,    al
  948.     mov     rxcr_bits,al    ; Save a copy of what we set it to
  949.     ret
  950.  
  951.  
  952.     public    set_multicast_list
  953. ; Enter with ds:si ->list of multicast addresses, cx = number of addresses.
  954. ; Return nc if we set all of them, or cy,dh=error if we didn't.
  955. set_multicast_list:
  956.     mov    dh,NO_MULTICAST
  957.     stc
  958.     ret
  959.  
  960.  
  961. ; Set the multicast filter mask bits in case promiscuous rcv wanted
  962. set_8390_multi:
  963.     loadport        ; Base of device
  964.     setport    EN_CCMD        ; Chip command register
  965.     mov    cx,8        ; Eight bytes of multicast filter
  966.     mov    si,offset mcast_list_bits  ; Where bits are, if not all ones
  967.     push    cs
  968.     pop     ds
  969.     pushf
  970.     cli            ; Protect from irq changing page bits
  971.     mov    al,ENC_NODMA+ENC_PAGE1
  972.     out    dx,al        ; Switch to page one for writing eaddr
  973.     setport    EN1_MULT    ; Where it goes in 8390
  974.     mov    al,mcast_all_flag  ; Want all ones or just selected bits?
  975.     or    al,al
  976.     jz    set_mcast_2     ; z = just selected ones
  977.     mov    al,0ffh        ; Ones for filter
  978. set_mcast_all:
  979.     out    dx,al        ; Write a mask byte
  980.     inc    dl        ; Step to next one
  981.     jmp    $+2        ; limit chip access rate
  982.     loop    set_mcast_all
  983.     jmp short set_mcast_x
  984.  
  985. set_mcast_2:
  986.     lodsb                   ; Get a byte of mask bits
  987.     out    dx,al        ; Write a mask byte
  988.     inc    dl        ; Step to next I/O register
  989.     jmp    $+2        ; limit chip access rate
  990.     loop    set_mcast_2
  991. set_mcast_x:
  992.     loadport        ; Base of device
  993.     setport    EN_CCMD        ; Chip command register
  994.     mov    al,ENC_NODMA+ENC_PAGE0
  995.     out    dx,al        ; Restore to page zero
  996.     popf            ; OK for interrupts now
  997.     ret
  998.  
  999.  
  1000.     public    terminate
  1001. terminate:
  1002.     loadport        ; First, pulse the board reset
  1003.     setport    EN_CMD
  1004.     mov    al,EN_RESET
  1005.     out    dx,al        ; Turn on board reset bit
  1006.     jmp    $+2
  1007.     xor    al,al
  1008.     jmp    $+2
  1009.     out    dx,al        ; Turn off board reset bit
  1010.     ret
  1011.  
  1012.     public    reset_interface
  1013. reset_interface:
  1014.     assume ds:code
  1015.     loadport        ; Base of device
  1016.     setport    EN_CCMD        ; Chip command reg
  1017.     mov al,    ENC_STOP+ENC_NODMA
  1018.     out dx,    al        ; Stop the DS8390
  1019.     setport    EN0_ISR        ; Interrupt status reg
  1020.     mov al,    0ffh        ; Clear all pending interrupts
  1021.     out dx,    al        ; ..
  1022.     setport    EN0_IMR        ; Interrupt mask reg
  1023.     xor al,    al        ; Turn off all enables
  1024.     out dx,    al        ; ..
  1025.     setport    EN_REG5
  1026.     mov    al,ENR5_EIL
  1027.     test    board_features,BF_EIL
  1028.     jz    reset_no_eil
  1029.     out    dx,al            ; enable 8390 interrupts to bus
  1030. reset_no_eil:
  1031.     ret
  1032.  
  1033.  
  1034.     public    xmit
  1035. ; Process a transmit interrupt with the least possible latency to achieve
  1036. ;   back-to-back packet transmissions.
  1037. ; Second entry point, xmit_fake, used when chip is hung.
  1038. ; May only use ax and dx.
  1039. xmit:
  1040.     assume    ds:nothing
  1041.     mkle LE_XMIT_E, ax, di, es
  1042.  
  1043. ; Was there a transmit interrupt from this card?
  1044.     loadport        ; Base of device
  1045.     setport    EN0_ISR        ; Point at interrupt status register
  1046.     in al,    dx        ; Get pending interrupts
  1047.     mov xmit_isr, al    ; Store ISR for later reference
  1048.     test al,ENISR_TX+ENISR_TX_ERR    ; Frame transmitted?
  1049.     jz    xmit_done    ; No
  1050.  
  1051. ; Get the transmit status.
  1052.      setport    EN0_TSR
  1053.     in al,    dx
  1054.     mov xmit_tsr, al    ; Store TSR for later reference
  1055.  
  1056. ; Clear the interrupt.
  1057. xmit_fake:            ; Entry point used when chip is hung.
  1058.     setport    EN0_ISR        ; Clear the TX and TX error complete flags
  1059.     mov al,    ENISR_TX+ENISR_TX_ERR
  1060.     out dx,    al
  1061.     cmp sm_tnum, 1        ; Any packets left to transmit?
  1062.     jbe    xmit_done    ; No
  1063.  
  1064. ; Quickly start the next transmission going.
  1065.     setport    EN0_TCNTLO    ; Low byte of TX count
  1066.     mov ax,    sm_tcnt        ; Get length of next packet
  1067.     out dx,    al        ; Tell card the low byte
  1068.     setport    EN0_TCNTHI    ; High byte of TX length
  1069.     mov al,    ah        ; Get the high byte into al
  1070.     out dx,    al        ; Tell card the high byte
  1071.     setport    EN0_TPSR    ; Transmit Page Start Register
  1072.     mov al,    sm_tpsr        ; Get the starting page into al
  1073.     out dx,    al        ; Tell card the starting page
  1074.     setport    EN_CCMD        ; Chip command reg
  1075.     mov al,    ENC_TRANS+ENC_NODMA
  1076.     out dx,    al        ; Start the transmitter
  1077.  
  1078. xmit_done:
  1079.     mkle LE_XMIT_X, ax, di, es
  1080.     ret
  1081.  
  1082.  
  1083.     public    tx_isr
  1084. ; Process a transmit interrupt with the least possible latency.
  1085. ; Enter with al = contents of interrupt status register (EN0_ISR)
  1086. tx_isr:
  1087.     assume    ds:code
  1088.     mkle LE_TXISR_E, 0, 0, 0
  1089.  
  1090. ; Examine the status bits in and count errors.
  1091.     test xmit_isr, ENISR_TX    ; Non-error TX?
  1092.     jz    tx_err        ; No, do TX error completion.
  1093.     test xmit_tsr, ENTSR_COLL16 ; Jammed for 16 transmit tries?
  1094.     jz    tx_rbfree    ; No.
  1095.     call    count_out_err    ; Yes, count those.
  1096.     jmp    tx_rbfree
  1097. tx_err:
  1098.     test xmit_tsr, ENTSR_FU    ; FIFO Underrun?
  1099.     jz    tx_rbfree    ; No.
  1100.     call    count_out_err    ; Yes, count those.
  1101.  
  1102. ; Now, drain the last packet from the TX shared memory ring buffer.
  1103. tx_rbfree:
  1104.     mov bx,    sm_tboundary    ; Get sm_tboundary
  1105.     mov cl, 7        ; Compute index into length table
  1106.     shr bx,    cl
  1107.     mov sm_tlen[bx], 0    ; Zero length
  1108.     mov bh,    sm_tpsr        ; Get the starting page into bh
  1109.     xor bl,    bl        ; Clear bl
  1110.     mov sm_tboundary, bx    ; Set new sm_tboundary
  1111.     dec    sm_tnum        ; Decrement number of packets
  1112.     jz    tx_dequeue    ; No interrupts following this one
  1113.  
  1114. ; Now, set up sm_tpsr and sm_tcnt for the next transmit interrupt.
  1115.     mov di,    sm_tcnt        ; Get length of current packet
  1116.     lea dx, [bx+255][di]    ; Compute new sm_tpsr
  1117.     xor dl,    dl
  1118.     cmp dh,    sm_rstart_pg    ; See if sm_tpsr has wrapped
  1119.     jb    tx_nowrap    ; It has not yet wrapped
  1120. tx_wrap:
  1121.     xor dx,    dx        ; Wrap to top
  1122. tx_nowrap:
  1123.     cmp    dx, sm_tcur    ; Any more packets following this one?
  1124.     jbe    tx_settpsr    ; No
  1125.     mov bx,    dx        ; Compute index of length of next packet
  1126.     mov cl, 7
  1127.     shr bx,    cl
  1128.     mov ax,    sm_tlen[bx]    ; Get length of next packet
  1129.     or ax,    ax        ; Should we punt rest of TX buffer?
  1130.     je    tx_wrap        ; Yes, wrap to top and try again
  1131. tx_settpsr:
  1132.     mov sm_tpsr, dh        ; Store back new sm_tpsr
  1133.     mov sm_tcnt, ax        ;  and new sm_tcnt
  1134.  
  1135. ; Finally, process any packets in the transmit queue.
  1136. tx_dequeue:
  1137.     send_queueempty        ; Queue empty?
  1138.     jz    tx_done        ; Yes.
  1139.  
  1140.     send_peekqueue        ; Peek at the first element in the queue
  1141.     lds si,    es:[di].buffer    ; Get buffer segment:offset
  1142.     assume    ds:nothing
  1143.     mov ax,    es:[di].len    ; Get buffer length
  1144.     call    tx_rballoc    ; Allocate space in the ring buffer
  1145.     jc    tx_done        ; Still no space for this packet.
  1146.  
  1147. ; Do the actual copy of the packet into the ring buffer.
  1148.     call    sm_copy
  1149.  
  1150. ; Start the packet transmission.
  1151.     call    tx_start    ; Set up hints and start transmission
  1152.  
  1153.     mov    ax,0
  1154.     call    send_dequeue    ; Dequeue this element
  1155.  
  1156. tx_done:
  1157.     mov dx,    cs        ; Restore ds=cs
  1158.     mov ds,    dx
  1159.     assume    ds:code
  1160.     mkle LE_TXISR_X, 0, 0, 0
  1161.     ret
  1162.  
  1163.  
  1164. ;called when we want to determine what to do with a received packet.
  1165. ;enter with cx = packet length, es:di -> packet type.
  1166. ;It returns with es:di = 0 if don't want this type or if no buffer available.
  1167.     extrn    recv_find: near
  1168.  
  1169. ;called after we have copied the packet into the buffer.
  1170. ;enter with ds:si ->the packet, cx = length of the packet.
  1171.     extrn    recv_copy: near
  1172.  
  1173. ;called to re-enable interrupts
  1174.     extrn    re_enable_interrupts: near
  1175.  
  1176.     extrn    count_in_err: near
  1177.     extrn    count_out_err: near
  1178.  
  1179.     public    recv
  1180. ; Called from the recv isr.  All registers have been saved, and ds=cs.
  1181. ; Actually, not just receive, but all interrupts come here.
  1182. ; Upon exit, the interrupt will be acknowledged.
  1183. recv:
  1184.     assume    ds:code
  1185.     mkle LE_RECV_E, 0, 0, 0
  1186.  
  1187. ; First cleanup after xmit routine if transmit interrupt occurred.
  1188. check_isr:
  1189.     mov al,    xmit_isr    ; Get xmit ISR
  1190.     test al,ENISR_TX+ENISR_TX_ERR    ; Frame transmitted?
  1191.     jz    tx_no_frame    ; No
  1192.     call    tx_isr        ; Yes, cleanup after transmit interrupt
  1193.     mov al,    xmit_isr    ; Get xmit ISR
  1194.  
  1195. tx_no_frame:
  1196.     and al,    ENISR_ALL    ; Any interrupts at all?
  1197. ifndef    debug
  1198.     jnz    isr_test_overrun
  1199. else
  1200.     jz    isr_no_isr
  1201.     jmp    isr_test_overrun
  1202. isr_no_isr:
  1203. endif
  1204.     mkle LE_RECV_X, 0, 0, 0
  1205.     ret            ; Go if none
  1206.  
  1207. ; Now, a messy procedure for handling the case where the rcvr
  1208. ; over-runs its ring buffer.  This is spec'ed by National for the chip.
  1209. isr_test_overrun: 
  1210.     test al,ENISR_OVER    ; Was there an overrun?
  1211.     jnz    recv_overrun    ; Go if so.
  1212.     jmp    recv_no_overrun    ; Go if not.
  1213. recv_overrun:
  1214.     loadport        ; Base of device
  1215.     setport    EN_CCMD        ; Stop the card
  1216.     mov al,    ENC_STOP+ENC_NODMA
  1217.     out dx,    al        ; Write "stop" to command register
  1218.  
  1219.     mov al, ENC_NODMA+ENC_PAGE1    ; Could be in previous out, but
  1220.     out dx,al        ; was only tested this way
  1221.     setport EN1_CURPAG    ; Get current page
  1222.     in al,dx
  1223.     mov bl,al        ; save it
  1224.     setport    EN_CCMD        ;
  1225.     mov al, ENC_NODMA+ENC_PAGE0
  1226.     out dx,al        ; Back to page 0
  1227.  
  1228. ; Remove one frame from the ring
  1229.     setport    EN0_BOUNDARY    ; Find end of this frame
  1230.     in al,    dx        ; Get memory page number
  1231.     inc    al        ; Page plus 1
  1232.     cmp al,    sm_rstop_pg    ; Wrapped around ring?
  1233.     jnz    rcv_ovr_nwrap    ; Go if not
  1234.     mov al,    sm_rstart_pg    ; Yes, wrap the page pointer
  1235. rcv_ovr_nwrap:
  1236.  
  1237.     cmp    al,bl        ; Check if buffer emptry
  1238.     je    rcv_ovr_empty    ; Yes ? Don't receive anything
  1239.  
  1240.     xor ah,    ah        ; Convert page to segment
  1241.     mov cl,    4
  1242.     mov bl,    al        ; Page number as arg to rcv_frm
  1243.     shl ax,    cl        ; ..
  1244.     add ax,    mem_base    ; Page in this memory
  1245.     mov es,    ax        ; Segment pointer to the frame header
  1246.     push    es        ; Hold this frame pointer for later
  1247.     mov ax,    es:[EN_RBUF_STAT]    ; Get the buffer status byte
  1248.     test al,ENPS_RXOK    ; Is this frame any good?
  1249.     jz    rcv_ovr_ng    ; Skip if not
  1250.      call    rcv_frm        ; Yes, go accept it
  1251. rcv_ovr_ng:
  1252.     pop    es        ; Back to start of this frame
  1253.     mov ax,    es:[EN_RBUF_NXT_PG and 0fffeh] ; Get pointer to next frame
  1254.     mov    al,ah
  1255.     dec    al        ; Back up one page
  1256.     cmp al,    sm_rstart_pg    ; Did it wrap?
  1257.     jae    rcv_ovr_nwr2
  1258.     mov al,    sm_rstop_pg    ; Yes, back to end of ring
  1259.     dec    al
  1260. rcv_ovr_nwr2:
  1261.     loadport        ; Base of device
  1262.     setport    EN0_BOUNDARY    ; Point at boundary reg
  1263.     out dx,    al        ; Set the boundary
  1264.  
  1265. rcv_ovr_empty:
  1266.     loadport        ; Base of device
  1267.     setport    EN0_RCNTLO    ; Point at byte count regs
  1268.     xor al,    al        ; Clear them
  1269.     out dx,    al        ; ..
  1270.     setport    EN0_RCNTHI
  1271.     out dx,    al
  1272.     setport    EN0_ISR        ; Point at status reg
  1273.     mov cx,    8000h        ; Timeout counter
  1274. rcv_ovr_rst_loop:
  1275.     in al,    dx        ; Is it finished resetting?
  1276.     test al,ENISR_RESET    ; ..
  1277.     jmp    $+2        ; limit chip access rate
  1278.     loopnz    rcv_ovr_rst_loop; Loop til reset, or til timeout
  1279.     loadport        ; Base of device
  1280.      setport    EN0_TXCR    ; Point at Transmit control reg
  1281.     mov al,    ENTXCR_LOOP    ; Put transmitter in loopback mode
  1282.     out dx,    al        ; ..
  1283.     setport    EN_CCMD        ; Point at Chip command reg
  1284.     mov al,    ENC_START+ENC_NODMA
  1285.     out dx,    al        ; Start the chip running again
  1286.     setport    EN0_TXCR    ; Back to TX control reg
  1287.     xor al,    al        ; Clear the loopback bit
  1288.     out dx,    al        ; ..
  1289.     setport    EN0_ISR        ; Point at Interrupt status register
  1290.     mov al,    ENISR_OVER    ; Clear the overrun interrupt bit
  1291.     out dx,    al        ; ..
  1292.     call    count_in_err    ; Count the anomaly
  1293.      jmp    isr_no_stat    ; Done with the overrun case
  1294.  
  1295. recv_no_overrun:
  1296. ; Handle receive flags, normal and with error (but not overrun).
  1297.     test al,ENISR_RX+ENISR_RX_ERR    ; Frame received without overrun?
  1298.     jnz    recv_frame    ; Go if so.
  1299.      jmp    isr_no_rx
  1300. recv_frame:
  1301.     loadport        ; Point at Chip's Command Reg
  1302.      setport    EN_CCMD        ; ..
  1303.     mov al,    ENC_NODMA+ENC_PAGE1
  1304.     out dx,    al        ; Switch to page 1 registers
  1305.     setport    EN1_CURPAG    ;Get current page of rcv ring
  1306.     in al,    dx        ; ..
  1307.     mov ah,    al        ; Hold current page in AH
  1308.      setport    EN_CCMD        ; Back to page zero registers
  1309.     mov al,    ENC_NODMA+ENC_PAGE0
  1310.     out dx,    al        ; Switch back to page 0 registers
  1311.     setport    EN0_BOUNDARY    ;Get boundary page
  1312.     in al,    dx        ; ..
  1313.     inc    al        ; Step boundary from last used page
  1314.     cmp al,    sm_rstop_pg    ; Wrap if needed
  1315.     jne    rx_nwrap3    ; Go if not
  1316.     mov al,    sm_rstart_pg    ; Wrap to first RX page
  1317. rx_nwrap3:
  1318.     cmp al,    ah        ; Read all the frames?
  1319.     je    recv_frame_break    ; Finished them all
  1320.     mov bl,    al        ; Page number as arg to rcv_frm
  1321.     xor ah,    ah        ; Make segment pointer to this frame
  1322.     mov cl,    4        ; 16 * pages = paragraphs
  1323.     shl ax,    cl        ; ..
  1324.     add ax,    mem_base    ; That far into shared memory
  1325.     mov es,    ax        ; Segment part of pointer
  1326.     push    es        ; Hold on to this pointer for later
  1327.     mov ax,    es:[EN_RBUF_STAT]    ; Get the buffer status byte
  1328.     test al,ENPS_RXOK    ; Good frame?
  1329.     jz    recv_no_rcv
  1330.     call    rcv_frm        ; Yes, go accept it
  1331. recv_no_rcv:
  1332.     pop    es        ; Back to base of frame
  1333.     mov ax,    es:[EN_RBUF_NXT_PG and 0fffeh] ; Start of next frame
  1334.     mov    al,ah
  1335.     dec    al        ; Make previous page for new boundary
  1336.     cmp al,    sm_rstart_pg    ; Wrap around the bottom?
  1337.     jae    rcv_nwrap4
  1338.     mov al,    sm_rstop_pg    ; Yes
  1339.     dec    al
  1340. rcv_nwrap4:
  1341.     loadport        ; Point at the Boundary Reg again
  1342.      setport    EN0_BOUNDARY    ; ..
  1343.     out dx,    al        ; Set new boundary
  1344.     jmp    recv_frame    ; See if any more frames
  1345.  
  1346. recv_frame_break:
  1347.     loadport        ; Point at Interrupt Status Reg
  1348.      setport    EN0_ISR        ; ..
  1349.     mov al,    ENISR_RX+ENISR_RX_ERR+ENISR_OVER
  1350.     out dx,    al        ; Clear those requests
  1351.     jmp    isr_no_stat    ; See if any other interrupts pending
  1352.  
  1353. isr_no_rx:
  1354. if    0    ; The following code never executed
  1355. ; Now check to see if any counters are getting full
  1356.     test al,ENISR_COUNTERS    ; Interrupt to handle counters?
  1357.     jnz    isr_stat    ; nz = yes
  1358.     jmp    isr_no_stat
  1359. isr_stat:
  1360. ; We have to read the counters to clear them and to clear the interrupt.
  1361. ; The structure of the PC/FTP driver system doesn't give us
  1362. ; anything useful to do with the data, though.
  1363.     loadport        ; Point at first counter
  1364.      setport    EN0_COUNTER0
  1365.     in    al,dx        ; Read the count, ignore it
  1366.     setport    EN0_COUNTER1
  1367.     in    al,dx        ; Read the count, ignore it
  1368.     setport    EN0_COUNTER2
  1369.     in    al,dx        ; Read the count, ignore it
  1370.     setport    EN0_ISR        ; Clear the statistics completion flag
  1371.     mov    al,ENISR_COUNTERS
  1372.     out    dx,al
  1373. endif        ; never executed code
  1374.  
  1375. isr_no_stat:
  1376.     call    xmit        ; Let xmit read ISR
  1377.      jmp    check_isr    ; Anything else to do?
  1378.  
  1379.  
  1380. ; Do the work of copying out a receive frame.
  1381. ; Called with bl/ the page number of the frame header in shared memory/
  1382. ; Also, es/ the paragraph number of that page.
  1383.  
  1384. rcv_frm:
  1385. ; Old version checked size, memory space, queue length here. Now done
  1386. ; in higher level code.
  1387. ; Set cx to length of this frame.
  1388.     mkle LE_RCVFRM_E, 0, 0, 0
  1389.  
  1390.         mov cx, es:[EN_RBUF_SIZE]       ; Extract size of frame
  1391.         sub cx, EN_RBUF_NHDR            ; Less the header stuff
  1392.         cmp cx, 1514
  1393.         jbe rcv_size_ok                 ; is the size sane? 
  1394.         cmp ch, cl                      ; is it starlan bug (dup of low byte)
  1395.         jz  rcv_starlan_bug
  1396.         mov cx, 1514                    ; cap the length
  1397.         jmp rcv_size_ok
  1398. rcv_starlan_bug:                        ; fix the starlan bug
  1399.         mov ch, es:[EN_RBUF_NXT_PG]     ; Page after this frame
  1400.         cmp ch, bl
  1401.         ja  rcv_frm_no_wrap
  1402.         add ch, sm_rstop_pg             ; Wrap if needed
  1403.         dec ch
  1404. rcv_frm_no_wrap:
  1405.         sub ch, bl
  1406.         dec ch
  1407. rcv_size_ok:
  1408. ; Set es:di to point to Ethernet type field.  es is already at base of
  1409. ; page where this frame starts.  Set di after the header and two addresses.
  1410.     mov di,    EN_RBUF_NHDR+EADDR_LEN+EADDR_LEN
  1411.     push    bx            ; Save page number in bl
  1412.     push    cx            ; Save frame size
  1413.     push    es
  1414.     mov ax,    cs            ; Set ds = code
  1415.     mov ds,    ax
  1416.     assume    ds:code
  1417.     call    recv_find        ; See if type and size are wanted
  1418.     pop    ds            ; RX page pointer in ds now
  1419.     assume    ds:nothing
  1420.     pop    cx
  1421.     pop    bx
  1422.     cld            ; Copies below are forward, please
  1423.     mov ax,    es        ; Did recv_find give us a null pointer?
  1424.     or ax,    di        ; ..
  1425.     je    rcv_no_copy    ; If null, don't copy the data    
  1426.  
  1427.     push    cx        ; We will want the count and pointer
  1428.     push    es        ;  to hand to client after copying,
  1429.     push    di        ;  so save them at this point
  1430.  
  1431. ;; if ( (((size + 255 + EN_RBUF_NHDR) >> 8) + pg) > sm_rstop_pg){
  1432.     mov ax,    cx        ; Length of frame
  1433.     add ax,    EN_RBUF_NHDR+255 ; Including the overhead bytes, rounded up
  1434.     add ah,    bl        ; Compute page with last byte of data in ah
  1435.     cmp ah,    sm_rstop_pg    ; Over the top of the ring?
  1436.     ja    rcopy_wrap    ; Yes, move in two pieces
  1437.     mov si,    EN_RBUF_NHDR    ; One piece, starts here in first page (in ds)
  1438.     mov ax,    cx        ; Move length to ax
  1439.     jmp    rcopy_one_piece    ; Go move it
  1440.  
  1441. rcopy_wrap:
  1442. ;; Copy in two pieces due to buffer wraparound.
  1443. ;; n = ((sm_rstop_pg - pg) << 8) - EN_RBUF_NHDR;    /* To top of mem */
  1444.     mov ah,    sm_rstop_pg    ; Compute length of first part
  1445.     sub ah,    bl        ;  as all of the pages up to wrap point
  1446.     xor al,    al        ; 16-bit count
  1447.     sub ax,    EN_RBUF_NHDR    ; Less the four overhead bytes
  1448.     sub cx,    ax        ; Move the rest in second part
  1449.     push    cx        ; Save count of second part
  1450.     mov si,    EN_RBUF_NHDR    ; ds:si points at first byte to move
  1451.     call    sm_copy        ; Copy first piece
  1452.     mov ax,    mem_base    ; Paragraph of base of shared memory
  1453.     mov ds,    ax        ; ..
  1454.     mov ah,    sm_rstart_pg    ; Offset to start of first receive page
  1455.     xor al,    al        ; 16-bit offset
  1456.     mov si, ax
  1457.     pop    ax        ; Bytes left to move
  1458. rcopy_one_piece:
  1459.     call    sm_copy        ; Copy piece
  1460.     pop    si        ; Recover pointer to destination
  1461.     pop    ds        ; Tell client it's his source
  1462.     pop    cx        ; And it's this long
  1463.     assume    ds:nothing
  1464.     call    recv_copy    ; Give it to him
  1465. rcv_no_copy:
  1466.     push    cs        ; Put ds back in code space
  1467.     pop    ds        ; ..
  1468.     assume    ds:code
  1469.     mkle LE_RCVFRM_X, 0, 0, 0
  1470.     ret            ; That's it for rcv_frm
  1471.  
  1472.  
  1473.     public    recv_exiting
  1474. recv_exiting:
  1475. ;called from the recv isr after interrupts have been acknowledged.
  1476. ;Only ds and ax have been saved.
  1477.     assume    ds:nothing
  1478.     ret
  1479.  
  1480.  
  1481. ;any code after this will not be kept after initialization.
  1482. end_resident    label    byte
  1483.  
  1484.  
  1485.     public    usage_msg
  1486. usage_msg    db    "usage:", CR, LF
  1487.         db    "  WD8003E [-n] [-d] [-w] <packet_int_no> <int_level> <io_addr> <mem_segm_addr>",CR,LF,'$'
  1488.  
  1489.     public    copyright_msg
  1490. copyright_msg    db    "Packet driver for Western Digital WD80x3 E S W(T) EB(T) E(T)/A ..., version "
  1491.         db    '0'+majver,".",'0'+version,CR,LF
  1492.         db    "Portions Copyright 1988, Robert C. Clements, K1BC",CR,LF,'$'
  1493.  
  1494. no_board_msg:
  1495.     db    "WD8003E apparently not present at this IO address.",CR,LF,'$'
  1496. occupied_msg:
  1497.     db    "Suggested WD8003E memory address bad or already occupied",CR,LF,'$'
  1498. int_no_name    db    "Interrupt number ",'$'
  1499. io_addr_name    db    "I/O port ",'$'
  1500. mem_base_name    db    "Memory address ",'$'
  1501. mem_size_name    db    "Memory size ",'$'
  1502.  
  1503. occupied_switch    db    0        ;if nonzero, don't use occupied test.
  1504.  
  1505.     extrn    set_recv_isr: near
  1506.     extrn    skip_blanks: near
  1507.  
  1508. ;enter with si -> argument string, di -> word to store.
  1509. ;if there is no number, don't change the number.
  1510.     extrn    get_number: near
  1511.  
  1512. ;enter with dx -> name of word, di -> dword to print.
  1513.     extrn    print_number: near
  1514.  
  1515.     public    parse_args
  1516. parse_args:
  1517.     call    skip_blanks
  1518.     cmp    al,'-'            ;did they specify a switch?
  1519.     jne    not_switch
  1520.     cmp    byte ptr [si+1],'o'    ;did they specify '-o'?
  1521.     je    got_occupied_switch
  1522.     stc                ;no, must be an error.
  1523.     ret
  1524. got_occupied_switch:
  1525.     mov    occupied_switch,1
  1526.     add    si,2            ;skip past the switch's characters.
  1527.     jmp    parse_args        ;go parse more arguments.
  1528. not_switch:
  1529.     mov    di,offset int_no
  1530.     call    get_number
  1531.     mov    di,offset io_addr
  1532.     call    get_number
  1533.     mov    di,offset mem_base
  1534.     call    get_number
  1535.     ret
  1536.  
  1537.     extrn    etopen_diagn: byte
  1538. addr_not_avail:
  1539. no_memory:
  1540.     mov    dx,offset occupied_msg
  1541.     mov    etopen_diagn,34
  1542.     jmp    short error_wrt
  1543. bad_cksum:
  1544.     mov    dx,offset no_board_msg
  1545.     mov    etopen_diagn,37
  1546. error_wrt:
  1547.     mov    ah,9
  1548.     int    21h
  1549.     stc
  1550.     ret
  1551.  
  1552.  
  1553.     public    etopen
  1554. etopen:                ; Initialize interface
  1555.     call    terminate    ; First, pulse the board reset
  1556.     test    sys_features,MICROCHANNEL
  1557.     jz    etopen_no_mc
  1558.     or    board_features,BF_EIL+BF_WTS+BF_16K
  1559.     loadport
  1560.     setport    EN_REG5
  1561.     mov    al,ENR5_EIL
  1562.     out    dx,al        ; enable 8390 interrupts to bus
  1563.     jmp    etopen_have_id
  1564. etopen_no_mc:            ; Check for WD8013EBT
  1565.     loadport        ; WD8013EBT doesn't have register alaasing
  1566.     setport    EN_CMD        ; Register 0 may be aliased to Register 8
  1567.     mov bx,    dx
  1568.     setport    EN_SAPROM
  1569.     mov cx,    EN_SAPROM-EN_CMD ; Check 8 bytes
  1570. alias_loop:
  1571.     in al,    dx        ; Get one register
  1572.     mov ah,    al
  1573.     xchg bx, dx        ; Switch to other register
  1574.     in al,    dx        ; Get other register
  1575.     cmp al,    ah        ; Are they the same?
  1576.     jne    not_aliased    ; Nope, not aliased
  1577.     inc    bx        ; Increment register pair
  1578.     inc    dx
  1579.     dec    cx        ; Decrement loop counter
  1580.     jne    alias_loop    ; Finished?
  1581.     jmp    etopen_have_id    ; Aliased; not WD8013EBT
  1582. not_aliased:            ; Not aliased; Check for 16-bit board
  1583.     loadport
  1584.     setport    EN_REG1        ; Bit 0 must be unmodifiable
  1585.     in al,    dx        ; Get register 1
  1586.     mov bl,    al        ; Store original value
  1587.     xor al,    ENR1_BUS16BIT    ; Flip bit 0
  1588.     out dx,    al        ; Write it back
  1589.     and al,    ENR1_BUS16BIT    ; Throw other bits away
  1590.     mov ah,    al        ; Store bit value
  1591.     in al,    dx        ; Read register again
  1592.     and al,    ENR1_BUS16BIT    ; Throw other bits away
  1593.     cmp al,    ah        ; Was it modified?
  1594.     jne    board16bit    ; No; board is a WD8013EBT !
  1595.     mov al,    bl        ; Get original value
  1596.     out dx,    al        ; Write it back
  1597.     jmp    etopen_have_id
  1598. board16bit:            ; But is it plugged into a 16-bit slot?
  1599.     and al,    ENR1_BUS16BIT    ; Throw other bits away
  1600.     je    etopen_have_id    ; Nope; silly board installer!
  1601.     or    board_features,BF_WTS+BF_MEM16EN+BF_16K
  1602.     setport    EN_REG5
  1603.     mov    al,ENR5_LAN16EN+ENR5_LA19 ; Write LA19 now, but not MEM16EN
  1604.     out    dx,al        ; enable 8390 interrupts to bus
  1605.     
  1606. etopen_have_id:
  1607.     loadport
  1608.     setport    EN_CCMD        ; DS8390 chip's command register
  1609.     mov al,    ENC_NODMA+ENC_PAGE0    
  1610.     out dx,    al        ; Switch to page zero
  1611.     setport    EN0_ISR        ; Clear all interrupt flags
  1612.     mov al,    0ffh        ; ..
  1613.     out dx,    al        ; ..
  1614. ; Copy our Ethernet address from PROM into the DS8390
  1615. ; (No provision in driver spec for setting a false address.)
  1616.     setport    EN_CCMD        ; Chip command register
  1617.     mov al,    ENC_NODMA+ENC_PAGE1
  1618.     out dx,    al        ; Switch to page one for writing eaddr
  1619.     mov cl,    EADDR_LEN    ; Loop for six bytes
  1620.     xor ch,    ch        ; Clear the index of bytes
  1621.     xor bx,    bx        ; Clear the addr ROM checksum
  1622. cpy_adr_loop:
  1623.     loadport        ; Base of registers
  1624.     setport    EN_SAPROM    ; Prom address
  1625.     add dl,    ch        ; Plus which byte this is
  1626.     in al,    dx        ; Get a byte of address
  1627.     add    bl,al        ; Compute the checksum
  1628.     add dl,    EN1_PHYS-EN_SAPROM ; Point at reg in chip
  1629.     out dx,    al        ; Copy that byte
  1630.     inc    ch        ; Step the index
  1631.     dec    cl        ; Count bytes
  1632.     jnz    cpy_adr_loop    ; Loop for six
  1633.     loadport        ; Get last two bytes into cksum
  1634.     setport    EN_SAPROM+EADDR_LEN
  1635.     in al,    dx        ; Get seventh byte
  1636.     add bl,    al        ; Add it in
  1637.     inc    dx        ; Step to eighth byte
  1638.     in al,    dx        ; Get last byte
  1639.     add bl,    al        ; Final checksum
  1640.     cmp bl, 0ffh        ; Correct?
  1641.     je    good_cksum    ; Damn 128 byte jump limit...
  1642.     jmp    bad_cksum    ; No, board is not happy
  1643. good_cksum:
  1644. ; Clear the multicast filter enables, we don't want any of them.
  1645.     mov cl,    8        ; Eight bytes of multicast filter
  1646.     xor al,    al        ; Zeros for filter
  1647.     loadport        ; Base of multicast filter locations
  1648.     setport    EN1_MULT    ; ..
  1649. clr_mcast_l:
  1650.     out dx,    al        ; Clear a byte
  1651.     inc    dl        ; Step to next one
  1652.     dec    cl        ; Count 8 filter locs
  1653.     jnz    clr_mcast_l    ; ..    
  1654.     loadport        ; Base of I/O regs
  1655.     setport    EN_CCMD        ; Chip command register
  1656.     mov al,    ENC_NODMA+ENC_PAGE0
  1657.     out dx,    al        ; Back to page zero
  1658.     setport    EN0_DCFG    ; Configure the fifo organization
  1659.     mov al,    ENDCFG_BM8    ; Fifo threshold = 8 bytes
  1660.     test    board_features,BF_WTS
  1661.     jz    bytemove
  1662.     or    al,ENDCFG_WTS    ; word access for 16-bit cards
  1663. bytemove:
  1664.     out dx,    al
  1665.     setport    EN0_RCNTLO    ; Clear the byte count registers
  1666.     xor al,    al        ; ..
  1667.     out dx,    al
  1668.     setport    EN0_RCNTHI
  1669.     out dx,    al        ; Clear high byte, too
  1670.     setport    EN0_RXCR    ; Set receiver to monitor mode
  1671.     mov al,    ENRXCR_MON
  1672.     out dx,    al
  1673.     setport    EN0_TXCR    ; Set transmitter mode to normal
  1674.     xor al,    al
  1675.     out dx,    al
  1676.  
  1677. ; Check if the shared memory address range is availabe to us
  1678.     mov    bx,mem_base
  1679.     test    occupied_switch,1
  1680.     jnz    no_lim_chk
  1681.     cmp    bh,80h            ; low limit is 8000
  1682.     jae    fr_8000
  1683.     jmp    no_memory
  1684. fr_8000:
  1685.     cmp    bh,0f0h            ; upper limit is F000
  1686.     jb    to_F000
  1687.     jmp    no_memory
  1688. to_F000:
  1689.     test    bx,01ffh        ; must be on a 8 k boundary
  1690.     jz    eightk
  1691.     jmp    no_memory
  1692. eightk:
  1693. no_lim_chk:
  1694.     mov    di,8*1024/16        ; 8 kbyte
  1695.     mov    sm_rstop_pg,32
  1696.     test    board_features,BF_16K
  1697.     jz    just_8k
  1698.     mov    di,16*1024/16        ; 16 kbytes
  1699.     mov    sm_rstop_pg,64
  1700. just_8k:
  1701.     test    occupied_switch,1    ; did they insist?
  1702.     jnz    is_avail        ; yes, don't check.
  1703.     call    occupied_chk        ; check if address range is available
  1704.     jnc    is_avail
  1705.     jmp    addr_not_avail        ; we HAVE to have at least 8/16 kbyte
  1706. is_avail:
  1707.     test    board_features,BF_16K
  1708.     jnz    not_32k
  1709.     mov    di,32*1024/16        ; may be there is space for 32 kbyte
  1710.     call    occupied_chk
  1711.     jc    not_32k            ; no, then don't try it later either
  1712.     and    bh,7
  1713.     jnz    not_32k            ; must be on a 32k boundary
  1714.     mov    sm_rstop_pg,128        ; yes, there is space for a WD8003EBT
  1715. not_32k:
  1716.  
  1717. ; Turn on the shared memory block
  1718.     setport    EN_CMD        ; Point at board command register
  1719.     mov ax,    mem_base    ; Find where shared memory will be mapped
  1720.     mov al,    ah        ; Shift to right location
  1721.     sar al,    1        ;  in the map control word
  1722.     and al,    EN_MEM_MASK    ; Just these bits
  1723.     or al,    EN_MEMEN    ; Command to turn on map
  1724.     test    sys_features,MICROCHANNEL
  1725.     jz    AT_card
  1726.     mov    al,EN_MEMEN    ; membase handled different for MC card
  1727. AT_card:
  1728.     out dx,    al        ; Create that memory
  1729.  
  1730. ; Find how much memory this card has (without destroying other memory)
  1731.     mov    si,ax            ; save bord command value
  1732.     mov    es,mem_base
  1733.     mov    bl,0FFh            ; first try 32 kbyte (WD8003EBT)
  1734.     mov    bh,sm_rstop_pg        ;   or what is available
  1735.     dec    bh
  1736. memloop:
  1737.     dec    bx            ; use even address
  1738.     cli                ; disable interrupts
  1739.     mov    cx,es:[bx]        ; save old memory contents
  1740.     mov    word ptr es:[bx],05A5Ah    ; put testpattern
  1741.     loadport
  1742.     setport    EN_CCMD            ; drain the board bus for any
  1743.     in    al,dx            ;   capacitive memory
  1744.     cmp    word ptr es:[bx],05A5Ah    ; any real memory there?
  1745.     jne    not_our_mem        ;   no
  1746.     setport    EN_CMD            ;   yes
  1747.     mov    ax,si
  1748.     and    al,0FFh xor EN_MEMEN
  1749.     out    dx,al            ; turn off our memory
  1750.     jmp    short $+2
  1751.     or    al,EN_MEMEN
  1752.     cmp    word ptr es:[bx],05A5Ah    ; was it OUR memory?
  1753.     out    dx,al
  1754.     jmp    short $+2
  1755.     mov    es:[bx],cx
  1756.     sti
  1757.     jne    our_mem            ;   yes, it wasn't there any more
  1758. not_our_mem:                ;   no, it was still there
  1759.     shr    bx,1            ; test if half as much memory
  1760.     cmp    bx,1FFFh        ; down to 8 kbyte
  1761.     jae    memloop
  1762.     jmp    no_memory        ; no memory at address mem_base
  1763. our_mem:                ; it IS our memory!
  1764.     inc    bh
  1765.     mov    sm_rstop_pg,bh        ; # of 256 byte ring bufs + 1
  1766.     cmp    bh,32            ; Eight k card?
  1767.     jbe    rstart_set        ; Yes
  1768.     mov    sm_rstart_pg,SM_TMAX_PG    ; Allocate more transmit buffers
  1769.     mov    xmt_bufs,SM_TMAX_PG/6    ; Set xmt_bufs
  1770.     mov    al,bh            ; Calculate rcv_bufs as
  1771.     sub    al,SM_TMAX_PG        ;  bufs - xmt_bufs
  1772.     xor    ah,ah
  1773.     mov    cl,6            ;  divide by 6 (pages per MTU pkt)
  1774.     div    cl
  1775.     xor    ah,ah
  1776.     mov    rcv_bufs,ax
  1777. rstart_set:
  1778.     mov    ch,bh
  1779.     xor    cl,cl
  1780.     mov    ax,mem_base
  1781.     call    memory_test        ; check all of that memory
  1782.     je    mem_ok
  1783.     jmp    no_memory
  1784. mem_ok:
  1785.  
  1786. ; Set up control of shared memory, buffer ring, etc.
  1787.     loadport
  1788.     setport    EN0_STARTPG    ; Set receiver's first buffer page
  1789.     mov al,    sm_rstart_pg
  1790.     out dx,    al
  1791.     setport    EN0_STOPPG    ;  and receiver's last buffer page + 1
  1792.     mov al,    sm_rstop_pg
  1793.     out dx,    al
  1794.     setport    EN0_BOUNDARY    ; Set initial "last page we have emptied"
  1795.     mov al,    sm_rstart_pg
  1796.     out dx,    al
  1797.     setport    EN_CCMD        ; Switch to page one registers
  1798.     mov al,    ENC_NODMA+ENC_PAGE1
  1799.     out dx,    al
  1800.     setport    EN1_CURPAG    ; Set current shared page for RX to work on
  1801.     mov al,    sm_rstart_pg
  1802.     inc al
  1803.     out dx,    al
  1804.     setport    EN_CCMD        ; Switch back to page zero registers
  1805.     mov al,    ENC_NODMA+ENC_PAGE0
  1806.     out dx,    al
  1807.     setport    EN0_IMR        ; Clear all interrupt enable flags
  1808.     xor al,    al
  1809.     out dx,    al
  1810.     setport    EN0_ISR        ; Clear all interrupt assertion flags
  1811.     mov al,    0ffh        ; again for safety before making the
  1812.     out dx,    al        ; interrupt be enabled
  1813.     call    set_recv_isr    ; Put ourselves in interrupt chain
  1814.     loadport
  1815.     setport    EN_CCMD        ; Now start the DS8390
  1816.     mov al,    ENC_START+ENC_NODMA
  1817.     out dx,    al        ; interrupt be enabled
  1818.     setport    EN0_RXCR    ; Tell it to accept broadcasts
  1819.     mov al,    ENRXCR_BCST
  1820.     out dx,    al
  1821.     setport    EN0_IMR        ; Tell card it can cause these interrupts
  1822.     mov al,    ENISR_ALL
  1823.     out dx,    al
  1824.  
  1825.     mov al,    int_no        ; Get board's interrupt vector
  1826.     add al,    8
  1827.     cmp al,    8+8        ; Is it a slave 8259 interrupt?
  1828.     jb    set_int_num    ; No.
  1829.     add al,    70h - 8 - 8    ; Map it to the real interrupt.
  1830. set_int_num:
  1831.     xor ah,    ah        ; Clear high byte
  1832.     mov int_num, ax        ; Set parameter_list int num.
  1833.  
  1834.     mov    dx,offset end_resident
  1835.     clc
  1836.     ret
  1837.  
  1838.     public    print_parameters
  1839. print_parameters:
  1840. ;echo our command-line parameters
  1841.     mov    di,offset int_no
  1842.     mov    dx,offset int_no_name
  1843.     call    print_number
  1844.     mov    di,offset io_addr
  1845.     mov    dx,offset io_addr_name
  1846.     call    print_number
  1847.     mov    di,offset mem_base
  1848.     mov    dx,offset mem_base_name
  1849.     call    print_number
  1850.     xor    ax,ax
  1851.     push    ax
  1852.     mov    ah,sm_rstop_pg
  1853.     push    ax
  1854.     mov    di,sp
  1855.     mov    dx,offset mem_size_name
  1856.     call    print_number
  1857.     add    sp,4
  1858.     ret
  1859.  
  1860.     include    memtest.asm
  1861.     include    occupied.asm
  1862.  
  1863. code    ends
  1864.     end
  1865.