home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
lan
/
driver6s
/
ne2000.asm
< prev
next >
Wrap
Assembly Source File
|
1990-04-10
|
37KB
|
1,336 lines
version equ 3
page ,132
include defs.asm
;/* PC/FTP Packet Driver source, conforming to version 1.05 of the spec,
;* for the NE2000 interface card.
;* Robert C Clements, K1BC, 14 February, 1989
;* Portions (C) Copyright 1988, 1989 Robert C Clements
;* Modified from 3com503 driver by D.J.Horne
;*
; Copyright, 1988, 1989, Russell Nelson
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, version 1.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
;* Change history:
;* Updated to driver spec version 1.08 Feb. 17, 1989 by Russell Nelson.
;* Changes 27 Jul 89 by Bob Clements (/Rcc)
;* Added Thick versus Thin Ethernet switch 27 Jul 89 by Bob Clements (/Rcc)
;* Added call to memory_test.
;* Added rcv_mode logic. Started, but didn't finish, multicast logic.
;* Fixed get_address to return current, not PROM, address.
;* Minor races fixed.
;* Changes 19 Oct 89, Dave Horne
;* Modified for NE2000, use i/o instead of shared memory,
;* remove thick/thin logic, remove gate array logic
code segment byte public
assume cs:code, ds:code
; Stuff specific to the NE2000 Ethernet controller board
; WD version in C by Bob Clements, K1BC, May 1988 for the KA9Q TCP/IP package
; 3Com version based on WD8003E version in .ASM, also by Bob Clements, dated
; 19 August 1988. The WD and 3Com cards both use the National DS8390.
; NE2000 based on 3COM503 version.
; Symbol prefix "EN" is for Ethernet, National chip
; Symbol prefix "NE" is for NE2000 register(s)
; The EN registers - the DS8390 chip registers
; These appear at Base+0 through Base+0F
; There are two (really 3) pages of registers in the chip. You select
; which page you want, then address them at offsets 00-0F from base.
; The chip command register (EN_CCMD) appears in both pages.
EN_CCMD equ 000h ; Chip's command register
; Page 0
EN0_STARTPG equ 001h ; Starting page of ring bfr
EN0_STOPPG equ 002h ; Ending page +1 of ring bfr
EN0_BOUNDARY equ 003h ; Boundary page of ring bfr
EN0_TSR equ 004h ; Transmit status reg
EN0_TPSR equ 004h ; Transmit starting page
EN0_TCNTLO equ 005h ; Low byte of tx byte count
EN0_TCNTHI equ 006h ; High byte of tx byte count
EN0_ISR equ 007h ; Interrupt status reg
EN0_RSARLO equ 008h ; Remote start address reg 0
EN0_RSARHI equ 009h ; Remote start address reg 1
EN0_RCNTLO equ 00ah ; Remote byte count reg
EN0_RCNTHI equ 00bh ; Remote byte count reg
EN0_RXCR equ 00ch ; RX control reg
EN0_TXCR equ 00dh ; TX control reg
EN0_COUNTER0 equ 00dh ; Rcv alignment error counter
EN0_DCFG equ 00eh ; Data configuration reg
EN0_COUNTER1 equ 00eh ; Rcv CRC error counter
EN0_IMR equ 00fh ; Interrupt mask reg
EN0_COUNTER2 equ 00fh ; Rcv missed frame error counter
; Page 1
EN1_PHYS equ 001h ; This board's physical enet addr
EN1_CURPAG equ 007h ; Current memory page
EN1_MULT equ 008h ; Multicast filter mask array (8 bytes)
; Board regs
NE_DATAPORT equ 10h
NE_OTHERPORT equ 1fh
; Chip commands in EN_CCMD
ENC_STOP equ 001h ; Stop the chip
ENC_START equ 002h ; Start the chip
ENC_TRANS equ 004h ; Transmit a frame
ENC_RREAD equ 008h ; remote read
ENC_RWRITE equ 010h ; remote write
ENC_NODMA equ 020h ; No remote DMA used on this card
ENC_PAGE0 equ 000h ; Select page 0 of chip registers
ENC_PAGE1 equ 040h ; Select page 1 of chip registers
; Commands for RX control reg
ENRXCR_MON equ 020h ; Monitor mode (no packets rcvd)
ENRXCR_PROMP equ 010h ; Promiscuous physical addresses
ENRXCR_MULTI equ 008h ; Multicast (if pass filter)
ENRXCR_BCST equ 004h ; Accept broadcasts
ENRXCR_BAD equ 003h ; Accept runts and bad CRC frames
; Commands for TX control reg
ENTXCR_LOOP equ 002h ; Set loopback mode
; Bits in EN0_DCFG - Data config register
ENDCFG_BM8 equ 049h ; Set burst mode, 8 deep FIFO, words
; Bits in EN0_ISR - Interrupt status register
ENISR_RX equ 001h ; Receiver, no error
ENISR_TX equ 002h ; Transmitter, no error
ENISR_RX_ERR equ 004h ; Receiver, with error
ENISR_TX_ERR equ 008h ; Transmitter, with error
ENISR_OVER equ 010h ; Receiver overwrote the ring
ENISR_COUNTERS equ 020h ; Counters need emptying
ENISR_RDC equ 040h ; remote dma complete
ENISR_RESET equ 080h ; Reset completed
ENISR_ALL equ 03fh ; Interrupts we will enable
; Bits in received packet status byte and EN0_RSR
ENPS_RXOK equ 001h ; Received a good packet
; Bits in TX status reg
ENTSR_PTX equ 001h ; Packet transmitted without error
ENTSR_COLL equ 004h ; Collided at least once
ENTSR_COLL16 equ 008h ; Collided 16 times and was dropped
ENTSR_FU equ 020h ; TX FIFO Underrun
; Shared memory management parameters
XMIT_MTU equ 600h ; Largest packet we have room for.
SM_TSTART_PG equ 040h ; First page of TX buffer
SM_RSTART_PG equ 046h ; Starting page of RX ring
SM_RSTOP_PG equ 080h ; Last page +1 of RX ring
; Description of header of each packet in receive area of memory
EN_RBUF_STAT equ 0 ; Received frame status
EN_RBUF_NXT_PG equ 1 ; Page after this frame
EN_RBUF_SIZE_LO equ 2 ; Length of this frame
EN_RBUF_SIZE_HI equ 3 ; Length of this frame
EN_RBUF_NHDR equ 4 ; Length of above header area
; End of NE2000 parameter definitions
pause_ macro
jmp $+2
endm
longpause macro
push cx
mov cx,0
loop $
pop cx
endm
; The following two values may be overridden from the command line.
; If they are omitted from the command line, these defaults are used.
; The shared memory base is set by a jumper. We read it from the
; card and set up accordingly.
public int_no, io_addr
int_no db 2,0,0,0 ; Interrupt level
io_addr dw 0300h,0 ; I/O address for card (jumpers)
public driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class db 1 ;from the packet spec
driver_type db 54 ;from the packet spec
driver_name db 'NE2000',0 ;name of the driver.
driver_function db 2
parameter_list label byte
db 1 ;major rev of packet driver
db 9 ;minor rev of packet driver
db 14 ;length of parameter list
db EADDR_LEN ;length of MAC-layer address
dw GIANT ;MTU, including MAC headers
dw MAX_MULTICAST * EADDR_LEN ;buffer size of multicast addrs
dw 0 ;(# of back-to-back MTU rcvs) - 1
dw 0 ;(# of successive xmits) - 1
dw 0 ;Interrupt # to hook for post-EOI
;processing, 0 == none,
rxcr_bits db ENRXCR_BCST ; Default to ours plus multicast
public card_hw_addr, curr_hw_addr, mcast_list_bits, mcast_all_flag
card_hw_addr db 0,0,0,0,0,0 ;Physical ethernet address
curr_hw_addr db 0,0,0,0,0,0 ;Address set into the 8390
mcast_list_bits db 0,0,0,0,0,0,0,0 ;Bit mask from last set_multicast_list
mcast_all_flag db 0 ;Non-zero if hware should have all
; ones in mask rather than this list.
mcast_sw_filter db 0 ; set if software filter is required.
is_186 db 0
mcast_sw_fin dw 0
mcast_sw_fout dw 0
public rcv_modes
rcv_modes dw 7 ;number of receive modes in our table.
dw 0 ;There is no mode zero
dw rcv_mode_1
dw rcv_mode_2
dw rcv_mode_3
dw rcv_mode_4
dw rcv_mode_5
dw rcv_mode_6
public mcast_tab
mcast_hcount dw 0 ; multicast header count
mcast_tab_b db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh ; entry for broadcast
mcast_tab db (MAX_MULTICAST*EADDR_LEN) dup (0)
;
; a temp buffer for the received header
;
RCV_HDR_SIZE equ 18 ; 2 ids @6 + protocol, + 4byte header
rcv_hdr db RCV_HDR_SIZE dup(0)
;
; The board data
;
public board_data
BOARD_DATA_SIZE equ 32
board_data db BOARD_DATA_SIZE dup(0)
soft_tx_errors dw 0,0
soft_tx_err_bits db 0
soft_rx_errors dw 0,0
soft_rx_err_bits db 0
; send_pkt: - The Transmit Frame routine
public send_pkt
send_pkt:
;enter with ds:si -> packet, cx = packet length.
;exit with nc if ok, or else cy if error, dh set to error number.
assume ds:nothing
loadport ; Point at chip command register
setport EN_CCMD ; ..
pause_
mov bx, 8000h ; Avoid infinite loop
tx_wait:
in al, dx ; Get chip command state
test al,ENC_TRANS ; Is transmitter still running?
jz tx_idle ; Go if free
dec bx ; Count the timeout
jnz tx_wait ; Fall thru if TX is stuck
call count_out_err ; Should count these error timeouts
; Maybe need to add recovery logic here
tx_idle:
cmp cx,XMIT_MTU ; Is this packet too large?
ja send_pkt_toobig
cmp cx, RUNT ; Is the frame long enough?
jnb tx_oklen ; Go if OK
mov cx, RUNT ; Stretch frame to minimum allowed
tx_oklen:
push cx ; Hold count for later
loadport ; Set up for address
setport EN0_ISR
pause_
mov al,ENISR_RDC ; clear remote interrupt int.
out dx,al
setport EN0_TCNTLO ; Low byte of TX count
pause_
mov al, cl ; Get the count
out dx, al ; Tell card the count
setport EN0_TCNTHI ; High byte of TX count
pause_
mov al, ch ; Get the count
out dx, al ; Tell card the count
xor ax, ax ; Set up ax at base of tx buffer
mov ah, SM_TSTART_PG ; Where to put tx frame
pop cx ; Get back count to give to board
call block_output
loadport
mov cx,0
setport EN0_ISR
in al,dx
tx_check_rdc:
test al,ENISR_RDC ; dma done ???
jnz tx_start
loop tx_check_rdc
jmp tx_no_rdc
tx_start:
setport EN0_TPSR ; Transmit Page Start Register
pause_
mov al, SM_TSTART_PG
out dx, al ; Start the transmitter
setport EN_CCMD ; Chip command reg
pause_
mov al, ENC_TRANS+ENC_NODMA+ENC_START
out dx, al ; Start the transmitter
clc ; Successfully started
sti
ret ; End of transmit-start routine
send_pkt_toobig:
mov dh,NO_SPACE
stc
sti
ret
tx_no_rdc:
mov dh,CANT_SEND
stc
sti
ret
count_soft_err:
add word ptr soft_tx_errors,1
adc word ptr soft_tx_errors+2,0
or byte ptr soft_tx_err_bits,al
ret
movemem:
;does the same thing as "rep movsb", only 50% faster.
;moves words instead of bytes, and handles the case of both addresses odd
;efficiently. There is no way to handle one address odd efficiently.
;This routine always aligns the source address in the hopes that the
;destination address will also get aligned. This is from Phil Karn's
;code from ec.c, a part of his NET package. I bummed a few instructions
;out.
jcxz movemem_cnte ; If zero, we're done already.
test si,1 ; Does source start on odd byte?
jz movemem_adre ; Go if not
movsb ; Yes, move the first byte
dec cx ; Count that byte
movemem_adre:
shr cx,1 ; convert to word count
rep movsw ; Move the bulk as words
jnc movemem_cnte ; Go if the count was even
movsb ; Move leftover last byte
movemem_cnte:
ret
public get_address
get_address:
;get the address of the interface.
;enter with es:di -> place to get the address, cx = size of address buffer.
;exit with nc, cx = actual size of address, or cy if buffer not big enough.
; Give caller the one currently in the 8390, not necessarily the one in PROM.
assume ds:code
cmp cx, EADDR_LEN ; Caller wants a reasonable length?
jb get_addr_x ; No, fail.
mov cx, EADDR_LEN ; Move one ethernet address from our copy
mov si, offset curr_hw_addr ; Copy from most recent setting
rep movsb
mov cx, EADDR_LEN ; Tell caller how many bytes we fed him
clc ; Carry off says success
ret
get_addr_x:
stc ; Tell caller our addr is too big for him
ret
;
;get the board data. This is (16) bytes starting at remote
;dma address 0. Put it in a buffer called board_data.
get_board_data:
mov cx,10h ; get 16 bytes,
push ds
pop es ; set es to ds
mov di,offset board_data
mov ax,0 ; from address 0
call sp_block_input
ret
public set_address
set_address:
assume ds:nothing
;enter with ds:si -> Ethernet address, CX = length of address.
;exit with nc if okay, or cy, dh=error if any errors.
;
cmp cx,EADDR_LEN ;ensure that their address is okay.
je set_address_4
mov dh,BAD_ADDRESS
stc
jmp short set_address_done
set_address_4:
push cs ; Copy from them to our RAM copy
pop es ; Destination of move
mov di, offset curr_hw_addr
rep movsb ; Move their address
call set_8390_eaddr ; Put that address in the chip
set_address_okay:
mov cx,EADDR_LEN ;return their address length.
clc
set_address_done:
push cs
pop ds
assume ds:code
ret
; Copy our Ethernet address from curr_hw_addr into the DS8390
set_8390_eaddr:
push cs ; Get it from our local RAM copy
pop ds
mov si, offset curr_hw_addr
mov cx, EADDR_LEN ; Move one ethernet address from our copy
loadport
setport EN_CCMD ; Chip command register
pause_
cli ; Protect from irq changing page bits
mov al, ENC_NODMA+ENC_PAGE1+ENC_STOP
out dx, al ; Switch to page one for writing eaddr
setport EN1_PHYS ; Where it goes in 8390
pause_
set_8390_1:
lodsb
out dx,al
inc dx
loop set_8390_1
loadport
setport EN_CCMD ; Chip command register
pause_
mov al, ENC_NODMA+ENC_PAGE0+ENC_STOP
out dx, al ; Restore to page zero
sti ; OK for interrupts now
ret
; Routines to set address filtering modes in the DS8390
rcv_mode_1: ; Turn off receiver
mov al, ENRXCR_MON ; Set to monitor for counts but accept none
jmp short rcv_mode_set
rcv_mode_2: ; Receive only packets to this interface
mov al, 0 ; Set for only our packets
jmp short rcv_mode_set
rcv_mode_3: ; Mode 2 plus broadcast packets (This is the default)
mov al, ENRXCR_BCST ; Set four ours plus broadcasts
jmp short rcv_mode_set
rcv_mode_4: ; Mode 3 plus selected multicast packets
mov al, ENRXCR_BCST+ENRXCR_MULTI ; Ours, bcst, and filtered multicasts
mov mcast_all_flag,0 ; need to do sw filter.
mov mcast_sw_filter,1 ; because chip filter is not 100%
jmp short rcv_mode_set
rcv_mode_5: ; Mode 3 plus ALL multicast packets
mov al, ENRXCR_BCST+ENRXCR_MULTI ; Ours, bcst, and filtered multicasts
mov mcast_all_flag,1
jmp short rcv_mode_set
rcv_mode_6: ; Receive all packets (Promiscuous physical plus all multi)
mov al, ENRXCR_BCST+ENRXCR_MULTI+ENRXCR_PROMP
mov mcast_all_flag,1
rcv_mode_set:
push ax ; Hold mode until masks are right
call set_8390_multi ; Set the multicast mask bits in chip
pop ax
loadport
setport EN0_RXCR ; Set receiver to selected mode
pause_
out dx, al
mov rxcr_bits,al ; Save a copy of what we set it to
ret
public set_multicast_list
set_multicast_list:
;enter with ds:si ->list of multicast addresses, cx = number of addresses.
;return nc if we set all of them, or cy,dh=error if we didn't.
assume ds:nothing
push cs
pop es ; set es to destination
mov di,offset mcast_tab
mov ax,cx ; save byte count
repz movsb
mov dx,0
mov cx,6
div cx
mov mcast_hcount,ax
;
mov word ptr mcast_list_bits,0
mov word ptr mcast_list_bits+2,0
mov word ptr mcast_list_bits+4,0
mov word ptr mcast_list_bits+6,0
;
mov cx,mcast_hcount
inc cx
mov di,offset mcast_tab_b
set_mcl_1:
call add_mc_bits
add di,6
loop set_mcl_1
call set_8390_multi ; Set the multicast mask bits in chip
clc
mov dh,0
ret
;
; multicast is at es:di
assume ds:nothing
add_mc_bits:
push cx
push di
mov cx,6
mov dx,0ffffh ; this is msw.
mov bx,0ffffh ; set 32 bit number
add_mcb_1:
mov al,es:[di]
inc di
call upd_crc ; update crc
loop add_mcb_1 ; and loop.
mov ah,0
mov al,dh ; get ms 8 bits,
rol al,1
rol al,1
rol al,1 ; put 3 bits at bottom
and al,7
mov dl,al ; save in dl
mov al,dh ; get ms 8 bits,
ror al,1
ror al,1 ; but at bottom
and al,7
mov cl,al ; save in cl
mov al,1
rol al,cl ; set the correct bit,
mov di,offset mcast_list_bits
mov dh,0
add di,dx
or cs:[di],al
pop di
pop cx
ret
;
; dx is high,
; bx is low.
; al is data
upd_crc:
push cx
mov cx,8 ; do 8 bits
mov ah,0
upd_crc1:
shl bx,1 ; shift bx
rcl dx,1 ; through dx
rcl ah,1 ; carry is at bottom of ah
xor ah,al ; xor with lsb of data
rcr ah,1 ; and put in carry bit
jnc upd_crc2
;
; autodin is x^32+x^26+x^23x^22+x^16+
; x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+1
xor dx,0000010011000001b
xor bx,0001110110110111b
upd_crc2:
shr al,1 ; shift the data
loop upd_crc1
pop cx
ret
; Set the multicast filter mask bits in case promiscuous rcv wanted
set_8390_multi:
push cs
pop ds
assume ds:code
loadport
setport EN_CCMD ; Chip command register
pause_
mov cx, 8 ; Eight bytes of multicast filter
mov si, offset mcast_list_bits ; Where bits are, if not all ones
cli ; Protect from irq changing page bits
mov al, ENC_NODMA+ENC_PAGE1+ENC_STOP
out dx, al ; Switch to page one for writing eaddr
setport EN1_MULT ; Where it goes in 8390
pause_
mov al, mcast_all_flag ; Want all ones or just selected bits?
or al, al
je set_mcast_2 ; Just selected ones
mov al, 0ffh ; Ones for filter
set_mcast_all:
out dx, al ; Write a mask byte
inc dl ; Step to next one
loop set_mcast_all ; ..
jmp short set_mcast_x
set_mcast_2:
lodsb ; Get a byte of mask bits
out dx, al ; Write a mask byte
inc dl ; Step to next I/O register
loop set_mcast_2 ; ..
set_mcast_x:
loadport
setport EN_CCMD ; Chip command register
pause_
mov al, ENC_NODMA+ENC_PAGE0+ENC_START
out dx, al ; Restore to page zero
sti ; OK for interrupts now
ret
public reset_chip
reset_chip:
assume ds:nothing
loadport ; Base of I/O regs
setport NE_OTHERPORT
in al,dx
longpause
out dx,al ; should set command 21, 80
longpause
setport EN_CCMD ; Chip command reg
pause_
mov al, ENC_STOP+ENC_NODMA
out dx, al ; Stop the DS8390
setport EN0_ISR
pause_
mov cx,0
reset_chip_loop:
in al,dx ; get isr
and al,ENISR_RESET
jnz reset_chip_done
jmp reset_chip_loop
reset_chip_done:
ret
public terminate
terminate:
ret
public reset_interface
reset_interface:
assume ds:code
call reset_chip
loadport ; Base of I/O regs
setport EN0_ISR ; Interrupt status reg
pause_
mov al, 0ffh ; Clear all pending interrupts
out dx, al ; ..
setport EN0_IMR ; Interrupt mask reg
pause_
xor al, al ; Turn off all enables
out dx, al ; ..
ret
;
; Special case Block input routine. Used on extra memory
; space for board ID etc. DMA count is set X2,
; CX = byte count, es:si = buffer location, ax = buffer address
sp_block_input:
push ax ; save buffer address
loadport
setport EN_CCMD
pause_
mov al,ENC_NODMA+ENC_STOP
out dx,al ; stop & clear the chip
setport EN0_RCNTLO ; remote byte count 0
pause_
mov ax,cx
add ax,ax
out dx,al
setport EN0_RCNTHI
pause_
mov al,ah
out dx,al
pop ax ; get our page back
setport EN0_RSARLO
pause_
out dx,al ; set as hi address
setport EN0_RSARHI
pause_
mov al,ah
out dx,al
setport EN_CCMD
pause_
mov al,ENC_RREAD+ENC_START ; read and start
out dx,al
setport NE_DATAPORT
pause_
jmp read_loop
;
; Block input routine
; CX = byte count, es:si = buffer location, ax = buffer address
public block_input
block_input:
push ax ; save buffer address
loadport
setport EN_CCMD
pause_
mov al,ENC_NODMA+ENC_PAGE0+ENC_START
out dx,al
setport EN0_RCNTLO ; remote byte count 0
pause_
mov al,cl
out dx,al
setport EN0_RCNTHI
pause_
mov al,ch
out dx,al
pop ax ; get our page back
setport EN0_RSARLO
pause_
out dx,al ; set as hi address
setport EN0_RSARHI
pause_
mov al,ah
out dx,al
setport EN_CCMD
pause_
mov al,ENC_RREAD+ENC_START ; read and start
out dx,al
setport NE_DATAPORT
pause_
cmp byte ptr is_186,0
jnz read_186
read_loop:
in al,dx ; get a byte
stosb ; save it
loop read_loop
ret
read_186:
inc cx ; make even
shr cx,1 ; word count
db 0f3h, 06dh ;masm 4.0 doesn't grok "rep insw"
ret
;
; Block output routine
; CX = byte count, ds:si = buffer location, ax = buffer address
block_output:
assume ds:nothing
push ax ; save buffer address
inc cx ; make even
and cx,0fffeh
loadport
setport EN_CCMD
pause_
mov al,ENC_NODMA+ENC_START
out dx,al ; stop & clear the chip
setport EN0_RCNTLO ; remote byte count 0
pause_
mov al,cl
out dx,al
setport EN0_RCNTHI
pause_
mov al,ch
out dx,al
pop ax ; get our page back
setport EN0_RSARLO
pause_
out dx,al ; set as lo address
setport EN0_RSARHI
pause_
mov al,ah
out dx,al
setport EN_CCMD
pause_
mov al,ENC_RWRITE+ENC_START ; write and start
out dx,al
setport NE_DATAPORT
pause_
cmp byte ptr is_186,0
jnz write_186
write_loop:
lodsb ; get a byte
out dx,al ; save it
loop write_loop
ret
write_186:
shr cx,1 ; word count
db 0f3h, 06fh ;masm 4.0 doesn't grok "rep outsw"
ret
; Linkages to non-device-specific routines
;called when we want to determine what to do with a received packet.
;enter with cx = packet length, es:di -> packet type.
;It returns with es:di = 0 if don't want this type or if no buffer available.
extrn recv_find: near
;called after we have copied the packet into the buffer.
;enter with ds:si ->the packet, cx = length of the packet.
extrn recv_copy: near
extrn count_in_err: near
extrn count_out_err: near
public recv
recv:
;called from the recv isr. All registers have been saved, and ds=cs.
;Actually, not just receive, but all interrupts come here.
;Upon exit, the interrupt will be acknowledged.
assume ds:code
check_isr: ; Was there an interrupt from this card?
loadport ; Point at card's I/O port base
setport EN0_IMR ; point at interrupt masks
pause_ ; switch off, this way we can
mov al,0 ; leave the chip running.
out dx,al ; no interrupts please.
setport EN0_ISR ; Point at interrupt status register
pause_
in al, dx ; Get pending interrupts
and al, ENISR_ALL ; Any?
jnz isr_test_overrun
jmp interrupt_done ; Go if none
; First, a messy procedure for handling the case where the rcvr
; over-runs its ring buffer. This is spec'ed by National for the chip.
; This is handled differently in sample code from 3Com and from WD.
; This is close to the WD version. May need tweaking if it doesn't
; work for the 3Com card.
isr_test_overrun:
test al,ENISR_OVER ; Was there an overrun?
jnz recv_overrun ; Go if so.
jmp recv_no_overrun ; Go if not.
recv_overrun:
setport EN_CCMD ; Stop the chip
pause_
mov al, ENC_STOP+ENC_NODMA
out dx, al ; Write "stop" to command register
mov al, ENC_NODMA+ENC_PAGE1 ; Could be in previous out, but
out dx,al ; was only tested this way
setport EN1_CURPAG ; Get current page
in al,dx
mov bl,al ; save it
setport EN_CCMD ;
mov al, ENC_NODMA+ENC_PAGE0
out dx,al ; Back to page 0
; Remove one frame from the ring
setport EN0_BOUNDARY ; Find end of this frame
pause_
in al, dx ; Get memory page number
inc al ; Page plus 1
cmp al, SM_RSTOP_PG ; Wrapped around ring?
jnz rcv_ovr_nwrap ; Go if not
mov al, SM_RSTART_PG ; Yes, wrap the page pointer
rcv_ovr_nwrap:
cmp al,bl ; Check if buffer emptry
je rcv_ovr_empty ; Yes ? Don't receive anything
mov ah,al ; make a byte address. e.g. page
mov bl,ah ; and save in bl
mov al,0 ; 46h becomes 4600h into buffer
mov cx,RCV_HDR_SIZE ; size of rcv_hdr
mov di,offset rcv_hdr ;point to header
push ds
pop es ; set es to right place
call block_input
mov al, rcv_hdr+EN_RBUF_STAT ; Get the buffer status byte
test al,ENPS_RXOK ; Is this frame any good?
jz rcv_ovr_ng ; Skip if not
call rcv_frm ; Yes, go accept it
rcv_ovr_ng:
mov al, rcv_hdr+EN_RBUF_NXT_PG ; Get pointer to next frame
dec al ; Back up one page
cmp al, SM_RSTART_PG ; Did it wrap?
jge rcv_ovr_nwr2
mov al, SM_RSTOP_PG-1 ; Yes, back to end of ring
rcv_ovr_nwr2:
loadport ; Point at boundary reg
setport EN0_BOUNDARY ; ..
pause_
out dx, al ; Set the boundary
rcv_ovr_empty:
setport EN0_RCNTLO ; Point at byte count regs
pause_
xor al, al ; Clear them
out dx, al ; ..
setport EN0_RCNTHI
pause_
out dx, al
setport EN0_ISR ; Point at status reg
pause_
mov cx, 8000h ; Timeout counter
rcv_ovr_rst_loop:
in al, dx ; Is it finished resetting?
test al,ENISR_RESET ; ..
jnz rcv_ovr_rst ; Go if so
dec cx ; Loop til reset, or til timeout
jnz rcv_ovr_rst_loop
rcv_ovr_rst:
loadport ; Point at Transmit control reg
setport EN0_TXCR ; ..
pause_
mov al, ENTXCR_LOOP ; Put transmitter in loopback mode
out dx, al ; ..
setport EN_CCMD ; Point at Chip command reg
pause_
mov al, ENC_START+ENC_NODMA
out dx, al ; Start the chip running again
setport EN0_TXCR ; Back to TX control reg
pause_
xor al, al ; Clear the loopback bit
out dx, al ; ..
setport EN0_ISR ; Point at Interrupt status register
pause_
mov al, ENISR_OVER ; Clear the overrun interrupt bit
out dx, al ; ..
call count_in_err ; Count the anomaly
jmp check_isr ; Done with the overrun case
recv_no_overrun:
; Handle receive flags, normal and with error (but not overrun).
test al,ENISR_RX+ENISR_RX_ERR ; Frame received without overrun?
jnz recv_frame ; Go if so.
jmp recv_no_frame ; Go if not.
recv_frame:
loadport ; Point at Chip's Command Reg
setport EN_CCMD ; ..
pause_
mov al, ENC_NODMA+ENC_PAGE1+ENC_START
out dx, al ; Switch to page 1 registers
setport EN1_CURPAG ;Get current page of rcv ring
pause_
in al, dx ; ..
mov ah, al ; Hold current page in AH
setport EN_CCMD ; Back to page zero registers
pause_
mov al, ENC_NODMA+ENC_PAGE0+ENC_START
out dx, al ; Switch back to page 0 registers
setport EN0_BOUNDARY ;Get boundary page
pause_
in al, dx ; ..
inc al ; Step boundary from last used page
cmp al, SM_RSTOP_PG ; Wrap if needed
jne rx_nwrap3 ; Go if not
mov al, SM_RSTART_PG ; Wrap to first RX page
rx_nwrap3:
cmp al, ah ; Read all the frames?
je recv_frame_break ; Finished them all
mov ah,al ; make a byte address. E.G. page
mov al,0 ; 46h becomes 4600h into buffer
mov bl,ah
mov cx,RCV_HDR_SIZE
mov di,offset rcv_hdr
push ds
pop es ; set es to right place
call block_input
mov al, rcv_hdr+EN_RBUF_STAT ; Get the buffer status byte
test al,ENPS_RXOK ; Good frame?
jz recv_err_no_rcv
call rcv_frm ; Yes, go accept it
jmp recv_no_rcv
recv_err_no_rcv:
or byte ptr soft_rx_err_bits,al
add word ptr soft_rx_errors,1
adc word ptr soft_rx_errors+2,0
recv_no_rcv:
mov al, rcv_hdr+EN_RBUF_NXT_PG ; Start of next frame
dec al ; Make previous page for new boundary
cmp al, SM_RSTART_PG ; Wrap around the bottom?
jge rcv_nwrap4
mov al, SM_RSTOP_PG-1 ; Yes
rcv_nwrap4:
loadport ; Point at the Boundary Reg again
setport EN0_BOUNDARY ; ..
pause_
out dx, al ; Set new boundary
jmp recv_frame ; See if any more frames
recv_frame_break:
loadport ; Point at Interrupt Status Reg
setport EN0_ISR ; ..
pause_
mov al, ENISR_RX+ENISR_RX_ERR+ENISR_OVER
out dx, al ; Clear those requests
setport EN_CCMD
pause_
mov al, ENC_NODMA+ENC_PAGE0+ENC_START
out dx,al
jmp check_isr ; See if any other interrupts pending
recv_no_frame: ; Handle transmit flags.
test al,ENISR_TX+ENISR_TX_ERR ; Frame transmitted?
jnz isr_tx ; Go if so.
jmp isr_no_tx ; Go if not.
isr_tx:
mov ah, al ; Hold interrupt status bits
loadport ; Point at Transmit Status Reg
setport EN0_TSR ; ..
pause_
in al, dx ; ..
test ah,ENISR_TX ; Non-error TX?
jz isr_tx_err ; No, do TX error completion
call count_soft_err ; soft error ??
test al,ENTSR_COLL16 ; Jammed for 16 transmit tries?
jz isr_tx_njam ; Go if not
call count_out_err ; Yes, count those
isr_tx_njam:
setport EN0_ISR ; Clear the TX complete flag
pause_
mov al, ENISR_TX ; ..
out dx, al ; ..
jmp isr_tx_done
isr_tx_err:
test al,ENTSR_FU ; FIFO Underrun?
jz isr_txerr_nfu
call count_out_err ; Yes, count those
isr_txerr_nfu:
loadport ; Clear the TX error completion flag
setport EN0_ISR ; ..
pause_
mov al, ENISR_TX_ERR ; ..
out dx, al ; ..
isr_tx_done:
; If TX queue and/or TX shared memory ring buffer were being
; used, logic to step through them would go here. However,
; in this version, we just clear the flags for background to notice.
jmp check_isr ; See if any other interrupts on
isr_no_tx:
; Now check to see if any counters are getting full
test al,ENISR_COUNTERS ; Interrupt to handle counters?
jnz isr_stat ; Go if so.
jmp isr_no_stat ; Go if not.
isr_stat:
; We have to read the counters to clear them and to clear the interrupt.
; Version 1 of the PC/FTP driver spec doesn't give us
; anything useful to do with the data, though.
; Fix this up for V2 one of these days.
loadport ; Point at first counter
setport EN0_COUNTER0 ; ..
pause_
in al, dx ; Read the count, ignore it.
setport EN0_COUNTER1
pause_
in al, dx ; Read the count, ignore it.
setport EN0_COUNTER2
pause_
in al, dx ; Read the count, ignore it.
setport EN0_ISR ; Clear the statistics completion flag
pause_
mov al, ENISR_COUNTERS ; ..
out dx, al ; ..
isr_no_stat:
jmp check_isr ; Anything else to do?
interrupt_done:
ret
; Do the work of copying out a receive frame.
; Called with bl/ the page number of the frame header in shared memory
public rcv_frm
rcv_frm:
; first do a software multicast filter.
push bx ; save page.
cmp mcast_sw_filter,1 ; do software check of mcast ?
jnz rcv_frm_ok ; no, accept.
mov ax,word ptr rcv_hdr+EN_RBUF_NHDR ; get first word of address
test al,1 ; odd first byte
jz rcv_frm_ok ; must be our address if even
inc word ptr mcast_sw_fin
mov bx,word ptr rcv_hdr+EN_RBUF_NHDR+2 ; get second word of address
mov dx,word ptr rcv_hdr+EN_RBUF_NHDR+4 ; get third word of address
mov di,offset mcast_tab_b ; point to table and broadcast
mov cx,mcast_hcount ; get number in table
inc cx ; plus the broadcast
rcv_loop:
mov si,di ; save this table entry
cmp ax,[di]
jnz rcv_trynext
inc di
inc di
cmp bx,[di]
jnz rcv_trynext
inc di
inc di
cmp dx,[di]
jz rcv_mc_ok ; got it.
rcv_trynext:
mov di,si ; get table back,
add di,6
loop rcv_loop
pop bx ; restore bx
jmp rcv_no_copy
rcv_mc_ok:
inc word ptr mcast_sw_fout
rcv_frm_ok:
; Set cx to length of this frame.
mov ch, rcv_hdr+EN_RBUF_SIZE_HI ; Extract size of frame
mov cl, rcv_hdr+EN_RBUF_SIZE_LO ; Extract size of frame
sub cx, EN_RBUF_NHDR ; Less the header stuff
; Set es:di to point to Ethernet type field.
mov di, offset rcv_hdr+EN_RBUF_NHDR+EADDR_LEN+EADDR_LEN
push cx ; Save frame size
push es
mov ax, cs ; Set ds = code
mov ds, ax
mov es,ax
assume ds:code
call recv_find ; See if type and size are wanted
pop ds ; RX page pointer in ds now
assume ds:nothing
pop cx
pop bx
cld ; Copies below are forward, please
mov ax, es ; Did recv_find give us a null pointer?
or ax, di ; ..
je rcv_no_copy ; If null, don't copy the data
push cx ; We will want the count and pointer
push es ; to hand to client after copying,
push di ; so save them at this point
mov ah,bl ; set ax to page to start from
mov al,EN_RBUF_NHDR ; skip the header stuff
call block_input
pop si ; Recover pointer to destination
pop ds ; Tell client it's his source
pop cx ; And it's this long
assume ds:nothing
call recv_copy ; Give it to him
rcv_no_copy:
push cs ; Put ds back in code space
pop ds ; ..
assume ds:code
ret ; That's it for rcv_frm
public recv_exiting
recv_exiting:
;called from the recv isr after interrupts have been acknowledged.
;Only ds and ax have been saved.
assume ds:nothing
push dx
loadport
setport EN0_IMR ; Tell card it can cause these interrupts
pause_
mov al, ENISR_ALL
out dx, al
pop dx
ret
;any code after this will not be kept after initialization.
end_resident label byte
public usage_msg
usage_msg db "usage: NE2000 <packet_int_no> <int_level> <io_addr>",CR,LF,'$'
public copyright_msg
copyright_msg db "Packet driver for Novell NE2000, version ",'0'+majver,".",'0'+version,CR,LF
db "Portions Copyright 1989, Robert C. Clements, K1BC",CR,LF,'$'
cfg_err_msg:
db "NE2000 Configuration failed. Check parameters.",CR,LF,'$'
int_no_name:
db "Interrupt number ",'$'
io_addr_name:
db "I/O port ",'$'
using_186_msg db "Using 80[123]86 I/O instructions.",CR,LF,'$'
extrn set_recv_isr: near
;enter with si -> argument string, di -> word to store.
;if there is no number, don't change the number.
extrn get_number: near
public parse_args
parse_args:
;exit with nc if all went well, cy otherwise.
mov di, offset int_no ; May override interrupt channel
mov bx, offset int_no_name ; Message for it
call get_number
mov di, offset io_addr ; May override I/O address
mov bx, offset io_addr_name ; Message for it
call get_number
; mov di, offset mem_base ; Not movable in 3C503
; mov bx, offset mem_base_name ; Message for it
; call get_number ; Must get from jumpers.
clc
ret
cfg_error:
mov dx,offset cfg_err_msg
error:
mov ah,9 ; Type the msg
int 21h
stc ; Indicate error
ret ; Return to common code
; Called once to initialize the NE2000 card
extrn memory_test: near
public etopen
etopen: ; Initialize interface
;Determine the processor type. The 8088 and 8086 will actually shift ax
;over by 33 bits, while the 80[123]86 use a shift count mod 32.
;This bit lifted from NI5010 driver.
mov cl,33
mov ax,0ffffh
shl ax,cl
jz not_186
mov is_186,1
mov dx,offset using_186_msg
mov ah,9
int 21h
not_186:
; Now, initialize the DS8390 Ethernet Controller chip
ini_8390:
call reset_chip
loadport
setport EN0_DCFG ; Configure the fifo organization
pause_
mov al, ENDCFG_BM8 ; Fifo threshold = 8 bytes
out dx, al
setport EN_CCMD ; DS8390 chip's command register
pause_
mov al, ENC_NODMA+ENC_PAGE0+ENC_STOP
out dx, al ; Switch to page zero
setport EN0_TXCR ; Set transmitter mode to normal
pause_
xor al, al
out dx, al
setport EN0_RXCR ; Set receiver to monitor mode
pause_
mov al, ENRXCR_MON
out dx, al
; Set up control of shared memory, buffer ring, etc.
setport EN0_STARTPG ; Set receiver's first buffer page
pause_
mov al, SM_RSTART_PG
out dx, al
setport EN0_STOPPG ; and receiver's last buffer page + 1
pause_
mov al, SM_RSTOP_PG
out dx, al
setport EN0_BOUNDARY ; Set initial "last page we have emptied"
pause_
mov al, SM_RSTOP_PG ; (WD doc says set to RSTART_PG)
dec al ; (3Com doc says set to RSTOP_PG-1 ?)
; ; (and 3Com handling of BOUNDARY is
; ; different throughout.)
out dx, al ; (Check out why WD and 3Com disagree)
;
setport EN0_IMR ; Clear all interrupt enable flags
pause_
xor al, al
out dx, al
setport EN0_ISR ; Clear all interrupt assertion flags
pause_
mov al, 0ffh
out dx, al
setport EN_CCMD
pause_
mov al, ENC_NODMA+ENC_PAGE1+ENC_STOP
out dx,al
setport EN1_CURPAG
pause_
mov al, SM_RSTART_PG
out dx, al
setport EN_CCMD
pause_
mov al, ENC_NODMA+ENC_PAGE0+ENC_START
out dx,al
call get_board_data ; read board data
push ds ; Copy from card's address to current address
pop es
mov si, offset board_data ; address is at start
mov di, offset curr_hw_addr
mov cx, EADDR_LEN ; Copy one address length
rep movsb ; ..
call set_8390_eaddr ; Now set the address in the 8390 chip
call set_8390_multi ; Put the right stuff into 8390's multicast masks
loadport ; Base of I/O regs
setport EN_CCMD ; Chip command register
pause_
mov al, ENC_NODMA+ENC_PAGE0+ENC_STOP
out dx, al ; Back to page zero
setport EN0_RCNTLO ; Clear the byte count registers
pause_
xor al, al ; ..
out dx, al
setport EN0_RCNTHI
pause_
out dx, al ; Clear high byte, too
setport EN0_IMR ; Clear all interrupt enable flags
pause_
xor al, al
out dx, al
setport EN0_ISR ; Clear all interrupt assertion flags
pause_
mov al, 0ffh ; again for safety before making the
out dx, al ; interrupt be enabled
call set_recv_isr ; Put ourselves in interrupt chain
loadport
setport EN_CCMD ; Now start the DS8390
pause_
mov al, ENC_START+ENC_NODMA
out dx, al ; interrupt be enabled
setport EN0_RXCR ; Tell it what frames to accept
pause_
mov al, rxcr_bits ; As most recently set by set_mode
out dx, al
setport EN0_IMR ; Tell card it can cause these interrupts
pause_
mov al, ENISR_ALL
out dx, al
done: mov dx, offset end_resident ; Report our size
clc ; Say no error
ret ; Back to common code
code ends
end