Category : Network Files
Archive   : TCP_SRC.ZIP
Filename : ISOLINK.ASM
; Packet driver for BICC Data Networks' ISOLINK 4110-2 ethernet
; controller, written by
; Rainer Toebbicke
; European Organisation of Nuclear Research (CERN)
; Geneva, Switzerland
; based on the "generic" packet driver by Russell Nelson.
version equ 1 ;this is the minor version
include defs.asm ;SEE ENCLOSED COPYRIGHT MESSAGE
; BICC ISOLINK card constants
.286c ; we are at least on a 80286!
code segment para public
assume cs:code, ds:code
public begin ;makes us appear in the link map
begin equ $ ;used for alignment operations below
;Lance initialisation block, must be on word boundary
IBmode dw 00h ;mode word
IBpadr db 6 dup (0) ;physical addr
IBladrf db 8 dup (0ffh) ;logical addr filter
IBrdraL dw 0 ;Receive Descr Ring ptr
IBrdraH dw 0
IBrdraHF equ byte ptr IBrdraH+1
IBtdraL dw 0 ;Transmit Descr Ring ptr
IBtdraH dw 0
IBtdraHF equ byte ptr IBtdraH+1
CSR0 equ 0
c0_FIX equ 0000h
c0_ERR equ 8000h
c0_BABL equ 4000h
c0_CERR equ 2000h
c0_MISS equ 1000h
c0_MERR equ 0800h
c0_RINT equ 0400h
c0_TINT equ 0200h
c0_ErrClear equ c0_BABL+c0_CERR+c0_MISS+c0_MERR
c0_IDON equ 0100h
c0_INTR equ 0080h
c0_INEA equ 0040h
c0_RXON equ 0020h
c0_TXON equ 0010h
c0_TDMD equ 0008h
c0_STOP equ 0004h
c0_STRT equ 0002h
c0_INIT equ 0001h
CSR1 equ 1
CSR2 equ 2
CSR3 equ 3
RD struc ;Receive descriptor
RBadrL dw 0
RBadrH dw 0
RBbcnt dw 0 ;buffer size
RBmcnt dw 0 ;packet size
RD ends
RBflags equ byte ptr RBadrH+1
RBown equ 080h ;1=owned by Lance, 0=by host
RBerr equ 040h ;error summary bit
RBfram equ 020h
RBoflo equ 010h
RBcrc equ 008h
RBbuff equ 004h
RBstp equ 002h ;start of packet
RBenp equ 001h ;end of packet
TD struc ;Transmit descriptor
TBadrL dw 0
TBadrH dw 0
TBbcnt dw 0 ;buffer size
TBtdr dw 0 ;more flags
TD ends
TBflags equ byte ptr TBadrH+1
TBown equ 080h ;1=owned by Lance, 0=by host
TBerr equ 040h ;error summary bit
TBstp equ 002h ;Start of packet
TBenp equ 001h ;End of packet
TBbcntF equ byte ptr TBbcnt+1
public int_no
int_no dw 10,0 ;must be four bytes long for get_number.
public driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class db BLUEBOOK, IEEE8023, 0 ;from the packet spec
driver_type db 5 ;from the packet spec
driver_name db 'ISOLINK',0 ;name of the driver.
driver_function db 2
parameter_list label byte
db 1 ;major rev of packet driver
db 9 ;minor rev of packet driver
db 14 ;length of parameter list
db EADDR_LEN ;length of MAC-layer address
dw GIANT ;MTU, including MAC headers
dw MAX_MULTICAST * EADDR_LEN ;buffer size of multicast addrs
dw 0 ;(# of back-to-back MTU rcvs) - 1
dw 0 ;(# of successive xmits) - 1
int_num dw 0 ;Interrupt # to hook for post-EOI
;processing, 0 == none,
extrn sys_features: byte
public rcv_modes
rcv_modes dw 7 ;number of receive modes in our table.
dw 0
dw rcv_mode_1
dw 0
dw rcv_mode_3
dw 0,0
dw rcv_mode_6
rbfstart dw FirstDescr ;1st Rcv buffer descr
rbfcurr dw ?
tbfstart dw ? ;1st Xmit buffer descr
tbfcurr dw ?
tbfend dw ? ;end of Xmit buffers
xmt_buffsz_r dw ? ;xmit buffer sizes
xmt_buffsz_rn dw ? ;same, but times -1
pklen dw ? ;length of packet
pklen_rem dw ? ;remaining to be received
rcv_csr0 dw ?
rcvbuffp dd ?
RAP dw 0 ;Register Address Port
RDP dw 0 ;Register Data Port
options db 0
options_HMA equ 40h ;running in HMA
word_16 dw 16
extrn count_out_err:near, count_in_err:near
seg2lin proc near
; convert (ax:dx=offset:segment) to linear address (ax:dx=low:high)
push bx
push ax ;save offset
mov ax,dx
mul word_16 ;segment to linear address
pop bx
add ax,bx ;plus offset
adc dx,0
pop bx ;restore
ret
seg2lin endp
lin2seg proc near
; convert linear addr (ax:dx=low:high) to (ax:dx=offset:segment)
; with minimal offset to avoid wrap-around problems
; the 80286 can address 0-10ffefh in real mode:
cmp dl,010h ;over 1 Megabyte?
je l2sHMA ;yes, segment is 0FFFFh
push ax
shr ax,4 ;convert to paragraphs
shl dx,4+8
or dx,ax ;segment ok
pop ax
and ax,0fh ;offset
ret
l2sHMA:
; effectively subtract 0ffff0h from the linear address to form the
; offset:
mov dx,0ffffh ;this we know already
sub ax,0fff0h ;only have to do low order word
ret
lin2seg endp
; write bx to Lance control & status reg [ax]
wrcsr0 proc near
xor ax,ax ;write to CSR0
wrcsr: ;CSR in ax
mov dx,RAP ;address CSR
out dx,ax
mov dx,RDP ;data port
mov ax,bx
out dx,ax
ret
wrcsr0 endp
; read Lance control reg [ax]
rdcsr0 proc near
xor ax,ax ;read CSR 0
rdcsr: ;CSR in ax
mov dx,RAP
out dx,ax
mov dx,RDP
in ax,dx
ret
rdcsr0 endp
rcv_nxt_d proc near
; release current and advance to next receive descriptor
mov RBflags[bx],RBown ;give buffer to Lance
add bx,8
cmp bx,tbfstart ;end of ring?
jb rcv_nxt_ok ;no...
mov bx,rbfstart ;restart at first descriptor
rcv_nxt_ok:
mov rbfcurr,bx
ret
rcv_nxt_d endp
xmt_nxt_d proc near
xmt_nxt_d endp
wait_own_0 proc near
; wait for "own" bit in descriptor [bx] to clear
test TBflags[bx],TBown
jnz wo_wait ;no, have to wait
ret
wo_wait:
push cx ;save reg
mov cx,0ffffh
wo_lp1:
test TBflags[bx],TBown
jz wo_clear ;ok...
loop wo_lp1
wo_clear:
pop cx
ret
wait_own_0 endp
rcv_getaddr proc near
; obtain buffer addr from descriptor [bx]
mov ax,RBadrL[bx]
mov dx,RBadrH[bx]
call lin2seg
mov word ptr rcvbuffp,ax
mov word ptr rcvbuffp+2,dx
ret
rcv_getaddr endp
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 proc near
;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
push ds ;save packet segment
push cs
pop ds
assume ds:code
; packet must be at least 64 bytes long (with fcs)...
cmp cx,RUNT
jnl send_L_ok
mov cl,RUNT
send_L_ok:
; get next buffer descriptor
mov bx,tbfcurr
call wait_own_0 ;wait until it's free
; If the next buffer is big enough to hold the packet, we will
; copy the packet and send it out.
; Otherwise we send it from the user's buffer but then have to
; wait until it is sent out.
; This may speed up fast machines on the expense of some memory
cmp cx,xmt_buffsz_r ;longer than our buffers?
jng send_copy ;no, copy the packet
send_user_buff: ;send from user's buffer
mov ax,si ;input buffer addr
pop dx ;segment
call seg2lin ;convert to linear addr
push TBadrL[bx] ;save original buffer address
push TBadrH[bx] ;...
mov TBadrL[bx],ax
mov TBadrH[bx],dx
jmp short send_send
send_copy:
mov ax,TBadrL[bx]
mov dx,TBadrH[bx] ;buffer address
call lin2seg ;convert to offset:segment
mov es,dx
mov di,ax
pop ds ;restore packet segment
assume ds:nothing
push cx ;save length
; The packet is copied two bytes at a time, starting with the even(!)
; address of the destination buffer.
shr cx,1 ;convert to words, can't be zero
rep movsw ;copy buffer
jnc send_moved ;was original length even?
movsb
send_moved:
pop cx
push cs
pop ds ;restore ds
assume ds:code
send_send:
neg cx ;length in two's complement
mov TBbcnt[bx],cx
or TBflags[bx],TBown+TBstp+TBenp
push bx ;save ring entry address
mov bx,c0_TDMD+c0_INEA
call wrcsr0 ;start transmitter immediately
pop bx ;restore ring entry addr
xor al,al ;assume no error
cmp cx,xmt_buffsz_rn ;was this a big buffer?
jnl send_next ;no, all done
call wait_own_0 ;wait for send ok
mov al,TBflags[bx] ;save flags
mov cx,xmt_buffsz_rn ;original buffer size negated
pop TBadrH[bx]
pop TBadrL[bx]
mov TBbcnt[bx],cx
; advance to next transmit descriptor
send_next:
add bx,8
cmp bx,tbfend ;end of ring?
jb xmt_nxt_ok ;no...
mov bx,tbfstart ;restart at first descriptor
xmt_nxt_ok:
mov tbfcurr,bx
send_done:
test al,TBerr ;were there problems?
jnz send_err
clc
ret
send_err:
call count_out_err
stc
ret
send_pkt endp
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
jnb get_addr_ok ;buffer ok
stc
ret
get_addr_ok:
push si
mov si,offset IBpadr
rep movsb
pop si
mov cl,EADDR_LEN
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
jnb set_addr_ok ;buffer ok
mov dh,BAD_ADDRESS
stc
ret
set_addr_ok:
assume es:nothing
clc
ret
reset_mode proc near
; stop and restart the receiver
call reset_interface ;stop it
mov bx,c0_INIT ;re-init
call wrcsr0
mov cx,-1 ;wait until done
rm_lp:
call rdcsr0
test ax,c0_IDON ;ok?
jnz rm_IDON ;yes...
loop rm_lp ;else continue
rm_IDON:
mov bx,c0_STRT+c0_INEA+c0_IDON+c0_RXON+c0_TXON
call wrcsr0 ;start the receiver
ret
reset_mode endp
rcv_mode_1:
mov IBmode,0003h
jmp reset_mode
rcv_mode_3:
mov IBmode,0
jmp reset_mode
rcv_mode_6:
mov IBmode,8000h
jmp reset_mode
public set_multicast_list
set_multicast_list:
;enter with es:di ->list of multicast addresses, cx = number of bytes.
;return nc if we set all of them, or cy,dh=error if we didn't.
mov dh,NO_MULTICAST
stc
ret
public terminate
terminate:
ret
public get_multicast_list
get_multicast_list:
;return with nc, es:di ->list of multicast addresses, cx = number of bytes.
;return cy, NO_ERROR if we don't remember all of the addresses ourselves.
;return cy, NO_MULTICAST if we don't implement multicast.
mov dh,NO_MULTICAST
stc
ret
public reset_interface
reset_interface proc near
;reset the interface.
assume ds:code
mov bx,c0_STOP+c0_FIX
call wrcsr0
ret
reset_interface endp
;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
public recv
recv proc near
;called from the recv isr. All registers have been saved, and ds=cs.
;Upon exit, the interrupt will be acknowledged.
assume ds:code
call rdcsr0 ;obtain interrupting status
mov rcv_csr0,ax ;save
and ax,0ffffh-c0_INEA ;disable interrupts
mov bx,ax
call wrcsr0 ;clear acknowledged i-sources
test bx,c0_MISS ;out of buffers?
jz rcv_noerr ;no...
call count_in_err
rcv_noerr:
test rcv_csr0,c0_RINT ;received a packet
jz rcv_norint
rcv_rint:
call do_rcv ;process...
rcv_norint:
mov bx,c0_INEA ;re-enable interrupts
call wrcsr0
;The following is currently disabled because of perhaps
;unrelated problems: the clearing of the Int-sources is still
;done immediately after the interrupt....
;
;The interrupt sources are cleared very late increasing the
;possibility that we may handle several incoming packets without
;the processor being interrupted. However, this makes it possible
;that we miss an interrupt - so we have to check twice.
; mov bx,rcv_csr0 ;re-enable interrupts and
; call wrcsr0 ;clear acknowledged i-sources
; mov bx,rbfcurr ;current receive descriptor
; test RBflags[bx],RBown ;could have missed incoming pkt
; jnz rcv_ret ;no...
; call do_rcv ;process it, int already acked
rcv_ret:
ret
recv endp
do_rcv proc near
mov bx,rbfcurr ;address descriptor
;The following loop continues to check the next descriptor for
;work. It can therefore happen that a we already check descriptors
;while the Lance has not yet finished receiving the whole packet on
;remaining descriptors!
rcv_lp:
mov al,RBflags[bx]
test al,RBown ;something in buffer?
jz rcv_dobuf ;yes...
ret ;else return
rcv_dobuf:
test al,RBstp ;start of packet?
jz rcv_next_bx ;no, unrolling err chain
rcv_GetEnp:
test al,RBerr ;anything wrong with it?
jz rcv_ge_noerr ;no, seems ok.
mov dl,al ;copy flags
and dl,RBenp+RBoflo
cmp dl,RBenp+RBoflo ;this is no error!
je rcv_enp ;get length
jmp short rcv_do_err
rcv_ge_noerr:
test al,RBenp ;end of packet?
jnz rcv_enp ;yes, get length
; not on last segment
; test al,RBoflo ;possible error
; jnz rcv_do_err_0
add bx,8 ;to next descriptor
cmp bx,tbfstart ;wrap around?
jb rcv_ge_bx ;no...
mov bx,rbfstart
rcv_ge_bx:
cmp bx,rbfcurr ;make sure we don't loop!
je rcv_do_err_0 ;this is nonsense
mov al,RBflags[bx] ;get new flags
test al,RBown ;must be ours!
jz rcv_GetEnp ;ok, go ahead
; when we arrive here, we are processing a packet that has not yet
; been completely received. Leave everything as it is, we are
; guaranteed to check again, when the interrupt comes in at the
; latest.
ret
rcv_do_err_0:
; should count it somewhere else, but where?
rcv_do_err:
call count_in_err
rcv_next:
mov bx,rbfcurr ;restore current desc
rcv_next_bx:
call rcv_nxt_d ;next descriptor
jmp rcv_lp
;end of packet, check errors and obtain length
rcv_enp:
; test al,RBfram+RBcrc ;possible errors?
; jnz rcv_do_err
mov cx,RBmcnt[bx] ;message length
sub cx,4 ;strip FCS
mov bx,rbfcurr ;restore descriptor addr
cmp cx,GIANT ;reasonable size?
jg rcv_do_err ;rubbish!
jcxz rcv_next ;nothing to do...
mov pklen,cx ;save packet length
mov pklen_rem,cx ;remains to be rcvd
call rcv_getaddr
mov di,ax
mov es,dx
add di,EADDR_LEN*2 ;point to type field
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:
call recv_find ;want this packet?
mov ax,es
or ax,di
mov bx,rbfcurr ;restore in any case!
jz rcv_next_bx ;no buffer, give up
push es
push di
rcv_seg_lp:
; the packet's segments are copied into the client's buffer.
; The calculated length did not include the 4 byte fcs. Care must
; be taken not to copy the fcs, which may (in the worst case) be
; part of the last two segments!
test RBflags[bx],RBenp ;packet ends here?
jz rcv_seg_l_buf ;no, use buffer size
mov cx,pklen_rem ;remaining msg length
or cx,cx ;still data?
jle rcv_done ;no, just part of fcs
jmp short rcv_seg_l_ok
rcv_seg_l_buf:
mov cx,RBbcnt[bx] ;buffer size
neg cx ;it's in two's complement
sub pklen_rem,cx ;correct remaining len
jnl rcv_seg_l_ok ;use whole buffer
; the current buffer contains part of the fcs...
add cx,pklen_rem ;use only data part
rcv_seg_l_ok:
lds si,rcvbuffp ;point to buffer
assume ds:nothing
; The segment is copied two bytes at a time; we know that the input
; buffer starts on an even address.
shr cx,1 ;CF set when not even length!
jz rcv_last
rep movsw ;copy the packet
rcv_last:
jnc rcv_moved
movsb ;copy last byte
rcv_moved:
push cs
pop ds
assume ds:code
test RBflags[bx],RBenp ;end of packet?
jnz rcv_done ;yes...
call rcv_nxt_d ;to next descriptor
call rcv_getaddr ;next buffer addr
jmp rcv_seg_lp
rcv_done:
mov cx,pklen
pop si ;restore buffer addr
pop ds
assume ds:nothing
call recv_copy ;wake up client
push cs
pop ds
assume ds:code
;test jmp rcv_next ;do next one
mov bx,rbfcurr
call rcv_nxt_d
ret
do_rcv endp
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
; Most of the initialisation code is used only once and can be
; dicarded when init finishes, but not this:
; we formatted the buffer descriptors (a maximum space for
; that could be provided), but where could we place this code
; if buffers are allocated immediately afterwards?
; Too dangerous in the buffer area, since we will start the receiver now.
; Possible alternative: relocate to an address after the buffers, but
; since it's only a few bytes...
eto_go:
assume ds:code
call wrcsr ;start the Lance
pop dx ;get our ending paragraph
mov ah,31h
int 21h ;terminate, stay resident
; Provide maximum space for buffer descriptors before more
; discardable initialisation code starts, since the decriptors
; must be formatted. Buffers may follow immediately afterwards,
; but will not be used until the receiver is started.
; All resident code and data must be before this label!
; on quadword boundary...
org begin+( (($-begin+7)/8)*8 )
FirstDescr db ((128+128)*8) dup (0)
public usage_msg
usage_msg db "usage: ISOLINK [-n] [-d] [-w]
db "options: [defaults]",CR,LF
db " /D
db " /I
db " /P [(8)280]",CR,LF
db " /R<# recv buffs>
db " /T<# xmit buffs>
db " /X (requires himem.sys installed)",CR,LF
db "$"
public copyright_msg
copyright_msg db "Packet driver for BICC 4110-2/3 ISOLINK controllers,"
db "version ",'0'+majver,".",'0'+version,CR,LF
db "Written by R.Toebbicke, CERN, Switzerland",CR,LF
db "* Copyright CERN, Geneva 1990 - Copyright and any other",CR,LF
db "* appropriate legal protection of these computer programs",CR,LF
db "* and associated documentation reserved in all countries",CR,LF
db "* of the world.",CR,LF,'$'
int_no_name db "Interrupt number $"
BaseName db "I/O Port address $"
BasePrt dw 08280h,0
DMAName db "DMA channel number $"
DMAPrt dw 0,0
extrn our_isr: near, their_isr: dword
extrn packet_int_no: byte
extrn phd_environ: word
extrn decout: near
rcv_buffno dw 16,0
rcv_buffnoname db "Number of receive buffers $"
rcv_bufflog db 4
rcv_buffsz dw 256,0
rcv_buffszname db "Receive buffer size $"
xmt_buffno dw 1,0
xmt_buffnoname db "Number of transmit buffers $"
xmt_bufflog db 0
xmt_buffsz dw 0,0
xmt_buffszname db "Transmit buffer size $"
orgseg dw ?
linaddrL dw 0
linaddrH dw 0
; keep these two together!
HMAaddr dw 00010h
HMAaddrS dw 0FFFFh
XMSctl dd ?
PS2IO dw 08280h, 09250h, 0a390h, 0b1d0h
PS2INT db 9, 10, 11, 15, 3, 4, 5, 0
extrn set_recv_isr: near
;enter with si -> argument string, di -> word to store.
;if there is no number, don't change the number.
extrn get_number: near
;enter with dx -> name of word, di -> dword to print.
extrn print_number: near
extrn skip_blanks: near
argerr db "Error in options: '"
argerrc db " "
db "'.",CR,LF,"$"
parse_err db 0
public parse_args
parse_args proc near
; If we run on a PS/2, read the default configuration from the POS regs
test sys_features,MICROCHANNEL
jnz do_mc_defaults
jmp parse_args_l
do_mc_defaults:
mov bx,08h ;1st slot
pa_slot_l:
mov dx,96h
mov ax,bx
out dx,al ;put in setup mode
mov dx,101h
in al,dx
xchg al,ah ;high id byte
dec dx
in al,dx ;low id byte
cmp ax,0808h ;ISOLINK card?
je pa_found_iso ;yes...
inc bx ;next slot
cmp bx,0fh ;too far?
jna pa_slot_l ;no, try next
mov dx,96h
xor al,al
out dx,al ;exit setup mode
jmp parse_args_l
pa_found_iso:
mov dx,102h
in al,dx ;get I/O address code
xor ah,ah
and al,6
mov bx,ax
mov ax,PS2IO[bx] ;I/O port address
mov BasePrt,ax
mov dx,104h
in al,dx
xor ah,ah
shr al,5 ;isolate interrupt bits
mov bx,ax
mov al,PS2INT[bx] ;interrupt number
mov int_no,ax
parse_args_l:
call skip_blanks
cmp al,CR ;end of args?
je pa_ret ;yes...
cmp al,'/' ;option following?
je pa_doopt ;yes
pa_err:
mov argerrc,al
mov dx,offset argerr
pa_err2:
mov ah,9
int 21h ;print error message
mov parse_err,1 ;indicate error
pa_ret:
ret
pa_doopt:
inc si
lodsb ;get option byte
cmp al,'a' ;in lowercase range?
jna pa_upper
sub al,20h ;convert to uppercase
pa_upper:
cmp al,'I'
je pa_doInt
cmp al,'P'
je pa_doPort
cmp al,'D'
je pa_doDMA
cmp al,'R'
je pa_doRcv
cmp al,'T'
je pa_doTrans
cmp al,'X' ;into extended memory?
je pa_doext
jmp pa_err
pa_doInt:
mov di,offset int_no
call get_number
jmp parse_args_l
pa_doPort:
mov di,offset BasePrt
call get_number
jmp parse_args_l
pa_doDMA:
mov di,offset DMAPrt
call get_number
jmp parse_args_l
pa_doRcv:
mov di,offset rcv_buffno
call get_number
mov di,offset rcv_buffsz
call get_number
jmp parse_args_l
pa_doTrans:
mov di,offset xmt_buffno
call get_number
mov di,offset xmt_buffsz
call get_number
jmp parse_args_l
pa_doext: ;set up for HMA
or options,options_HMA ;indicate we want to run up there
jmp parse_args_l
parse_args endp
public print_parameters
print_parameters proc
mov di,offset int_no
mov dx,offset int_no_name
call print_number
mov di,offset BasePrt
mov dx,offset BaseName
call print_number
mov di,offset DMAPrt
mov dx,offset DMAName
call print_number
mov di,offset rcv_buffno
mov dx,offset rcv_buffnoname
call print_number
mov di,offset rcv_buffsz
mov dx,offset rcv_buffszname
call print_number
mov di,offset xmt_buffno
mov dx,offset xmt_buffnoname
call print_number
mov di,offset xmt_buffsz
mov dx,offset xmt_buffszname
call print_number
ret
print_parameters endp
inv_DMA db "Invalid DMA channel number.",CR,LF,"$"
init_errmsg db "Lance initialisation failed.",CR,LF,"$"
inv_buff db "Invalid buffer specification.",CR,LF,"$"
HMAnoHIMEM db "HMA not available: HIMEM.SYS not installed.",CR,LF,"$"
HMAnotavail db "HMA not available.",CR,LF,"$"
HMAalloc db "Using extended memory (HMA).",CR,LF,"$"
HMAtoobig db "Buffers exceed HMA size.",CR,LF,"$"
HMAmsg_1 db "/X configured $"
HMAmsg_2 db " receive buffers of length $"
HMAmsg_3 db "."
crlf_msg db CR,LF,"$"
really_bad_msg db "This driver may be buggy. Do you really want to run it (y/n)? ",'$'
public etopen
etopen proc near
assume ds:code
mov dx,offset really_bad_msg
mov ah,9
int 21h
mov ah,7
int 21h
cmp al,'y'
je etopen_1
cmp al,'Y'
je etopen_1
int 20h
etopen_1:
mov dx,offset crlf_msg
mov ah,9
int 21h
test parse_err,0ffh ;any parsing error?
jnz eto_errexit2 ;yes, give up
cmp packet_int_no,0
mov dx,offset usage_msg
jz eto_errexit
; check DMA port
mov ax,DMAPrt
or ax,ax
jb eto_invalid_DMA ;should not be negative
cmp ax,3
jna eto_DMA_ok
eto_invalid_DMA:
mov dx,offset inv_DMA
eto_errexit:
mov ah,9
int 21h
eto_errexit2:
stc
ret
eto_DMA_ok:
; check receive buffers
mov bx,rcv_buffno
call do_power_of_2
cmp ax,128
jg eto_inv_Buff
or ax,ax ;must have something!
jz eto_inv_Buff
mov rcv_bufflog,cl
mov rcv_buffno,ax
mov ax,rcv_buffsz ;check buffer size
cmp ax,100 ;at least that big
jg eto_rcvsz_1
mov ax,100
eto_rcvsz_1:
cmp ax,GIANT+4 ;biggest packet plus fcs
jl eto_rcvsz_2 ;does not exceed
mov ax,GIANT+4 ;take maximum
eto_rcvsz_2:
mov rcv_buffsz,ax
; check transmit buffers
mov bx,xmt_buffno
call do_power_of_2
or ax,ax ;must have something!
jz eto_inv_Buff
cmp ax,128
jna eto_xmt_buffok
eto_inv_Buff:
mov dx,offset inv_Buff
jmp eto_errexit
eto_xmt_buffok:
mov xmt_bufflog,cl
mov xmt_buffno,ax
mov ax,xmt_buffsz ;check buffer size
cmp ax,RUNT ;at least that big
jg eto_xmtsz_1
mov ax,0 ;else don't use
eto_xmtsz_1:
cmp ax,GIANT ;biggest packet w/o fcs
jl eto_xmtsz_2 ;does not exceed
mov ax,GIANT ;take maximum
eto_xmtsz_2:
mov xmt_buffsz,ax
mov xmt_buffsz_r,ax ;again for our resident part
neg ax
mov xmt_buffsz_rn,ax ;times -1 for resident part
; set RAP & RDP addresses
mov ax,BasePrt
add ax,0ch
mov RDP,ax
add ax,02h
mov RAP,ax
; this code is from Russell Nelson's packet driver skeleton.
; Need it here, since we won't return from etopen: as soon as the
; receiver is started, this place may get clobbered with Ethernet packets
mov ah,35h ;remember their packet interrupt.
mov al,packet_int_no
int 21h
mov their_isr.offs,bx
mov their_isr.segm,es
mov ah,25h ;install our packet interrupt
mov dx,offset our_isr
int 21h
; First of all: stop the Lance, sets required bits in CSR3 as well
call reset_interface
call set_recv_isr ;intercept interrupts
; Now we decide whether we will run in the HMA (above 1 MB) before
; we do any calculations involving real addresses
test options,options_HMA ;will we?
jnz eto_tryHMA ;yes
jmp eto_HMAok ;no...
; first check of HIMEM.SYS is installed and if we can globally
; enable the HMA
eto_tryHMA:
mov ax,4300h
int 2Fh ; Is an XMS Driver installed?
mov dx,offset HMAnoHIMEM ;error message in case...
cmp al,80h
jne eto_noHMA ;no...
mov ax,4310h
int 2fh ;obtain XMS control addr
mov word ptr XMSctl,bx ;save entry point
mov word ptr XMSctl+2,es
mov dx,0ffffh ;allocate whole HMA
mov ah,1
call XMSctl
mov dx,offset HMAnotavail ;error msg in case...
cmp ax,1
jne eto_noHMA ;not available
mov ah,3 ;global enable A20 line
call XMSctl
cmp ax,1 ;ok?
je eto_HMA_enabled
eto_noHMA:
mov ah,9
int 21h ;error msg addr in dx!
and options,not options_HMA ;turn off HMA bit
jmp eto_HMAok ;continue as usual
eto_HMA_enabled:
; mov dx,offset HMAalloc
; mov ah,9
; int 21h ;confirm
; maximize the receive buffer space to utilise the whole HMA:
; 1. calculate remaining space excluding receive buffers and descriptors
; 2. start with 128 receive buffers
; 3. calculate remaining space for buffers
; 4. divide by number of buffers to obtain (even) buffer size
; 5. if size inferior to defaulted or specified size half number
; of buffers and restart on step 2.
mov bx,0fff0h ;total HMA size
sub bx,offset FirstDescr ;minus resident code
mov ax,xmt_buffsz ;xmit buffer size
add ax,8 ;plus descriptor
mul xmt_buffno ;=total xmit space
or dx,dx ;not more than 64k!
jnz try_err ;ooops
sub bx,ax ;remaining space without recv
jc try_err ;must be positive!
mov cx,128 ;try 128 buffers
try_lp:
mov ax,cx ;number of buffers
shl ax,3 ;8 bytes long
sub ax,bx
neg ax ;remaining space for buffers
xor dx,dx ;accumulator extension
div cx ;calculate buffer length
and ax,0fffeh ;even length
cmp ax,rcv_buffsz ;long enough?
jnb try_ok ;yes...
shr cx,1 ;else half number of buffers
jnz try_lp ;and retry
try_err:
mov dx,offset HMAtoobig
jmp eto_errexit
try_ok:
cmp ax,GIANT+4 ;maximum size
jng try_ok_2 ;does not exceed
mov ax,GIANT+4 ;else maximum length
try_ok_2:
mov rcv_buffno,cx
mov rcv_buffsz,ax
mov bx,cx
call do_power_of_2 ;have to do it again
mov rcv_bufflog,cl
mov dx,offset HMAmsg_1
mov ah,9
int 21h
mov ax,rcv_buffno
xor dx,dx
call decout
mov dx,offset HMAmsg_2
mov ah,9
int 21h
mov ax,rcv_buffsz
xor dx,dx
call decout
mov dx,offset HMAmsg_3
mov ah,9
int 21h
; la demarche a suivre:
; we copy ourselves into the HMA completely, format all the
; descriptors, correct the interrupt addr and exit to DOS
mov ax,cs
mov orgseg,ax ;save original segment
les di,dword ptr HMAaddr ;that's where we will be
mov cx,offset end_code ;length(!) of code to be copied
mov si,10h ;start at offset 10
sub cx,si ;correct length
rep movsb ;copy us
push HMAaddrS ;our new segment
; careful: here comes a jump!
call setcs ;we're in the HMA
push cs
pop ds ;our data as well
eto_HMAok:
; get linear address for receive buffer descriptor start
push ds
mov ax,offset FirstDescr
pop dx
call seg2lin
; Store in Init Block
mov IBrdraL,ax
mov IBrdraH,dx
; calculate linear address of first buffer
mov bx,offset FirstDescr
mov rbfstart,bx
mov rbfcurr,bx
mov ax,rcv_buffno
push ds
pop dx
add ax,xmt_buffno ;result is still in segment
shl ax,3 ;times eight
add ax,bx ;won't leave segment!
call seg2lin ;convert to linear addr
mov linaddrL,ax ;first available buffer addr
mov linaddrH,dx
; format receive buffer descriptors
mov si,rcv_buffsz ;buffer size
mov cx,rcv_buffno ;number of buffers
mov dl,RBown ;flags
eto_rcvloop:
call format_descr
add bx,8
loop eto_rcvloop
;set transmit descriptor ring address
push ds
mov ax,bx
pop dx
call seg2lin
mov IBtdraL,ax
mov IBtdraH,dx
; format transmit buffer descriptors
mov tbfstart,bx ;remember start
mov tbfcurr,bx
mov dl,0 ;initial flags
mov cx,xmt_buffno ;number of buffers
mov si,xmt_buffsz ;buffer size
eto_xmtloop:
call format_descr
add bx,8
loop eto_xmtloop
mov tbfend,bx ;remember end
; finish initialisation block
mov al,rcv_bufflog ;# of rcv buffers
shl al,5
mov IBrdraHF,al
mov al,xmt_bufflog ;# of xmt buffers
shl al,5
mov IBtdraHF,al
; copy Ethernet address out of ROM
mov cx,6
push ds
pop es
mov di,offset IBpadr
mov dx,BasePrt
eto_addrloop:
in ax,dx
inc dx
inc dx
stosb
loop eto_addrloop
; init DMA controller
test sys_features,MICROCHANNEL ;running on a PS/2?
jnz eto_DMA_done ;yes, no DMA
mov al,0d0h ;mode reg bits
mov dx,0bh ;addr of mode reg
or al,byte ptr DMAPrt ;or in DMA number
out dx,al
jmp $+2
and al,3 ;leave only DMA channel #
mov dx,0ah ;mask register addr
out dx,al ;clear the DMA channel
eto_DMA_done:
; set initialisation block addr
push ds
pop dx
mov ax,offset IBmode
call seg2lin
push dx ;don't loose it
mov bx,ax
mov ax,CSR1
call wrcsr ;low order 16 bits of addr
pop bx
mov ax,CSR2
call wrcsr ;high order bits
; init the controller and wait for completion
mov bx,c0_FIX+c0_INIT
call wrcsr0
mov cx,-1 ;wait for completion
eto_initlp:
call rdcsr0
test ax,c0_IDON ;done?
jnz eto_init_ok ;good!
loop eto_initlp
mov dx,offset init_errmsg
call outofHMA ;get out of HMA
jmp eto_errexit
eto_init_ok:
test options,options_HMA ;are we in HMA?
jz eto_tsr ;no, use TSR code
xor ax,ax
; correct the packet and hardware interrupt addresses
mov es,ax
xor bh,bh
mov bl,packet_int_no
shl bx,2 ;point to interrupt vector
push cs
pop es:[bx+2]
mov bx,int_no ;card interrupt
add bx,68h ;assume above 8
cmp bx,8+68h ;is it?
jnb eto_HMA_int_ok ;yes...
sub bx,60h ;PS/2 can use interrupt < 8
eto_HMA_int_ok:
shl bx,2 ;point to interrupt vector
push cs
pop es:[bx+2]
; Now start the receiver and exit to DOS
call outofHMA ;out of danger zone (buffers!)
mov bx,c0_STRT+c0_INEA+c0_IDON+c0_RXON+c0_TXON ; ...SET...
call wrcsr0 ; ...GO!
int 20h ;exit...
eto_tsr:
;calculate end of resident part (relative to pgm start)
mov ax,linaddrL
mov dx,linaddrH
add ax,15 ;round to paragraph
adc dl,0
shr ax,4
shl dl,4
or ah,dl
mov dx,ds ;minus pgm start addr
sub ax,dx
push ax ;save the address
; again some code from the packet driver skeleton...
mov ah,49h ;free our environment, because
mov es,phd_environ ; we won't need it.
int 21h
mov bx,1 ;get the stdout handle.
mov ah,3eh ;close it in case they redirected it.
int 21h
mov ax,csr0 ; READY...
mov bx,c0_STRT+c0_INEA+c0_IDON+c0_RXON+c0_TXON ; ...SET...
jmp eto_go ; ...GO!
etopen endp
do_power_of_2 proc near
; adjust bx to valid # of buffers.
; on exit: ax=fitting number, cx=log2(ax)
mov cx,7 ;allow 7 shifts
mov ax,bx
dpw2_loop:
cmp ax,1
je dpw2_done
shr ax,1
loop dpw2_loop
dpw2_done:
mov ax,7
sub ax,cx ;get number of shifts
mov cx,ax
mov ax,1
jcxz dpw2_ret
shl ax,cl
dpw2_ret:
ret
do_power_of_2 endp
format_descr proc near
; format buffer descriptor pointed to by [bx]
; si=buffer size, dl=flags
push dx ;save flags
mov ax,linaddrL ;next available buffer addr
mov dx,linaddrH
mov RBadrL[bx],ax ;buffer addr
mov RBadrH[bx],dx
add ax,si ;point to next available addr
adc dx,0
mov linaddrL,ax
mov linaddrH,dx
mov ax,si ;get buffer size
; or ah,0f0h ;reserved bits to 1
neg ax ;two's complement
mov RBbcnt[bx],ax
pop dx ;restore flags
mov RBflags[bx],dl
ret
format_descr endp
outofHMA proc near ;leave the HMA
test options,options_HMA ;are we in it?
jz ooHret ;no...
push orgseg
call setcs
push cs
pop ds
ooHret:
ret
outofHMA endp
setcs proc near
db 0cbh ;RET FAR (!)
setcs endp
end_code equ $
code ends
end
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
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/