nelson@sun.soe.clarkson.edu (Russ Nelson) (07/28/88)
Here is a packet driver for the Micom NI5210 that works under KA9Q's NET
version 871225.30 and successors. It is freely copyable but may not
be sold for profit. Perhaps someone would like to port it to the
3c501 and/or wd8003? I don't have access to a wd8003, and I refuse to
use a 3c501.
-russ
;/* PC/FTP Packet Driver source, conforming to version 1.05 of the spec
;* Russell Nelson, Clarkson University. July 20, 1988
;* Portions (C) Copyright 1988 Russell Nelson
;*
;* Permission is granted to any individual or institution to use, copy,
;* modify, or redistribute this software and its documentation provided
;* this notice and the copyright notices are retained. This software may
;* not be distributed for profit, either in original form or in derivative
;* works. Russell Nelson makes no representations about the suitability
;* of this software for any purpose. RUSSELL NELSON GIVES NO WARRANTY,
;* EITHER EXPRESS OR IMPLIED, FOR THE PROGRAM AND/OR DOCUMENTATION
;* PROVIDED, INCLUDING, WITHOUT LIMITATION, WARRANTY OF MERCHANTABILITY
;* AND WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE.
;*/
;/*
;* NCSA Telnet source code
;* National Center for Supercomputing Applications
;* November 1, 1987
;* (C) Copyright 1987 The Board of Trustees of the University of Illinois
;*
;* Permission is granted to any individual or institution to use, copy,
;* modify, or redistribute this software and its documentation provided
;* this notice and the copyright notices are retained. This software
;* may not be distributed for profit, either in original form or in
;* derivative works. The University of Illinois makes no representations
;* about the suitability of this software for any purpose.
;* THE UNIVERSITY OF ILLINOIS GIVES NO WARRANTY,
;* EITHER EXPRESS OR IMPLIED, FOR THE PROGRAM AND/OR DOCUMENTATION PROVIDED,
;* INCLUDING, WITHOUT LIMITATION, WARRANTY OF MERCHANTABILITY AND WARRANTY
;* OF FITNESS FOR A PARTICULAR PURPOSE.
;*/
TITLE NETSUPPORT -- LOW LEVEL DRIVERS FOR ETHERNET
;
; Assembler support for interrupt-driven Ethernet I/O on the PC
;
; Tim Krauskopf
; National Center for Supercomputing Applications
; 9/1/87 Ungermann-Bass driver started, PC bus
; 9/14/87 MICOM NI5210 driver started, PC bus
;
;
NAME NET
code segment
assume cs:code, ds:code
org 2ch
phd_environ dw ?
org 80h
phd_dioa label byte
org 100h
start:
jmp start_1
;
; Packet Driver Error numbers
BAD_HANDLE equ 1 ;invalid handle number
NO_CLASS equ 2 ;no interfaces of specified class found
NO_TYPE equ 3 ;no interfaces of specified type found
NO_NUMBER equ 4 ;no interfaces of specified number found
BAD_TYPE equ 5 ;bad packet type specified
NO_MULTICAST equ 6 ;this interface does not support
CANT_TERMINATE equ 7 ;this packet driver cannot terminate
BAD_MODE equ 8 ;an invalid receiver mode was specified
NO_SPACE equ 9 ;operation failed because of insufficient
TYPE_INUSE equ 10 ;the type had previously been accessed,
BAD_COMMAND equ 11 ;the command was out of range, or not
CANT_SEND equ 12 ;the packet couldn't be sent (usually
;
; Equates for controlling the MICOM board
;
; I/O addresses, writing anything in AL trips these gates
;
; First six addresses are the EPROM board Ether address (read)
;
IORESET EQU 0 ; reset the board
IOCA EQU 1 ; execute command which is in SCB
IODIS EQU 2 ; disable network connect
IOENA EQU 3 ; enable network
IOINTON EQU 4 ; enable interrupts
IOINTOF EQU 5 ; disable interrupts, '586 thinks it still ints
;
; Structure elements specific to the Intel 82586 chip
;
BDBASE EQU 1874 ; base address for 30 buffer descriptors
BUFBASE EQU 2174 ; base address for 30 200 byte buffers
BDSTAT EQU 0 ; status word in BD
BDLINK EQU 2 ; 16pointer to next BD
BDPTR EQU 4 ; 24pointer to actual buffer
BDSIZE EQU 8 ; size of the buffer
;
SCB EQU 10 ; system control block base
SSTAT EQU 0 ; status word for SCB
SCOM EQU 2 ; command word in SCB
SCBL EQU 4 ; 16pointer to command block list
SRFA EQU 6 ; 16pointer to receive frame list
SERRS EQU 8 ; 4 words of error counts
;
FDBASE EQU 1214 ; base addr for 30 frame descriptors
FDSTAT EQU 0 ; status word for frame
FDEOL EQU 2 ; end of FD list flag
FDLINK EQU 4 ; 16pointer to next FD
FDPTR EQU 6 ; 16pointer to list of BD's
;
TSTAT EQU 0 ; status word for xmit
TCOM EQU 2 ; command to transmit
TLINK EQU 4 ; 16pointer to next command (always ffff)
TPTR EQU 6 ; 16pointer to xmit TBD
TTRIES EQU 8 ; number of transmit retries
;
SCPTR EQU 01ff6h ; hardwired address for SCP
ISCPTR EQU 01feeh ; my address for ISCP, points to SCB
CCBPTR EQU 26 ; offset of configure command block
TCBPTR EQU 44 ; xmit CB offset
TBDPTR EQU 60 ; xmit BD offset
TBUFPTR EQU 68 ; xmit buffer offset
;
; Data segment
;
firstfd dw FDBASE ; start of FD queue
lastfd dw 0 ; end of the FD chain
lastbd dw 0 ; end of the BD chain
io_addr dw 0360h ; I/O address for card (jumpers)
base_addr dw 0d000h ; base segment for board (jumper set)
int_no db 2,? ; interrupt number (jumper set)
packet_int_no db 6ch,? ; interrupt to communicate.
is_at dw ? ; =1 if we're on an AT.
;
; data for configuring and setting up the MICOM board
;
; chip always looks at SCP for config info which points to ISCP for the
; pointer to the CONTROL BLOCK which handles everything from there.
; Kind of indirect, but it works.
;
SCP DB 1 ; bus use flag
DB 5 DUP(0) ; unused
DW ISCPTR ; 24pointer to ISCP offset
DW 0 ; high part
;
; Intermediate SCP
;
ISCP DW 1 ; busy flag
DW SCB ; 16pointer to SCB
DW 0,0 ; base for all 16 pointers, lo, hi
; board is hardwired to 0 for these values
;
; Configuration block for 82586, this comprises one config command
; Parameters taken from MICOM driver
;
CBCONF DW 0 ; status word
DW 8002H ; end of command list + configure command
DW 0ffffh ; link to next command (not used)
DW 080CH ; fifo=8, byte count=C
DW 2E00H ; important! Addr (AL) not inserted on the fly!
DW 6000H ; IFS = 60h
DW 0F200H ; retry=F, slot time=200h
DW 0 ; flags, set to 1 for promiscuous
DW 40H ; min frame length=40h
;
; CB for xmit, followed by BD for xmit, copied together
;
TCB DW 0 ; status word
DW 08004H ; command word for xmit + EL
DW 0ffffh ; no command link
DW TBDPTR ; 16pointer to xmit BD
DW 0,0,0,0 ; no addressing used here
;
; BD template for xmit
TBD DW 0
DW 0 ; next BD pointer, unused
DW TBUFPTR ; 24pointer to xmit buffer
DW 0 ; high part of pointer
functions label word
dw driver_info
dw access_type
dw release_type
dw send_pkt
dw terminate
dw get_address
dw reset_interface
ni5210_name db 'NI5210',0
MAX_P_LEN equ 2 ;IEEE 802.3 has max type length of 2.
per_handle struc
in_use db ? ;non-zero if this handle is in use.
packet_type db MAX_P_LEN dup(?);associated packet type.
packet_type_len dw ? ;associated packet type length.
receiver dd ? ;receiver handler.
per_handle ends
max_handle equ 2
handles db max_handle*(size per_handle) dup(0)
end_handles label byte
free_handle dw ? ;->a handle not in use.
found_handle dw ? ;->the handle for our packet.
regs struc
_ES dw ?
_DS dw ?
_BP dw ?
_DI dw ?
_SI dw ?
_DX dw ?
_CX dw ?
_BX dw ?
_AX dw ?
_IP dw ?
_CS dw ?
_F dw ? ;flags, Carry flag is bit 0.
regs ends
bytes struc
dw ?
dw ?
dw ?
dw ?
dw ?
_DL db ?
_DH db ?
_CL db ?
_CH db ?
_BL db ?
_BH db ?
_AL db ?
_AH db ?
bytes ends
segmoffs struc
offs dw ?
segm dw ?
segmoffs ends
their_isr dd ?
our_isr:
jmp our_isr_0 ;the required signature.
db 'PKT DRVR',0
our_isr_0:
push ax
push bx
push cx
push dx
push si
push di
push bp
push ds
push es
mov bx,cs ;set up ds.
mov ds,bx
mov bp,sp ;we use bp to access the original regs.
and _F[bp],not 1 ;start by clearing the carry flag.
mov bl,ah ;jump to the correct function.
mov bh,0
dec bx ;first function is zero.
cmp bx,7 ;highest function is 7 (now 6).
jae our_isr_2
add bx,bx
jmp functions[bx]
our_isr_2:
mov _DH[bp],BAD_COMMAND
our_isr_error:
or _F[bp],1 ;return their carry flag.
our_isr_return:
pop es
pop ds
pop bp
pop di
pop si
pop dx
pop cx
pop bx
pop ax
iret
driver_info:
call verify_handle
mov _BX[bp],0 ;version 0.
mov _CH[bp],1 ;Ethernet
mov _DX[bp],11 ;NI5210
mov _CL[bp],0 ;number zero.
mov _DS[bp],ds
mov _SI[bp],offset ni5210_name
mov _AL[bp],1 ;basic driver
jmp our_isr_return
access_type_class:
mov _DH[bp],NO_CLASS
jmp our_isr_error
access_type_type:
mov _DH[bp],NO_CLASS
jmp our_isr_error
access_type_number:
mov _DH[bp],NO_NUMBER
jmp our_isr_error
access_type:
cmp _AL[bp],1 ;Ethernet?
jne access_type_class ;no.
cmp _BX[bp],-1 ;generic type?
je access_type_2 ;yes.
cmp _BX[bp],11 ;NI5210 type?
jne access_type_type ;no.
access_type_2:
cmp _DL[bp],0 ;generic number?
je access_type_3
cmp _DL[bp],1 ;our number?
jne access_type_number
access_type_3:
cmp _CX[bp],0 ;we'll let them specify zero.
je access_type_7
cmp _CX[bp],MAX_P_LEN ;IEEE 802.3 allows only MAX_P_LEN
jne access_type_inuse ;no - can't be ours.
access_type_7:
; now we do two things--look for an open handle, and check the existing
; handles to see if they're replicating a packet type.
mov free_handle,0 ;remember no free handle yet.
mov bx,offset handles
access_type_4:
cmp [bx].in_use,0 ;is this handle in use?
je access_type_5 ;no - don't check the type.
mov es,_DS[bp] ;get a pointer to their type.
mov di,_SI[bp]
mov cx,_CX[bp]
cmp cx,[bx].packet_type_len ;lengths equal?
jne access_type_6 ;no - keep looking.
lea si,[bx].packet_type
or cx,cx ;in case cx is zero.
repe cmpsb
jne short access_type_6 ;go look at the next one.
access_type_inuse:
mov _DH[bp],TYPE_INUSE
jmp our_isr_error
access_type_5:
cmp free_handle,0 ;found a free handle yet?
jne access_type_6 ;yes.
mov free_handle,bx ;remember a free handle
access_type_6:
add bx,(size per_handle) ;go to the next handle.
cmp bx,offset end_handles
jb access_type_4
mov bx,free_handle ;did we find a free handle?
or bx,bx
je access_type_handle ;no - return error.
mov [bx].in_use,1 ;remember that we're using it.
mov ax,_DI[bp] ;remember the receiver type.
mov [bx].receiver.offs,ax
mov ax,_ES[bp]
mov [bx].receiver.segm,ax
push ds
mov ax,ds
mov es,ax
mov ds,_DS[bp] ;remember their type.
mov si,_SI[bp]
mov cx,_CX[bp]
mov es:[bx].packet_type_len,cx ;remember the length.
lea di,[bx].packet_type
rep movsb
pop ds
mov _AX[bp],bx ;return the handle to them.
jmp our_isr_return
access_type_handle:
mov _DH[bp],BAD_HANDLE
jmp our_isr_error
release_type:
call verify_handle ;mark this handle as being unused.
mov [bx].in_use,0
jmp our_isr_return
send_pkt:
mov ax,base_addr
mov es,ax ; base for board
push ds ; set up proper ds for the buffer
mov ds,_DS[bp] ; address for buffer
mov si,_SI[bp]
mov cx,_CX[bp] ; count of bytes
mov dx,cx ; save a copy, might be less than 60, ok
cmp dx,60 ; minimum length for Ether
jnb oklen
mov dx,60 ; make sure size at least 60
oklen:
mov di,TBUFPTR ; start of xmit buffer
;
; check for previous xmit
xwait:
mov bx,word ptr es:[SCB+SCOM] ; is command zeroed yet?
or bx,bx
jnz xwait ; not there yet, wait for it
;
; move the data
rep movsb ; copy into buffer
;
; put the correct size into the TDB
;
or dx,08000h ; end of frame bit flag
mov word ptr es:[TBDPTR],dx ; store it
mov word ptr es:[TCBPTR],0 ; zero status wd
mov word ptr es:[TCBPTR+TCOM],08004h; xmit command in TCB
mov word ptr es:[SCB+SCOM],0100h ; execute command
pop ds ; get back my ds
mov dx,io_addr
inc dx
out dx,al ; issue CA to get it going
jmp our_isr_return
terminate:
call verify_handle
;don't really know what else to do here.
mov _DH[bp],CANT_TERMINATE
jmp our_isr_error
get_address:
call verify_handle
mov dx,io_addr ; Get our IO base address.
mov es,_ES[bp] ; get new one
mov di,_DI[bp] ; get pointer, es:di is ready
mov cx,6
cmp cx,_CX[bp] ;is there enough room?
ja get_address_space ;no.
mov _CX[bp],cx ;Tell them how long our address is.
cld
get_address_1:
in al,dx ; get a byte of the eprom address
stosb ; put it away
inc dx ; next register
loop get_address_1 ; go back for rest
jmp our_isr_return
get_address_space:
mov _DH[bp],NO_SPACE
jmp our_isr_error
reset_interface:
call verify_handle
;do nothing--return with no error.
jmp our_isr_return
verify_handle:
;Ensure that their handle is real. Jump to our_isr_error if it
;isn't real.
mov bx,_BX[bp] ;is this handle in range?
cmp bx,offset handles
jb verify_handle_bad ;no - must be bad.
cmp bx,offset end_handles
jae verify_handle_bad ;no - must be bad.
cmp [bx].in_use,0 ;if it's not in use, it's bad.
je verify_handle_bad
ret
verify_handle_bad:
mov _DH[bp],BAD_HANDLE
add sp,2 ;pop off our return address.
jmp our_isr_error
their_recv_isr dd ?
recv_isr:
push ax
push bx
push cx
push dx
push si
push di
push bp
push ds
push es
mov bx,cs ;ds = cs.
mov ds,bx
mov es,base_addr ;es = base_addr (of board).
mov ax,es:[SCB+SSTAT] ;get the status.
recv_isr_1:
cmp word ptr es:[SCB+SCOM],0 ;are we done yet?
jne recv_isr_1 ;no -- keep waiting.
and ax,0f000h ;isolate the ACK bits.
mov es:[SCB+SCOM],ax ;make a command to
;acknowledge the interrupt.
mov dx,io_addr ;acknowledge the interrupts.
inc dx ; issue CA
out dx,al
test ah,40h ;did we receive a frame?
je recv_isr_2
call recv ;yes - receive any frames...
recv_isr_2:
;
; The following code is ruthlessly stolen from Phil Karn's NET package.
;
cmp is_at,1
jnz recv_isr_3 ; Only one 8259, so skip this stuff
mov al,0bh ; read in-service register from
out 0a0h,al ; secondary 8259
nop ; settling delay
nop
nop
in al,0a0h ; get it
or al,al ; Any bits set?
jz recv_isr_3 ; nope, not a secondary interrupt
mov al,20h ; Get EOI instruction
out 0a0h,al ; Secondary 8259 (PC/AT only)
recv_isr_3:
mov al,20h ;acknowledge the interrupt.
out 20h,al
pop es
pop ds
pop bp
pop di
pop si
pop dx
pop cx
pop bx
pop ax
iret
maskint:
mov dx,21h ;assume the master 8259.
mov cl,int_no
cmp cl,8 ;using the slave 8259 on an AT?
jb mask_not_irq2
mov dx,0a1h ;go enable it on slave 8259
sub cl,8
mask_not_irq2:
in al,dx ;enable interrupts on the master 8259.
mov ah,1 ;clear the bit.
shl ah,cl
or al,ah
out dx,al
ret
unmaskint:
mov dx,21h ;assume the master 8259.
mov cl,int_no
cmp cl,8 ;using the slave 8259 on an AT?
jb unmask_not_irq2
mov dx,0a1h ;go enable it on slave 8259
sub cl,8
unmask_not_irq2:
in al,dx ;enable interrupts on the master 8259.
mov ah,1 ;clear the bit.
shl ah,cl
not ah
and al,ah
out dx,al
ret
recv:
; Get whatever packets are on the board
;
mov bx,firstfd ; get addr of first FD in list
mov ds,base_addr ; base for board
assume ds:nothing
;
;
ckframe:
mov ax,[bx+FDSTAT] ; status word of frame
test ax,08000h ; frame written?
jnz okframe
jmp ru_start ; no, restore receiver if necessary
ptrupdate_j_1:
jmp ptrupdate
frame_bad_j_1:
jmp frame_bad
; we have a frame, read it in
;
okframe:
test ax,02000h ;check frame OK bit
jz frame_bad_j_1 ;bad, fix it.
mov si,[bx+FDPTR] ;get pointer to buffer descriptor
xor cx,cx ;start with zero bytes.
countbuf: ;es:di is already set to receive packet
mov dx,si ;save a copy of current BD ptr
mov ax,[si+BDSTAT] ;get status and count word for BD
test ax,04000h ;is count field there?
jz ptrupdate_j_1 ;no - we give up here.
add cl,al ;add the count into cx.
adc ch,0
mov si,[si+BDLINK] ;go to next BD in list
test ax,8000h ;is this the last frame?
je countbuf ;no - keep counting.
mov ax,0 ;we have the size needed.
push bx
push cx
push cx ;save the packet length.
mov ax,cs ;we need ds = code.
mov ds,ax
assume ds:code
mov es,base_addr ;get a pointer to their type.
mov di,es:[bx+FDPTR] ;get pointer to buffer descriptor
mov di,es:[di+BDPTR] ;get offset of data
add di,6+6 ;skip the ethernet addreses and
; point to the packet type.
mov bx,offset handles
find_type_1:
cmp [bx].in_use,0 ;is this handle in use?
je find_type_2 ;no - don't check the type.
mov ax,[bx].receiver.offs ;do they have a receiver?
or ax,[bx].receiver.segm
je find_type_2 ;no - they're not serious about it.
mov cx,[bx].packet_type_len ;compare the packets.
lea si,[bx].packet_type
or cx,cx ;in case cx is zero.
push di
repe cmpsb
pop di
je find_type_3 ;we've got it!
find_type_2:
add bx,(size per_handle) ;go to the next handle.
cmp bx,offset end_handles
jb find_type_1
pop cx ;we didn't find it -- discard it.
xor di,di ;"return" a null pointer.
mov es,di
jmp short find_type_4
find_type_3:
pop cx ; the packet_length
mov found_handle,bx ;remember what our handle was.
mov ax,0 ;allocate request.
call [bx].receiver ;ask the client for a buffer.
find_type_4:
pop cx
pop bx
mov ds,base_addr ;restore ds to the board.
assume ds:nothing
mov ax,es ;is this pointer null?
or ax,di
je ptrupdate ;yes - just free the frame.
push cx
push es ;remember where the buffer pointer is.
push di
mov si,[bx+FDPTR] ;get pointer to buffer descriptor
copybuf:
mov dx,si ;save a copy of current BD ptr
xor ch,ch ;200 bytes is largest this can be
mov cl,[si+BDSTAT] ;get count word for BD
mov si,[si+BDPTR] ;get offset of data
rep movsb ;copy the bytes from this packet segment
mov si,dx ;get back current BD ptr
test [si+BDSTAT],8000h ;check EOF bit
mov si,[si+BDLINK] ;go to next BD in list
jz copybuf ;not done, keep copying it.
pop si ;now give the frame to the client.
pop ds
assume ds:nothing
pop cx
push bx
mov bx,found_handle
mov ax,1 ;store request.
call code:[bx].receiver
pop bx
jmp short ptrupdate
frame_bad:
;
; we are done with the frame, do the list management
;
ptrupdate:
push cs
pop ds
assume ds:code
mov es,base_addr ; reload board segment
mov si,es:[bx+FDPTR] ; first BD in frame list
nextbd:
mov cx,es:[si+BDSTAT] ; count word for BD, EOF bit
test cx,08000h ; EOF bit, if set, save si in lastbd
jnz dolastbd
mov word ptr es:[si+BDSTAT],0 ; clear status word, EOF bit
cmp si,lastbd ; see if we are wrapping
jz dolastbd ; yes, just undo it
mov si,es:[si+BDLINK] ; follow link
jmp nextbd
dolastbd:
mov di,lastbd ; where end of BD list is now
mov lastbd,si ; store last known BD
mov word ptr es:[si+BDSIZE],08000h+200 ; end of list here
mov word ptr es:[si+BDSTAT],0 ; clear status word, EOF bit
; size field for not end of list
mov word ptr es:[di+BDSIZE],200 ; remove old end-of-list
;
; update the FD list flags, new end-of-list
;
mov word ptr es:[bx+FDEOL],08000h ; store new EOL
mov word ptr es:[bx+FDSTAT],0 ; clear status word for frame
mov di,lastfd ; get old end-of-list
mov word ptr es:[di+FDEOL],0 ; zero old one
mov lastfd,bx ; update stored pointer
mov si,es:[bx+FDLINK] ; where next fd is
mov firstfd,si ; store that info for next time
ru_start:
; re-start receive unit
;
; check to see if the receiver went off because of no resources
; and restart receiver if necessary
;
push cs
pop ds
mov es,base_addr
mov ax,es:[SCB+SSTAT] ; status word for SCB
and ax,070h ; receiver status
cmp al,020h ; receiver has no resources
jnz hasres
;
; setup lists for starting the RU on the chip
; we know that there isn't anything in the buffer that we want
;
mov bx,firstfd ; get first FD on free list (assume free)
mov word ptr es:[SCB+SRFA],bx ; put into SCB
mov si,lastbd ; pointer to a BD, end of chain
mov ax,word ptr es:[si+BDLINK] ; pointer to next BD
mov word ptr es:[bx+FDPTR],ax ; set to start of BDs
;
;
; Start the RU, doesn't need CB, only SCB parms.
; command, to start receiving again
;
mov word ptr es:[SCB+SSTAT],0 ; clear status word
mov word ptr es:[SCB+SCOM],010h ; start RU
mov dx,io_addr
inc dx ; issue CA
out dx,al
hasres:
ret
; end of resident code.
HT equ 09h
CR equ 0dh
LF equ 0ah
start_1:
mov dx,offset copyright_msg
mov ah,9
int 21h
mov si,offset phd_dioa+1
cmp byte ptr [si],CR ;end of line?
je usage_error
mov di,offset packet_int_no
call get_number
mov di,offset int_no
call get_number
mov di,offset io_addr
call get_number
mov di,offset base_addr
call get_number
mov ax,0f000h ;ROM segment
mov es,ax
cmp word ptr es:[0fffeh],0fch ;is this an AT?
jne not_at ;no.
inc is_at ;yes - remember that we've got an AT.
cmp int_no,2 ;map IRQ 2 to IRQ 9.
jne not_at
mov int_no,9
not_at:
call etopen ;init the ni5210.
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
mov ah,49h ;free our environment, because
mov es,phd_environ ; we won't need it after this.
int 21h
mov ah,31h ;terminate, stay resident.
mov dx,offset start_1+0fh
mov cl,4
shr dx,cl
int 21h
timeout_error:
mov dx,offset timeout_msg
jmp short error
no_5210_error:
mov dx,offset no_5210_msg
jmp short error
usage_error:
mov dx,offset usage_msg
error:
mov ah,9
int 21h
int 20h
copyright_msg db "NI5210 Packet Driver. Copyright 1988, Russell Nelson.",CR,LF
db "Derived from NCSA's NI5210 driver. Don't pretend that you",CR,LF
db "wrote it, and don't try to make money off of it.",CR,LF,'$'
usage_msg db "usage: ni5210 <packet_int_no> <int_no> <io_addr> <base_addr>",CR,LF,'$'
no_5210_msg db "No 5210 found at that address.",CR,LF,'$'
timeout_msg db "Timed out while initializing 5210.",CR,LF,'$'
;******************************************************************
; ETOPEN
; Initialize the Ethernet board, set receive type.
;
; usage: etopen(s,irq,address,io_addr)
; char s[6]; ethernet address
; int irq; (unused, we don't use no interrupts)
; int address base mem address
; int io_addr io base address
;
etopen:
;
; check for correct EPROM location
;
mov dx,io_addr ; i/o address
add dx,6
in al,dx
mov bl,al ; assemble pattern to check
inc dx
in al,dx
mov bh,al
cmp bx,05500h ; pattern known to be there in ROM
jz have_5210
jmp no_5210_error ;not there -- no 5210.
have_5210:
;
; Initialize MICOM 5210
;
; Install 8K SCP, we only use 8K bytes no matter what
;
mov es,base_addr ; set to base address
mov di,SCPTR
mov si,offset SCP ; get pre-set values
mov cx,5 ; 5 words
rep movsw ; install SCP
;
; Install 16K SCP, just in case they have that much.
;
mov si,offset SCP ; get pre-set values
mov di,SCPTR+2000h ; offset for 16K board
mov cx,5 ; 5 words
rep movsw ; install SCP
;
; Intermediate SCP
;
mov si,offset ISCP ; addr of pre-set values
mov di,ISCPTR
mov cx,4 ; 4 words
rep movsw ; install ISCP
;
; Turn off interrupts, I don't want them
;
mov dx,io_addr ; base for IO control
add dx,IOINTOF ; turn ints off
; out dx,al ; any value in ax
sub dx,IOINTOF ; return to base address = reset
out dx,al ; reset the chip
;
; Issue a CA to initialize the chip after reset
;
inc dx
out dx,al ; CA
;
; Disconnect from network
inc dx ; one greater than CA
out dx,al
;
; configure 82586
;
mov si,offset CBCONF ; configure command
mov di,CCBPTR ; where command will reside
mov cx,9
rep movsw ; copy to board
;
; issue the configure command
;
mov word ptr es:[SCB+SCOM],0100h ; do-command command
mov word ptr es:[SCB+SCBL],CCBPTR ; where conf command is
mov word ptr es:[SCB+SERRS],0 ; zero errs field
mov word ptr es:[SCB+SERRS+2],0 ; zero errs field
mov word ptr es:[SCB+SERRS+4],0 ; zero errs field
mov word ptr es:[SCB+SERRS+6],0 ; zero errs field
mov dx,io_addr
add dx,IOCA ; CA
out dx,al ; send it
xor cx,cx ; timeout
waitconf:
mov ax,word ptr es:[CCBPTR] ; get status word
test ax,08000h ; is command complete?
loopz waitconf
jnz confok
jmp timeout_error
confok:
;
; Next step, load our address into the board
; reuses the space that the configure command used, with different command
;
mov di,CCBPTR ; start of config command block
xor ax,ax
stosw ; zero status word for commmand
mov ax,8001h ; IA setup command + EL
stosw
xor ax,ax
dec ax
stosw ; set link value to -1 (unused)
; move my addr onto board location inside IA command
;
mov dx,io_addr ; Get our IO base address
mov cx,6
store_address_1:
in al,dx ; get a byte of the eprom address
stosb ; put it away
inc dx ; next register
loop store_address_1 ; go back for rest
;
; start the IA setup command
;
mov word ptr es:[SCB+SCOM],0100h ; do-command command
mov dx,io_addr
add dx,IOCA ; CA
out dx,al ; send it
xor cx,cx ; timeout
waitia:
mov ax,word ptr es:[CCBPTR] ; get status word
test ax,08000h ; is command complete?
loopz waitia
jnz iaok
jmp timeout_error
iaok:
;
; IA sent, setup all of the other data structures on the board
; start with xmit command descriptors
;
mov si,offset TCB ; template for xmit
mov di,TCBPTR ; where it goes on board
mov cx,12 ; copies CB and BD for xmit
rep movsw
;
; Set up frame and buffer descriptors, 30 each
;
mov cx,30 ; # of FDs
mov di,FDBASE ; base addr for FDs
fdloop:
xor ax,ax
mov bx,di ; save pointer
stosw ; clear status wd
stosw ; clear EL field
add bx,22 ; points to next one
mov es:[di],bx ; put in link ptr
inc di
inc di
dec ax
stosw ; clear BD ptr to -1
add di,14
loop fdloop
sub di,20 ; point back to last EL field
mov ax,08000h ; end of list
stosw ; put into last FD
sub di,4 ; back to beginning of last FD
mov lastfd,di ; save the pointer
mov word ptr es:[di+FDLINK],FDBASE ; make list circular,
; from last to first
mov ax,BDBASE ; first BD
mov word ptr es:[FDBASE+FDPTR],ax ; put it in the first FD frame
;
; now BDs
mov cx,30
mov di,BDBASE ; start of BD area
mov dx,BUFBASE ; start of buffer area
bdloop:
xor ax,ax
mov bx,di ; save pointer
stosw ; zero status field
add bx,10 ; point to next record
mov es:[di],bx ; put in link ptr
inc di
inc di
mov es:[di],dx ; address of buffer, lo part
inc di
inc di
stosw ; zero out high part
mov ax,200
stosw ; store length field
add dx,ax ; add in length of buffer, updates ptr
loop bdloop
sub di,2 ; back to last BD size field
mov ax,08000h+200 ; end of list + 200
stosw ; mark end of list
sub di,8 ; back to last BDLINK field
mov ax,BDBASE
stosw ; put link to beginning of list here
sub di,4 ; back to beginning of last BD
mov lastbd,di ; save pointer to end of list
;
; minor detail, but important
; Change SCB command block pointer to setup for xmit commands
; = only commands needed when operational
;
mov word ptr es:[SCB+SCBL],TCBPTR ; where xmit command is
;
; configure to connect to network
;
mov dx,io_addr
add dx,IOENA ; enable network
out dx,al ; any al value
;
; Start the RU, doesn't need CB, only SCB parms.
; command, to start receiving
;
mov word ptr es:[SCB],0 ; clear status word
mov word ptr es:[SCB+SRFA],FDBASE ; set to frame descriptors
mov word ptr es:[SCB+SCOM],010h ; start RU
mov dx,io_addr
inc dx ; issue CA
out dx,al
;
; Now reset CX, FR, CNA, and RNR so that we don't get a spurious interrupt.
;
store_ack_1:
cmp word ptr es:[SCB+SCOM],0 ;are we done yet?
jne store_ack_1 ;no -- keep waiting.
mov ax,es:[SCB+SSTAT] ;get the status.
and ax,0f000h ;isolate the ACK bits.
mov es:[SCB+SCOM],ax ;make a command to
;acknowledge the interrupt.
out dx,al ;issue CA (dx still set from above)
;
; Now hook in our interrupt
;
mov ah,35h ;get the old interrupt into es:bx
mov al,int_no
add al,8
cmp al,8+8 ;is it a slave 8259 interrupt?
jb set_recv_isr ;no.
add al,70h - 8 - 8 ;map it to the real interrupt.
set_recv_isr:
int 21h
mov their_recv_isr.offs,bx
mov their_recv_isr.segm,es
mov ah,25h ;now set our recv interrupt.
mov dx,offset recv_isr
int 21h
;
; Now enable interrupts
;
call unmaskint
ret
get_number:
;get a hex number from [si], skipping leading blanks.
get_number_0:
lodsb ;skip blanks.
cmp al,' '
je get_number_0
cmp al,HT
je get_number_0
dec si
cmp al,CR ;is there really a number here?
je get_number_3
xor ah,ah ;get a hex number.
xor cx,cx
get_number_1:
lodsb
call get_digit
jc get_number_2
shl cx,1
shl cx,1
shl cx,1
shl cx,1
add cx,ax
jmp get_number_1
get_number_2:
dec si
mov [di],cx ;store the parsed number.
clc
ret
get_number_3:
stc
ret
get_digit:
;enter with al = character
;return nc, al=digit, or cy if not a digit.
cmp al,'0' ;decimal digit?
jb get_digit_1 ;no.
cmp al,'9' ;. .?
ja get_digit_2 ;no.
sub al,'0'
clc
ret
get_digit_2:
or al,20h
cmp al,'a' ;hex digit?
jb get_digit_1
cmp al,'f' ;hex digit?
ja get_digit_1
sub al,'a'-10
clc
ret
get_digit_1:
stc
ret
comment \
/* Install hardware interrupt handler.
* Takes IRQ numbers from 0-7 (0-15 on AT) and maps to actual 8086/286 vectors
* Note that bus line IRQ2 maps to IRQ9 on the AT
*/
setirq(irq,handler)
unsigned irq;
void (*handler)();
{
/* Set interrupt vector */
if(irq < 8){
setvect(8+irq,handler);
} else if(irq < 16){
isat = 1;
setvect(0x70 + irq - 8,handler);
} else {
return -1;
}
return 0;
}
/* Return pointer to hardware interrupt handler.
* Takes IRQ numbers from 0-7 (0-15 on AT) and maps to actual 8086/286 vectors
*/
void
(*getirq(irq))()
unsigned int irq;
{
void (*getvect())();
/* Set interrupt vector */
if(irq < 8){
return getvect(8+irq);
} else if(irq < 16){
return getvect(0x70 + irq - 8);
} else {
return NULLVFP;
}
}
/* Disable hardware interrupt */
maskoff(irq)
unsigned irq;
{
if(irq < 8){
setbit(0x21,(char)(1<<irq));
} else if(irq < 16){
irq -= 8;
setbit(0xa1,(char)(1<<irq));
} else {
return -1;
}
return 0;
}
/* Enable hardware interrupt */
maskon(irq)
unsigned irq;
{
if(irq < 8){
clrbit(0x21,(char)(1<<irq));
} else if(irq < 16){
irq -= 8;
clrbit(0xa1,(char)(1<<irq));
} else {
return -1;
}
return 0;
}
/* Return 1 if specified interrupt is enabled, 0 if not, -1 if invalid */
getmask(irq)
unsigned irq;
{
if(irq < 8)
return (inportb(0x21) & (1 << irq)) ? 0 : 1;
else if(irq < 16){
irq -= 8;
return (inportb(0xa1) & (1 << irq)) ? 0 : 1;
} else
return -1;
}
\
code ends
end start
--
nelson@clutx.bitnet, nelson@clutx.clarkson.edu, uunet!clutx.clarkson.edu!nelson