home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Serving the Web
/
ServingTheWeb1995.disc1of1.iso
/
connect
/
tcpip
/
crynwr
/
pktd11c
/
lance.asm
< prev
next >
Wrap
Assembly Source File
|
1993-12-14
|
20KB
|
873 lines
lance_version equ 3
; Copyright, 1990-1992, Russell Nelson, Crynwr Software
; 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.
CSR0 equ 0
CSR1 equ 1
CSR2 equ 2
CSR3 equ 3
DMA_8MASK_REG equ 0Ah
DMA_16MASK_REG equ 0D4h
DMA_8MODE_REG equ 0Bh
DMA_16MODE_REG equ 0D6h
CASCADE_MODE equ 0C0h
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 GIANT+2 ;dword-align.
RECEIVE_BUF_SIZE equ GIANT+4+2 ;LANCE copies in 4 bytes of checksum, plus dword-align.
;
; 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
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 0 ;only ours.
dw rcv_mode_3 ;ours plus broadcast
dw rcv_mode_4 ;some multicasts
dw rcv_mode_5 ;all multicasts
dw rcv_mode_6 ;all packets
;
;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(<>)
save_csr1 dw ?
save_csr2 dw ?
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 bad_command_intercept
bad_command_intercept:
;called with ah=command, unknown to the skeleton.
;exit with nc if okay, cy, dh=error if not.
mov dh,BAD_COMMAND
stc
ret
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 ds:si -> packet, cx = packet length.
;exit with nc if ok, or else cy if error, dh set to error number.
assume ds:nothing
cmp cx,GIANT
ja send_err
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_err:
mov dh,NO_SPACE
stc
ret
send_pkt_2:
;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
shr cx,1
rep movsw
jnc send_pkt_3
movsb
send_pkt_3:
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 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:
movseg es,cs
mov di,offset init_addr
rep movsb
mov ax,init_mode
call initialize ;initialize with our new address.
set_address_okay:
mov cx,EADDR_LEN ;return their address length.
clc
set_address_done:
movseg ds,cs
assume ds:code
ret
rcv_mode_1:
mov ax,M_DRX ;disable the receiver.
jmp initialize
rcv_mode_3:
xor ax,ax ;don't accept any multicast frames.
call initialize_multi
mov ax,0 ;non-promiscuous mode
jmp short initialize
set_hw_multi:
mov ax,init_mode
jmp short set_hw_multi_1
rcv_mode_4:
mov ax,0 ;non-promiscuous mode
set_hw_multi_1:
movseg es,cs
mov di,offset init_filter
mov si,offset mcast_list_bits
mov cx,8/2
rep movsw
jmp short initialize
rcv_mode_5:
mov ax,-1 ;accept any multicast frames.
call initialize_multi
mov ax,0 ;non-promiscuous mode
jmp short initialize
rcv_mode_6:
mov ax,M_PROM ;promiscuous mode
initialize:
mov init_mode,ax
loadport
mov ax,CSR0_STOP ;reset the INIT bit
outport CSR0
mov ax,0 ;write the bus config register.
outport CSR3
mov ax,save_csr1 ;write the low word.
outport CSR1
mov ax,save_csr2 ;write the high word.
outport CSR2
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.
movseg es,cs
mov di,offset init_filter
mov cx,8/2
rep stosw
ret
public terminate
terminate:
loadport
mov ax,CSR0_STOP ;reset the INIT bit
outport CSR0
;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:
push ds ;restore their interrupt 9 (keyboard).
lds dx,their_9
mov ax,2519h
int 21h
pop ds
push ds ;restore their interrupt 19 (reboot).
lds dx,their_19
mov ax,2519h
int 21h
pop ds
ret
our_19:
assume ds:nothing
push ax
push dx
loadport
mov ax,CSR0_STOP ;reset the INIT bit
outport CSR0
pop dx
pop ax
db 0eah ;jmp xxxx:yyyy
their_19 dd ?
our_9:
assume ds:nothing
push ax ;did they press delete?
in al,60h
cmp al,53h
jnz our_9_2 ;no.
push es ;are control and alt pressed also?
mov ax,40h
mov es,ax
mov al,es:[17h]
and al,0ch
cmp al,0ch
jnz our_9_1 ;no, don't do the reboot thing.
push dx ;loadport and outport use dx.
loadport
mov ax,CSR0_STOP ;reset the INIT bit
outport CSR0
pop dx
our_9_1:
pop es
our_9_2:
pop ax
db 0eah
their_9 dd ?
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, dl = packet class.
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
;call this routine to schedule a subroutine that gets run after the
;recv_isr. This is done by stuffing routine's address in place
;of the recv_isr iret's address. This routine should push the flags when it
;is entered, and should jump to recv_exiting_exit to leave.
;enter with ax = address of routine to run.
extrn schedule_exiting: near
;recv_exiting jumps here to exit, after pushing the flags.
extrn recv_exiting_exit: near
;enter with dx = amount of memory desired.
;exit with nc, dx -> that memory, or cy if there isn't enough memory.
extrn malloc: near
extrn count_in_err: near
extrn count_out_err: near
LANCE_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 LANCE_ISR_ACKNOWLEDGE
out dx,ax
in ax,dx ; follow all writes by a read
test bx,CSR0_RINT ;receive interrupt?
je recv_done_1 ;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.
recv_done_1:
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
push es
push di
push bx
mov cx,code:[bx].rmd3
and cx,0fffh ;strip off the reserved bits
sub cx,4 ;leave the CRC behind.
add di,EADDR_LEN+EADDR_LEN ;skip the ethernet addreses and
; point to the packet type.
mov dl, BLUEBOOK ;assume bluebook Ethernet.
mov ax, es:[di]
xchg ah, al
cmp ax, 1500
ja BlueBookPacket
inc di ;set di to 802.2 header
inc di
mov dl, IEEE8023
BlueBookPacket:
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
shr cx,1
rep movsw
jnc rec_pkt_3
movsb
rec_pkt_3:
pop cx
pop si
pop ds
assume ds:nothing
call recv_copy
jmp short recv_free
recv_err:
call count_in_err
recv_free:
movseg ds,cs
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
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 multicrc.asm
include timeout.asm
public timer_isr
timer_isr:
;if the first instruction is an iret, then the timer is not hooked
iret
;any code after this will not be kept. Buffers used by the program, if any,
;are allocated from the memory between end_resident and end_free_mem.
public end_resident,end_free_mem
align 4 ;just for efficiency's sake.
end_resident label byte
db (RECEIVE_BUF_COUNT*RECEIVE_BUF_SIZE) + (TRANSMIT_BUF_COUNT*TRANSMIT_BUF_SIZE) dup(?)
end_free_mem label byte
int_no_name db "Interrupt number ",'$'
io_addr_name db "I/O port ",'$'
dma_no_name db "DMA number ",'$'
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
;-> the assigned Ethernet address of the card.
extrn rom_address: byte
public etopen
etopen:
assume ds:code
call check_board
jnc etopen_1
ret
etopen_1:
;This routine will put the (host) DMA controller into
;cascade mode of operation.
mov al,dma_no
cmp al,4 ;If channel 5, 6 or 7
ja dma_16 ; use sixteen bit dma.
dma_8:
or al,CASCADE_MODE
out DMA_8MODE_REG,al ;set the mode first, then unmask it.
and al,DMA_CHANNEL_FIELD
out DMA_8MASK_REG,al
jmp short dma_done
dma_16:
and al,DMA_CHANNEL_FIELD ;set the mode first, then unmask it.
or al,CASCADE_MODE
out DMA_16MODE_REG,al
and al,DMA_CHANNEL_FIELD
out DMA_16MASK_REG,al
dma_done:
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 al,int_no
call maskint ;disable these interrupts.
loadport
reset_lance
mov ax,20
call set_timeout
setport ADDR_REG
mov ax,CSR0
out dx,ax
in ax,dx
setport DATA_REG
etreset:
in ax,dx
test ax,CSR0_STOP
jnz reset_ok
call do_timeout
jnz etreset
mov dx,offset bad_reset_msg
stc
ret
no_memory:
mov dx,offset no_memory_msg
stc
ret
reset_ok:
;set up transmit descriptor ring.
movseg es,ds
mov cx,TRANSMIT_BUF_COUNT
mov bx,offset transmit_dscps
setup_transmit:
mov dx,TRANSMIT_BUF_SIZE
call malloc
jc no_memory
mov di,dx
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)
loop setup_transmit
;set up receive descriptor ring.
mov cx,RECEIVE_BUF_COUNT
mov bx,offset receive_dscps
setup_receive:
mov dx,RECEIVE_BUF_SIZE
call malloc
jc no_memory
mov di,dx
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)
loop setup_receive
;initialize the board.
mov cx,EADDR_LEN ;get our address.
mov di,offset rom_address
loadport ; Get our Ethernet address base.
setport EBASE
get_address_1:
insb ; get a byte of the eprom address
inc dx ; next register
loop get_address_1 ; go back for rest
mov si,offset rom_address ; copy it to the init table.
mov di,offset init_addr
mov cx,EADDR_LEN
rep movsb
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.
mov save_csr1,ax
mov save_csr2,dx
call rcv_mode_3
jnc init_ok
mov dx,offset bad_init_msg
stc
ret
init_ok:
;
; Now hook in our interrupt
;
call set_recv_isr
mov ax,3519h ;remember their reboot interrupt.
int 21h
mov their_19.offs,bx
mov their_19.segm,es
mov ah,25h ;install our reboot interrupt
mov dx,offset our_19
int 21h
mov ax,3509h ;remember their reboot interrupt.
int 21h
mov their_9.offs,bx
mov their_9.segm,es
mov ah,25h ;install our reboot interrupt
mov dx,offset our_9
int 21h
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 dma_no
mov dx,offset dma_no_name
call print_number
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