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