home *** CD-ROM | disk | FTP | other *** search
- version equ 6
-
- 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.
-
- ; Version 10 updated by Drew D. Perkins at Carnegie Mellon University to
- ; handle WD8013EBT 16-bit card, to implement a transmit ring buffer, and
- ; to implement the High Performance Option.
-
- ; 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 word 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_REG1 equ 001h ; 8013 bus size register
- EN_REG5 equ 005h ; New command register (REGISTER 5)
- EN_SAPROM equ 008h ; Window on station addr prom
- EN_REGE equ 00eh ; Board Id (code) byte
-
- ; 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
-
- ; Bits in REG1
- ENR1_BUS16BIT equ 001h ; Bus is 16 bits
-
- ; Commands for REG5 register
- ENR5_MEM16EN equ 080h ; Enable 16 bit memory access from bus (8013)
- ENR5_LAN16EN equ 040h ; Enable 16 bit memory access from chip (8013)
- ENR5_MEM_MASK equ 01fh ; B23-B19 of address of the memory (8013)
- ENR5_LA19 equ 001h ; B19 of address of the memory (8013)
- ENR5_EIL equ 004h ; Enable 8390 interrupts to bus (microchannel)
-
- ; Bits in the REGE register
- ENRE_MICROCHANEL equ 080h ; Microchannel bus (vs. PC/AT)
- ENRE_LARGERAM equ 040h ; Large RAM
- ENRE_SOFTCONFIG equ 020h ; Soft config
- ENRE_REVMASK equ 01eh ; Revision mask
- ENRE_ETHERNET equ 001h ; Ethernet (vs. Starlan)
-
- ; 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
-
- ; The transmit process now implements a transmit ring buffer. The 8390
- ; chip divides transmit buffer memory into 256 byte pages. On a card
- ; with only 8 Kbytes of total buffer memory, we allocate 6 pages for
- ; transmit--enough for one 1514 byte packet--leaving 6.5 Kbytes for
- ; packet reception. On cards with more memory, we allocate 18 pages for
- ; transmit--enough for three 1514 byte packets--leaving 11.5 Kbytes (on
- ; a 16 Kbyte card) or 27.5 Kbytes (on a 32 Kbyte card) for packet
- ; reception.
-
- ; The ring buffer code views the allocated transmit buffer as a ring
- ; with one small kink thrown in. Unfortunately, the 8390 doesn't
- ; actually implement a ring itself for transmit. You can only tell it
- ; the starting page of a packet and the length of the packet. The
- ; packet has to be entirely contiguous in the buffer. Therefore, our
- ; ring has to prematurely wrap from the end back to the beginning
- ; whenever normal placement of a packet would cause it to straddle the
- ; transmit/receive buffer boundary. When the ring wraps, the remaining
- ; space at the end of the buffer is wasted.
-
- ; The ring buffer code uses four variables: sm_tcur, sm_tboundary,
- ; sm_rstart_pg and sm_tlen. These first two variables parallel the
- ; curpage and boundary page registers that the chip uses for the receive
- ; buffer. sm_tcur always indicates the next place that a packet will be
- ; added to the ring buffer (the tail of the queue). sm_tboundary
- ; indicates the next place a packet will be removed from ring buffer
- ; (the head of the queue). When the ring buffer is completely empty
- ; completely full, sm_tcur equals sm_tboundary. sm_rstart_pg indicates
- ; the start page of the receive ring buffer, and hence the stop page of
- ; the transmit ring buffer. The transmit ring buffer always starts at
- ; page zero. sm_tlen is an array of packet lengths which shadows the
- ; actual ring buffer; there is one element in the array for each buffer
- ; page. Each element of the array stores the length of a packet which
- ; starts on the corresponding page, or zero if there is no packet
- ; starting at that page. A zero is also stored at the page indicated by
- ; sm_tcur except when the ring buffer is completely full, in which case
- ; sm_tcur equals sm_tboundary and sm_tlen[sm_tcur] is not zero.
-
- ; The transmit process also implements a very low latency transmit
- ; interrupt scheme which uses three other important variables: sm_tpsr,
- ; sm_tcnt, and sm_tnum. These variables contain the pre-calculated
- ; values that the interrupt handler should hand the chip in order to
- ; initiate the next transmission as soon as possible. sm_tpsr indicates
- ; the starting page of the next packet that the transmit interrupt code
- ; should initiate transmission of. sm_tcnt indicates its length.
- ; sm_tnum always indicates the number of packets in the ring buffer
- ; which are completely ready to be transmitted, not how many packets are
- ; allocated in the ring buffer. That is, sm_tnum is not incremented
- ; until the packet has been copied into the ring buffer, potentially a
- ; long time after space for the packet has been allocated. sm_tnum is
- ; not decremented until the packet has actually been transmitted.
- ; Therefore, at interrupt time sm_tnum will have to be 2 or greater to
- ; indicate that there is actually another packet waiting to go.
-
- ; Since sm_tpsr at interrupt time always indicates the next page to be
- ; transmitted, it also contains the value that sm_tboundary should be updated
- ; to in order to free the associated buffer space. This fact is used to avoid
- ; recomputation of sm_tboundary from the normal ring buffer data structure
- ; (specifically sm_tboundary and sm_tlen). Unfortunately, using this scheme
- ; also added alot of hair (as you will see) to the code to make sure that
- ; sm_tpsr and sm_tboundary always tracked each other appropriately in all
- ; boundary conditions. If I was going to do this again, I would probably
- ; reevaluate this design decision and do things a bit differently (probably
- ; I would use another array to indicate the next sm_tboundary value).
-
- public sm_tcur, sm_tboundary, sm_tlen, sm_tnum, sm_tcnt, sm_tpsr
- SM_TSTART_PG equ 0 ; First page of TX buffer
- SM_TMAX_PG equ 18 ; 3 MTU-sized packets
- sm_tcur dw 0 ; Offset of current page of TX buffer
- sm_tboundary dw 0 ; Offset of boundary page of TX buffer
- sm_tlen dw SM_TMAX_PG dup (0) ; Length of packets in TX buffer
- sm_tnum dw 0 ; Number of packets in TX buffer
- sm_tcnt dw 0 ; Length of next packet in TX buffer
- sm_tpsr db 0 ; Starting page of next packet in TX buffer
- sm_rstart_pg db 6 ; Starting page of ring
- sm_rstop_pg db 32 ; Last page +1 of ring
-
- xmit_isr db 0 ; ISR shared between xmit and recv
- xmit_tsr db 0 ; TSR shared between xmit and recv
-
- board_features db 0 ; Board features
- BF_EIL equ 1
- BF_WTS equ 2 ; 16-bit board, enable Word Transfer
- BF_MEM16EN equ 4 ; 16-bit board, enable 16-bit memory
- BF_16K equ 8 ; Board has 16 KB or shared memory
-
- ; 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 6 ;basic, high-performance, extended
- parameter_list label byte
- db 1 ;major rev of packet driver
- db 10 ;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 mcast addrs
- rcv_bufs dw 4 ;(# of back-to-back MTU rcvs) - 1
- xmt_bufs dw 0 ;(# of successive xmits) - 1
- int_num 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
-
- extrn send_head: dword ; head of transmit queue
- extrn send_tail: dword ; tail of transmit queue
-
- extrn send_queue: near
- ; Called when we want to queue an iocb.
- ; Enter with es:di -> iocb.
-
- extrn send_dequeue: near
- ; Called to dequeue an iocb and possibly call its upcall.
- ; Enter with interrupts disabled.
- ; Destroys ds:si.
-
-
- ifdef debug ; Include a very useful logging mechanism.
-
- ; The log entry structure. Log entries include useful data such as
- ; a type (each place a log entry is made uses a different type), various
- ; chip status, ring buffer status, log entry dependent data, and optionally
- ; 8259 interrupt controller status.
- logentry struc
- le_type db 0 ; Log entry type
- le_ccmd db ? ; Value of CCMD register
- le_isr db ? ; Value of ISR register
- le_tsr db ? ; Value of TSR register
- le_tcur dw ? ; Value of sm_tcur
- le_tboundary dw ? ; Value of sm_tboundary
- le_tnum dw ? ; Value of sm_tnum
- le_dw dw ? ; Log type specific dw data
- ifndef mkle8259 ; Log 8259 status?
- le_dd dd ? ; Log type specific dd data
- else
- le_irr1 db ? ; Value of 8259-1 IRR register
- le_isr1 db ? ; Value of 8259-1 ISR register
- le_irr2 db ? ; Value of 8259-2 IRR register
- le_isr2 db ? ; Value of 8259-2 ISR register
- endif
- logentry ends
-
- ; The types of log entries.
- LE_SP_E equ 0 ; send_pkt entry
- LE_SP_X equ 1 ; send_pkt exit
- LE_ASP_E equ 2 ; as_send_pkt entry
- LE_ASP_X equ 3 ; as_send_pkt exit
- LE_RBALLOC_E equ 4 ; tx_rballoc entry
- LE_RBALLOC_X equ 5 ; tx_rballoc exit
- LE_COPY_E equ 6 ; sm_copy entry
- LE_COPY_X equ 7 ; sm_copy exit
- LE_START_E equ 8 ; tx_start entry
- LE_START_X equ 9 ; tx_start exit
- LE_XMIT_E equ 0ah ; xmit entry
- LE_XMIT_X equ 0bh ; xmit exit
- LE_TXISR_E equ 0ch ; txisr entry
- LE_TXISR_X equ 0dh ; txisr exit
- LE_RECV_E equ 0eh ; recv entry
- LE_RECV_X equ 0fh ; recv exit
- LE_RCVFRM_E equ 10h ; rcv_frm entry
- LE_RCVFRM_X equ 11h ; rcv_frm exit
- LE_COPY_L equ 12h ; sm_copy loop
- LE_TIMER_E equ 13h ; timer entry
- LE_TIMER_X equ 14h ; timer exit
-
- public log, log_index
- log logentry 256 dup (<>) ; The log itself
- log_index db 0 ; Index to current log entry
-
- ; The macro used to create log entries.
- mkle macro letype, ledw, ledd, ledd2 ; Make an entry in the log
- pushf ; Save interrupt enable state
- cli ; Disable interrupts
- push dx ; Save registers
- push bx
- push ax
- mov bl, log_index ; Get current log_index
- xor bh, bh ; Clear high byte
- shl bx, 1 ; Multiply by sixteen
- shl bx, 1
- shl bx, 1
- shl bx, 1
- mov log[bx].le_type, letype ; Store log entry type
- loadport ; Base of device
- setport EN_CCMD ; Point at chip command register
- in al, dx ; Get chip command state
- mov log[bx].le_ccmd, al ; Store CCMD value
- setport EN0_ISR ; Point at chip command register
- in al, dx ; Get chip command state
- mov log[bx].le_isr, al ; Store ISR value
- setport EN0_TSR ; Point at chip command register
- in al, dx ; Get chip command state
- mov log[bx].le_tsr, al ; Store TSR value
- mov ax, sm_tcur ; Get current sm_tcur
- mov log[bx].le_tcur, ax ; Store sm_tcur value
- mov ax, sm_tboundary ; Get current sm_tboundary
- mov log[bx].le_tboundary, ax ; Store sm_tboundary value
- mov ax, sm_tnum ; Get current sm_tnum
- mov log[bx].le_tnum, ax ; Store sm_tnum value
- mov log[bx].le_dw, ledw ; Store log entry dw
- ifndef mkle8259 ; Include extra per-type data
- mov word ptr log[bx].le_dd, ledd ; Store low word of log entry dd
- mov word ptr log[bx].le_dd+2, ledd2 ; Store high word of log entry dd
- else ; Include 8259 status
- mov al,0ah ; read request register from
- out 0a0h,al ; secondary 8259
- nop ; settling delay
- nop
- nop
- in al,0a0h ; get it
- mov log[bx].le_irr2, al
- mov al,0bh ; read in-service register from
- out 0a0h,al ; secondary 8259
- nop ; settling delay
- nop
- nop
- in al,0a0h ; get it
- mov log[bx].le_isr2, al
- mov al,0ah ; read request register from
- out 020h,al ; primary 8259
- nop ; settling delay
- nop
- nop
- in al,020h ; get it
- mov log[bx].le_irr1, al
- mov al,0bh ; read in-service register from
- out 020h,al ; primary 8259
- nop ; settling delay
- nop
- nop
- in al,020h ; get it
- mov log[bx].le_isr1, al
- endif
- ifdef screenlog ; Log the entry type to the screen too
- push es
- mov ax, 0b800h ; Color screen only...
- mov es, ax
- mov bl, log_index ; Get current log_index
- xor bh, bh ; Clear high byte
- shl bx, 1 ; Multiply by sixteen
- add bx, 3360
- mov byte ptr es:[bx-1], 07h
- mov byte ptr es:[bx], letype+30h
- mov byte ptr es:[bx+1], 70h
- pop es
- endif
- inc log_index ;
- pop ax ; Restore registers
- pop bx
- pop dx
- popf ; Restore interrupt enable state
- endm
- else
- mkle macro letype, ledw, ledd, ledd2 ; Define an empty macro
- endm
- endif
-
- public send_pkt
- ; The Transmit Packet routine.
- ; Enter with ds:si -> packet, cx = packet length,
- ; Exit with nc if ok, or else cy if error, dh set to error number.
- send_pkt:
- assume ds:nothing
- mkle LE_SP_E, cx, si, ds
-
- call re_enable_interrupts
-
- ; Convert synchronous call to asynchronous call.
- add sp, -(size iocb) ; Allocate iocb on stack
- mov di, sp ; Get iob offset
- mov ax, ss ; Get iocb segment
- mov es, ax
- mov word ptr es:[di].buffer, si ; Set buffer offset
- mov word ptr es:[di].buffer+2, ds ; Set buffer segment
- mov es:[di].len, cx ; Set buffer length
- mov es:[di].flags, 0 ; Clear all flags
- call as_send_pkt ; Call asynchronous send (es:di preserved)
- ifndef debug
- jc sp_error ; Error, return immediately
- else
- jnc sp_loop
- jmp sp_error
- endif
-
- ; Check if asynchronous send has completed.
- sp_loop:
- mov cx, 8000h ; Avoid infinite loop
- sp_chkdone:
- test es:[di].flags, DONE ; Transmission complete?
- ifndef debug
- jz sp_notdone ; No
- else
- jnz sp_done
- jmp sp_notdone
- sp_done:
- endif
- mov dh, es:[di].ret_code ; Get return code
- cmp dh, 0 ; Error?
- ifndef debug
- jne sp_error ; Yes
- else
- je sp_success
- jmp sp_error
- sp_success:
- endif
-
- ; Send has completed successfully.
- add sp, size iocb ; Free iocb on stack
- mkle LE_SP_X, cx, 1, 0
- clc ; No error, clear carry
- ret
-
- ; Send has completed unsuccessfully.
- sp_error:
- add sp, size iocb ; Free iocb on stack
- mkle LE_SP_X, cx, 0, 0
- stc ; popf may have cleared carry
- ret
-
- sp_notdone:
- pushf ; Make interrupt enable bit accessible
- pop bx ; in bx
- test bx, EI ; Were interrupts enabled on pkt driver entry?
- jnz sp_deccnt ; Yes
-
- ; Interrupts are disabled, poll chip to determine when the transmit completes.
- loadport ; Base of device
- setport EN0_ISR ; Point at interrupt status register
- in al, dx ; Get pending interrupts
- test al,ENISR_ALL ; Any interrupts?
- jz sp_deccnt ; No
- pushf ; Push flags
- call xmit ; Fake first half of transmit interrupt
- jmp sp_fakeint ; Fake second half of transmit interrupt
-
- ; Decrement the loop counter
- sp_deccnt:
- dec cx ; Reached zero?
- ifndef debug
- jnz sp_chkdone ; No, loop again
- else
- jz sp_txstuck
- jmp sp_chkdone
- sp_txstuck:
- endif
-
- ; TX is stuck. Fall thru into sp_fakeint code after setting xmit variables.
- pushf ; Make sure interrupts are disabled
- cli
- mov xmit_isr, ENISR_TX_ERR ; Frame transmitted error
- mov xmit_tsr, 0 ; Unknown error
- call count_out_err ; Count the anomaly
- call xmit_fake ; Fake first half of transmit interrupt
- sp_fakeint:
- push es ; Push iocb segment
- push di ; Push iocb offset
- mov dx, cs ; Set up ds=cs
- mov ds, dx
- call recv ; Let recv do the hard work
- pop di ; Pop iocb offset
- pop es ; Pop iocb segment
- popf ; Restore interrupt enable bit
- jmp sp_loop ; Restart loop (check the DONE bit again)
-
-
- 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:
- assume ds:nothing
- mkle LE_ASP_E, cx, di, es
-
- ; Check if this packet is too large.
- cmp cx, GIANT ; Bigger than this?
- jbe tx_oklen1 ; No
- mov dh, CANT_SEND ; Yes, return error
- mov es:[di].ret_code, dh
- or es:[di].flags, DONE
- stc
- ret
-
- ; Frame is small enough, check if it's large enough.
- tx_oklen1:
- cmp cx, RUNT ; Smaller than this?
- jnb tx_oklen2 ; No
- mov cx, RUNT ; Yes, stretch frame to minimum allowed
- mov es:[di].len, cx ; Update iocb too
-
- ; Frame is an acceptable size.
- tx_oklen2:
- push es ; Push iocb segment
- push di ; and offset
-
- ; If queue is empty and have ring buffer space, allocate it. Else, queue iocb.
- pushf ; Push original interrupt enable flag
- cli ; Disable interrupts
- send_queueempty ; Queue empty?
- ifndef debug
- jnz tx_queue ; No, queue this one too
- else
- jz tx_alloc
- jmp tx_queue
- tx_alloc:
- endif
- mov ax, cx ; Move length to ax
- call tx_rballoc ; Allocate space in the ring buffer
- ifndef debug
- jc tx_nospace ; No space right now, queue the packet
- else
- jnc tx_space
- jmp tx_nospace
- tx_space:
- endif
- popf ; Pop original interrupt enable flag
-
- ; Do the actual copy of the packet into the ring buffer.
- call sm_copy
-
- ; Start the packet transmission.
- pushf ; Push original interrupt enable flag
- cli ; Disable interrupts
- call tx_start ; Set up hints and start transmission
- popf ; Pop original interrupt enable flag
-
- pop di ; Pop iocb segment
- pop es ; and offset
- or es:flags[di], DONE ; Mark done
- mov es:ret_code[di], 0 ; Success
- mkle LE_ASP_X, 0, 0, 0
- clc
- ret ; End of send_pkt routine
-
- ; No space in ring buffer, get the iocb from the stack and queue it.
- tx_nospace:
- mov bx, sp ; Need es:di -> iocb, but can't pop flags.
- mov di, ss:[bx+2] ; Get iocb offset
- mov es, ss:[bx+4] ; Get iocb segment
-
- ; Queue the iocb and return.
- tx_queue:
- call send_queue
- popf ; Pop original interrupt enable flag
- pop di ; Pop iocb segment
- pop es ; and offset
- mkle LE_ASP_X, 1, 0, 0
- clc
- ret
-
-
- public tx_rballoc
- ; Allocate some space in the transmit ring buffer.
- ; Enter with ds:si -> buffer source, ax = length, interrupts disabled.
- ; If not enough space, exit with: cy, ds:si -> source, ax = length.
- ; Else, nc, ds:si -> buffer source, es:di -> buffer destination, ax = length,
- ; bl = transmit starting page.
- tx_rballoc:
- assume ds:nothing
- mkle LE_RBALLOC_E, ax, 0, 0
-
- ; First, compute likely destination, index, and new sm_tcur (may have to wrap)
- mov es, mem_base ; Paragraph of the TX buffer
- mov di, sm_tcur ; Offset of current TX buffer page
- mov bx, ax ; Length of packet
- lea dx, [bx+255][di] ; Compute packet end + 255
- xor dl, dl ; Only want the page number
-
- ; Second, make sure ring buffer isn't completely full (sm_tlen[sm_tcur] != 0)
- mov bx, di ; Compute index into length table
- mov cl, 7 ; Index is offset shifted right seven bits
- shr bx, cl
- tx_chkfull:
- cmp sm_tlen[bx], 0 ; Current sm_tlen must equal zero
- ifndef debug
- jne tx_noroom ; TX buffer full
- else
- je tx_room
- jmp tx_noroom
- tx_room:
- endif
-
- ; Third, check if packet will fit in ring buffer at this position
- cmp di, sm_tboundary ; Has sm_tcur wrapped?
- ifndef debug
- jae tx_notwrapped ; No
- else
- jb tx_wrapped
- jmp tx_notwrapped
- tx_wrapped:
- endif
- cmp dx, sm_tboundary ; Will packet fit between sm_tcur/sm_tboundary?
- ifndef debug
- ja tx_noroom ; No
- else
- jbe tx_willfit
- jmp tx_noroom
- endif
- tx_willfit: ; Yes, packet will fit
- mov sm_tlen[bx], ax ; Copy length to sm_tlen
- mov sm_tcur, dx ; Update current TX buffer offset
- shr bx, 1 ; Convert length table index to tpsr
- mkle LE_RBALLOC_X, bx, di, es
- clc ; Clear carry
- ret ; Return success
-
- tx_notwrapped: ; sm_tcur >= sm_tboundary
- cmp dh, sm_rstart_pg ; Will packet fit between sm_tcur/sm_rstart_pg?
- ifndef debug
- jb tx_willfit ; Yes
- else
- jae tx_mightfit
- jmp tx_willfit
- tx_mightfit:
- endif
- jne tx_wrapcur ; No
- xor dx, dx ; Packet fit exactly, but wrap sm_tcur now
- jmp tx_willfit
-
- ; Packet won't fit in remaining TX ring buffer, wrap sm_tcur.
- ; If ring buffer is completely empty, wrap sm_tboundary as well.
- ; If ring buffer is not empty, sm_tpsr will need updated if sm_tnum is 1.
- tx_wrapcur:
- sub dx, di ; Compute next sm_tcur (offset zero + length)
- mov di, sm_tboundary ; Get sm_tboundary (don't need di for a bit)
- mov cl, 7 ; Convert it to an index into length table
- shr di, cl
- xor cx, cx ; Put a zero value in a register
- cmp sm_tlen[di], cx ; Is the buffer completely empty?
- mov di, cx ; Wrap sm_tcur to zero
- jne tx_notempty ; No
- mov sm_tboundary, cx ; Wrap sm_tboundary
- mov bx, cx ; Wrap index
- jmp tx_notwrapped ; Check new position
-
- tx_notempty:
- mov sm_tlen[bx], cx ; Insert 0 length pkt to skip remaining buffer
- mov sm_tcur, cx ; Interrupt handler may need this to be updated
- mov bx, cx ; Wrap index
- cmp sm_tnum, 1 ; Will sm_tpsr be corrupted?
- jne tx_tpsrok ; No, its ok
- mov sm_tpsr, cl ; Wrap sm_tpsr as well
- tx_tpsrok:
- jmp tx_chkfull ; Check packet fit again
-
- ; Packet won't fit now, return failure.
- tx_noroom:
- mkle LE_RBALLOC_X, 0, 0, 0
- stc ; Set carry
- ret ; Return failure
-
-
- public sm_copy
- ; Copy a packet from the receive ring buffer.
- ; Enter with ds:si -> source, es:di -> destination, ax = length.
- ; Exits with ds:si updated, es:di updated, ax and bx preserved,
- ; cx, and dx destroyed.
- sm_copy:
- assume ds:nothing
- mkle LE_COPY_E, ax, di, es
-
- push ax ; Preserve ax
- push bx ; Preserve bx
- mov cx, ax ; Copy length to cx
- shr cx, 1 ; Convert bytes to words
- pushf ; Save carry and interrupt enable bits
- test board_features, BF_MEM16EN ; Is this a WD8013?
- ifndef debug
- jne sm_wd8013 ; yes
- else
- je sm_wd8003
- jmp sm_wd8013
- sm_wd8003:
- endif
- rep movsw ; Copy a word at a time
- sm_copied:
- mkle LE_COPY_L, 0, 0, 0
- popf ; Restore carry bit
- jnc sm_even ; odd byte left over?
- lodsw ; yes, word fetch
- stosb ; and byte store
- sm_even:
- pop bx ; Get back bx
- pop ax ; Get back ax
- mkle LE_COPY_X, 0, 0, 0
- ret ; Return success
-
- sm_wd8013: ; Board is a WD8013
- cli ; Disable interrupts
- loadport ; Base of device
- setport EN_REG5 ; Enable 16-bit access
- mov al, ENR5_MEM16EN+ENR5_LAN16EN+ENR5_LA19
- out dx, al
- mov bx, sp
- test ss:[bx], EI ; Were interrupts enabled on entry?
- jne sm_piecemeal ; Yes, copy piecemeal
- sm_allatonce: ; No, copy all at once
- rep movsw ; Copy packet
- sm_disable:
- mov al, ENR5_LAN16EN+ENR5_LA19 ; Disable 16-bit access to WD8013
- out dx, al
- jmp sm_copied
-
- ; Do copy a piece at a time, enabling and disabling interrupts as we go,
- ; in order to guarantee reasonable interrupt latency
- sm_piecemeal:
- mov ax, cx ; Copy length to ax
- mov bx, cx ; and bx
- add bx, 63
- mov cx, 6 ; Divide length by 64 to get number of loops
- shr bx, cl
- mov cx, ax ; Copy length back to cx
- and cx, 63 ; Do up to 63 copies first time
- jne sm_copypiece ; cx was not zero, do cx copies
- sm_loop:
- mov cx, 64 ; Do 64 copies
- sm_copypiece:
- rep movsw ; Copy a word at a time
- dec bx ; Decrement loop counter
- je sm_disable ; All done
- mov al, ENR5_LAN16EN+ENR5_LA19 ; Disable 16-bit access to WD8013
- out dx, al
- mkle LE_COPY_L, bx, di, es
- sti ; Enable interrupts for a moment
- cli ; Disable interrupts again
- mov al, ENR5_MEM16EN+ENR5_LAN16EN+ENR5_LA19
- out dx, al
- jmp sm_loop ; Do more copies
-
-
- public tx_start
- ; Start the transmitter and manage the low-latency transmitter hints.
- ; Enters with ax = length, bl = transmit starting page.
- tx_start:
- assume ds:nothing
- mkle LE_START_E, ax, bx, 0
-
- ; First, increment the count of packets in the ring buffer and figure out what
- ; needs to be done.
- inc sm_tnum ; Increment count of packets (pkt now queued)
- cmp sm_tnum, 3 ; Already have a second packet set up?
- jae tx_running ; Yes.
- cmp sm_tnum, 2 ; Transmitter already running?
- jae tx_setnext ; Yes
-
- ; Start the transmitter running.
- loadport ; Base of device
- setport EN0_TCNTLO ; Low byte of TX count
- out dx, al ; Tell card the low byte
- setport EN0_TCNTHI ; High byte of TX length
- mov al, ah ; Get the high byte into al
- out dx, al ; Tell card the high byte
- setport EN0_TPSR ; Transmit Page Start Register
- mov al, bl ; Get the starting page
- out dx, al ; Tell card the starting page
- setport EN_CCMD ; Chip command reg
- mov al, ENC_TRANS+ENC_NODMA
- out dx, al ; Start the transmitter
-
- ; Set new sm_tboundary. If we wrapped sm_tcur in sm_rballoc AND the last
- ; interrupt came in during the sm_copy then sm_tboundary will not have
- ; wrapped and will now be corrupt.
- mov byte ptr sm_tboundary+1, bl
- mov bl, byte ptr sm_tcur+1 ; Get sm_tcur page number (to set sm_tpsr)
-
- ; Set the low-latency transmitter hints.
- tx_setnext:
- mov sm_tcnt, ax ; Set up next transmit count
- mov sm_tpsr, bl ; Set up next transmit starting page
-
- tx_running:
- mkle LE_START_X, ax, bx, 0
- ret
-
-
- public drop_pkt
- ; Drop a packet from the queue.
- ; Enter with es:di -> iocb.
- drop_pkt:
- assume ds:nothing
-
- ret
-
-
- public 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.
- get_address:
- 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
- ; Enter with ds:si -> Ethernet address, CX = length of address.
- ; Exit with nc if okay, or cy, dh=error if any errors.
- set_address:
- assume ds:nothing
-
- 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 ; Base of device
- 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 ; Base of device
- 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
- ; 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.
- set_multicast_list:
- mov dh,NO_MULTICAST
- stc
- ret
-
-
- ; Set the multicast filter mask bits in case promiscuous rcv wanted
- set_8390_multi:
- loadport ; Base of device
- 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
- pushf
- 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 ; Base of device
- setport EN_CCMD ; Chip command register
- mov al,ENC_NODMA+ENC_PAGE0
- out dx,al ; Restore to page zero
- popf ; OK for interrupts now
- ret
-
-
- public terminate
- terminate:
- loadport ; First, pulse the board reset
- setport EN_CMD
- mov al,EN_RESET
- out dx,al ; Turn on board reset bit
- jmp $+2
- xor al,al
- jmp $+2
- out dx,al ; Turn off board reset bit
- ret
-
- public reset_interface
- reset_interface:
- assume ds:code
- loadport ; Base of device
- 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 ; ..
- setport EN_REG5
- mov al,ENR5_EIL
- test board_features,BF_EIL
- jz reset_no_eil
- out dx,al ; enable 8390 interrupts to bus
- reset_no_eil:
- ret
-
-
- public xmit
- ; Process a transmit interrupt with the least possible latency to achieve
- ; back-to-back packet transmissions.
- ; Second entry point, xmit_fake, used when chip is hung.
- ; May only use ax and dx.
- xmit:
- assume ds:nothing
- mkle LE_XMIT_E, ax, di, es
-
- ; Was there a transmit interrupt from this card?
- loadport ; Base of device
- setport EN0_ISR ; Point at interrupt status register
- in al, dx ; Get pending interrupts
- mov xmit_isr, al ; Store ISR for later reference
- test al,ENISR_TX+ENISR_TX_ERR ; Frame transmitted?
- jz xmit_done ; No
-
- ; Get the transmit status.
- setport EN0_TSR
- in al, dx
- mov xmit_tsr, al ; Store TSR for later reference
-
- ; Clear the interrupt.
- xmit_fake: ; Entry point used when chip is hung.
- setport EN0_ISR ; Clear the TX and TX error complete flags
- mov al, ENISR_TX+ENISR_TX_ERR
- out dx, al
- cmp sm_tnum, 1 ; Any packets left to transmit?
- jbe xmit_done ; No
-
- ; Quickly start the next transmission going.
- setport EN0_TCNTLO ; Low byte of TX count
- mov ax, sm_tcnt ; Get length of next packet
- out dx, al ; Tell card the low byte
- setport EN0_TCNTHI ; High byte of TX length
- mov al, ah ; Get the high byte into al
- out dx, al ; Tell card the high byte
- setport EN0_TPSR ; Transmit Page Start Register
- mov al, sm_tpsr ; Get the starting page into al
- out dx, al ; Tell card the starting page
- setport EN_CCMD ; Chip command reg
- mov al, ENC_TRANS+ENC_NODMA
- out dx, al ; Start the transmitter
-
- xmit_done:
- mkle LE_XMIT_X, ax, di, es
- ret
-
-
- public tx_isr
- ; Process a transmit interrupt with the least possible latency.
- ; Enter with al = contents of interrupt status register (EN0_ISR)
- tx_isr:
- assume ds:code
- mkle LE_TXISR_E, 0, 0, 0
-
- ; Examine the status bits in and count errors.
- test xmit_isr, ENISR_TX ; Non-error TX?
- jz tx_err ; No, do TX error completion.
- test xmit_tsr, ENTSR_COLL16 ; Jammed for 16 transmit tries?
- jz tx_rbfree ; No.
- call count_out_err ; Yes, count those.
- jmp tx_rbfree
- tx_err:
- test xmit_tsr, ENTSR_FU ; FIFO Underrun?
- jz tx_rbfree ; No.
- call count_out_err ; Yes, count those.
-
- ; Now, drain the last packet from the TX shared memory ring buffer.
- tx_rbfree:
- mov bx, sm_tboundary ; Get sm_tboundary
- mov cl, 7 ; Compute index into length table
- shr bx, cl
- mov sm_tlen[bx], 0 ; Zero length
- mov bh, sm_tpsr ; Get the starting page into bh
- xor bl, bl ; Clear bl
- mov sm_tboundary, bx ; Set new sm_tboundary
- dec sm_tnum ; Decrement number of packets
- jz tx_dequeue ; No interrupts following this one
-
- ; Now, set up sm_tpsr and sm_tcnt for the next transmit interrupt.
- mov di, sm_tcnt ; Get length of current packet
- lea dx, [bx+255][di] ; Compute new sm_tpsr
- xor dl, dl
- cmp dh, sm_rstart_pg ; See if sm_tpsr has wrapped
- jb tx_nowrap ; It has not yet wrapped
- tx_wrap:
- xor dx, dx ; Wrap to top
- tx_nowrap:
- cmp dx, sm_tcur ; Any more packets following this one?
- jbe tx_settpsr ; No
- mov bx, dx ; Compute index of length of next packet
- mov cl, 7
- shr bx, cl
- mov ax, sm_tlen[bx] ; Get length of next packet
- or ax, ax ; Should we punt rest of TX buffer?
- je tx_wrap ; Yes, wrap to top and try again
- tx_settpsr:
- mov sm_tpsr, dh ; Store back new sm_tpsr
- mov sm_tcnt, ax ; and new sm_tcnt
-
- ; Finally, process any packets in the transmit queue.
- tx_dequeue:
- send_queueempty ; Queue empty?
- jz tx_done ; Yes.
-
- send_peekqueue ; Peek at the first element in the queue
- lds si, es:[di].buffer ; Get buffer segment:offset
- assume ds:nothing
- mov ax, es:[di].len ; Get buffer length
- call tx_rballoc ; Allocate space in the ring buffer
- jc tx_done ; Still no space for this packet.
-
- ; Do the actual copy of the packet into the ring buffer.
- call sm_copy
-
- ; Start the packet transmission.
- call tx_start ; Set up hints and start transmission
-
- mov ax,0
- call send_dequeue ; Dequeue this element
-
- tx_done:
- mov dx, cs ; Restore ds=cs
- mov ds, dx
- assume ds:code
- mkle LE_TXISR_X, 0, 0, 0
- 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
-
- ;called to re-enable interrupts
- extrn re_enable_interrupts: near
-
- extrn count_in_err: near
- extrn count_out_err: near
-
- public 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.
- recv:
- assume ds:code
- mkle LE_RECV_E, 0, 0, 0
-
- ; First cleanup after xmit routine if transmit interrupt occurred.
- check_isr:
- mov al, xmit_isr ; Get xmit ISR
- test al,ENISR_TX+ENISR_TX_ERR ; Frame transmitted?
- jz tx_no_frame ; No
- call tx_isr ; Yes, cleanup after transmit interrupt
- mov al, xmit_isr ; Get xmit ISR
-
- tx_no_frame:
- and al, ENISR_ALL ; Any interrupts at all?
- ifndef debug
- jnz isr_test_overrun
- else
- jz isr_no_isr
- jmp isr_test_overrun
- isr_no_isr:
- endif
- mkle LE_RECV_X, 0, 0, 0
- ret ; Go if none
-
- ; Now, 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:
- loadport ; Base of device
- 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 ; Base of device
- setport EN0_BOUNDARY ; Point at boundary reg
- out dx, al ; Set the boundary
-
- rcv_ovr_empty:
- loadport ; Base of device
- 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 ; Base of device
- setport EN0_TXCR ; Point at Transmit control reg
- 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 isr_no_stat ; 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 isr_no_rx
- 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 isr_no_stat ; See if any other interrupts pending
-
- isr_no_rx:
- if 0 ; The following code never executed
- ; 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
- endif ; never executed code
-
- isr_no_stat:
- call xmit ; Let xmit read ISR
- 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.
- mkle LE_RCVFRM_E, 0, 0, 0
-
- 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)
- mov ax, cx ; Move length to ax
- 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 si, EN_RBUF_NHDR ; ds:si points at first byte to move
- call sm_copy ; Copy first piece
- mov ax, mem_base ; Paragraph of base of shared memory
- mov ds, ax ; ..
- mov ah, sm_rstart_pg ; Offset to start of first receive page
- xor al, al ; 16-bit offset
- mov si, ax
- pop ax ; Bytes left to move
- rcopy_one_piece:
- call sm_copy ; Copy piece
- 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
- mkle LE_RCVFRM_X, 0, 0, 0
- 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:", CR, LF
- db " WD8003E [-n] [-d] [-w] <packet_int_no> <int_level> <io_addr> <mem_segm_addr>",CR,LF,'$'
-
- public copyright_msg
- copyright_msg db "Packet driver for Western Digital WD80x3 E S W(T) EB(T) E(T)/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 bad or already occupied",CR,LF,'$'
- int_no_name db "Interrupt number ",'$'
- io_addr_name db "I/O port ",'$'
- mem_base_name db "Memory address ",'$'
- mem_size_name db "Memory size ",'$'
-
- occupied_switch db 0 ;if nonzero, 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:
- no_memory:
- mov dx,offset occupied_msg
- mov etopen_diagn,34
- jmp short error_wrt
- bad_cksum:
- mov dx,offset no_board_msg
- mov etopen_diagn,37
- error_wrt:
- mov ah,9
- int 21h
- stc
- ret
-
-
- public etopen
- etopen: ; Initialize interface
- call terminate ; First, pulse the board reset
- test sys_features,MICROCHANNEL
- jz etopen_no_mc
- or board_features,BF_EIL+BF_WTS+BF_16K
- loadport
- setport EN_REG5
- mov al,ENR5_EIL
- out dx,al ; enable 8390 interrupts to bus
- jmp etopen_have_id
- etopen_no_mc: ; Check for WD8013EBT
- loadport ; WD8013EBT doesn't have register alaasing
- setport EN_CMD ; Register 0 may be aliased to Register 8
- mov bx, dx
- setport EN_SAPROM
- mov cx, EN_SAPROM-EN_CMD ; Check 8 bytes
- alias_loop:
- in al, dx ; Get one register
- mov ah, al
- xchg bx, dx ; Switch to other register
- in al, dx ; Get other register
- cmp al, ah ; Are they the same?
- jne not_aliased ; Nope, not aliased
- inc bx ; Increment register pair
- inc dx
- dec cx ; Decrement loop counter
- jne alias_loop ; Finished?
- jmp etopen_have_id ; Aliased; not WD8013EBT
- not_aliased: ; Not aliased; Check for 16-bit board
- loadport
- setport EN_REG1 ; Bit 0 must be unmodifiable
- in al, dx ; Get register 1
- mov bl, al ; Store original value
- xor al, ENR1_BUS16BIT ; Flip bit 0
- out dx, al ; Write it back
- and al, ENR1_BUS16BIT ; Throw other bits away
- mov ah, al ; Store bit value
- in al, dx ; Read register again
- and al, ENR1_BUS16BIT ; Throw other bits away
- cmp al, ah ; Was it modified?
- jne board16bit ; No; board is a WD8013EBT !
- mov al, bl ; Get original value
- out dx, al ; Write it back
- jmp etopen_have_id
- board16bit: ; But is it plugged into a 16-bit slot?
- and al, ENR1_BUS16BIT ; Throw other bits away
- je etopen_have_id ; Nope; silly board installer!
- or board_features,BF_WTS+BF_MEM16EN+BF_16K
- setport EN_REG5
- mov al,ENR5_LAN16EN+ENR5_LA19 ; Write LA19 now, but not MEM16EN
- out dx,al ; enable 8390 interrupts to bus
-
- etopen_have_id:
- loadport
- 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?
- je good_cksum ; Damn 128 byte jump limit...
- jmp bad_cksum ; No, board is not happy
- good_cksum:
- ; 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 board_features,BF_WTS
- jz bytemove
- or al,ENDCFG_WTS ; word access for 16-bit cards
- 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
- test occupied_switch,1
- jnz no_lim_chk
- cmp bh,80h ; low limit is 8000
- jae fr_8000
- jmp no_memory
- fr_8000:
- cmp bh,0f0h ; upper limit is F000
- jb to_F000
- jmp no_memory
- to_F000:
- test bx,01ffh ; must be on a 8 k boundary
- jz eightk
- jmp no_memory
- eightk:
- no_lim_chk:
- mov di,8*1024/16 ; 8 kbyte
- mov sm_rstop_pg,32
- test board_features,BF_16K
- jz just_8k
- mov di,16*1024/16 ; 16 kbytes
- mov sm_rstop_pg,64
- just_8k:
- test occupied_switch,1 ; did they insist?
- jnz 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:
- test board_features,BF_16K
- 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
- cmp bh,32 ; Eight k card?
- jbe rstart_set ; Yes
- mov sm_rstart_pg,SM_TMAX_PG ; Allocate more transmit buffers
- mov xmt_bufs,SM_TMAX_PG/6 ; Set xmt_bufs
- mov al,bh ; Calculate rcv_bufs as
- sub al,SM_TMAX_PG ; bufs - xmt_bufs
- xor ah,ah
- mov cl,6 ; divide by 6 (pages per MTU pkt)
- div cl
- xor ah,ah
- mov rcv_bufs,ax
- rstart_set:
- 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.
- loadport
- 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
- inc al
- 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 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
- clc
- ret
-
- public print_parameters
- print_parameters:
- ;echo our command-line parameters
- 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
- xor ax,ax
- push ax
- mov ah,sm_rstop_pg
- push ax
- mov di,sp
- mov dx,offset mem_size_name
- call print_number
- add sp,4
- ret
-
- include memtest.asm
- include occupied.asm
-
- code ends
- end
-