home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
lan
/
driver6s
/
ni6510.asm
< prev
next >
Wrap
Assembly Source File
|
1990-03-22
|
20KB
|
871 lines
version equ 0
; Copyright, 1990, 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.
.286 ;the 6510 requires a 286.
include defs.asm
DATA_REG equ 0
ADDR_REG equ 2
CSR0 equ 0
CSR1 equ 1
CSR2 equ 2
CSR3 equ 3
RESET equ 4
CONFIG equ 5
EBASE equ 8
DMA_8MASK_REG equ 0Ah
DMA_16MASK_REG equ 0D4h
DMA_8MODE_REG equ 0Bh
DMA_16MODE_REG equ 0D6h
CASCADE_MODE equ 0D0h
SET_DMA_MASK equ 4
DMA_CHANNEL_FIELD equ 3
outport macro reg
push ax
setport ADDR_REG
mov ax,reg
out dx,ax
in ax,dx ;always follow a write by a read
setport DATA_REG
pop ax
out dx,ax
in ax,dx ;always follow a write by a read
endm
;
; Control and Status Register 0 (CSR0) bit definitions
;
CSR0_ERR equ 8000h ; Error summary
CSR0_BABL equ 4000h ; Babble transmitter timeout error
CSR0_CERR equ 2000h ; Collision Error
CSR0_MISS equ 1000h ; Missed packet
CSR0_MERR equ 0800h ; Memory Error
CSR0_RINT equ 0400h ; Reciever Interrupt
CSR0_TINT equ 0200h ; Transmit Interrupt
CSR0_IDON equ 0100h ; Initialization Done
CSR0_INTR equ 0080h ; Interrupt Flag
CSR0_INEA equ 0040h ; Interrupt Enable
CSR0_RXON equ 0020h ; Receiver on
CSR0_TXON equ 0010h ; Transmitter on
CSR0_TDMD equ 0008h ; Transmit Demand
CSR0_STOP equ 0004h ; Stop
CSR0_STRT equ 0002h ; Start
CSR0_INIT equ 0001h ; Initialize
;
; Initialization Block Mode operation Bit Definitions.
;
M_PROM equ 8000h ; Promiscuous Mode
M_INTL equ 0040h ; Internal Loopback
M_DRTY equ 0020h ; Disable Retry
M_COLL equ 0010h ; Force Collision
M_DTCR equ 0008h ; Disable Transmit CRC)
M_LOOP equ 0004h ; Loopback
M_DTX equ 0002h ; Disable the Transmitter
M_DRX equ 0001h ; Disable the Reciever
;
; Receive message descriptor bit definitions.
;
RCV_OWN equ 8000h ; owner bit 0 = host, 1 = lance
RCV_ERR equ 4000h ; Error Summary
RCV_FRAM equ 2000h ; Framing Error
RCV_OFLO equ 1000h ; Overflow Error
RCV_CRC equ 0800h ; CRC Error
RCV_BUF_ERR equ 0400h ; Buffer Error
RCV_START equ 0200h ; Start of Packet
RCV_END equ 0100h ; End of Packet
;
; Transmit message descriptor bit definitions.
;
XMIT_OWN equ 8000h ; owner bit 0 = host, 1 = lance
XMIT_ERR equ 4000h ; Error Summary
XMIT_RETRY equ 1000h ; more the 1 retry needed to Xmit
XMIT_1_RETRY equ 0800h ; one retry needed to Xmit
XMIT_DEF equ 0400h ; Deferred
XMIT_START equ 0200h ; Start of Packet
XMIT_END equ 0100h ; End of Packet
;
; Miscellaneous Equates
;
TRANSMIT_BUF_COUNT equ 1
RECEIVE_BUF_COUNT equ 8
TRANSMIT_BUF_SIZE equ 1518
RECEIVE_BUF_SIZE equ 1518
;
; Receive Message Descriptor
;
rcv_msg_dscp struc
rmd0 dw ? ; Rec. Buffer Lo-Address
rmd1 dw ? ; Status bits / Hi-Address
rmd2 dw ? ; Buff Byte-length (2's Comp)
rmd3 dw ? ; Receive message length
rcv_msg_dscp ends
;
; Transmit Message Descriptor
;
xmit_msg_dscp struc
tmd0 dw ? ; Xmit Buffer Lo-Address
tmd1 dw ? ; Status bits / Hi-Address
tmd2 dw ? ; Buff Byte-length (2's Comp)
tmd3 dw ? ; Buffer Status bits & TDR value
xmit_msg_dscp ends
;
; Upcall structure. MUST be the same size as xmit_msg_dscp!
;
upcall_struc struc
upcall_upcall dd ?
upcall_buffer dd ?
upcall_struc ends
code segment para public
assume cs:code, ds:code
public int_no
int_no db 2,0,0,0 ;must be four bytes long for get_number.
io_addr dw -1,-1
dma_no db ?
public driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class db 1 ;from the packet spec
driver_type db 35 ;from the packet spec
driver_name db 'NI6510',0 ;name of the driver.
driver_function db 2 ;basic, extended
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 RECEIVE_BUF_COUNT-1 ;(# of back-to-back MTU rcvs) - 1
dw TRANSMIT_BUF_COUNT-1 ;(# of successive xmits) - 1
dw 0 ;Interrupt # to hook for post-EOI
;processing, 0 == none,
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 0 ;only ours.
dw rcv_mode_3 ;ours plus broadcast
dw 0 ;some multicasts
dw rcv_mode_5 ;all multicasts
dw rcv_mode_6 ;all packets
multi_filter db 0 ;<>0 if we need to filter multicasts.
mcast_list_bits db 8 dup(0)
;
;the LANCE requires that the descriptor pointers be on a qword boundary.
;
align 8
transmit_dscps xmit_msg_dscp TRANSMIT_BUF_COUNT dup(<>)
receive_dscps rcv_msg_dscp RECEIVE_BUF_COUNT dup(<>)
transmit_upcall upcall_struc TRANSMIT_BUF_COUNT dup(<>)
transmit_head dw transmit_dscps ;->next packet to be filled by host.
receive_head dw receive_dscps ;->next packet to be filled by LANCE.
;
; LANCE Initialization Block
;
align 2
init_block label byte
init_mode dw 0
init_addr db EADDR_LEN dup(?) ; Our Ethernet address
init_filter db 8 dup(0) ;Multicast filter.
init_receive dw ?,? ;Receive Ring Pointer.
init_transmit dw ?,? ;Transmit Ring Pointer.
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
xor bx,bx
mov ax,18
call set_timeout
send_pkt_1:
test transmit_dscps[bx].tmd1,XMIT_OWN ;Did the lance chip give it back?
je send_pkt_2
call do_timeout
jne send_pkt_1
mov dh,CANT_SEND
stc
ret
send_pkt_2:
;remember the upcall pointer for later upcalls.
mov transmit_upcall[bx].upcall_upcall.offs,di
mov transmit_upcall[bx].upcall_upcall.segm,es
mov transmit_upcall[bx].upcall_buffer.offs,si
mov transmit_upcall[bx].upcall_buffer.segm,ds
;reset error indications.
and transmit_dscps[bx].tmd1,not (XMIT_ERR or XMIT_DEF or XMIT_1_RETRY or XMIT_RETRY) ;Did the lance chip give it back?
mov transmit_dscps[bx].tmd3,0 ;reset all error bits.
mov ax,cx ;store the count.
cmp ax,RUNT ; minimum length for Ether
ja oklen
mov ax,RUNT ; make sure size at least RUNT
oklen:
neg ax
mov transmit_dscps[bx].tmd2,ax
mov ax,transmit_dscps[bx].tmd0 ;store the packet.
mov dx,transmit_dscps[bx].tmd1
call phys_to_segmoffs
rep movsb
or transmit_dscps[bx].tmd1,XMIT_OWN ;give it to the lance chip.
;Inform LANCE that it should poll for a packet.
loadport
mov ax,CSR0_INEA or CSR0_TDMD
outport CSR0
clc
ret
public get_address
get_address:
;get the address of the interface.
;enter with es:di -> place to get the address, cx = size of address buffer.
;exit with nc, cx = actual size of address, or cy if buffer not big enough.
assume ds:code
cmp cx,EADDR_LEN ;make sure that we have enough room.
jb get_address_2
mov cx,EADDR_LEN
loadport ; Get our Ethernet address base.
setport EBASE
cld
get_address_1:
insb ; get a byte of the eprom address
inc dx ; next register
loop get_address_1 ; go back for rest
mov cx,EADDR_LEN
clc
ret
get_address_2:
stc
ret
public set_address
set_address:
;enter with ds:si -> Ethernet address, CX = length of address.
;exit with nc if okay, or cy, dh=error if any errors.
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:
push cs
pop es
mov di,offset init_addr
rep movsb
call initialize ;initialize with our new address.
set_address_okay:
mov cx,EADDR_LEN ;return their address length.
clc
set_address_done:
push cs
pop ds
assume ds:code
ret
rcv_mode_1:
mov ax,M_DRX or M_DTX ;disable the receiver and transmitter.
jmp initialize_nomulti
rcv_mode_3:
xor ax,ax ;don't accept any multicast frames.
call initialize_multi
mov ax,0 ;non-promiscuous mode
jmp short initialize_nomulti
rcv_mode_5:
mov ax,0 ;non-promiscuous mode
mov ax,-1 ;accept any multicast frames.
call initialize_multi
jmp short initialize_nomulti
rcv_mode_6:
mov ax,M_PROM ;promiscuous mode
initialize_nomulti:
mov multi_filter,0
initialize:
mov init_mode,ax
loadport
mov ax,CSR0_STOP ;reset the INIT bit.
outport CSR0
mov ax,CSR0_INEA or CSR0_STRT or CSR0_INIT ;reinit and restart.
outport CSR0
setport DATA_REG
mov ax,36 ;wait one second for the board
call set_timeout ; to timeout.
initialize_1:
in ax,dx
test ax,CSR0_IDON
jne initialize_2
call do_timeout
jne initialize_1
stc
ret
initialize_2:
clc
ret
initialize_multi:
;enter with ax = value for all multicast hash bits.
push cs
pop es
mov di,offset init_filter
mov cx,8/2
rep stosw
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 ;for some reason we can't do multi's.
stc
ret
public terminate
terminate:
call rcv_mode_1 ;don't receive any apckets.
;This routine will remove the (host) DMA controller from
;cascade mode of operation.
mov al,dma_no
or al,SET_DMA_MASK
cmp dma_no,4 ;If channel 5 or 6,
ja terminate_16 ; use sixteen bit dma.
terminate_8:
out DMA_8MASK_REG,al
jmp short terminate_done
terminate_16:
out DMA_16MASK_REG,al
terminate_done:
ret
public reset_interface
reset_interface:
;reset the interface.
assume ds:code
ret
;called when we want to determine what to do with a received packet.
;enter with cx = packet length, es:di -> packet type.
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
NI6510_ISR_ACKNOWLEDGE equ (CSR0_INEA or CSR0_TDMD or CSR0_STOP or CSR0_STRT or CSR0_INIT)
public recv
recv:
;called from the recv isr. All registers have been saved, and ds=cs.
;Upon exit, the interrupt will be acknowledged.
assume ds:code
loadport
setport ADDR_REG
mov ax,CSR0
out dx,ax
in ax,dx
setport DATA_REG
in ax,dx
mov bx,ax ;make a copy.
; Acknowledge the Interrupt from the controller, but disable further
; controller Interrupts until we service the current interrupt.
;
;(CSR0_INEA or CSR0_TDMD or CSR0_STOP or CSR0_STRT or CSR0_INIT)
;
and ax,not NI6510_ISR_ACKNOWLEDGE
out dx,ax
in ax,dx ; follow all writes by a read
test bx,CSR0_RINT ;receive interrupt?
je recv_done ;no, we're done.
mov bx,receive_head
recv_search:
test code:[bx].rmd1,RCV_OWN ;do we own this buffer?
je recv_own ;yes - process it.
call inc_recv_ring ;go to the next one.
cmp bx,receive_head ;did we get back to the beginning?
jne recv_search ;not yet.
jmp short recv_done ;yes -- spurious interrupt!
recv_own:
test code:[bx].rmd1,RCV_ERR ;Any errors in this buffer?
jne recv_err ;yes -- ignore this packet.
mov ax,code:[bx].rmd0 ;fetch the packet.
mov dx,code:[bx].rmd1
call phys_to_segmoffs
if 0 ;not in this release, sigh.
cmp multi_filter,0 ;should we filter multicasts?
je recv_filter ;no.
call multicast_filter ;yes -- is it one of ours?
jc recv_free ;no - ignore it.
recv_filter:
endif
push es
push di
push bx
mov cx,code:[bx].rmd3
and cx,0fffh ;strip off the reserved bits
add di,EADDR_LEN+EADDR_LEN ;skip the ethernet addreses and
; point to the packet type.
push cx
call recv_find
pop cx
pop bx
pop si
pop ds
assume ds:nothing
mov ax,es ;is this pointer null?
or ax,di
je recv_free ;yes - just free the frame.
push es
push di
push cx
rep movsb
pop cx
pop si
pop ds
assume ds:nothing
call recv_copy
jmp short recv_free
recv_err:
call count_in_err
recv_free:
push cs
pop ds
assume ds:code
;clear any error bits.
and code:[bx].rmd1,not (RCV_ERR or RCV_FRAM or RCV_OFLO or RCV_CRC or RCV_BUF_ERR)
or code:[bx].rmd1,RCV_OWN ;give it back to the lance.
call inc_recv_ring ;go to the next one.
test code:[bx].rmd1,RCV_OWN ;Do we own this one?
je recv_own
mov receive_head,bx ;remember where the next one starts.
recv_done:
loadport ;enable interrupts again.
setport DATA_REG
mov ax,CSR0_INEA
out dx,ax
ret
inc_recv_ring:
;advance bx to the next receive ring descriptor.
assume ds:nothing
add bx,(size rcv_msg_dscp)
cmp bx,offset receive_dscps + RECEIVE_BUF_COUNT * (size rcv_msg_dscp)
jb inc_recv_ring_1
mov bx,offset receive_dscps
inc_recv_ring_1:
ret
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
phys_to_segmoffs:
;enter with dx:ax as the physical address of the buffer,
;exit with es:di -> buffer.
shl dx,16-4 ;move the upper four bits into position.
mov di,ax ;now get the low 12 bits of the segment.
shr di,4
or dx,di ;combine them.
mov es,dx
mov di,ax
and di,0fh ;now compute the offset.
ret
include timeout.asm
;we use this memory for buffers once we've gone resident.
transmit_bufs equ $
receive_bufs equ transmit_bufs + TRANSMIT_BUF_COUNT * TRANSMIT_BUF_SIZE
end_resident equ receive_bufs + RECEIVE_BUF_COUNT * RECEIVE_BUF_SIZE
public usage_msg
usage_msg db "usage: ni6510 <packet_int_no> <int_no> <io_addr>",CR,LF,'$'
no_board_msg db "No NI6510 detected.",CR,LF,'$'
io_addr_funny_msg label byte
db "No NI6510 detected, continuing anyway.",CR,LF,'$'
bad_reset_msg db "Unable to reset the NI6510.",CR,LF,'$'
bad_init_msg db "Unable to initialize the NI6510.",CR,LF,'$'
public copyright_msg
copyright_msg db "Packet driver for a Racal Interlan NI6510, version ",'0'+majver,".",'0'+version,CR,LF
db '$'
int_nos db 9, 12, 15, 5 ;interrupt numbers.
dma_nos db 0, 3, 5, 6 ;dma channel numbers
int_no_name db "Interrupt number ",'$'
io_addr_name db "I/O port ",'$'
extrn set_recv_isr: near
extrn maskint: near
;enter with si -> argument string, di -> dword 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.
assume ds:code
mov di,offset int_no
call get_number
mov di,offset io_addr
call get_number
clc
ret
public etopen
etopen:
assume ds:code
cmp io_addr,-1 ;Did they ask for auto-detect?
je find_board
call detect_board ;no, just verify its existance.
je find_board_found
mov dx,offset io_addr_funny_msg
mov ah,9
int 21h
jmp find_board_found
find_board:
mov io_addr,300h ;Search for the Ethernet address.
mov io_addr+2,0
find_board_0:
call detect_board
je find_board_found
find_board_again:
add io_addr,20h ;not at this port, try another.
cmp io_addr,360h
jbe find_board_0
mov dx,offset no_board_msg ;Tell them that we can't find it.
mov ah,9
int 21h
stc
ret
find_board_found:
loadport ;get the configuration register and
setport CONFIG ; determine the interrupt number.
in al,dx
shr al,2
and al,3
mov bl,al
xor bh,bh
mov al,int_nos[bx]
mov int_no,al
;This routine will put the (host) DMA controller into
;cascade mode of operation.
in al,dx ;get the dma channel field.
and al,3
mov bl,al
xor bh,bh
mov al,dma_nos[bx]
mov dma_no,al
mov ah,al ;save a copy.
or al,SET_DMA_MASK
cmp ah,4 ;If channel 5 or 6,
ja dma_16 ; use sixteen bit dma.
dma_8:
out DMA_8MASK_REG,al
mov al,ah
or al,CASCADE_MODE
out DMA_8MODE_REG,al
mov al,ah
out DMA_8MASK_REG,al
jmp short dma_done
dma_16:
out DMA_16MASK_REG,al
mov al,ah
or al,CASCADE_MODE
out DMA_16MODE_REG,al
mov al,ah
out DMA_16MASK_REG,al
dma_done:
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 al,int_no
call maskint ;disable these interrupts.
loadport
setport RESET
out dx,al
setport CSR0
in ax,dx
cmp ax,CSR0_STOP
je reset_ok
mov dx,offset bad_reset_msg
mov ah,9
int 21h
stc
ret
reset_ok:
;set up transmit descriptor ring.
push ds
pop es
mov cx,TRANSMIT_BUF_COUNT
mov bx,offset transmit_dscps
mov di,offset transmit_bufs
setup_transmit:
call segmoffs_to_phys
or dx,XMIT_START or XMIT_END
mov [bx].tmd0,ax ;points to the buffer.
mov [bx].tmd1,dx
add bx,(size xmit_msg_dscp)
add di,TRANSMIT_BUF_SIZE
loop setup_transmit
;set up receive descriptor ring.
mov cx,RECEIVE_BUF_COUNT
mov bx,offset receive_dscps
mov di,offset receive_bufs
setup_receive:
call segmoffs_to_phys
or dx,RCV_OWN
mov [bx].rmd0,ax ;points to the buffer.
mov [bx].rmd1,dx
mov [bx].rmd2,-RECEIVE_BUF_SIZE
mov [bx].rmd3,0
if 0
push di ;initialize the buffers to 55aa.
push cx
mov cx,RECEIVE_BUF_SIZE/2
mov ax,55aah
rep stosw
pop cx
pop di
endif
add bx,(size rcv_msg_dscp)
add di,RECEIVE_BUF_SIZE
loop setup_receive
;initialize the board.
mov cx,EADDR_LEN ;get our address.
mov di,offset init_addr
call get_address
mov cx,RECEIVE_BUF_COUNT
call compute_log2
mov di,offset receive_dscps
call segmoffs_to_phys
or dx,cx ;include the buffer size bits.
mov init_receive[0],ax
mov init_receive[2],dx
mov cx,TRANSMIT_BUF_COUNT
call compute_log2
mov di,offset transmit_dscps
call segmoffs_to_phys
or dx,cx ;include the buffer size bits.
mov init_transmit[0],ax
mov init_transmit[2],dx
mov di,offset init_block ;now tell the board where the init
call segmoffs_to_phys ; block is.
push dx
push ax
loadport
mov ax,0 ;write the bus config register.
outport CSR3
pop ax ;write the low word.
outport CSR1
pop ax ;write the high word.
outport CSR2
mov ax,0 ;non-promiscuous mode
call initialize
jnc init_ok
mov dx,offset bad_init_msg
mov ah,9
int 21h
stc
ret
init_ok:
;
; Now hook in our interrupt
;
call set_recv_isr
mov dx,offset end_resident
clc
ret
compute_log2:
;enter with cx = number of buffers.
;exit with cx = log2(number of buffers) << 13.
mov ax,-1
compute_log2_1:
inc ax
shr cx,1
jne compute_log2_1
shl ax,13
mov cx,ax
ret
segmoffs_to_phys:
;enter with es:di -> buffer.
;exit with dx:ax as the physical address of the buffer,
mov dx,es ;get the high 4 bits of the segment,
shr dx,16-4
mov ax,es ;and the low 12 bits of the segment.
shl ax,4
add ax,di ;add in the offset.
adc dx,0
ret
detect_board:
;test to see if a board is located at io_addr.
;return nz if not.
loadport
setport EBASE
in al,dx ;Check for Interlan's prefix word.
cmp al,2
jne detect_board_exit
setport EBASE+1
in al,dx
cmp al,7
jne detect_board_exit
setport EBASE+EADDR_LEN ;first byte following should be 0
in al,dx
cmp al,0
jne detect_board_exit
setport EBASE+EADDR_LEN+1 ;second byte should be 55h
in al,dx
cmp al,55h
detect_board_exit:
ret
code ends
end