Category : Network Files
Archive   : PKTD11C.ZIP
Filename : LANCE.ASM

 
Output of file : LANCE.ASM contained in archive : PKTD11C.ZIP
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




  3 Responses to “Category : Network Files
Archive   : PKTD11C.ZIP
Filename : LANCE.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/