home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
lan
/
driver6s
/
3c503.asm
< prev
next >
Wrap
Assembly Source File
|
1990-04-10
|
40KB
|
1,140 lines
version equ 3
include defs.asm
;/* PC/FTP Packet Driver source, conforming to version 1.05 of the spec,
;* for the 3-Com 3C503 interface card.
;* Robert C Clements, K1BC, 14 February, 1989
;* Portions (C) Copyright 1988, 1989 Robert C Clements
;*
; 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.
comment /
From: "James A. Harvey" <IJAH400@indyvax.iupui.edu>
Subject: Patches for 6.x packet drivers; lockup problem fixed!
Now for the best part, the lockup problem fix. I think this may be one that
I keep hearing about that for most people the machine locks up for a minute
on startup, but then continues. For me it was worse because it appears that
the "recovery" time is only short on heavily loaded networks. The lockup is
caused by the "first page for RX" being set improperly in etopen; I finally
figured it out by looking at code from other drivers that used the DS8390
chip. One must switch to the page 1 command registers first.
/
code segment byte public
assume cs:code, ds:code
HT equ 09h
CR equ 0dh
LF equ 0ah
;
; Packet Driver Error numbers
BAD_HANDLE equ 1 ;invalid handle number
NO_CLASS equ 2 ;no interfaces of specified class found
NO_TYPE equ 3 ;no interfaces of specified type found
NO_NUMBER equ 4 ;no interfaces of specified number found
BAD_TYPE equ 5 ;bad packet type specified
NO_MULTICAST equ 6 ;this interface does not support
CANT_TERMINATE equ 7 ;this packet driver cannot terminate
BAD_MODE equ 8 ;an invalid receiver mode was specified
NO_SPACE equ 9 ;operation failed because of insufficient
TYPE_INUSE equ 10 ;the type had previously been accessed,
BAD_COMMAND equ 11 ;the command was out of range, or not
CANT_SEND equ 12 ;the packet couldn't be sent (usually
; Stuff specific to the 3-Com 3C503 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.
; Symbol prefix "EN" is for Ethernet, National chip
; Symbol prefix "E33" is for _E_thernet, _3_Com 50_3_
; Symbol prefix "E33G" is for registers in the Gate array ASIC.
; The E33 registers - For the ASIC on the 3C503 card:
; Offsets from the board's base address, which can be set by
; jumpers to be one of the following 8 values (hex):
; 350, 330, 310, 300, 2E0, 2A0, 280, 250
; Factory default address is 300H.
; The card occupies a block of 16 I/O addresses.
; It also occupies 16 addresses at base+400 through base+40F.
; These high-addressed registers are in the ASIC.
; Recall that the normal PC I/O decoding is only 10 bits. The 11'th
; bit (400H) can be used on the same card for additional registers.
; This offset requires word, not byte, arithmetic
; on the DX register for the setport macro. Current SETPORT is OK.
; The card can also be jumpered to have the shared memory disabled
; or enabled at one of four addresses: C8000, CC000, D8000 or DC000.
; This version of the driver REQUIRES the shared memory to be
; enabled somewhere.
; The card can be operated using direct I/O instructions or by
; using the PC's DMA channels instead of the shared memory, but
; I haven't included the code for those other two methods.
; They would be needed in a system where all four possible addresses
; for the shared memory are in use by other devices. /Rcc
; Blocks of I/O addresses:
E33GA equ 400h ; Registers in the gate array.
E33_SAPROM equ 000h ; Window on station addr prom (if
; E33G_CNTRL bits 3,2 = 0,1
; The EN registers - the DS8390 chip registers
; These appear at Base+0 through Base+0F when bits 3,2 of
; E33G_CNTRL are 0,0.
; 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)
; 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 048h ; Set burst mode, 8 deep FIFO
; 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
; Registers in the 3-Com custom Gate Array
E33G_STARTPG equ E33GA+00h ; Start page, must match EN0_STARTPG
E33G_STOPPG equ E33GA+01h ; Stop page, must match EN0_STOPPG
E33G_NBURST equ E33GA+02h ; Size of DMA burst before relinquishing bus
E33G_IOBASE equ E33GA+03h ; Bit coded: where I/O regs are jumpered.
; (Which you have to know already to read it)
E33G_ROMBASE equ E33GA+04h ; Bit coded: Where/whether EEPROM&DPRAM exist
E33G_GACFR equ E33GA+05h ; Config/setup bits for the ASIC GA
E33G_CNTRL equ E33GA+06h ; Board's main control register
E33G_STATUS equ E33GA+07h ; Status on completions.
E33G_IDCFR equ E33GA+08h ; Interrupt/DMA config register
; (Which IRQ to assert, DMA chan to use)
E33G_DMAAH equ E33GA+09h ; High byte of DMA address reg
E33G_DMAAL equ E33GA+0ah ; Low byte of DMA address reg
E33G_VP2 equ E33GA+0bh ; Vector pointer - for clearing RAM select
E33G_VP1 equ E33GA+0ch ; on a system reset, to re-enable EPROM.
E33G_VP0 equ E33GA+0dh ; 3Com says set this to Ctrl-Alt-Del handler
E33G_FIFOH equ E33GA+0eh ; FIFO for programmed I/O data moves ...
E33G_FIFOL equ E33GA+0fh ; .. low byte of above.
; Bits in E33G_CNTRL register:
ECNTRL_RESET equ 001h ; Software reset of the ASIC and 8390
ECNTRL_THIN equ 002h ; Onboard thin-net xcvr enable
ECNTRL_SAPROM equ 004h ; Map the station address prom
ECNTRL_DBLBFR equ 020h ; FIFO configuration bit
ECNTRL_OUTPUT equ 040h ; PC-to-3C501 direction if 1
ECNTRL_START equ 080h ; Start the DMA logic
; Bits in E33G_STATUS register:
ESTAT_DPRDY equ 080h ; Data port (of FIFO) ready
ESTAT_UFLW equ 040h ; Tried to read FIFO when it was empty
ESTAT_OFLW equ 020h ; Tried to write FIFO when it was full
ESTAT_DTC equ 010h ; Terminal Count from PC bus DMA logic
ESTAT_DIP equ 008h ; DMA In Progress
; Bits in E33G_GACFR register:
EGACFR_NORM equ 049h ; Enable 8K shared mem, no DMA TC int
EGACFR_IRQOFF equ 0c9h ; Above, and disable 8390 IRQ line
; Shared memory management parameters
XMIT_MTU equ 600h ; Largest packet we have room for.
SM_TSTART_PG equ 020h ; First page of TX buffer
SM_RSTART_PG equ 026h ; Starting page of RX ring
SM_RSTOP_PG equ 040h ; Last page +1 of RX 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_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 3C503 parameter definitions
; 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, thick_or_thin
int_no db 2,0,0,0 ; Interrupt level
io_addr dw 0300h,0 ; I/O address for card (jumpers)
thick_or_thin dw 1,0 ; Non-zero means thin net
public mem_base
mem_base dw 00000h,0 ; Shared memory addr (jumpers)
; (Not changeable by software in 3C503) ; (0 if disabled by jumpers)
thin_bit db ECNTRL_THIN ; Default to thin cable
rxcr_bits db ENRXCR_BCST ; Default to ours plus multicast
public driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class db 1 ;from the packet spec
driver_type db 12 ;from the packet spec
driver_name db '3C503',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,
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.
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
; 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
loadport ; Set up for address of TX buffer in
setport E33G_GACFR ; Make sure gate array is set up and
mov al, EGACFR_NORM ; the RAM is enabled (not EPROM)
out dx, al ; ..
mov ax, cs:mem_base ; Set up ES at the shared RAM
mov es, ax ; ..
xor ax, ax ; Set up DI at base of tx buffer
mov ah, SM_TSTART_PG ; Where to put tx frame
mov di, ax ; ..
call movemem
pop cx ; Get back count to give to board
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 ; Successfully started
ret ; End of transmit-start routine
send_pkt_toobig:
mov dh,NO_SPACE
stc
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
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
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_PHYS ; Where it goes in 8390
set_8390_1:
lodsb
out dx,al
inc dx
loop set_8390_1
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
; 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
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
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
out dx, al ; ..
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 E33G_GACFR ; Make sure gate array is set up and
mov al, EGACFR_NORM ; the RAM is enabled (not EPROM)
out dx, al ; ..
setport EN0_ISR ; Point at interrupt status register
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
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 al, 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 al, es:[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 ; ..
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 ; ..
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 ; ..
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 al, 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 al, es:[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 ; ..
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 ; 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 ; ..
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?
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/
; 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 ch, es:[EN_RBUF_SIZE_HI] ; Extract size of frame
mov cl, es:[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. 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?
jg 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:
call movemem
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: 3C503 <packet_int_no> <int_level(2-5)> <io_addr> <thin_net_flag>",CR,LF,'$'
public copyright_msg
copyright_msg db "Packet driver for 3-Com 3C503, version ",'0'+majver,".",'0'+version,CR,LF
db "Portions Copyright 1989, Robert C. Clements, K1BC",CR,LF,'$'
cfg_err_msg:
db "3C503 Configuration failed. Check parameters.",CR,LF,'$'
no_mem_msg:
db "3C503 memory jumper must be set to enable memory.",CR,LF
db "Driver cannot run with memory disabled.",'$'
int_no_name:
db "Interrupt number ",'$'
io_addr_name:
db "I/O port ",'$'
mem_base_name:
db "Memory address ",'$'
thick_thin_msg:
db "Flag, non-zero if thin Ethernet: ",'$'
mem_busted_msg:
db "Shared RAM on 3C503 card is defective or there is an address conflict.",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
;enter with dx -> name of word, di -> dword to print.
extrn print_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
call get_number
mov di, offset io_addr ; May override I/O address
call get_number
mov di, offset thick_or_thin ; May override thick/thin cable flag
call get_number
mov ax, thick_or_thin ; Now make the right bit
cmp ax, 0
je parse_thin1 ; If zero, leave bit off
mov al, ECNTRL_THIN ; Else the bit for the card
parse_thin1:
mov thin_bit,al ; Save for setting up the card
; 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
mem_busted:
mov dx, offset mem_busted_msg
jmp short error
nomem_error:
mov dx,offset no_mem_msg
jmp short error
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 3C503 card
public etopen
etopen: ; Initialize interface
; First, initialize the Gate Array (ASIC) card logic. Later do the 8390.
loadport ; First, pulse the board reset
setport E33G_CNTRL
mov al, thin_bit ; Thick or thin cable bit
or al, ECNTRL_RESET
out dx, al ; Turn on board reset bit
and al, ECNTRL_THIN
out dx, al ; Turn off board reset bit
; Now get the board's physical address from on-board PROM into card_hw_addr
cli ; Protect the E33G_CNTRL contents
setport E33G_CNTRL ; Switch control bits to enable SA PROM
mov al, thin_bit
or al, ECNTRL_SAPROM
out dx, al ; ..
setport E33_SAPROM ; Where the address prom is
cld ; Make sure string mode is right
push cs ; Point es:di at local copy space
pop es
mov di, offset card_hw_addr
mov cx, EADDR_LEN ; Set count for loop
ini_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 ini_addr_loop ; Loop over six bytes
loadport ; Re-establish I/O base after dx mods
setport E33G_CNTRL ; Switch control bits to turn off SA PROM
mov al, thin_bit
out dx, al ; Turn off SA PROM windowing
sti ; Ok for E33G_CNTRL to change now
; Point the "Vector Pointer" registers off into the boonies so we
; don't get the shared RAM disabled on us while we're using it.
; Ideally a warm boot should reset this too, to get to ROM on this card,
; but I don't know a guaranteed way to determine that value.
setport E33G_VP2
mov al, 0ffh ; Point this at the ROM restart location
out dx, al ; of ffff0h.
setport E33G_VP1
out dx, al
xor al, al
setport E33G_VP0
out dx, al
;Make sure shared memory is jumpered on. Find its address.
setport E33G_ROMBASE ; Point at rom/ram cfg reg
in al, dx ; Read it
test al,0f0h ; Any bits on?
jne memcfg_1 ; Yes
jmp nomem_error ; No, can't run without it
memcfg_1:
mov bx, 0c600h ; Build mem segment here
test al,0c0h ; DC00 or D800?
je memcfg_2 ; No
add bx, 01000h ; Yes, make Dx00
memcfg_2:
test al,0a0h ; DC00 or CC00?
je memcfg_3
add bx, 00400h ; Yes, make xC00
memcfg_3:
mov mem_base,bx ; Remember segment addr of memory
; Set up Gate Array's Config Reg to enable and size the RAM.
setport E33G_GACFR ; Make sure gate array is set up and
mov al, EGACFR_IRQOFF ; the RAM is enabled (not EPROM)
out dx, al ; ..
; Check the card's memory
mov ax, mem_base ; Set segment of the shared memory
add ax, 16*SM_TSTART_PG ; which starts 2000h up from "base"
mov cx, 2000h ; Length of RAM to test
call memory_test ; Check it out
jz mem_works ; Go if it's OK
jmp mem_busted ; Go report failure if it's bad
mem_works:
; Set up control of shared memory, buffer ring, etc.
loadport
setport E33G_STARTPG ; Set ASIC copy of rx's first buffer page
mov al, SM_RSTART_PG
out dx, al
setport E33G_STOPPG ; and ASIC copy of rx's last buffer page + 1
mov al, SM_RSTOP_PG
out dx, al
; Set up interrupt/DMA control register in ASIC.
; For now, we won't use the DMA, so B0-B3 are zero.
xor ah, ah ; Get the interrupt level from arg line
mov al, int_no ; ..
cmp al, 9 ; If converted to 9, make back into 2
jne get_irq1 ; Not 9
mov al, 2 ; Card thinks it's IRQ2
get_irq1: ; Now should have level in range 2-5
sub ax, 2 ; Make 0-3 for tables
cmp ax, 5-2 ; In range?
jna get_irq2
jmp cfg_error ; If not, can't configure.
get_irq2:
xor cx, cx ; Make the bit for the ASIC
mov cl, al ; Shift count
mov al, 10h ; Bit for irq2
jcxz get_irq3 ; Go if it's 2
shl al, cl ; Shift over for 3-5
get_irq3:
setport E33G_IDCFR ; Point at ASIC reg for IRQ level
out dx, al ; Set the bit
setport E33G_NBURST ; Set burst size to 8
mov al, 8
out dx, al ; ..
setport E33G_DMAAH ; Set up transmit bfr in DMA addr
mov al, SM_TSTART_PG
out dx, al
xor ax, ax
setport E33G_DMAAL
out dx, al
; Now, initialize the DS8390 Ethernet Controller chip
ini_8390:
setport EN_CCMD ; DS8390 chip's command register
mov al, ENC_NODMA+ENC_PAGE0+ENC_STOP
out dx, al ; Switch to page zero
setport EN0_ISR ; Clear all interrupt flags
mov al, 0ffh ; ..
out dx, al ; ..
setport EN0_DCFG ; Configure the fifo organization
mov al, ENDCFG_BM8 ; Fifo threshold = 8 bytes
out dx, al
setport EN0_TXCR ; Set transmitter mode to normal
xor al, al
out dx, al
setport EN0_RXCR ; Set receiver to monitor mode
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
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 ; (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)
push ds ; Copy from card's address to current address
pop es
mov si, offset card_hw_addr
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 ;[jah] Switch to page 1 registers
mov al,ENC_NODMA+ENC_PAGE1 ;[jah]
out dx, al ;[jah]
setport EN1_CURPAG ; Set current shared page for RX to work on
mov al, SM_RSTART_PG+1
out dx, al
setport EN_CCMD ; Chip command register
mov al, ENC_NODMA+ENC_PAGE0
out dx, al ; Back to page zero
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_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 what frames to accept
mov al, rxcr_bits ; As most recently set by set_mode
out dx, al
setport E33G_GACFR ; Now let it interrupt us
mov al, EGACFR_NORM ; and leave RAM enabled
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 ; May override interrupt channel
mov dx, offset int_no_name ; Message for it
call print_number
mov di, offset io_addr ; May override I/O address
mov dx, offset io_addr_name ; Message for it
call print_number
mov di, offset thick_or_thin ; May override thick/thin cable flag
mov dx, offset thick_thin_msg ; Message for it
call print_number
mov dx, offset end_resident ; Report our size
clc ; Say no error
ret ; Back to common code
include memtest.asm
code ends
end