home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
lan
/
driver6s
/
wd8003e.asm
< prev
next >
Wrap
Assembly Source File
|
1990-04-10
|
34KB
|
1,044 lines
version equ 5
include defs.asm
; PC/FTP Packet Driver source, conforming to version 1.05 of the spec
; Updated to version 1.08 Feb. 17, 1989 by Russell Nelson.
; Robert C Clements, K1BC, August 19, 1988
; Portions (C) Copyright 1988 Robert C Clements
; Version 3 updated by Jan Engvald LDC to handle WD8003ET/A (micro channel
; card) and to utilize all 32 kbyte memory on the WD8003EBT card.
; 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.
code segment byte public
assume cs:code, ds:code
; Stuff specific to the Western Digital WD003E Ethernet controller board
; C version by Bob Clements, K1BC, May 1988 for the KA9Q TCP/IP package
; Symbol prefix "EN" is for Ethernet, Western-digital card
; The EN registers - First, the board registers */
EN_CMD equ 000h ; Board's command register
EN_REG5 equ 005h ; New command register (REGISTER 5)
EN_SAPROM equ 008h ; Window on station addr prom
; The EN registers - Next, the DS8390 chip registers */
; There are two (really 3) pages of registers in the chip. You select
; which page you want, then address them at offsets 10-1F from base.
; The chip command register (EN_CCMD) appears in both pages.
EN_CCMD equ 010h ; Chip's command register
; Page 0
EN0_STARTPG equ 011h ; Starting page of ring bfr
EN0_STOPPG equ 012h ; Ending page +1 of ring bfr
EN0_BOUNDARY equ 013h ; Boundary page of ring bfr
EN0_TSR equ 014h ; Transmit status reg
EN0_TPSR equ 014h ; Transmit starting page
EN0_TCNTLO equ 015h ; Low byte of tx byte count
EN0_TCNTHI equ 016h ; High byte of tx byte count
EN0_ISR equ 017h ; Interrupt status reg
EN0_RCNTLO equ 01ah ; Remote byte count reg
EN0_RCNTHI equ 01bh ; Remote byte count reg
EN0_RXCR equ 01ch ; RX control reg
EN0_TXCR equ 01dh ; TX control reg
EN0_COUNTER0 equ 01dh ; Rcv alignment error counter
EN0_DCFG equ 01eh ; Data configuration reg
EN0_COUNTER1 equ 01eh ; Rcv CRC error counter
EN0_IMR equ 01fh ; Interrupt mask reg
EN0_COUNTER2 equ 01fh ; Rcv missed frame error counter
; Page 1
EN1_PHYS equ 011h ; This board's physical enet addr
EN1_CURPAG equ 017h ; Current memory page
EN1_MULT equ 018h ; Desired multicast addr
; Board commands in EN_CMD
EN_RESET equ 080h ; Reset the board
EN_MEMEN equ 040h ; Enable the shared memory
EN_MEM_MASK equ 03fh ; B18-B13 of address of the shared memory
; Commands for REG5 register
ENR5_EIL equ 004h ; enable 8390 interrupts to bus
; 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_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
ENRXCR_RUNT equ 002h ; accept runt packets
ENRXCR_BCST equ 004h ; Accept broadcasts
ENRXCR_MULTI equ 008h ; Accept multicasts
ENRXCR_PROMP equ 010h ; physical promiscuous mode
; Commands for TX control reg
ENTXCR_LOOP equ 002h ; Set loopback mode
; Bits in EN0_DCFG - Data config register
ENDCFG_BM8 equ 048h ; Set burst mode, 8 deep FIFO
ENDCFG_WTS equ 1 ; Word Transfer Select
; 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_RESET equ 080h ; Reset completed
ENISR_ALL equ 01fh ; 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_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 0 ; First page of TX buffer
SM_RSTART_PG equ 6 ; Starting page of ring
SM_BASE equ 0C400h ; Default para where shared memory starts
; Real value set at attach time.
sm_rstop_pg db 32 ; Last page +1 of ring
; Description of header of each packet in receive area of shared memory
EN_RBUF_STAT equ 0 ; Received frame status
EN_RBUF_NXT_PG equ 1 ; Page after this frame
EN_RBUF_SIZE equ 2 ; Length of this frame (word access)
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 WD8003E parameter definitions
; The following three values may be overridden from the command line.
; If they are omitted from the command line, these defaults are used.
public int_no, io_addr, mem_base
int_no db 3,0,0,0 ; Interrupt level
io_addr dw 0280h,0 ; I/O address for card (jumpers)
mem_base dw 0d000h,0 ; Shared memory addr (software)
public driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class db 1 ;from the packet spec
driver_type db 14 ;from the packet spec
driver_name db 'WD8003E',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,
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.
public rcv_modes
rcv_modes dw 7 ;number of receive modes in our table.
dw 0 ;there is no mode 1.
dw rcv_mode_1
dw rcv_mode_2
dw rcv_mode_3
dw rcv_mode_4
dw rcv_mode_5
dw rcv_mode_6
rxcr_bits db ENRXCR_BCST ; Default to ours plus multicast
extrn sys_features: byte
microchannel equ 2 ; flag in above byte
; send_pkt: - The Transmit Frame routine
public send_pkt
send_pkt:
;enter with es:di->upcall routine, (0:0) if no upcall is desired.
; (only if the high-performance bit is set in driver_function)
;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 ; ..
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
; 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
; Now compute destination of move in es:di
mov ax, mem_base ; Compute base of transmit buffer
; add ax, SM_TSTART_PG*16 ; The right page in mem (currently zero)
mov es, ax ; Paragraph of the TX buffer
xor di, di ; Fill starting at beginning of paragraph
; Can't use movemem which word aligns to the source, but needs to word
; align to the destination writing to WD8003ET/A. Fortunately works for
; all cards.
inc cx ; if odd bytes pad to word
shr cx,1 ; convert bytes to words
rep movsw ; word access required by MC card
pop cx ; Get back count to give to board
loadport ; Base of I/O regs
setport EN0_TCNTLO ; Low byte of TX count
mov al, cl ; Get the count
out dx, al ; Tell card the count
setport EN0_TCNTHI ; High byte of TX count
mov al, ch ; Get the count
out dx, al ; Tell card the count
setport EN0_TPSR ; Transmit Page Start Register
mov al, SM_TSTART_PG
out dx, al ; Start the transmitter
setport EN_CCMD ; Chip command reg
mov al, ENC_TRANS+ENC_NODMA
out dx, al ; Start the transmitter
clc
ret ; End of transmit-start routine
send_pkt_toobig:
mov dh,NO_SPACE
stc
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.
assume ds:code
cmp cx, EADDR_LEN ; Caller wants a reasonable length?
jb get_addr_x ; No, fail.
mov cx, EADDR_LEN ; Yes. Set count for loop
loadport ; Base of device
setport EN_SAPROM ; Where the address prom is
cld ; Make sure string mode is right
get_addr_loop:
in al, dx ; Get a byte of address
stosb ; Feed it to caller
inc dx ; Next byte at next I/O port
loop get_addr_loop ; Loop over six bytes
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
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:
loadport
setport EN1_PHYS
set_address_1:
lodsb
out dx,al
inc dx
loop set_address_1
set_address_okay:
mov cx,EADDR_LEN ;return their address length.
clc
set_address_done:
push cs
pop ds
assume ds:code
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
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
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.
mov dh,NO_MULTICAST
stc
ret
; Set the multicast filter mask bits in case promiscuous rcv wanted
set_8390_multi:
loadport
setport EN_CCMD ; Chip command register
mov cx,8 ; Eight bytes of multicast filter
mov si,offset mcast_list_bits ; Where bits are, if not all ones
push cs
pop ds
cli ; Protect from irq changing page bits
mov al,ENC_NODMA+ENC_PAGE1
out dx,al ; Switch to page one for writing eaddr
setport EN1_MULT ; Where it goes in 8390
mov al,mcast_all_flag ; Want all ones or just selected bits?
or al,al
jz set_mcast_2 ; z = 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
jmp $+2 ; limit chip access rate
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
jmp $+2 ; limit chip access rate
loop set_mcast_2
set_mcast_x:
loadport
setport EN_CCMD ; Chip command register
mov al,ENC_NODMA+ENC_PAGE0
out dx,al ; Restore to page zero
sti ; OK for interrupts now
ret
public terminate
terminate:
ret
public reset_interface
reset_interface:
assume ds:code
loadport ; Base of I/O regs
setport EN_CCMD ; Chip command reg
mov al, ENC_STOP+ENC_NODMA
out dx, al ; Stop the DS8390
setport EN0_ISR ; Interrupt status reg
mov al, 0ffh ; Clear all pending interrupts
out dx, al ; ..
setport EN0_IMR ; Interrupt mask reg
xor al, al ; Turn off all enables
setport EN_REG5
mov al,ENR5_EIL
test sys_features,microchannel
jz reset_no_mc
out dx,al ; enable 8390 interrupts to bus
reset_no_mc:
ret
;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 interrupt status register
setport EN0_ISR ; ..
in al, dx ; Get pending interrupts
and al, ENISR_ALL ; Any?
jnz isr_test_overrun
ret ; 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.
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 card
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
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
xor ah, ah ; Convert page to segment
mov cl, 4
mov bl, al ; Page number as arg to rcv_frm
shl ax, cl ; ..
add ax, mem_base ; Page in this memory
mov es, ax ; Segment pointer to the frame header
push es ; Hold this frame pointer for later
mov ax, es:[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:
pop es ; Back to start of this frame
mov ax, es:[EN_RBUF_NXT_PG and 0fffeh] ; Get pointer to next frame
mov al,ah
dec al ; Back up one page
cmp al, SM_RSTART_PG ; Did it wrap?
jae rcv_ovr_nwr2
mov al, sm_rstop_pg ; Yes, back to end of ring
dec al
rcv_ovr_nwr2:
loadport ; Point at boundary reg
setport EN0_BOUNDARY ; ..
out dx, al ; Set the boundary
rcv_ovr_empty:
setport EN0_RCNTLO ; Point at byte count regs
xor al, al ; Clear them
out dx, al ; ..
setport EN0_RCNTHI
out dx, al
setport EN0_ISR ; Point at status reg
mov cx, 8000h ; Timeout counter
rcv_ovr_rst_loop:
in al, dx ; Is it finished resetting?
test al,ENISR_RESET ; ..
jmp $+2 ; limit chip access rate
loopnz rcv_ovr_rst_loop; Loop til reset, or til timeout
loadport ; Point at Transmit control reg
setport EN0_TXCR ; ..
mov al, ENTXCR_LOOP ; Put transmitter in loopback mode
out dx, al ; ..
setport EN_CCMD ; Point at Chip command reg
mov al, ENC_START+ENC_NODMA
out dx, al ; Start the chip running again
setport EN0_TXCR ; Back to TX control reg
xor al, al ; Clear the loopback bit
out dx, al ; ..
setport EN0_ISR ; Point at Interrupt status register
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 ; ..
mov al, ENC_NODMA+ENC_PAGE1
out dx, al ; Switch to page 1 registers
setport EN1_CURPAG ;Get current page of rcv ring
in al, dx ; ..
mov ah, al ; Hold current page in AH
setport EN_CCMD ; Back to page zero registers
mov al, ENC_NODMA+ENC_PAGE0
out dx, al ; Switch back to page 0 registers
setport EN0_BOUNDARY ;Get boundary page
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 bl, al ; Page number as arg to rcv_frm
xor ah, ah ; Make segment pointer to this frame
mov cl, 4 ; 16 * pages = paragraphs
shl ax, cl ; ..
add ax, mem_base ; That far into shared memory
mov es, ax ; Segment part of pointer
push es ; Hold on to this pointer for later
mov ax, es:[EN_RBUF_STAT] ; Get the buffer status byte
test al,ENPS_RXOK ; Good frame?
jz recv_no_rcv
call rcv_frm ; Yes, go accept it
recv_no_rcv:
pop es ; Back to base of frame
mov ax, es:[EN_RBUF_NXT_PG and 0fffeh] ; Start of next frame
mov al,ah
dec al ; Make previous page for new boundary
cmp al, SM_RSTART_PG ; Wrap around the bottom?
jae rcv_nwrap4
mov al, sm_rstop_pg ; Yes
dec al
rcv_nwrap4:
loadport ; Point at the Boundary Reg again
setport EN0_BOUNDARY ; ..
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 ; ..
mov al, ENISR_RX+ENISR_RX_ERR+ENISR_OVER
out dx, al ; Clear those requests
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 ; ..
in al, dx ; ..
test ah,ENISR_TX ; Non-error TX?
jz isr_tx_err ; No, do TX error completion
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
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 ; ..
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 ; nz = yes
jmp isr_no_stat
isr_stat:
; We have to read the counters to clear them and to clear the interrupt.
; The structure of the PC/FTP driver system doesn't give us
; anything useful to do with the data, though.
loadport ; Point at first counter
setport EN0_COUNTER0
in al,dx ; Read the count, ignore it
setport EN0_COUNTER1
in al,dx ; Read the count, ignore it
setport EN0_COUNTER2
in al,dx ; Read the count, ignore it
setport EN0_ISR ; Clear the statistics completion flag
mov al,ENISR_COUNTERS
out dx,al
isr_no_stat:
jmp check_isr ; Anything else to do?
; Do the work of copying out a receive frame.
; Called with bl/ the page number of the frame header in shared memory/
; Also, es/ the paragraph number of that page.
rcv_frm:
; Old version checked size, memory space, queue length here. Now done
; in higher level code.
; Set cx to length of this frame.
mov cx, es:[EN_RBUF_SIZE] ; Extract size of frame
sub cx, EN_RBUF_NHDR ; Less the header stuff
cmp cx, 1514
jbe rcv_size_ok ; is the size sane?
cmp ch, cl ; is it starlan bug (dup of low byte)
jz rcv_starlan_bug
mov cx, 1514 ; cap the length
jmp rcv_size_ok
rcv_starlan_bug: ; fix the starlan bug
mov ch, es:[EN_RBUF_NXT_PG] ; Page after this frame
cmp ch, bl
ja rcv_frm_no_wrap
add ch, sm_rstop_pg ; Wrap if needed
dec ch
rcv_frm_no_wrap:
sub ch, bl
dec ch
rcv_size_ok:
; Set es:di to point to Ethernet type field. es is already at base of
; page where this frame starts. Set di after the header and two addresses.
mov di, EN_RBUF_NHDR+EADDR_LEN+EADDR_LEN
push bx ; Save page number in bl
push cx ; Save frame size
push es
mov ax, cs ; Set ds = code
mov ds, 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
;; if ( (((size + 255 + EN_RBUF_NHDR) >> 8) + pg) > sm_rstop_pg){
mov ax, cx ; Length of frame
add ax, EN_RBUF_NHDR+255 ; Including the overhead bytes, rounded up
add ah, bl ; Compute page with last byte of data in ah
cmp ah, sm_rstop_pg ; Over the top of the ring?
ja rcopy_wrap ; Yes, move in two pieces
mov si, EN_RBUF_NHDR ; One piece, starts here in first page (in ds)
jmp rcopy_one_piece ; Go move it
rcopy_wrap:
;; Copy in two pieces due to buffer wraparound. */
;; n = ((sm_rstop_pg - pg) << 8) - EN_RBUF_NHDR; /* To top of mem */
mov ah, sm_rstop_pg ; Compute length of first part
sub ah, bl ; as all of the pages up to wrap point
xor al, al ; 16-bit count
sub ax, EN_RBUF_NHDR ; Less the four overhead bytes
sub cx, ax ; Move the rest in second part
push cx ; Save count of second part
mov cx, ax ; Count for first move
mov si, EN_RBUF_NHDR ; ds:si points at first byte to move
shr cx, 1 ; All above are even numbers, do words.
rep movsw ; Move first part of frame
mov ax, mem_base ; Paragraph of base of shared memory
mov ds, ax ; ..
mov si, SM_RSTART_PG*256 ; Offset to start of first receive page
pop cx ; Bytes left to move
rcopy_one_piece:
shr cx,1 ; convert bytes to words
rep movsw ; word access desired for MC card
jnc rcv_even ; odd byte left over?
lodsw ; yes, word fetch
stosb ; and byte store
rcv_even:
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
ret
;any code after this will not be kept after initialization.
end_resident label byte
public usage_msg
usage_msg db "usage: WD8003E <packet_int_no> <int_level> <io_addr> <mem_base>",CR,LF,'$'
public copyright_msg
copyright_msg db "Packet driver for Western Digital WD8003 E EBT EB ET/A and E/A, version "
db '0'+majver,".",'0'+version,CR,LF
db "Portions Copyright 1988, Robert C. Clements, K1BC",CR,LF,'$'
no_board_msg:
db "WD8003E apparently not present at this IO address.",CR,LF,'$'
occupied_msg:
db "Suggested WD8003E memory address already occupied",CR,LF,'$'
int_no_name db "Interrupt number ",'$'
io_addr_name db "I/O port ",'$'
mem_base_name db "Memory address ",'$'
occupied_switch db 0 ;if zero, don't use occupied test.
extrn set_recv_isr: near
extrn skip_blanks: near
;enter with si -> argument string, di -> word to store.
;if there is no number, don't change the number.
extrn get_number: near
;enter with dx -> name of word, di -> dword to print.
extrn print_number: near
public parse_args
parse_args:
call skip_blanks
cmp al,'-' ;did they specify a switch?
jne not_switch
cmp byte ptr [si+1],'o' ;did they specify '-o'?
je got_occupied_switch
stc ;no, must be an error.
ret
got_occupied_switch:
mov occupied_switch,1
add si,2 ;skip past the switch's characters.
jmp parse_args ;go parse more arguments.
not_switch:
mov di,offset int_no
call get_number
mov di,offset io_addr
call get_number
mov di,offset mem_base
call get_number
ret
extrn etopen_diagn: byte
addr_not_avail:
mov dx,offset occupied_msg
mov etopen_diagn,34
jmp short error_wrt
bad_cksum:
no_memory:
mov dx,offset no_board_msg
mov etopen_diagn,37
error_wrt:
mov ah,9
int 21h
stc
ret
public etopen
etopen: ; Initialize interface
loadport ; First, pulse the board reset
setport EN_CMD
mov al, EN_RESET
out dx, al ; Turn on board reset bit
xor al, al
out dx, al ; Turn off board reset bit
setport EN_REG5
mov al,ENR5_EIL
test sys_features,microchannel
jz etopen_no_mc
out dx,al ; enable 8390 interrupts to bus
etopen_no_mc:
setport EN_CCMD ; DS8390 chip's command register
mov al, ENC_NODMA+ENC_PAGE0
out dx, al ; Switch to page zero
setport EN0_ISR ; Clear all interrupt flags
mov al, 0ffh ; ..
out dx, al ; ..
; Copy our Ethernet address from PROM into the DS8390
; (No provision in driver spec for setting a false address.)
setport EN_CCMD ; Chip command register
mov al, ENC_NODMA+ENC_PAGE1
out dx, al ; Switch to page one for writing eaddr
mov cl, EADDR_LEN ; Loop for six bytes
xor ch, ch ; Clear the index of bytes
xor bx, bx ; Clear the addr ROM checksum
cpy_adr_loop:
loadport ; Base of registers
setport EN_SAPROM ; Prom address
add dl, ch ; Plus which byte this is
in al, dx ; Get a byte of address
add bl,al ; Compute the checksum
add dl, EN1_PHYS-EN_SAPROM ; Point at reg in chip
out dx, al ; Copy that byte
inc ch ; Step the index
dec cl ; Count bytes
jnz cpy_adr_loop ; Loop for six
loadport ; Get last two bytes into cksum
setport EN_SAPROM+EADDR_LEN
in al, dx ; Get seventh byte
add bl, al ; Add it in
inc dx ; Step to eighth byte
in al, dx ; Get last byte
add bl, al ; Final checksum
cmp bl, 0ffh ; Correct?
jnz bad_cksum ; No, board is not happy
; Clear the multicast filter enables, we don't want any of them.
mov cl, 8 ; Eight bytes of multicast filter
xor al, al ; Zeros for filter
loadport ; Base of multicast filter locations
setport EN1_MULT ; ..
clr_mcast_l:
out dx, al ; Clear a byte
inc dl ; Step to next one
dec cl ; Count 8 filter locs
jnz clr_mcast_l ; ..
loadport ; Base of I/O regs
setport EN_CCMD ; Chip command register
mov al, ENC_NODMA+ENC_PAGE0
out dx, al ; Back to page zero
setport EN0_DCFG ; Configure the fifo organization
mov al, ENDCFG_BM8 ; Fifo threshold = 8 bytes
test sys_features,microchannel
jz bytemove
or al,ENDCFG_WTS ; word access for MC card
bytemove:
out dx, al
setport EN0_RCNTLO ; Clear the byte count registers
xor al, al ; ..
out dx, al
setport EN0_RCNTHI
out dx, al ; Clear high byte, too
setport EN0_RXCR ; Set receiver to monitor mode
mov al, ENRXCR_MON
out dx, al
setport EN0_TXCR ; Set transmitter mode to normal
xor al, al
out dx, al
; Check if the shared memory address range is availabe to us
mov bx,mem_base
cmp bh,0c0h ; low limit is C000
jae c0_up
jmp no_memory
c0_up:
cmp bh,0e0h ; upper limit is E000
jb e0_down
jmp no_memory
e0_down:
test bx,01ffh ; must be on a 8 k boundary
jz eightk
jmp no_memory
eightk:
mov di,8*1024/16 ; 8 kbyte
mov sm_rstop_pg,32
test sys_features,microchannel
jz just_8k
mov cx,16*1024/16 ; 16 kbytes
mov sm_rstop_pg,64
just_8k:
cmp occupied_switch,0 ; did they insist?
jne is_avail ; yes, don't check.
call occupied_chk ; check if address range is available
jnc is_avail
jmp addr_not_avail ; we HAVE to have at least 8/16 kbyte
is_avail:
; If I knew how to recognize an WD8003 EBT or EB card, the following code
; could be skipped with just a slight change in the above code /JE.
test sys_features,microchannel
jnz not_32k
mov di,32*1024/16 ; may be there is space for 32 kbyte
call occupied_chk
jc not_32k ; no, then don't try it later either
and bh,7
jnz not_32k ; must be on a 32k boundary
mov sm_rstop_pg,128 ; yes, there is space for a WD8003EBT
not_32k:
; Turn on the shared memory block
setport EN_CMD ; Point at board command register
mov ax, mem_base ; Find where shared memory will be mapped
mov al, ah ; Shift to right location
sar al, 1 ; in the map control word
and al, EN_MEM_MASK ; Just these bits
or al, EN_MEMEN ; Command to turn on map
test sys_features,microchannel
jz AT_card
mov al,EN_MEMEN ; membase handled different for MC card
AT_card:
out dx, al ; Create that memory
; Find how much memory this card has (without destroying other memory)
mov si,ax ; save bord command value
mov es,mem_base
mov bl,0FFh ; first try 32 kbyte (WD8003EBT)
mov bh,sm_rstop_pg ; or what is available
dec bh
memloop:
dec bx ; use even address
cli ; disable interrupts
mov cx,es:[bx] ; save old memory contents
mov word ptr es:[bx],05A5Ah ; put testpattern
loadport
setport EN_CCMD ; drain the board bus for any
in al,dx ; capacitive memory
cmp word ptr es:[bx],05A5Ah ; any real memory there?
jne not_our_mem ; no
setport EN_CMD ; yes
mov ax,si
and al,0FFh xor EN_MEMEN
out dx,al ; turn off our memory
jmp short $+2
or al,EN_MEMEN
cmp word ptr es:[bx],05A5Ah ; was it OUR memory?
out dx,al
jmp short $+2
mov es:[bx],cx
sti
jne our_mem ; yes, it wasn't there any more
not_our_mem: ; no, it was still there
shr bx,1 ; test if half as much memory
cmp bx,1FFFh ; down to 8 kbyte
jae memloop
jmp no_memory ; no memory at address mem_base
our_mem: ; it IS our memory!
inc bh
mov sm_rstop_pg,bh ; # of 256 byte ring bufs + 1
mov ch,bh
xor cl,cl
mov ax,mem_base
call memory_test ; check all of that memory
je mem_ok
jmp no_memory
mem_ok:
; Set up control of shared memory, buffer ring, etc.
setport EN0_STARTPG ; Set receiver's first buffer page
mov al, SM_RSTART_PG
out dx, al
setport EN0_STOPPG ; and receiver's last buffer page + 1
mov al, sm_rstop_pg
out dx, al
setport EN0_BOUNDARY ; Set initial "last page we have emptied"
mov al, SM_RSTART_PG
out dx, al
setport EN_CCMD ; Switch to page one registers
mov al, ENC_NODMA+ENC_PAGE1
out dx, al
setport EN1_CURPAG ; Set current shared page for RX to work on
mov al, SM_RSTART_PG+1
out dx, al
setport EN_CCMD ; Switch back to page zero registers
mov al, ENC_NODMA+ENC_PAGE0
out dx, al
setport EN0_IMR ; Clear all interrupt enable flags
xor al, al
out dx, al
setport EN0_ISR ; Clear all interrupt assertion flags
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
mov al, ENC_START+ENC_NODMA
out dx, al ; interrupt be enabled
setport EN0_RXCR ; Tell it to accept broadcasts
mov al, ENRXCR_BCST
out dx, al
setport EN0_IMR ; Tell card it can cause these interrupts
mov al, ENISR_ALL
out dx, al
mov di,offset int_no
mov dx,offset int_no_name
call print_number
mov di,offset io_addr
mov dx,offset io_addr_name
call print_number
mov di,offset mem_base
mov dx,offset mem_base_name
call print_number
mov dx,offset end_resident
clc
ret
include memtest.asm
include occupied.asm
code ends
end