home *** CD-ROM | disk | FTP | other *** search
- 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 word 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
- int_num 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 as_send_pkt
- ; The Asynchronous Transmit Packet routine.
- ; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
- ; interrupts possibly enabled.
- ; Exit with nc if ok, or else cy if error, dh set to error number.
- ; es:di and interrupt enable flag preserved on exit.
- as_send_pkt:
- ret
-
- public drop_pkt
- ; Drop a packet from the queue.
- ; Enter with es:di -> iocb.
- drop_pkt:
- assume ds:nothing
- ret
-
- public xmit
- ; Process a transmit interrupt with the least possible latency to achieve
- ; back-to-back packet transmissions.
- ; May only use ax and dx.
- xmit:
- assume ds:nothing
- ret
-
-
- 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
-
-
- include movemem.asm
-
-
- 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 [-n] [-d] [-w] <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 al, int_no ; Get board's interrupt vector
- add al, 8
- cmp al, 8+8 ; Is it a slave 8259 interrupt?
- jb set_int_num ; No.
- add al, 70h - 8 - 8 ; Map it to the real interrupt.
- set_int_num:
- xor ah, ah ; Clear high byte
- mov int_num, ax ; Set parameter_list int num.
-
- mov dx, offset end_resident ; Report our size
- clc ; Say no error
- ret ; Back to common code
-
- public print_parameters
- print_parameters:
- 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
- ret
-
- include memtest.asm
-
- code ends
-
- end
-