home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PROGRAMS / UTILS / NOVELL / PKTDRV7.ZIP / 3C503.ASM < prev    next >
Encoding:
Assembly Source File  |  1990-07-27  |  39.7 KB  |  1,160 lines

  1. version    equ    3
  2.  
  3.     include    defs.asm
  4.  
  5. ;/* PC/FTP Packet Driver source, conforming to version 1.05 of the spec,
  6. ;*  for the 3-Com 3C503 interface card.
  7. ;*  Robert C Clements, K1BC, 14 February, 1989
  8. ;*  Portions (C) Copyright 1988, 1989 Robert C Clements
  9. ;*
  10. ;  Copyright, 1988, 1989, Russell Nelson
  11.  
  12. ;   This program is free software; you can redistribute it and/or modify
  13. ;   it under the terms of the GNU General Public License as published by
  14. ;   the Free Software Foundation, version 1.
  15. ;
  16. ;   This program is distributed in the hope that it will be useful,
  17. ;   but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. ;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19. ;   GNU General Public License for more details.
  20. ;
  21. ;   You should have received a copy of the GNU General Public License
  22. ;   along with this program; if not, write to the Free Software
  23. ;   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  24.  
  25. ;* Change history:
  26. ;*  Updated to driver spec version 1.08 Feb. 17, 1989 by Russell Nelson.
  27. ;*  Changes 27 Jul 89 by Bob Clements (/Rcc)
  28. ;*    Added Thick versus Thin Ethernet switch  27 Jul 89 by Bob Clements (/Rcc)
  29. ;*    Added call to memory_test.
  30. ;*    Added rcv_mode logic.  Started, but didn't finish, multicast logic. 
  31. ;*      Fixed get_address to return current, not PROM, address.
  32. ;*      Minor races fixed.
  33.  
  34. comment /
  35. From: "James A. Harvey" <IJAH400@indyvax.iupui.edu>
  36. Subject: Patches for 6.x packet drivers; lockup problem fixed!
  37.  
  38. Now for the best part, the lockup problem fix.  I think this may be one that
  39. I keep hearing about that for most people the machine locks up for a minute
  40. on startup, but then continues.  For me it was worse because it appears that
  41. the "recovery" time is only short on heavily loaded networks.  The lockup is
  42. caused by the "first page for RX" being set improperly in etopen; I finally
  43. figured it out by looking at code from other drivers that used the DS8390
  44. chip.  One must switch to the page 1 command registers first.
  45. /
  46.  
  47.  
  48. code    segment    word public
  49.     assume    cs:code, ds:code
  50.  
  51. HT    equ    09h
  52. CR    equ    0dh
  53. LF    equ    0ah
  54.  
  55. ;
  56. ;  Packet Driver Error numbers
  57. BAD_HANDLE    equ    1        ;invalid handle number
  58. NO_CLASS    equ    2        ;no interfaces of specified class found
  59. NO_TYPE        equ    3        ;no interfaces of specified type found
  60. NO_NUMBER    equ    4        ;no interfaces of specified number found
  61. BAD_TYPE    equ    5        ;bad packet type specified
  62. NO_MULTICAST    equ    6        ;this interface does not support
  63. CANT_TERMINATE    equ    7        ;this packet driver cannot terminate
  64. BAD_MODE    equ    8        ;an invalid receiver mode was specified
  65. NO_SPACE    equ    9        ;operation failed because of insufficient
  66. TYPE_INUSE    equ    10        ;the type had previously been accessed,
  67. BAD_COMMAND    equ    11        ;the command was out of range, or not
  68. CANT_SEND    equ    12        ;the packet couldn't be sent (usually
  69.  
  70. ; Stuff specific to the 3-Com 3C503 Ethernet controller board
  71. ; WD version in C by Bob Clements, K1BC, May 1988 for the KA9Q TCP/IP package
  72. ; 3Com version based on WD8003E version in .ASM, also by Bob Clements, dated
  73. ;  19 August 1988.  The WD and 3Com cards both use the National DS8390.
  74.  
  75. ; Symbol prefix "EN" is for Ethernet, National chip
  76. ; Symbol prefix "E33" is for _E_thernet, _3_Com 50_3_
  77. ; Symbol prefix "E33G" is for registers in the Gate array ASIC.
  78.  
  79. ; The E33 registers - For the ASIC on the 3C503 card:
  80. ; Offsets from the board's base address, which can be set by
  81. ; jumpers to be one of the following 8 values (hex):
  82. ;  350, 330, 310, 300, 2E0, 2A0, 280, 250
  83. ; Factory default address is 300H.
  84. ; The card occupies a block of 16 I/O addresses.
  85. ; It also occupies 16 addresses at base+400 through base+40F.
  86. ; These high-addressed registers are in the ASIC.
  87. ; Recall that the normal PC I/O decoding is only 10 bits. The 11'th
  88. ; bit (400H) can be used on the same card for additional registers.
  89. ; This offset requires word, not byte, arithmetic
  90. ; on the DX register for the setport macro. Current SETPORT is OK.
  91.  
  92. ; The card can also be jumpered to have the shared memory disabled
  93. ; or enabled at one of four addresses: C8000, CC000, D8000 or DC000.
  94. ; This version of the driver REQUIRES the shared memory to be 
  95. ; enabled somewhere.
  96. ; The card can be operated using direct I/O instructions or by
  97. ; using the PC's DMA channels instead of the shared memory, but
  98. ; I haven't included the code for those other two methods. 
  99. ; They would be needed in a system where all four possible addresses
  100. ; for the shared memory are in use by other devices.  /Rcc
  101.  
  102. ; Blocks of I/O addresses:
  103.  
  104. E33GA        equ    400h    ; Registers in the gate array.
  105. E33_SAPROM    equ    000h    ; Window on station addr prom (if
  106.                 ; E33G_CNTRL bits 3,2 = 0,1
  107.  
  108. ; The EN registers - the DS8390 chip registers
  109. ; These appear at Base+0 through Base+0F when bits 3,2 of
  110. ; E33G_CNTRL are 0,0.
  111. ; There are two (really 3) pages of registers in the chip. You select
  112. ; which page you want, then address them at offsets 00-0F from base.
  113. ; The chip command register (EN_CCMD) appears in both pages.
  114.  
  115. EN_CCMD        equ    000h    ; Chip's command register
  116.  
  117. ; Page 0
  118.  
  119. EN0_STARTPG    equ    001h    ; Starting page of ring bfr
  120. EN0_STOPPG    equ    002h    ; Ending page +1 of ring bfr
  121. EN0_BOUNDARY    equ    003h    ; Boundary page of ring bfr
  122. EN0_TSR        equ    004h    ; Transmit status reg
  123. EN0_TPSR    equ    004h    ; Transmit starting page
  124. EN0_TCNTLO    equ    005h    ; Low  byte of tx byte count
  125. EN0_TCNTHI    equ    006h    ; High byte of tx byte count
  126. EN0_ISR        equ    007h    ; Interrupt status reg
  127. EN0_RSARLO    equ    008h    ; Remote start address reg 0
  128. EN0_RSARHI    equ    009h    ; Remote start address reg 1
  129. EN0_RCNTLO    equ    00ah    ; Remote byte count reg
  130. EN0_RCNTHI    equ    00bh    ; Remote byte count reg
  131. EN0_RXCR    equ    00ch    ; RX control reg
  132. EN0_TXCR    equ    00dh    ; TX control reg
  133. EN0_COUNTER0    equ    00dh    ; Rcv alignment error counter
  134. EN0_DCFG    equ    00eh    ; Data configuration reg
  135. EN0_COUNTER1    equ    00eh    ; Rcv CRC error counter
  136. EN0_IMR        equ    00fh    ; Interrupt mask reg
  137. EN0_COUNTER2    equ    00fh    ; Rcv missed frame error counter
  138.  
  139. ; Page 1
  140.  
  141. EN1_PHYS    equ    001h    ; This board's physical enet addr
  142. EN1_CURPAG    equ    007h    ; Current memory page
  143. EN1_MULT    equ    008h    ; Multicast filter mask array (8 bytes)
  144.  
  145. ; Chip commands in EN_CCMD
  146. ENC_STOP    equ    001h    ; Stop the chip
  147. ENC_START    equ    002h    ; Start the chip
  148. ENC_TRANS    equ    004h    ; Transmit a frame
  149. ENC_RREAD    equ    008h    ; remote read
  150. ENC_RWRITE    equ    010h    ; remote write
  151. ENC_NODMA    equ    020h    ; No remote DMA used on this card
  152. ENC_PAGE0    equ    000h    ; Select page 0 of chip registers
  153. ENC_PAGE1    equ    040h    ; Select page 1 of chip registers
  154.  
  155. ; Commands for RX control reg
  156. ENRXCR_MON    equ    020h    ; Monitor mode (no packets rcvd)
  157. ENRXCR_PROMP    equ    010h    ; Promiscuous physical addresses 
  158. ENRXCR_MULTI    equ    008h    ; Multicast (if pass filter)
  159. ENRXCR_BCST    equ    004h    ; Accept broadcasts
  160. ENRXCR_BAD    equ    003h    ; Accept runts and bad CRC frames
  161.  
  162. ; Commands for TX control reg
  163. ENTXCR_LOOP    equ    002h    ; Set loopback mode
  164.  
  165. ; Bits in EN0_DCFG - Data config register
  166. ENDCFG_BM8    equ    048h    ; Set burst mode, 8 deep FIFO
  167.  
  168. ; Bits in EN0_ISR - Interrupt status register
  169. ENISR_RX    equ    001h    ; Receiver, no error
  170. ENISR_TX    equ    002h    ; Transmitter, no error
  171. ENISR_RX_ERR    equ    004h    ; Receiver, with error
  172. ENISR_TX_ERR    equ    008h    ; Transmitter, with error
  173. ENISR_OVER    equ    010h    ; Receiver overwrote the ring
  174. ENISR_COUNTERS    equ    020h    ; Counters need emptying
  175. ENISR_RDC    equ    040h    ; remote dma complete
  176. ENISR_RESET    equ    080h    ; Reset completed
  177. ENISR_ALL    equ    03fh    ; Interrupts we will enable
  178.  
  179. ; Bits in received packet status byte and EN0_RSR
  180. ENPS_RXOK    equ    001h    ; Received a good packet
  181.  
  182. ; Bits in TX status reg
  183.  
  184. ENTSR_PTX    equ    001h    ; Packet transmitted without error
  185. ENTSR_COLL    equ    004h    ; Collided at least once
  186. ENTSR_COLL16    equ    008h    ; Collided 16 times and was dropped
  187. ENTSR_FU    equ    020h    ; TX FIFO Underrun
  188.  
  189. ; Registers in the 3-Com custom Gate Array
  190.  
  191. E33G_STARTPG    equ E33GA+00h    ; Start page, must match EN0_STARTPG
  192. E33G_STOPPG    equ E33GA+01h    ; Stop  page, must match EN0_STOPPG
  193. E33G_NBURST    equ E33GA+02h    ; Size of DMA burst before relinquishing bus
  194. E33G_IOBASE    equ E33GA+03h    ; Bit coded: where I/O regs are jumpered.
  195.                 ; (Which you have to know already to read it)
  196. E33G_ROMBASE    equ E33GA+04h    ; Bit coded: Where/whether EEPROM&DPRAM exist
  197. E33G_GACFR    equ E33GA+05h    ; Config/setup bits for the ASIC GA
  198. E33G_CNTRL    equ E33GA+06h    ; Board's main control register
  199. E33G_STATUS    equ E33GA+07h    ; Status on completions.
  200. E33G_IDCFR    equ E33GA+08h    ; Interrupt/DMA config register
  201.                 ; (Which IRQ to assert, DMA chan to use)
  202. E33G_DMAAH    equ E33GA+09h    ; High byte of DMA address reg
  203. E33G_DMAAL    equ E33GA+0ah    ; Low byte of DMA address reg
  204. E33G_VP2    equ E33GA+0bh    ; Vector pointer - for clearing RAM select
  205. E33G_VP1    equ E33GA+0ch    ;  on a system reset, to re-enable EPROM.
  206. E33G_VP0    equ E33GA+0dh    ;  3Com says set this to Ctrl-Alt-Del handler
  207. E33G_FIFOH    equ E33GA+0eh    ; FIFO for programmed I/O data moves ...
  208. E33G_FIFOL    equ E33GA+0fh    ; .. low byte of above.
  209.  
  210. ; Bits in E33G_CNTRL register:
  211.  
  212. ECNTRL_RESET    equ    001h    ; Software reset of the ASIC and 8390
  213. ECNTRL_THIN    equ    002h    ; Onboard thin-net xcvr enable
  214. ECNTRL_SAPROM    equ    004h    ; Map the station address prom
  215. ECNTRL_DBLBFR    equ    020h    ; FIFO configuration bit
  216. ECNTRL_OUTPUT    equ    040h    ; PC-to-3C501 direction if 1
  217. ECNTRL_START    equ    080h    ; Start the DMA logic
  218.  
  219. ; Bits in E33G_STATUS register:
  220.  
  221. ESTAT_DPRDY    equ    080h    ; Data port (of FIFO) ready
  222. ESTAT_UFLW    equ    040h    ; Tried to read FIFO when it was empty
  223. ESTAT_OFLW    equ    020h    ; Tried to write FIFO when it was full
  224. ESTAT_DTC    equ    010h    ; Terminal Count from PC bus DMA logic
  225. ESTAT_DIP    equ    008h    ; DMA In Progress
  226.  
  227. ; Bits in E33G_GACFR register:
  228.  
  229. EGACFR_NORM    equ    049h    ; Enable 8K shared mem, no DMA TC int
  230. EGACFR_IRQOFF    equ    0c9h    ; Above, and disable 8390 IRQ line
  231.  
  232. ; Shared memory management parameters
  233.  
  234. XMIT_MTU    equ    600h    ; Largest packet we have room for.
  235. SM_TSTART_PG    equ    020h    ; First page of TX buffer
  236. SM_RSTART_PG    equ    026h    ; Starting page of RX ring
  237. SM_RSTOP_PG    equ    040h    ; Last page +1 of RX ring
  238.  
  239. ; Description of header of each packet in receive area of shared memory
  240.  
  241. EN_RBUF_STAT    equ    0    ; Received frame status
  242. EN_RBUF_NXT_PG    equ    1    ; Page after this frame
  243. EN_RBUF_SIZE_LO    equ    2    ; Length of this frame
  244. EN_RBUF_SIZE_HI    equ    3    ; Length of this frame
  245. EN_RBUF_NHDR    equ    4    ; Length of above header area
  246.  
  247. ; End of 3C503 parameter definitions
  248.  
  249. ; The following two values may be overridden from the command line.
  250. ; If they are omitted from the command line, these defaults are used.
  251. ; The shared memory base is set by a jumper.  We read it from the
  252. ; card and set up accordingly.
  253.  
  254.     public    int_no, io_addr, thick_or_thin
  255. int_no        db    2,0,0,0        ; Interrupt level
  256. io_addr        dw    0300h,0        ; I/O address for card (jumpers)
  257. thick_or_thin    dw    1,0        ; Non-zero means thin net
  258.     public    mem_base
  259. mem_base    dw    00000h,0    ; Shared memory addr (jumpers)
  260. ; (Not changeable by software in 3C503)    ; (0 if disabled by jumpers)
  261. thin_bit    db    ECNTRL_THIN    ; Default to thin cable
  262. rxcr_bits       db      ENRXCR_BCST     ; Default to ours plus multicast
  263.  
  264.     public    driver_class, driver_type, driver_name, driver_function, parameter_list
  265. driver_class    db    1        ;from the packet spec
  266. driver_type    db    12        ;from the packet spec
  267. driver_name    db    '3C503',0    ;name of the driver.
  268. driver_function    db    2
  269. parameter_list    label    byte
  270.     db    1    ;major rev of packet driver
  271.     db    9    ;minor rev of packet driver
  272.     db    14    ;length of parameter list
  273.     db    EADDR_LEN    ;length of MAC-layer address
  274.     dw    GIANT    ;MTU, including MAC headers
  275.     dw    MAX_MULTICAST * EADDR_LEN    ;buffer size of multicast addrs
  276.     dw    0    ;(# of back-to-back MTU rcvs) - 1
  277.     dw    0    ;(# of successive xmits) - 1
  278. int_num    dw    0    ;Interrupt # to hook for post-EOI
  279.             ;processing, 0 == none,
  280.  
  281.     public    card_hw_addr, curr_hw_addr, mcast_list_bits, mcast_all_flag
  282. card_hw_addr    db    0,0,0,0,0,0    ;Physical ethernet address
  283. curr_hw_addr    db    0,0,0,0,0,0    ;Address set into the 8390
  284. mcast_list_bits db      0,0,0,0,0,0,0,0 ;Bit mask from last set_multicast_list
  285. mcast_all_flag  db      0               ;Non-zero if hware should have all
  286.                     ; ones in mask rather than this list.
  287.  
  288.     public    rcv_modes
  289. rcv_modes    dw    7        ;number of receive modes in our table.
  290.         dw    0               ;There is no mode zero
  291.         dw    rcv_mode_1
  292.         dw    rcv_mode_2
  293.         dw    rcv_mode_3
  294.         dw    rcv_mode_4
  295.         dw    rcv_mode_5
  296.         dw    rcv_mode_6
  297.  
  298. ; send_pkt: - The Transmit Frame routine
  299.  
  300.     public    as_send_pkt
  301. ; The Asynchronous Transmit Packet routine.
  302. ; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
  303. ;   interrupts possibly enabled.
  304. ; Exit with nc if ok, or else cy if error, dh set to error number.
  305. ;   es:di and interrupt enable flag preserved on exit.
  306. as_send_pkt:
  307.     ret
  308.  
  309.     public    drop_pkt
  310. ; Drop a packet from the queue.
  311. ; Enter with es:di -> iocb.
  312. drop_pkt:
  313.     assume    ds:nothing
  314.     ret
  315.  
  316.     public    xmit
  317. ; Process a transmit interrupt with the least possible latency to achieve
  318. ;   back-to-back packet transmissions.
  319. ; May only use ax and dx.
  320. xmit:
  321.     assume    ds:nothing
  322.     ret
  323.  
  324.  
  325.     public    send_pkt
  326. send_pkt:
  327. ;enter with es:di->upcall routine, (0:0) if no upcall is desired.
  328. ;  (only if the high-performance bit is set in driver_function)
  329. ;enter with ds:si -> packet, cx = packet length.
  330. ;exit with nc if ok, or else cy if error, dh set to error number.
  331.     assume    ds:nothing
  332.     loadport        ; Point at chip command register
  333.     setport EN_CCMD        ; ..
  334.     mov bx,    8000h        ; Avoid infinite loop
  335. tx_wait:
  336.     in al,    dx        ; Get chip command state
  337.     test al,ENC_TRANS    ; Is transmitter still running?
  338.     jz    tx_idle        ; Go if free
  339.     dec    bx        ; Count the timeout
  340.     jnz    tx_wait        ; Fall thru if TX is stuck
  341.                 ; Should count these error timeouts
  342.                 ; Maybe need to add recovery logic here
  343. tx_idle:
  344.     cmp    cx,XMIT_MTU    ; Is this packet too large?
  345.     ja    send_pkt_toobig
  346.  
  347.     cmp cx,    RUNT        ; Is the frame long enough?
  348.     jnb    tx_oklen    ; Go if OK
  349.     mov cx,    RUNT        ; Stretch frame to minimum allowed
  350. tx_oklen:
  351.     push    cx        ; Hold count for later
  352.     loadport        ; Set up for address of TX buffer in
  353.     setport    E33G_GACFR    ; Make sure gate array is set up and
  354.     mov al,    EGACFR_NORM    ;  the RAM is enabled (not EPROM)
  355.     out dx,    al        ; ..
  356.     mov ax,    cs:mem_base    ; Set up ES at the shared RAM
  357.     mov es,    ax        ; ..
  358.     xor ax,    ax        ; Set up DI at base of tx buffer
  359.     mov ah,    SM_TSTART_PG    ; Where to put tx frame
  360.     mov di,    ax        ; ..
  361.     call    movemem
  362.     pop    cx        ; Get back count to give to board
  363.     setport    EN0_TCNTLO    ; Low byte of TX count
  364.     mov al,    cl        ; Get the count
  365.     out dx,    al        ; Tell card the count
  366.     setport    EN0_TCNTHI    ; High byte of TX count
  367.     mov al,    ch        ; Get the count
  368.     out dx,    al        ; Tell card the count
  369.     setport    EN0_TPSR    ; Transmit Page Start Register
  370.     mov al,    SM_TSTART_PG
  371.     out dx,    al        ; Start the transmitter
  372.     setport    EN_CCMD        ; Chip command reg
  373.     mov al,    ENC_TRANS+ENC_NODMA
  374.     out dx,    al        ; Start the transmitter
  375.     clc            ; Successfully started
  376.     ret            ; End of transmit-start routine
  377. send_pkt_toobig:
  378.     mov    dh,NO_SPACE
  379.     stc
  380.     ret
  381.  
  382.  
  383.     include    movemem.asm
  384.  
  385.  
  386.     public    get_address
  387. get_address:
  388. ;get the address of the interface.
  389. ;enter with es:di -> place to get the address, cx = size of address buffer.
  390. ;exit with nc, cx = actual size of address, or cy if buffer not big enough.
  391. ; Give caller the one currently in the 8390, not necessarily the one in PROM.
  392.     assume ds:code
  393.     cmp cx,    EADDR_LEN    ; Caller wants a reasonable length?
  394.     jb    get_addr_x    ; No, fail.
  395.     mov cx,    EADDR_LEN    ; Move one ethernet address from our copy
  396.     mov si, offset curr_hw_addr     ; Copy from most recent setting
  397.     rep     movsb
  398.     mov cx,    EADDR_LEN    ; Tell caller how many bytes we fed him
  399.     clc            ; Carry off says success
  400.     ret
  401. get_addr_x:
  402.     stc            ; Tell caller our addr is too big for him
  403.     ret
  404.  
  405.  
  406.     public    set_address
  407. set_address:
  408.     assume    ds:nothing
  409. ;enter with ds:si -> Ethernet address, CX = length of address.
  410. ;exit with nc if okay, or cy, dh=error if any errors.
  411. ;
  412.     cmp    cx,EADDR_LEN        ;ensure that their address is okay.
  413.     je    set_address_4
  414.     mov    dh,BAD_ADDRESS
  415.     stc
  416.     jmp    short set_address_done
  417. set_address_4:
  418.     push    cs              ; Copy from them to our RAM copy
  419.     pop     es              ; Destination of move
  420.     mov di, offset curr_hw_addr
  421.     rep     movsb           ; Move their address
  422.     call    set_8390_eaddr  ; Put that address in the chip
  423. set_address_okay:
  424.     mov    cx,EADDR_LEN        ;return their address length.
  425.     clc
  426. set_address_done:
  427.     push    cs
  428.     pop    ds
  429.     assume    ds:code
  430.     ret
  431.  
  432. ; Copy our Ethernet address from curr_hw_addr into the DS8390
  433. set_8390_eaddr:
  434.     push    cs              ; Get it from our local RAM copy
  435.     pop     ds
  436.     mov si, offset curr_hw_addr
  437.     mov cx,    EADDR_LEN    ; Move one ethernet address from our copy
  438.     loadport
  439.     setport    EN_CCMD        ; Chip command register
  440.     cli            ; Protect from irq changing page bits
  441.     mov al,    ENC_NODMA+ENC_PAGE1
  442.     out dx,    al        ; Switch to page one for writing eaddr
  443.     setport    EN1_PHYS    ; Where it goes in 8390
  444. set_8390_1:
  445.     lodsb
  446.     out    dx,al
  447.     inc    dx
  448.     loop    set_8390_1
  449.     loadport
  450.     setport    EN_CCMD        ; Chip command register
  451.     mov al,    ENC_NODMA+ENC_PAGE0
  452.     out dx,    al        ; Restore to page zero
  453.     sti            ; OK for interrupts now
  454.     ret
  455.  
  456. ; Routines to set address filtering modes in the DS8390
  457. rcv_mode_1:     ; Turn off receiver
  458.     mov al,    ENRXCR_MON      ; Set to monitor for counts but accept none
  459.     jmp short rcv_mode_set
  460. rcv_mode_2:     ; Receive only packets to this interface
  461.     mov al, 0               ; Set for only our packets
  462.     jmp short rcv_mode_set
  463. rcv_mode_3:     ; Mode 2 plus broadcast packets (This is the default)
  464.     mov al,    ENRXCR_BCST     ; Set four ours plus broadcasts
  465.     jmp short rcv_mode_set
  466. rcv_mode_4:     ; Mode 3 plus selected multicast packets
  467.     mov al,    ENRXCR_BCST+ENRXCR_MULTI ; Ours, bcst, and filtered multicasts
  468.     mov     mcast_all_flag,0
  469.     jmp short rcv_mode_set
  470. rcv_mode_5:     ; Mode 3 plus ALL multicast packets
  471.     mov al,    ENRXCR_BCST+ENRXCR_MULTI ; Ours, bcst, and filtered multicasts
  472.     mov     mcast_all_flag,1
  473.     jmp short rcv_mode_set
  474. rcv_mode_6:     ; Receive all packets (Promiscuous physical plus all multi)
  475.     mov al,    ENRXCR_BCST+ENRXCR_MULTI+ENRXCR_PROMP
  476.     mov     mcast_all_flag,1
  477. rcv_mode_set:
  478.     push    ax              ; Hold mode until masks are right
  479.     call    set_8390_multi  ; Set the multicast mask bits in chip
  480.     pop     ax
  481.     loadport
  482.     setport    EN0_RXCR    ; Set receiver to selected mode
  483.     out dx,    al
  484.     mov     rxcr_bits,al    ; Save a copy of what we set it to
  485.     ret
  486.  
  487.  
  488.     public    set_multicast_list
  489. set_multicast_list:
  490. ;enter with ds:si ->list of multicast addresses, cx = number of addresses.
  491. ;return nc if we set all of them, or cy,dh=error if we didn't.
  492.     mov    dh,NO_MULTICAST
  493.     stc
  494.     ret
  495.  
  496.  
  497. ; Set the multicast filter mask bits in case promiscuous rcv wanted
  498. set_8390_multi:
  499.     loadport
  500.     setport    EN_CCMD        ; Chip command register
  501.     mov cx,    8        ; Eight bytes of multicast filter
  502.     mov si, offset mcast_list_bits  ; Where bits are, if not all ones
  503.     push    cs
  504.     pop     ds
  505.     cli            ; Protect from irq changing page bits
  506.     mov al,    ENC_NODMA+ENC_PAGE1
  507.     out dx,    al        ; Switch to page one for writing eaddr
  508.     setport    EN1_MULT    ; Where it goes in 8390
  509.     mov al, mcast_all_flag  ; Want all ones or just selected bits?
  510.     or al,  al
  511.     je      set_mcast_2     ; Just selected ones
  512.     mov al,    0ffh        ; Ones for filter
  513. set_mcast_all:
  514.     out dx,    al        ; Write a mask byte
  515.     inc    dl        ; Step to next one
  516.     loop    set_mcast_all    ; ..    
  517.     jmp short set_mcast_x
  518.  
  519. set_mcast_2:
  520.     lodsb                   ; Get a byte of mask bits
  521.     out dx,    al        ; Write a mask byte
  522.     inc    dl        ; Step to next I/O register
  523.     loop    set_mcast_2     ; ..    
  524. set_mcast_x:
  525.     loadport
  526.     setport    EN_CCMD        ; Chip command register
  527.     mov al,    ENC_NODMA+ENC_PAGE0
  528.     out dx,    al        ; Restore to page zero
  529.     sti            ; OK for interrupts now
  530.     ret
  531.  
  532.     public    terminate
  533. terminate:
  534.     ret
  535.  
  536.     public    reset_interface
  537. reset_interface:
  538.     assume ds:code
  539.     loadport        ; Base of I/O regs
  540.     setport    EN_CCMD        ; Chip command reg
  541.     mov al,    ENC_STOP+ENC_NODMA
  542.     out dx,    al        ; Stop the DS8390
  543.     setport    EN0_ISR        ; Interrupt status reg
  544.     mov al,    0ffh        ; Clear all pending interrupts
  545.     out dx,    al        ; ..
  546.     setport    EN0_IMR        ; Interrupt mask reg
  547.     xor al,    al        ; Turn off all enables
  548.     out dx,    al        ; ..
  549.     ret
  550.  
  551. ; Linkages to non-device-specific routines
  552. ;called when we want to determine what to do with a received packet.
  553. ;enter with cx = packet length, es:di -> packet type.
  554. ;It returns with es:di = 0 if don't want this type or if no buffer available.
  555.     extrn    recv_find: near
  556.  
  557. ;called after we have copied the packet into the buffer.
  558. ;enter with ds:si ->the packet, cx = length of the packet.
  559.     extrn    recv_copy: near
  560.  
  561.     extrn    count_in_err: near
  562.     extrn    count_out_err: near
  563.  
  564.     public    recv
  565. recv:
  566. ;called from the recv isr.  All registers have been saved, and ds=cs.
  567. ;Actually, not just receive, but all interrupts come here.
  568. ;Upon exit, the interrupt will be acknowledged.
  569.  
  570.     assume    ds:code
  571. check_isr:            ; Was there an interrupt from this card?
  572.     loadport        ; Point at card's I/O port base
  573.     setport    E33G_GACFR    ; Make sure gate array is set up and
  574.     mov al,    EGACFR_NORM    ;  the RAM is enabled (not EPROM)
  575.     out dx,    al        ; ..
  576.     setport    EN0_ISR        ; Point at interrupt status register
  577.     in al,    dx        ; Get pending interrupts
  578.     and al,    ENISR_ALL    ; Any?
  579.     jnz    isr_test_overrun
  580.     jmp    interrupt_done    ; Go if none
  581. ; First, a messy procedure for handling the case where the rcvr
  582. ; over-runs its ring buffer.  This is spec'ed by National for the chip.
  583. ; This is handled differently in sample code from 3Com and from WD.
  584. ; This is close to the WD version.  May need tweaking if it doesn't
  585. ; work for the 3Com card.
  586.  
  587. isr_test_overrun: 
  588.     test al,ENISR_OVER    ; Was there an overrun?
  589.     jnz    recv_overrun    ; Go if so.
  590.     jmp    recv_no_overrun    ; Go if not.
  591. recv_overrun:
  592.     setport    EN_CCMD        ; Stop the chip
  593.     mov al,    ENC_STOP+ENC_NODMA
  594.     out dx,    al        ; Write "stop" to command register
  595.  
  596.     mov al, ENC_NODMA+ENC_PAGE1    ; Could be in previous out, but
  597.     out dx,al        ; was only tested this way
  598.     setport EN1_CURPAG    ; Get current page
  599.     in al,dx
  600.     mov bl,al        ; save it
  601.     setport    EN_CCMD        ;
  602.     mov al, ENC_NODMA+ENC_PAGE0
  603.     out dx,al        ; Back to page 0
  604.  
  605. ; Remove one frame from the ring
  606.     setport    EN0_BOUNDARY    ; Find end of this frame
  607.     in al,    dx        ; Get memory page number
  608.     inc    al        ; Page plus 1
  609.     cmp al,    SM_RSTOP_PG    ; Wrapped around ring?
  610.     jnz    rcv_ovr_nwrap    ; Go if not
  611.     mov al,    SM_RSTART_PG    ; Yes, wrap the page pointer
  612. rcv_ovr_nwrap:
  613.  
  614.     cmp    al,bl        ; Check if buffer emptry
  615.     je    rcv_ovr_empty    ; Yes ? Don't receive anything
  616.  
  617.     xor ah,    ah        ; Convert page to segment
  618.     mov cl,    4
  619.     mov bl,    al        ; Page number as arg to rcv_frm
  620.     shl ax,    cl        ; ..
  621.     add ax,    mem_base    ; Page in this memory
  622.     mov es,    ax        ; Segment pointer to the frame header
  623.     push    es        ; Hold this frame pointer for later
  624.     mov al,    es:[EN_RBUF_STAT]    ; Get the buffer status byte
  625.     test al,ENPS_RXOK    ; Is this frame any good?
  626.     jz    rcv_ovr_ng    ; Skip if not
  627.      call    rcv_frm        ; Yes, go accept it
  628. rcv_ovr_ng:
  629.     pop    es        ; Back to start of this frame
  630.     mov al,    es:[EN_RBUF_NXT_PG]    ; Get pointer to next frame
  631.     dec    al        ; Back up one page
  632.     cmp al,    SM_RSTART_PG    ; Did it wrap?
  633.     jge    rcv_ovr_nwr2
  634.     mov al,    SM_RSTOP_PG-1    ; Yes, back to end of ring
  635. rcv_ovr_nwr2:
  636.     loadport        ; Point at boundary reg
  637.     setport    EN0_BOUNDARY    ; ..
  638.     out dx,    al        ; Set the boundary
  639. rcv_ovr_empty:
  640.     setport    EN0_RCNTLO    ; Point at byte count regs
  641.     xor al,    al        ; Clear them
  642.     out dx,    al        ; ..
  643.     setport    EN0_RCNTHI
  644.     out dx,    al
  645.     setport    EN0_ISR        ; Point at status reg
  646.     mov cx,    8000h        ; Timeout counter
  647. rcv_ovr_rst_loop:
  648.     in al,    dx        ; Is it finished resetting?
  649.     test al,ENISR_RESET    ; ..
  650.     jnz    rcv_ovr_rst    ; Go if so
  651.     dec    cx        ; Loop til reset, or til timeout
  652.     jnz    rcv_ovr_rst_loop
  653. rcv_ovr_rst:
  654.     loadport        ; Point at Transmit control reg
  655.      setport    EN0_TXCR    ; ..
  656.     mov al,    ENTXCR_LOOP    ; Put transmitter in loopback mode
  657.     out dx,    al        ; ..
  658.     setport    EN_CCMD        ; Point at Chip command reg
  659.     mov al,    ENC_START+ENC_NODMA
  660.     out dx,    al        ; Start the chip running again
  661.     setport    EN0_TXCR    ; Back to TX control reg
  662.     xor al,    al        ; Clear the loopback bit
  663.     out dx,    al        ; ..
  664.     setport    EN0_ISR        ; Point at Interrupt status register
  665.     mov al,    ENISR_OVER    ; Clear the overrun interrupt bit
  666.     out dx,    al        ; ..
  667.     call    count_in_err    ; Count the anomaly
  668.      jmp    check_isr    ; Done with the overrun case
  669.  
  670. recv_no_overrun:
  671. ; Handle receive flags, normal and with error (but not overrun).
  672.     test al,ENISR_RX+ENISR_RX_ERR    ; Frame received without overrun?
  673.     jnz    recv_frame    ; Go if so.
  674.     jmp    recv_no_frame    ; Go if not.
  675. recv_frame:
  676.     loadport        ; Point at Chip's Command Reg
  677.      setport    EN_CCMD        ; ..
  678.     mov al,    ENC_NODMA+ENC_PAGE1
  679.     out dx,    al        ; Switch to page 1 registers
  680.     setport    EN1_CURPAG    ;Get current page of rcv ring
  681.     in al,    dx        ; ..
  682.     mov ah,    al        ; Hold current page in AH
  683.      setport    EN_CCMD        ; Back to page zero registers
  684.     mov al,    ENC_NODMA+ENC_PAGE0
  685.     out dx,    al        ; Switch back to page 0 registers
  686.     setport    EN0_BOUNDARY    ;Get boundary page
  687.     in al,    dx        ; ..
  688.     inc    al        ; Step boundary from last used page
  689.     cmp al,    SM_RSTOP_PG    ; Wrap if needed
  690.     jne    rx_nwrap3    ; Go if not
  691.     mov al,    SM_RSTART_PG    ; Wrap to first RX page
  692. rx_nwrap3:
  693.     cmp al,    ah        ; Read all the frames?
  694.     je    recv_frame_break    ; Finished them all
  695.     mov bl,    al        ; Page number as arg to rcv_frm
  696.     xor ah,    ah        ; Make segment pointer to this frame
  697.     mov cl,    4        ; 16 * pages = paragraphs
  698.     shl ax,    cl        ; ..
  699.     add ax,    mem_base    ; That far into shared memory
  700.     mov es,    ax        ; Segment part of pointer
  701.     push    es        ; Hold on to this pointer for later
  702.     mov al,    es:[EN_RBUF_STAT]    ; Get the buffer status byte
  703.     test al,ENPS_RXOK    ; Good frame?
  704.     jz    recv_no_rcv
  705.     call    rcv_frm        ; Yes, go accept it
  706. recv_no_rcv:
  707.     pop    es        ; Back to base of frame
  708.     mov al,    es:[EN_RBUF_NXT_PG]    ; Start of next frame
  709.     dec    al        ; Make previous page for new boundary
  710.     cmp al,    SM_RSTART_PG    ; Wrap around the bottom?
  711.     jge    rcv_nwrap4
  712.     mov al,    SM_RSTOP_PG-1    ; Yes
  713. rcv_nwrap4:
  714.     loadport        ; Point at the Boundary Reg again
  715.      setport    EN0_BOUNDARY    ; ..
  716.     out dx,    al        ; Set new boundary
  717.     jmp    recv_frame    ; See if any more frames
  718.  
  719. recv_frame_break:
  720.     loadport        ; Point at Interrupt Status Reg
  721.      setport    EN0_ISR        ; ..
  722.     mov al,    ENISR_RX+ENISR_RX_ERR+ENISR_OVER
  723.     out dx,    al        ; Clear those requests
  724.     jmp    check_isr    ; See if any other interrupts pending
  725.  
  726. recv_no_frame:                ; Handle transmit flags.
  727.     test al,ENISR_TX+ENISR_TX_ERR    ; Frame transmitted?
  728.     jnz    isr_tx        ; Go if so.
  729.     jmp    isr_no_tx    ; Go if not.
  730. isr_tx:
  731.     mov ah,    al        ; Hold interrupt status bits
  732.     loadport        ; Point at Transmit Status Reg
  733.      setport    EN0_TSR        ; ..
  734.     in al,    dx        ; ..
  735.     test ah,ENISR_TX    ; Non-error TX?
  736.     jz    isr_tx_err    ; No, do TX error completion
  737.     test al,ENTSR_COLL16    ; Jammed for 16 transmit tries?
  738.     jz    isr_tx_njam    ; Go if not
  739.     call    count_out_err    ; Yes, count those
  740. isr_tx_njam:
  741.     setport    EN0_ISR        ; Clear the TX complete flag
  742.     mov al,    ENISR_TX    ; ..
  743.     out dx,    al        ; ..    
  744.     jmp    isr_tx_done
  745. isr_tx_err:
  746.     test al,ENTSR_FU    ; FIFO Underrun?
  747.     jz    isr_txerr_nfu
  748.     call    count_out_err    ; Yes, count those
  749. isr_txerr_nfu:
  750.     loadport        ; Clear the TX error completion flag
  751.     setport    EN0_ISR        ; ..
  752.     mov al,    ENISR_TX_ERR    ; ..
  753.     out dx,    al        ; ..    
  754. isr_tx_done:
  755. ; If TX queue and/or TX shared memory ring buffer were being
  756. ; used, logic to step through them would go here.  However,
  757. ; in this version, we just clear the flags for background to notice.
  758.  
  759.      jmp    check_isr    ; See if any other interrupts on
  760.  
  761. isr_no_tx:
  762. ; Now check to see if any counters are getting full
  763.     test al,ENISR_COUNTERS    ; Interrupt to handle counters?
  764.     jnz    isr_stat    ; Go if so.
  765.     jmp    isr_no_stat    ; Go if not.
  766. isr_stat:
  767. ; We have to read the counters to clear them and to clear the interrupt.
  768. ; Version 1 of the PC/FTP driver spec doesn't give us
  769. ; anything useful to do with the data, though.
  770. ; Fix this up for V2 one of these days.
  771.     loadport        ; Point at first counter
  772.      setport    EN0_COUNTER0    ; ..
  773.     in al,    dx        ; Read the count, ignore it.
  774.     setport    EN0_COUNTER1
  775.     in al,    dx        ; Read the count, ignore it.
  776.     setport    EN0_COUNTER2
  777.     in al,    dx        ; Read the count, ignore it.
  778.     setport    EN0_ISR        ; Clear the statistics completion flag
  779.     mov al,    ENISR_COUNTERS    ; ..
  780.     out dx,    al        ; ..
  781. isr_no_stat:
  782.      jmp    check_isr    ; Anything else to do?
  783.  
  784. interrupt_done:
  785.     ret
  786.  
  787. ; Do the work of copying out a receive frame.
  788. ; Called with bl/ the page number of the frame header in shared memory/
  789. ; Also, es/ the paragraph number of that page.
  790.  
  791. rcv_frm:
  792. ; Old version checked size, memory space, queue length here. Now done
  793. ; in higher level code.
  794. ; Set cx to length of this frame.
  795.     mov ch,    es:[EN_RBUF_SIZE_HI]    ; Extract size of frame
  796.     mov cl,    es:[EN_RBUF_SIZE_LO]    ; Extract size of frame
  797.     sub cx,    EN_RBUF_NHDR        ; Less the header stuff
  798. ; Set es:di to point to Ethernet type field.  es is already at base of
  799. ; page where this frame starts.  Set di after the header and two addresses.
  800.     mov di,    EN_RBUF_NHDR+EADDR_LEN+EADDR_LEN
  801.     push    bx            ; Save page number in bl
  802.     push    cx            ; Save frame size
  803.     push    es
  804.     mov ax,    cs            ; Set ds = code
  805.     mov ds,    ax
  806.     assume    ds:code
  807.     call    recv_find        ; See if type and size are wanted
  808.     pop    ds            ; RX page pointer in ds now
  809.     assume    ds:nothing
  810.     pop    cx
  811.     pop    bx
  812.     cld            ; Copies below are forward, please
  813.     mov ax,    es        ; Did recv_find give us a null pointer?
  814.     or ax,    di        ; ..
  815.     je    rcv_no_copy    ; If null, don't copy the data    
  816.  
  817.     push    cx        ; We will want the count and pointer
  818.     push    es        ;  to hand to client after copying,
  819.     push    di        ;  so save them at this point
  820.  
  821. ;; if ( (((size + 255 + EN_RBUF_NHDR) >> 8) + pg) > SM_RSTOP_PG){
  822.     mov ax,    cx        ; Length of frame
  823.     add ax,    EN_RBUF_NHDR+255 ; Including the overhead bytes, rounded up
  824.     add ah,    bl        ; Compute page with last byte of data in ah
  825.     cmp ah,    SM_RSTOP_PG    ; Over the top of the ring?
  826.     jg    rcopy_wrap    ; Yes, move in two pieces
  827.     mov si,    EN_RBUF_NHDR    ; One piece, starts here in first page (in ds)
  828.     jmp    rcopy_one_piece    ; Go move it
  829.  
  830. rcopy_wrap:
  831. ;; Copy in two pieces due to buffer wraparound. */
  832. ;; n = ((SM_RSTOP_PG - pg) << 8) - EN_RBUF_NHDR;    /* To top of mem */
  833.     mov ah,    SM_RSTOP_PG    ; Compute length of first part
  834.     sub ah,    bl        ;  as all of the pages up to wrap point
  835.     xor al,    al        ; 16-bit count
  836.     sub ax,    EN_RBUF_NHDR    ; Less the four overhead bytes
  837.     sub cx,    ax        ; Move the rest in second part
  838.     push    cx        ; Save count of second part
  839.     mov cx,    ax        ; Count for first move
  840.     mov si,    EN_RBUF_NHDR    ; ds:si points at first byte to move
  841.     shr cx,    1        ; All above are even numbers, do words.
  842.     rep    movsw        ; Move first part of frame
  843.     mov ax,    mem_base    ; Paragraph of base of shared memory
  844.     mov ds,    ax        ; ..
  845.     mov si,    SM_RSTART_PG*256  ; Offset to start of first receive page
  846.     pop    cx        ; Bytes left to move
  847. rcopy_one_piece:
  848.     call    movemem
  849.     pop    si        ; Recover pointer to destination
  850.     pop    ds        ; Tell client it's his source
  851.     pop    cx        ; And it's this long
  852.     assume    ds:nothing
  853.     call    recv_copy    ; Give it to him
  854. rcv_no_copy:
  855.     push    cs        ; Put ds back in code space
  856.     pop    ds        ; ..
  857.     assume    ds:code
  858.     ret            ; That's it for rcv_frm
  859.  
  860.  
  861.     public    recv_exiting
  862. recv_exiting:
  863. ;called from the recv isr after interrupts have been acknowledged.
  864. ;Only ds and ax have been saved.
  865.     assume    ds:nothing
  866.     ret
  867.  
  868.  
  869. ;any code after this will not be kept after initialization.
  870. end_resident    label    byte
  871.  
  872.  
  873.     public    usage_msg
  874. usage_msg    db    "usage: 3C503 [-n] [-d] [-w] <packet_int_no> <int_level(2-5)> <io_addr> <thin_net_flag>",CR,LF,'$'
  875.  
  876.     public    copyright_msg
  877. copyright_msg    db    "Packet driver for 3-Com 3C503, version ",'0'+majver,".",'0'+version,CR,LF
  878.         db    "Portions Copyright 1989, Robert C. Clements, K1BC",CR,LF,'$'
  879.  
  880. cfg_err_msg:
  881.     db    "3C503 Configuration failed. Check parameters.",CR,LF,'$'
  882. no_mem_msg:
  883.     db    "3C503 memory jumper must be set to enable memory.",CR,LF
  884.     db    "Driver cannot run with memory disabled.",'$'
  885. int_no_name:
  886.     db    "Interrupt number ",'$'
  887. io_addr_name:
  888.     db    "I/O port ",'$'
  889. mem_base_name:
  890.     db    "Memory address ",'$'
  891. thick_thin_msg:
  892.     db    "Flag, non-zero if thin Ethernet: ",'$'
  893. mem_busted_msg:
  894.     db    "Shared RAM on 3C503 card is defective or there is an address conflict.",CR,LF,'$'
  895.  
  896.     extrn    set_recv_isr: near
  897.  
  898. ;enter with si -> argument string, di -> word to store.
  899. ;if there is no number, don't change the number.
  900.     extrn    get_number: near
  901.  
  902. ;enter with dx -> name of word, di -> dword to print.
  903.     extrn    print_number: near
  904.  
  905.     public    parse_args
  906. parse_args:
  907. ;exit with nc if all went well, cy otherwise.
  908.     mov di,    offset int_no        ; May override interrupt channel
  909.     call    get_number
  910.     mov di,    offset io_addr        ; May override I/O address
  911.     call    get_number
  912.     mov di,    offset thick_or_thin    ; May override thick/thin cable flag
  913.     call    get_number
  914.     mov ax,    thick_or_thin        ; Now make the right bit
  915.     cmp ax,    0
  916.     je    parse_thin1        ; If zero, leave bit off
  917.     mov al,    ECNTRL_THIN        ; Else the bit for the card
  918. parse_thin1:
  919.     mov    thin_bit,al        ; Save for setting up the card
  920.  
  921. ;    mov di,    offset mem_base        ; Not movable in 3C503
  922. ;    mov bx,    offset mem_base_name    ; Message for it
  923. ;    call    get_number        ; Must get from jumpers.
  924.     clc
  925.     ret
  926.  
  927.  
  928. mem_busted:
  929.     mov dx,    offset mem_busted_msg
  930.     jmp    short error
  931. nomem_error:
  932.     mov    dx,offset no_mem_msg
  933.     jmp    short error
  934. cfg_error:
  935.     mov    dx,offset cfg_err_msg
  936. error:
  937.     mov    ah,9        ; Type the msg
  938.     int    21h
  939.     stc            ; Indicate error
  940.     ret            ; Return to common code
  941.  
  942. ; Called once to initialize the 3C503 card
  943.  
  944.     public    etopen
  945. etopen:                ; Initialize interface
  946. ; First, initialize the Gate Array (ASIC) card logic.  Later do the 8390.
  947.     loadport        ; First, pulse the board reset
  948.     setport    E33G_CNTRL
  949.     mov al,    thin_bit    ; Thick or thin cable bit
  950.     or al,    ECNTRL_RESET
  951.     out dx,    al        ; Turn on board reset bit
  952.     and al,    ECNTRL_THIN
  953.     out dx,    al        ; Turn off board reset bit
  954. ; Now get the board's physical address from on-board PROM into card_hw_addr
  955.     cli                     ; Protect the E33G_CNTRL contents
  956.     setport E33G_CNTRL    ; Switch control bits to enable SA PROM
  957.     mov al,    thin_bit
  958.     or al,    ECNTRL_SAPROM
  959.     out dx,    al        ; ..
  960.     setport    E33_SAPROM    ; Where the address prom is
  961.     cld            ; Make sure string mode is right
  962.     push    cs        ; Point es:di at local copy space
  963.     pop    es
  964.     mov di,    offset card_hw_addr
  965.     mov cx,    EADDR_LEN    ; Set count for loop
  966. ini_addr_loop:
  967.     in al,    dx        ; Get a byte of address
  968.     stosb            ; Feed it to caller
  969.     inc    dx        ; Next byte at next I/O port
  970.     loop    ini_addr_loop    ; Loop over six bytes
  971.     loadport        ; Re-establish I/O base after dx mods
  972.     setport E33G_CNTRL    ; Switch control bits to turn off SA PROM
  973.     mov al,    thin_bit
  974.     out dx,    al        ; Turn off SA PROM windowing
  975.     sti                     ; Ok for E33G_CNTRL to change now
  976. ; Point the "Vector Pointer" registers off into the boonies so we
  977. ; don't get the shared RAM disabled on us while we're using it.
  978. ; Ideally a warm boot should reset this too, to get to ROM on this card,
  979. ; but I don't know a guaranteed way to determine that value.
  980.     setport    E33G_VP2
  981.     mov al,    0ffh        ; Point this at the ROM restart location
  982.     out dx,    al        ;  of ffff0h.
  983.     setport E33G_VP1
  984.     out dx,    al
  985.     xor al,    al
  986.     setport E33G_VP0
  987.     out dx,    al
  988. ;Make sure shared memory is jumpered on. Find its address.
  989.     setport E33G_ROMBASE    ; Point at rom/ram cfg reg
  990.     in al,    dx        ; Read it
  991.     test al,0f0h        ; Any bits on?
  992.     jne    memcfg_1    ; Yes
  993.     jmp    nomem_error    ; No, can't run without it
  994. memcfg_1:
  995.     mov bx,    0c600h        ; Build mem segment here
  996.     test al,0c0h        ; DC00 or D800?
  997.     je    memcfg_2    ; No
  998.     add bx,    01000h        ; Yes, make Dx00
  999. memcfg_2:
  1000.     test al,0a0h        ; DC00 or CC00?
  1001.     je    memcfg_3
  1002.     add bx,    00400h        ; Yes, make xC00
  1003. memcfg_3:
  1004.     mov mem_base,bx        ; Remember segment addr of memory
  1005. ; Set up Gate Array's Config Reg to enable and size the RAM.
  1006.     setport    E33G_GACFR    ; Make sure gate array is set up and
  1007.     mov al,    EGACFR_IRQOFF    ;  the RAM is enabled (not EPROM)
  1008.     out dx,    al        ; ..
  1009. ; Check the card's memory
  1010.     mov ax,    mem_base    ; Set segment of the shared memory
  1011.     add ax,    16*SM_TSTART_PG    ;  which starts 2000h up from "base"
  1012.     mov cx,    2000h        ; Length of RAM to test
  1013.     call    memory_test    ; Check it out
  1014.     jz    mem_works    ; Go if it's OK
  1015.     jmp    mem_busted    ; Go report failure if it's bad
  1016. mem_works:
  1017. ; Set up control of shared memory, buffer ring, etc.
  1018.     loadport
  1019.     setport    E33G_STARTPG    ; Set ASIC copy of rx's first buffer page
  1020.     mov al,    SM_RSTART_PG
  1021.     out dx,    al
  1022.     setport    E33G_STOPPG    ;  and ASIC copy of rx's last buffer page + 1
  1023.     mov al,    SM_RSTOP_PG
  1024.     out dx,    al
  1025. ; Set up interrupt/DMA control register in ASIC.
  1026. ; For now, we won't use the DMA, so B0-B3 are zero.
  1027.     xor ah,    ah        ; Get the interrupt level from arg line
  1028.     mov al,    int_no        ; ..
  1029.     cmp al,    9        ; If converted to 9, make back into 2
  1030.     jne    get_irq1    ; Not 9
  1031.     mov al,    2        ; Card thinks it's IRQ2
  1032. get_irq1:            ; Now should have level in range 2-5
  1033.     sub ax,    2        ; Make 0-3 for tables
  1034.     cmp ax,    5-2        ; In range?
  1035.     jna    get_irq2
  1036.     jmp    cfg_error    ; If not, can't configure.
  1037. get_irq2:
  1038.     xor cx,    cx        ; Make the bit for the ASIC
  1039.     mov cl,    al        ; Shift count
  1040.     mov al,    10h        ; Bit for irq2
  1041.     jcxz    get_irq3    ; Go if it's 2
  1042.     shl al,    cl        ; Shift over for 3-5
  1043. get_irq3:
  1044.     setport    E33G_IDCFR    ; Point at ASIC reg for IRQ level
  1045.     out dx,    al        ; Set the bit
  1046.     setport    E33G_NBURST    ; Set burst size to 8
  1047.     mov al,    8
  1048.     out dx,    al        ; ..
  1049.     setport    E33G_DMAAH    ; Set up transmit bfr in DMA addr
  1050.     mov al,    SM_TSTART_PG
  1051.     out dx,    al
  1052.     xor ax,    ax
  1053.     setport E33G_DMAAL
  1054.     out dx,    al
  1055. ; Now, initialize the DS8390 Ethernet Controller chip
  1056. ini_8390:
  1057.     setport    EN_CCMD        ; DS8390 chip's command register
  1058.     mov al,    ENC_NODMA+ENC_PAGE0+ENC_STOP
  1059.     out dx,    al        ; Switch to page zero
  1060.     setport    EN0_ISR        ; Clear all interrupt flags
  1061.     mov al,    0ffh        ; ..
  1062.     out dx,    al        ; ..
  1063.     setport    EN0_DCFG    ; Configure the fifo organization
  1064.     mov al,    ENDCFG_BM8    ; Fifo threshold = 8 bytes
  1065.     out dx,    al
  1066.     setport    EN0_TXCR    ; Set transmitter mode to normal
  1067.     xor al,    al
  1068.     out dx,    al
  1069.     setport    EN0_RXCR    ; Set receiver to monitor mode
  1070.     mov al,    ENRXCR_MON
  1071.     out dx,    al
  1072. ; Set up control of shared memory, buffer ring, etc.
  1073.     setport    EN0_STARTPG    ; Set receiver's first buffer page
  1074.     mov al,    SM_RSTART_PG
  1075.     out dx,    al
  1076.     setport    EN0_STOPPG    ;  and receiver's last buffer page + 1
  1077.     mov al,    SM_RSTOP_PG
  1078.     out dx,    al
  1079.     setport    EN0_BOUNDARY    ; Set initial "last page we have emptied"
  1080.     mov al,    SM_RSTART_PG    ; (WD doc says set to RSTART_PG)
  1081. ;    dec    al        ; (3Com doc says set to RSTOP_PG-1 ?)
  1082. ;                ; (and 3Com handling of BOUNDARY is
  1083. ;                ;  different throughout.)
  1084.     out dx,    al        ; (Check out why WD and 3Com disagree)
  1085.     push    ds              ; Copy from card's address to current address
  1086.     pop     es
  1087.     mov si, offset card_hw_addr
  1088.     mov di, offset curr_hw_addr
  1089.     mov cx, EADDR_LEN       ; Copy one address length
  1090.     rep     movsb           ; ..
  1091.     call    set_8390_eaddr  ; Now set the address in the 8390 chip
  1092.     call    set_8390_multi  ; Put the right stuff into 8390's multicast masks
  1093.     loadport        ; Base of I/O regs
  1094.     setport EN_CCMD        ;[jah] Switch to page 1 registers
  1095.     mov al,ENC_NODMA+ENC_PAGE1    ;[jah]
  1096.     out dx, al        ;[jah]
  1097.     setport    EN1_CURPAG    ; Set current shared page for RX to work on
  1098.     mov al,    SM_RSTART_PG+1
  1099.     out dx,    al
  1100.     setport    EN_CCMD        ; Chip command register
  1101.     mov al,    ENC_NODMA+ENC_PAGE0
  1102.     out dx,    al        ; Back to page zero
  1103.     setport    EN0_RCNTLO    ; Clear the byte count registers
  1104.     xor al,    al        ; ..
  1105.     out dx,    al
  1106.     setport    EN0_RCNTHI
  1107.     out dx,    al        ; Clear high byte, too
  1108.     setport    EN0_IMR        ; Clear all interrupt enable flags
  1109.     xor al,    al
  1110.     out dx,    al
  1111.     setport    EN0_ISR        ; Clear all interrupt assertion flags
  1112.     mov al,    0ffh        ; again for safety before making the
  1113.     out dx,    al        ; interrupt be enabled
  1114.     call    set_recv_isr    ; Put ourselves in interrupt chain
  1115.     loadport
  1116.     setport    EN_CCMD        ; Now start the DS8390
  1117.     mov al,    ENC_START+ENC_NODMA
  1118.     out dx,    al        ; interrupt be enabled
  1119.     setport    EN0_RXCR    ; Tell it what frames to accept
  1120.     mov al,    rxcr_bits       ; As most recently set by set_mode
  1121.     out dx,    al
  1122.     setport    E33G_GACFR    ; Now let it interrupt us
  1123.     mov al,    EGACFR_NORM    ;  and leave RAM enabled
  1124.     out dx,    al
  1125.     setport    EN0_IMR        ; Tell card it can cause these interrupts
  1126.     mov al,    ENISR_ALL
  1127.     out dx,    al
  1128.  
  1129.     mov    al, int_no        ; Get board's interrupt vector
  1130.     add    al, 8
  1131.     cmp    al, 8+8            ; Is it a slave 8259 interrupt?
  1132.     jb    set_int_num        ; No.
  1133.     add    al, 70h - 8 - 8        ; Map it to the real interrupt.
  1134. set_int_num:
  1135.     xor    ah, ah            ; Clear high byte
  1136.     mov    int_num, ax        ; Set parameter_list int num.
  1137.  
  1138.     mov dx,    offset end_resident    ; Report our size
  1139.     clc                ; Say no error
  1140.     ret                ; Back to common code
  1141.  
  1142.     public    print_parameters
  1143. print_parameters:
  1144.     mov di,    offset int_no        ; May override interrupt channel
  1145.     mov dx,    offset int_no_name    ; Message for it
  1146.     call    print_number
  1147.     mov di,    offset io_addr        ; May override I/O address
  1148.     mov dx,    offset io_addr_name    ; Message for it
  1149.     call    print_number
  1150.     mov di,    offset thick_or_thin    ; May override thick/thin cable flag
  1151.     mov dx,    offset thick_thin_msg    ; Message for it
  1152.     call    print_number
  1153.     ret
  1154.  
  1155.     include memtest.asm
  1156.  
  1157. code    ends
  1158.  
  1159.     end
  1160.