Changes to pctools.c ----------------------------------------------------------------- extern int E4etopen(),E4getaddr(),E4setaddr(),E4recv(),E4xmit(),E4etupdate(); extern int E4etclose(),E4etdma(); ----------------------------------------- and these lines were add to "netconfig". ----------------------------------------- else if (!strncmp(s,"3c505",5) || !strncmp(s,"505",3)) { etopen = E4etopen; xmit = E4xmit; recv = E4recv; getaddr = E4getaddr; etupdate = E4etupdate; etclose = E4etclose; } ----------------------------------------------------------------- ;-------net505.asm------ page 55,132 title Driver routines for 3C505 Ethernet board ; ; Driver routines for 3C505 Ethernet board ; ; Bruce Orchard ; Waisman Center on Mental Retardation and Human Development ; University of Wisconsin-Madison ; ; April 7, 1988 ; 2/9/89 Changed to make compatible with Telnet 2.2 - Warren Van Houten ; 3/17/89 Changed to make compatible with msc 5.0 ; 7/14/89 Fixed getting ether address (far/near mismatch) - krus@diku.dk ; ;Microsoft EQU 1 ;Lattice EQU 1 ifndef Microsoft ifndef Lattice if2 %out %out ERROR: You have to specify "/DMicrosoft" OR "/DLattice" on the %out MASM command line to determine the type of assembly. %out endif end endif endif ifdef Microsoft x equ 6 ; Offset to parameters (skip bp, ip, cs) else NAME NET INCLUDE DOS.MAC SETX endif ; 3C505 control register bit definitions EC_ATTENTION equ 0200q ; Attention EC_FLUSH_DATA equ 0100q ; Flush data register EC_DMA_ENABLE equ 0040q ; DMA enable EC_TO_HOST equ 0020q ; Direction: To host EC_TERMINAL_COUNT_ENABLE equ 0010q ; Terminal count interrupt enable EC_COMMAND_ENABLE equ 0004q ; Command intterupt enable EC_FLAG2 equ 0002q ; Host status flag 2 EC_FLAG1 equ 0001q ; Host status flag 1 ; 3C505 status register bit definitions ES_DATA_READY equ 0200q ; Data register ready ES_HOST_COMMAND_EMPTY equ 0100q ; Host command register empty ES_ADAPTER_COMMAND_FULL equ 0040q ; Adapter command register full ES_TO_HOST equ 0020q ; Direction: To host ES_DMA_DONE equ 0010q ; DMA done ES_FLAG3 equ 0004q ; Adapter status flag 3 ES_FLAG2 equ 0002q ; Adapter status flag 2 ES_FLAG1 equ 0001q ; Adapter status flag 1 ; 3C505 aux DMA control register bit definitions EA_BURST equ 0001q ; Burst mode DMA ; 8259 equates IOCWR1 equ 20h ; Command register address 1 IMR1 equ 21h ; Interrupt mask register address 1 VEC1 equ 8 ; First vector for 8259 1 IOCWR2 equ 0a0h ; Command register address 2 IMR2 equ 0a1h ; Interrupt mask register address 2 VEC2 equ 70h ; First vector for 8259 2 EOI equ 60h ; End of interrupt command ; Time out values (1/18 second ticks) SECOND EQU 18 ; Ticks in 1 second RESDEL EQU 3 ; Delay before checking reset status RESTO EQU 15*SECOND ; Time out for reset completion CMDBTO EQU 3 ; Time out for command byte to be accepted CMDCTO EQU 3 ; Time out for command to be accepted RETRYDELAY EQU 3 ; Command retry delay RCMDTO EQU 3 ; Incoming command time out RESPTO EQU 3 ; Response time out ; BIOS data area bios_data segment at 40h org 06Ch timer_low dw ? ; BIOS timer counter timer_high dw ? timer_ofl dw ? bios_data ends ifdef Microsoft DGROUP group _DATA _DATA segment public 'DATA' ; PUBLIC STAT,BUFPT,BUFORG,BUFEND,BUFREAD,BUFBIG,BUFLIM,OFFS ; ; The pointers below are actually DWORDs but we access them two ; bytes at a time. ; ; STAT change to RSTAT because of name clash with MSC library routine ; EXTRN _RSTAT:BYTE ; last status from read EXTRN _BUFPT:WORD ; current buffer pointer EXTRN _BUFORG:WORD ; pointer to beginning of buffer EXTRN _BUFEND:WORD ; pointer to end of buffer EXTRN _BUFREAD:WORD ; pointer to where program is reading EXTRN _BUFBIG:WORD ; integer, how many bytes we have EXTRN _BUFLIM:WORD ; integer, max bytes we can have public _c5_droptot public _c5_wrapct public _c5_nocmd public _c5_cmdito _c5_droptot dw 0 ; total buffers dropped _c5_wrapct dw 0 ; buffer wraparounds _c5_nocmd dw 0 ; interrupt with command register empty _c5_cmdito dw 0 ; incoming command timeout assume DS:seg _c5_droptot else DSEG ; EXTRN RSTAT:BYTE ; last status from read EXTRN BUFPT:WORD ; current buffer pointer EXTRN BUFORG:WORD ; pointer to beginning of buffer EXTRN BUFEND:WORD ; pointer to end of buffer EXTRN BUFREAD:WORD ; pointer to where program is reading EXTRN BUFBIG:WORD ; integer, how many bytes we have EXTRN BUFLIM:WORD ; integer, max bytes we can have public c5_droptot public c5_wrapct public c5_nocmd public c5_cmdito c5_droptot dw 0 ; total buffers dropped c5_wrapct dw 0 ; buffer wraparounds c5_nocmd dw 0 ; interrupt with command register empty c5_cmdito dw 0 ; incoming command timeout assume DS:seg c5_droptot endif ksegbios dw ? ; bios data segment ksegdata dw ? ; DATA segment - might be different ; from seg c5_drop kseghere dw ? ; segment for the begining of this data ; group. irq dw ? ; Interrupt request level ioadr dw ? ; IO address dma dw ? ; DMA request level ecommand dw ? ; 3C505 command address estatus dw ? ; 3C505 status address edata dw ? ; 3C505 data address econtrol dw ? ; 3C505 control address eauxdma dw ? ; 3C505 aux DMA control address eoi1 dw ? ; End of interrupt command for 8259 1 eoi2 dw ? ; End of interrupt command for 8259 2 imr dw ? ; Interrupt mask register address vec dw ? ; Vector number oldioff dw ? ; Original interrupt offset oldiseg dw ? ; Original interrupt segment pcblen dw ? ; PCB length pcbad dw ? ; PCB address cmdlen dw ? ; Incoming command length rbufct dw ? ; receive buffer counter rdropnew dw ? ; receive buffers just dropped newstart dw ? ; number of receives to start savemask db ? ; Original interrupt mask maskbit db ? ; Interrupt mask bit lastcon db ? ; Last control to board CBSH equ 50 ; half of incoming command buffer CBS equ CBSH*2 ; incoming command buffer size icmdb db CBS dup (?) ; Incoming command buffer icmd db CBSH dup (?) ; incoming command fconc db 0 ; Flag: Configure 82586 fgeth db 0 ; Flag: Get Ethernet address fseth db 0 ; Flag: Set Ethernet address fxmit db 0 ; Flag: Transmit packet fadin db 0 ; Flag: Adapter info fstat db 0 ; Flag: Statistics even cconc db 02h ; Command: Configure 82586 db 2 ; -- 2 more bytes dw 1 ; -- receive broadcasts rconc db 2 dup (?) ; Response: Configure 82586 rconc_st dw ? ; -- status cgeth db 03h ; Command: Get Ethernet address db 00 ; 0 more bytes rgeth db 2 dup (?) ; Response: Get Ethernet address rgeth_ad db 6 dup (?) ; -- address cseth db 10h ; Command: Set Ethernet address db 06 ; 6 more bytes cseth_ad db 6 dup (?) ; -- address rseth db 2 dup (?) ; Response: Set Ethernet address rseth_status dw ? ; -- status cxmit db 09h ; Command: Transmit packet db 06 ; 6 more bytes cx_offset dw ? ; -- buffer offset cx_segment dw ? ; -- buffer segment cx_length dw ? ; -- buffer length rxmit db 2 dup (?) ; Response: Transmit packet rx_offset dw ? ; -- buffer offset rx_segment dw ? ; -- buffer segment rx_status dw ? ; -- completion status rx_cstatus dw ? ; -- 82586 status cr db 08h ; Command: Receive db 08 ; 8 more bytes cr_offset dw ? ; -- buffer offset cr_segment dw ? ; -- buffer segment cr_length dw ? ; -- buffer length cr_timeout dw ? ; -- timeout rr db 2 dup (?) ; Response: Receive rr_offset dw ? ; -- buffer offset rr_segment dw ? ; -- buffer segment rr_dmalen dw ? ; -- bytes to dma rr_length dw ? ; -- actual length rr_status dw ? ; -- completion status rr_rstatus dw ? ; -- 82586 receive status rr_time dd ? ; -- time tag cadin db 11h ; Command: Adapter info db 0 ; 0 more bytes radin db 2 dup (?) ; Response: Adapter info ra_rom dw ? ; -- ROM version ra_cs dw ? ; -- ROM checksum ra_mem dw ? ; -- RAM memory size ra_freeoff dw ? ; -- Free memory offset ra_freeseg dw ? ; -- Free memory segment cstat db 0ah ; Command: Network statistics db 0 ; 0 more bytes rstat db 2 dup (?) ; Response: Network statistics rs_rec dd ? ; -- Packets received rs_tran dd ? ; -- Packets sent rs_crc dw ? ; -- CRC error counter rs_align dw ? ; -- Alignment error counter rs_nors dw ? ; -- No resources error counter rs_or dw ? ; -- Overrun error counter TURNOFF db 08h TURNON db 0F7h ifdef Microsoft _DATA ends else ENDDS endif ; ; ; Macros for in and out ; MOUT MACRO REG,STUFF ; one byte to the given I/O register MOV DX, REG MOV AL, STUFF OUT DX, AL ENDM ; MOUTW MACRO REG, LO, HI ; two bytes to the I/O double port MOV DX, REG MOV AL, LO OUT DX, AL INC DX MOV AL, HI OUT DX, AL ENDM ; MIN MACRO REG ; get one byte to al MOV DX, REG IN AL, DX ENDM ; ; ; ; The subroutines to call from C ; ifdef Microsoft _TEXT segment public 'CODE' assume CS:_TEXT, ES:bios_data PUBLIC _E4RECV, _E4ETOPEN, _E4ETCLOSE, _E4GETADDR PUBLIC _E4SETADDR, _E4XMIT, _E4ETUPDATE, _E4ETDMA public _c5_get_adapter_info, _c5_get_statistics else PSEG PUBLIC E4RECV, E4ETOPEN, E4ETCLOS, E4GETADD PUBLIC E4SETADD, E4XMIT, E4ETUPDA, E4ETDMA public c5getada, c5getsta endif subttl _E4etopen: Initialize board page + ;****************************************************************** ; ETOPEN ; Initialize the Ethernet board, set receive type. ; ; usage: etopen(s,irq,addr,ioaddr) ; char s[6]; ethernet address ; int irq,addr,ioaddr; ; interrupt number, base mem address (unused) and ; i/o address to use ; ; _c5_init ; Initialize the board, etc. ; ; Arguments: a_ethadr equ x ; ethernet address a_irq equ a_ethadr+4 ; Interrupt request level (int) a_seg equ a_irq+2 ; Shared segment address (int) a_ioadr equ a_seg+2 ; IO address (int) ; ifdef Microsoft _E4etopen proc far else E4ETOPEN PROC FAR endif push bp ; save bp mov bp,sp ; bp -> return, parameters push ds ; save ds push es ; save es push si ; save si push di ; save di ifdef Microsoft mov ax, seg _c5_droptot ; ax -> data segment else mov ax, seg c5_droptot ; ax -> data segment endif mov ds, ax ; ds -> data segment mov kseghere, ax mov ax, seg _DATA mov ksegdata, ax mov ax, seg bios_data mov ksegbios, ax mov es, ksegbios ; es -> bios data segment mov ax, [bp+a_irq] ; interrupt level -> ax mov irq, ax ; save interrupt level mov ax, [bp+a_ioadr] ; IO address -> ax mov ioadr, ax ; save IO address mov ax, ioadr ; 3C505 IO address -> ax mov ecommand, ax ; save command address add ax, 2 ; status address -> ax mov estatus, ax ; save status address mov eauxdma, ax ; save aux dma address add ax, 2 ; data address -> ax mov edata, ax ; save data address add ax, 2 ; control address -> ax mov econtrol, ax ; save control address cli ; disable interrupts ; Set up the 8259 interrupt controller chip to what the 3c505 board is ; set at. mov ax, irq ; interrupt level -> ax cmp ax, 8 ; which 8259? jge o_1 ; 8259 2 mov bx, ax ; irq -> bx or ax, EOI ; 8259 1: make first EOI command mov eoi1, ax ; save first EOI command mov eoi2, 0 ; no second EOI command mov imr, IMR1 ; mask is in IMR1 add bx, VEC1 ; interrupt vector number -> bx mov vec, bx ; save vector number jmp o_2 ; skip 8259 2 case ; 8259 2: just keep low 8 bits of interrupt number o_1: and ax, 07Q mov bx, ax ; interrupt on 8259 -> bx or ax, EOI ; put in EOI command mov eoi2, ax ; save second EOI command mov eoi1, EOI+2 ; first EOI releases second mov imr, IMR2 ; mask is in IMR2 add bx, VEC2 ; interrupt vector number -> bx mov vec, bx ; save vector number mov dx, IOCWR2 ; dx -> command register 2 out dx, al ; do EOI 2 just in case o_2: mov ax, eoi1 ; EOI 1 command -> ax mov dx, IOCWR1 ; dx -> command register 1 out dx, al ; do EOI 1 just in case mov ax, vec ; vector number -> ax ; Install the interrupt handler. call IINST ; Save the old interrupt mask of the 8259 chip and then turn it on. mov cx, irq ; interrupt level -> cx and cx, 07q ; just keep level on 8259 mov ax, 1 ; 1 -> ax shl ax, cl ; make interrupt mask mov maskbit, al ; save mask bit mov dx, imr ; mask register address -> dx in al, dx ; get old mask mov savemask, al ; save mask mov bl, maskbit ; our interrupt bit -> bl not bl ; want to unmask it and al, bl ; combine with other interrupts out dx, al ; unmask our interrupt sti ; turn interrupts on ; Reset the 3c505 board - this takes about 15-20 seconds. mov al, EC_ATTENTION OR EC_FLUSH_DATA; Master reset command -> al mov dx, econtrol ; dx -> control register out dx, al ; do reset mov ax, timer_low ; current timer -> ax add ax, RESDEL ; + time to wait wlp1: cmp ax, timer_low ; compare to current time ja wlp1 ; wait for reset to propagate mov al, EC_COMMAND_ENABLE ; command interrupt enable -> al mov lastcon, al ; save last command out dx, al ; release reset mov ax, timer_low ; current timer -> ax add ax, RESDEL ; + time to wait wlp2: cmp ax, timer_low ; compare to current time ja wlp2 ; wait for CPU to start reset mov bx, timer_low ; current timer -> ax add bx, RESTO ; + time out wlp3: call getstat ; get status and ax, ES_FLAG1 OR ES_FLAG2 ; just keep flags cmp ax, ES_FLAG1 OR ES_FLAG2 ; both on? jne resdone ; no: reset completed cmp bx, timer_low ; have we waited too long? ja wlp3 ; no jmp openfail ; yes: open failed resdone: ; Set up the receive buffers. mov rbufct, 0 ; clear buffer counter mov cr_length, 1600 ; buffer length: 1600 irb1: mov ax, rbufct ; buffer counter -> ax mov cr_offset, ax ; use buffer number for offset inc ax ; count buffer mov rbufct, ax ; store buffer number mov ax, 10 ; pcb length -> ax mov si, offset cr ; si -> request call outpcb ; pass pcb mov ax, rbufct ; buffer counter -> ax cmp ax, 10 ; start 10 receives jl irb1 ; loop if more buffers ; We use the same character string pointer for both of the ; next two calls, so we don't adjust the stack pointer ; until we're done. mov ax, [bp+a_ethadr+2] push ax mov ax, [bp+a_ethadr] push ax ; Get the hardware ethernet address. call far ptr get_eth_addr or ax, ax jz callset add sp,4 jmp openfail callset: ; Set the 3c505 board to use that address call far ptr _e4setaddr add sp, 4 or ax, ax jnz openfail ; Tell the 3c505 board to start receiving packets. CALL E4OPEN ; ax = 0 e4open OK, ax = -1 then e4open failed jmp openx ; go return openfail: mov ax, -1 ; -1 -> ax, fail openx: pop di ; restore di pop si ; restore si pop es ; restore es pop ds ; restore ds pop bp ; restore bp ret ifdef Microsoft _E4etopen endp else E4ETOPEN ENDP endif subttl _E4etDMA: DMA request level. page + ;****************************************************************** ; ETDMA ; Initialize the DMA request level. ; ; not needed at this time ; a_dma equ a_ioadr+2 ; DMA request level ; ifdef Microsoft _E4etdma proc far else E4ETDMA PROC FAR endif push bp ; save bp mov bp,sp ; bp -> return, parameters push ds ; save ds ifdef Microsoft mov ax, seg _c5_droptot ; ax -> data segment else mov ax, seg c5_droptot ; ax -> data segment endif mov ds, ax mov ax, seg bios_data mov ksegbios, ax mov ax, [bp+a_dma] ; DMA level -> ax mov dma, ax ; save IO address xor ax, ax pop ds ; restore ds pop bp ; restore bp ret ifdef Microsoft _E4etdma endp else E4ETDMA ENDP endif subttl open: Open page + ; This routine turns tells the 3c505 board to start receiving packets. e4open proc near push bp ; save bp mov bp, sp ; bp -> return, parameters push ds ; save ds push es ; save es push si ; save si push di ; save di ifdef Microsoft mov ax, seg _c5_droptot ; ax -> data segment else mov ax, seg c5_droptot ; ax -> data segment endif mov ds, ax ; ds -> data segment mov es, ksegbios ; es -> bios data segment mov si, offset cconc ; si -> configure 82586 request mov ax, 4 ; request length -> ax mov fconc, 0 ; clear response received flag call outpcb ; send the pcb mov ax, timer_low ; current time -> ax add ax, RESTO ; + wait time op_1: test fconc, 0ffh ; answered yet? jnz op_2 ; yes cmp ax, timer_low ; expired? ja op_1 ; no mov ax, -1 ; return fail jmp op_x ; go return op_2: mov ax, 0 ; 0 -> ax, success op_x: pop di ; restore di pop si ; restore si pop es ; restore es pop ds ; restore ds pop bp ; restore bp ret ; Just return E4open endp subttl _E4etclose: Close board page + ;*********************************************************************** ; ETCLOSE ; shut it down, remove the interrupt handler ; ; usage: etclose(); ; ; ifdef Microsoft _E4ETCLOSE PROC FAR else E4ETCLOS PROC FAR endif CLI ; ; ; mask out IRQ on interrupt controller ; push ds ifdef Microsoft mov ax, seg _c5_droptot ; ax -> data segment else mov ax, seg c5_droptot ; ax -> data segment endif mov ds, ax MIN imr ; get current mask OR AL, TURNOFF ; force that bit on OUT DX, AL ; send it back to controller STI CALL DEINST ; restore old interrupt handler MOV BL, savemask ; get back saved setting of irq NOT BL ; flip it CLI MIN imr AND AL, BL ; restore setting of that bit OUT DX, AL STI xor ax, ax pop ds RET ifdef Microsoft _E4ETCLOSE ENDP else E4ETCLOS ENDP endif subttl _c5_getaddr: Get Ethernet address page + ;******************************************************************* ; GETADDR ; get the Ethernet address off of the board (This gets called ; before E4etopen) ; ; usage: getaddr(s,address,ioaddr); ; char s[6]; will get six bytes from the PROM ; int address; (unused here) ; int ioaddr; mem address and ioaddress to use ; ; _E4getaddr ; Get Ethernet address ; ; Arguments: a_eadr equ x ; Ethernet address (far char *) ag_ioadr equ a_eadr + 6 ifdef Microsoft _E4getaddr proc far else E4GETADD PROC FAR endif ret ifdef Microsoft _E4getaddr endp else E4GETADD ENDP endif get_eth_addr proc far ; ; This was the old getaddr routine. But, to advoid changing the Telnet ; source code i moved it here. The interrupt handler has to be installed ; before this routine is used. ; push bp mov bp, sp ; bp -> return, parameters push ds push es push si push di ifdef Microsoft mov ax, seg _c5_droptot ; ax -> data segment else mov ax, seg c5_droptot ; ax -> data segment endif mov ds, ax ; ds -> data segment mov es, ksegbios ; es -> bios data segment mov si, offset cgeth ; si -> request ethernet address mov ax, 2 ; request length -> ax mov fgeth, 0 ; clear response received flag call outpcb ; send the pcb mov ax, timer_low ; current time -> ax add ax, RESTO ; + wait time ga_1: test fgeth, 0ffh ; answered yet? jnz ga_2 ; yes cmp ax, timer_low ; expired? ja ga_1 ; no; mov ax, -1 ; return fail jmp ga_x ; go return ga_2: cld mov di, [bp+a_eadr] ; di -> destination offset push es ; save es mov es, [bp+a_eadr+2] ; es -> destination segment mov si, offset rgeth_ad ; si -> response mov cx, 6 ; address length -> cx rep movsb ; return address pop es mov ax, 0 ; 0 -> ax, success ga_x: pop di pop si pop es pop ds pop bp ret get_eth_addr endp subttl _c5_setaddr: Set Ethernet address page + ;****************************************************************** ; SETADDR ; set the Ethernet address on the board to 6 byte ID code ; ; usage: setaddr(s,basea,ioa); ; char s[6]; ethernet address to use ; int basea; shared memory base address (unused) ; int ioa; io address for board (unused) ; ; _c5_setaddr ; _E4setaddr ; Set Ethernet address ; ; Arguments: a_eadr equ x ; Ethernet address (far char *) ifdef Microsoft _E4setaddr proc far else E4SETADD PROC FAR endif push bp mov bp, sp ; bp -> return, parameters push ds push es push si push di ifdef Microsoft mov ax, seg _c5_droptot ; ax -> data segment else mov ax, seg c5_droptot ; ax -> data segment endif mov ds, ax ; ds -> data segment mov es, ksegbios ; es -> bios data segment push es mov di, offset cseth_ad ; si -> command mov ax, seg cseth_ad ; ax -> command segment mov es, ax ; es -> command segment push ds mov si, [bp+a_eadr] ; di -> destination offset mov ds, [bp+a_eadr+2] ; es -> destination segment mov cx, 6 ; address length -> cx rep movsb ; return address pop ds pop es mov si, offset cseth ; si -> request ethernet address mov ax, 8 ; request length -> ax mov fseth, 0 ; clear response received flag call outpcb ; send the pcb mov ax, timer_low ; current time -> ax add ax, RESTO ; + wait time sa_1: test fgeth, 0ffh ; answered yet? jnz sa_2 ; yes cmp ax, timer_low ; expired? ja sa_1 ; no mov ax, -1 ; return fail jmp sa_x ; go return sa_2: mov ax,0 ; 0 -> ax, success sa_x: pop di pop si pop es pop ds pop bp ret ifdef Microsoft _E4setaddr endp else E4SETADD ENDP endif subttl _c5_get_adapter_info: Get adapter information page + ; I don't have the information to use this routine ; ; _c5_get_adapter_info ; Get adapter information ; ; Arguments: a_adin equ x ; Adapter information (far struct r_adapter_info *) _c5_get_adapter_info proc far push bp ; save bp mov bp, sp ; bp -> return, parameters push ds ; save ds push es ; save es push si ; save si push di ; save di ifdef Microsoft mov ax, seg _c5_droptot ; ax -> data segment else mov ax, seg c5_droptot ; ax -> data segment endif mov ds, ax ; ds -> data segment mov es, ksegbios ; es -> bios data segment mov si, offset cadin ; si -> request adapter information mov ax, 2 ; request length -> ax mov fadin, 0 ; clear response received flag call outpcb ; send the pcb mov ax, timer_low ; current time -> ax add ax, RESTO ; + wait time ai_1: test fadin, 0ffh ; answered yet? jnz ai_2 ; yes cmp ax, timer_low ; expired? ja ai_1 ; no mov ax, -1 ; return fail jmp ai_x ; go return ai_2: mov di, [bp+a_adin] ; di -> destination offset push es ; save es mov es, [bp+a_adin+2] ; es -> destination segment mov si, offset radin ; si -> response mov cx, 12 ; address length -> cx rep movsb ; return address pop es ; restore es mov ax, 0 ; 0 -> ax, success ai_x: pop di ; restore di pop si ; restore si pop es ; restore es pop ds ; restore ds pop bp ; restore bp ret ; return _c5_get_adapter_info endp subttl _c5_get_statistics: Get statistics page + ; I don't have the information to use this routine ; ; _c5_get_statistics ; Get network statistics ; ; Arguments: a_stat equ x ; Statistics (far struct r_statistics *) _c5_get_statistics proc far push bp ; save bp mov bp, sp ; bp -> return, parameters push ds ; save ds push es ; save es push si ; save si push di ; save di ifdef Microsoft mov ax, seg _c5_droptot ; ax -> data segment else mov ax, seg c5_droptot ; ax -> data segment endif mov ds, ax ; ds -> data segment mov es, ksegbios ; es -> bios data segment mov si, offset cstat ; si -> request statistics mov ax, 2 ; request length -> ax mov fstat, 0 ; clear response received flag call outpcb ; send the pcb mov ax, timer_low ; current time -> ax add ax, RESTO ; + wait time st_1: test fstat, 0ffh ; answered yet? jnz st_2 ; yes cmp ax, timer_low ; expired? ja st_1 ; no mov ax, -1 ; return fail jmp st_x ; go return st_2: mov di, [bp+a_stat] ; di -> destination offset push es ; save es mov es, [bp+a_stat+2] ; es -> destination segment mov si, offset rstat ; si -> response mov cx, 18 ; statistics length -> cx rep movsb ; return address pop es ; restore es mov ax, 0 ; 0 -> ax, success st_x: pop di ; restore di pop si ; restore si pop es ; restore es pop ds ; restore ds pop bp ; restore bp ret ; return _c5_get_statistics endp subttl _E4recv: Receive message page + ;************************************************************************ ; Receive ; This is a CPU hook for boards that must be polled before we can ; deliver packets into the receive buffer. (i.e. no interrupts used) ; ; The 3COM 3C505 version uses interrupts, so this routine is a NOP ; for this board. ; ; usage: recv(); ; ifdef Microsoft _E4RECV PROC FAR else E4RECV PROC FAR endif RET ; for compatibility with other drivers ifdef Microsoft _E4RECV ENDP else E4RECV ENDP endif subttl _c5_xmit: Transmit message page + ;************************************************************************ ; XMIT ; send a packet to Ethernet ; Is not interrupt driven, just call it when you need it. ; ; usage: xmit(packet,count) ; char *packet; ; int count; ; ; Takes a packet raw, Ethernet packets start with destination address, ; and puts it out onto the wire. Count is the length of packet < 2048 ; ; checks for packets under the Ethernet size limit of 60 and handles them ; ; _c5_xmit ; _E4xmit ; Transmit message ; ; Arguments: a_xaddr equ x ; Pointer to buffer (far char *--must beeven) a_xlength equ a_xaddr+4 ; Length in bytes (int) ifdef Microsoft _E4xmit proc far else E4XMIT PROC FAR endif push bp mov bp, sp ; bp -> return, parameters push ds push es push si push di ifdef Microsoft mov ax, seg _c5_droptot ; ax -> data segment else mov ax, seg c5_droptot ; ax -> data segment endif mov ds, ax ; ds -> data segment mov es, ksegbios ; es -> bios data segment mov ax, [bp+a_xaddr] ; ax -> buffer offset mov cx_offset, ax ; put in request mov ax, [bp+a_xaddr+2] ; ax -> buffer segment mov cx_segment, ax ; put in request mov ax, [bp+a_xlength] ; message length -> ax cmp ax, 60 ; is buffer too short? jg xm_4 ; no mov ax, 60 ; yes: pad with garbage xm_4: inc ax ; round up sar ax, 1 ; divide by 2 shl ax, 1 ; multiply by 2 mov cx_length, ax ; put in request mov fxmit, 0 ; clear transmit done flag mov si, offset cxmit ; si -> request mov ax, 8 ; request length -> ax call outpcb ; send command mov bx, estatus ; bx -> status register mov dx, edata ; dx -> data register mov cx, cx_length ; length -> cx sar cx, 1 ; convert to words mov si, cx_offset ; offset -> si push ds mov ds, cx_segment ; segment -> ds xm_1: lodsw ; next word -> ax out dx, ax ; output it xchg dx, bx ; dx -> status register xm_2: in al, dx ; get status test al, ES_DATA_READY ; ready for next word? jz xm_2 ; no xchg dx, bx ; dx -> data register dec cx ; count word jnz xm_1 ; loop through buffer pop ds xm_3: test fxmit, 0ffh ; has transmit completed? jz xm_3 ; no mov ax, rx_status ; return status xm_x: pop di pop si pop es pop ds pop bp ret ifdef Microsoft _E4xmit endp else E4XMIT ENDP endif subttl _c5_update: Update receive buffer pointer page + ;************************************************************************* ; ETUPDATE ; update pointers and/or restart receiver when read routine has ; already removed the current packet ; ; usage: etupdate(); ; ; _c5_update ; _E4etupdate ; Update receive buffer pointer ; ;************ needs much more work to use with Lattice C ; ifdef Microsoft _E4etupdate proc far else E4ETUPDA PROC FAR endif push bp mov bp, sp ; bp -> return, parameters push ds push es push si push di push cx ifdef Microsoft mov ax, seg _c5_droptot ; ax -> data segment else mov ax, seg c5_droptot ; ax -> data segment endif mov ds, ax mov es, ksegbios ; es -> bios data segment push es ; save es mov ds, ksegdata les di, dword ptr _bufread ; es/di -> start of message mov ax, es:[di] ; message length -> ax pop es add di, ax ; advance by message length add di, 2 ; + 2 for message length cmp di, _bufend ; passed end? jb up_1 ; no mov di, _buforg ; yes: start over inc _c5_wrapct ; count wraparound up_1: mov _bufread, di ; store pointer cli ; protect bufbig mov bx, _bufbig ; amount of buffer in use -> bx sub bx, ax ; - size just released sub bx, 2 ; - 2 for message size mov _bufbig, bx ; store size left sti ; release bufbig cli ; protect drop count mov ax, rdropnew ; messages dropped recently -> ax mov rdropnew, 0 ; clear drop count sti ; release interrupts inc ax ; + 1 for buffer released mov ax, newstart ; = number to start up_2: mov ax, rbufct ; buffer counter -> ax mov cr_offset, ax ; use buffer number for offset inc ax ; count buffer mov rbufct, ax ; store buffer number mov ax, 10 ; pcb length -> ax mov si, offset cr ; si -> request call outpcb ; pass pcb dec newstart ; count receive started jg up_2 ; loop if more buffers up_x: pop cx pop di pop si pop es pop ds pop bp ret ifdef Microsoft _E4etupdate endp else E4ETUPDA ENDP endif subttl getstat: Get board status page + ; Get board status, waiting for it to become stable ; ; Return: ; al = status getstat proc near push bx push dx mov dx, estatus ; dx -> status register gs_1: in al, dx ; status -> al mov bl, al ; status -> bl in al, dx ; status -> al cmp al, bl ; same both times? jne gs_1 ; No: try again pop dx pop bx ret getstat endp subttl outpcb: Send PCB to board page + ; Send pcb to board, retry until accepted ; ; Entry: ; ax = number of bytes in pcb ; si = address of pcb outpcb proc near mov pcblen, ax ; save pcb length mov pcbad, si ; save pcb address ob_1: mov cx, pcblen ; length -> cx mov si, pcbad ; address -> si cli ; Protect last command mov al, lastcon ; last command -> ax and al, NOT (EC_FLAG1 OR EC_FLAG2) ; clear flags mov lastcon, al ; save lastcom sti ; enable interrupts mov dx, econtrol ; dx -> control register out dx, al ; send control mov dx, ecommand ; dx -> command register ob_2: mov al, [si] ; next command byte -> al out dx, al ; send command byte mov bx, timer_low ; current timer -> ax add bx, CMDBTO ; + time out wlp4: call getstat ; get status and al, ES_HOST_COMMAND_EMPTY ; has command been taken? jne ob_3 ; yes: go on cmp bx, timer_low ; have we waited too long? ja wlp4 ; no jmp cmdretry ; go retry command ob_3: inc si ; increment source pointer dec cx ; count byte jg ob_2 ; loop if more bytes mov dx, econtrol ; dx -> control register cli ; disable interrupts mov al, lastcon ; last control -> al or al, (EC_FLAG1 OR EC_FLAG2) ; set end of command mov lastcon, al ; save lastcon out dx, al ; send flag bits mov dx, ecommand ; dx -> command register mov ax, pcblen ; pcb length -> ax out dx, al ; send pcb length sti ; enable interrupts mov bx, timer_low ; current time -> bx add bx, CMDCTO ; + time out for command to be accepted wlp5: call getstat ; get status and al, (ES_FLAG1 OR ES_FLAG2) ; just keep status flags cmp al, 1 ; accepted? je cmdaccept ; yes cmp al, 2 ; rejected? je cmdretry ; yes cmp bx, timer_low ; have we waited too long? ja wlp5 ; no cmdretry: mov ax, timer_low ; current time -> ax add ax, RETRYDELAY ; + retry delay wlp6: cmp ax, timer_low ; have we waited long enough? ja wlp6 ; no jmp ob_1 ; go do retry cmdaccept: cli ; protect last control mov al, lastcon ; last control -> al and al, NOT (EC_FLAG1 OR EC_FLAG2) ; turn off end of command flag mov lastcon, al ; save last control sti ; reenable interrupts mov dx, econtrol ; dx -> control register out dx, al ; pass control byte mov ax, 0 ; return 0, success ret outpcb endp subttl Interrupt routine page + ;************************************************************************* ; Interrupt Handler ; installation and deinstallation ; ; the handler takes the receive packet out of the input buffer ; DEINST PROC NEAR push ds MOV ax, vec ; interrupt in table for 3com board MOV dx, oldioff ; get old ip from save spot MOV ds, oldiseg ; get old cs from save spot mov ah, 25h int 21h POP DS RET DEINST ENDP ; IINST PROC NEAR MOV CS:MYDS, DS ; store for use by handler PUSH DS push es push bx mov ax, vec ; get the current interupt vector mov ah, 35h ; that we are using. int 21h mov oldioff, bx ; save the vector for later mov oldiseg, es mov dx, offset IHAND ; set our interupt vector mov ax, vec MOV BX, CS MOV DS, BX mov ah, 25h int 21h pop bx pop es POP DS RET MYDS DW 00H ; the data segment for this assembly code ICNT DB 00H IHAND: ; not a public name, only handles ints push ds push es push si push di push bp push ax push bx push cx push dx sti ; let other interrupts come in cld ; increment ifdef Microsoft mov ax, seg _c5_droptot ; ax -> data segment else mov ax, seg c5_droptot ; ax -> data segment endif mov ds, ax ; ds -> data segment ; Check to see if we have a command in the command register icmdc: mov dx, estatus ; dx -> status register in al, dx ; status -> al and al, ES_ADAPTER_COMMAND_FULL ; command register full? jnz icmd0 ; yes inc _c5_nocmd ; count no command jmp ir_y ; no: no more commands this interrupt ; Yes we may have something. Clear the flags and then check to see if ; we really have something. icmd0: mov bx, timer_low ; current time -> bx add bx, RCMDTO ; + time to wait mov al, lastcon ; last control -> ax and al, NOT (EC_FLAG1 OR EC_FLAG2) ; clear flags mov lastcon, al ; save last control mov dx, econtrol ; dx -> control register out dx, al ; clear flags in control register ;---------------------------------------------------------------------------- ; This loop stores the command from the 3c505 board into the icmdb buffer. ; mov di, offset icmdb ; di -> incoming command buffer icmd1: mov dx, estatus ; dx -> status register in al, dx ; status -> al mov cx, ax ; status -> cx test al, ES_ADAPTER_COMMAND_FULL ; command register full? jnz icmd2 ; yes cmp bx, timer_low ; have we waited too long? ja icmd1 ; no inc _c5_cmdito ; count time out jmp ir_x ; yes: give up ; Yes we REALLY do have a command waiting from the 3c505 board. icmd2: mov dx, ecommand ; dx -> command register in al, dx ; get command byte and cl, ES_FLAG1 OR ES_FLAG2 ; just keep flags cmp cl, ES_FLAG1 OR ES_FLAG2 ; are both on? je icmd3 ; yes: end of command mov [di], al ; save byte inc di ; increment pointer mov ax, di ; current pointer -> ax sub ax, offset icmdb ; - start of buffer cmp ax, CBS ; full? jl icmd1 ; no mov si, (offset icmdb) + CBSH ; si -> middle of buffer mov di, offset icmdb ; di -> start of buffer mov cx, CBSH ; size of half buffer -> cx PUSH ds POP es rep movsb ; move buffer up jmp icmd1 ; loop for another byte ;------------------------------------------------------------------------- ; We've gotten the command from the board. icmd3: push ds pop es ; es -> segment of command area mov ah, 0 ; clear high byte of length mov cmdlen, ax ; save command length mov si, di ; si -> command buffer sub si, cmdlen ; back up to start of command mov di, offset icmd ; di -> command area mov cx, cmdlen ; command length -> cx rep movsb ; move command mov al, icmd ; first byte of command -> al cmp al, 32h ; configure 82586? je ic_conc ; yes cmp al, 33h ; get Ethernet address? je ic_geth ; yes cmp al, 38h ; receive complete? je ic_rec ; yes cmp al, 39h ; transmit complete? je ic_xmit ; yes cmp al, 3ah ; statistics response? je ic_stat ; yes cmp al, 40h ; set Ethernet address complete? jne ic_j4 ; no jmp ic_seth ; yes ic_j4: cmp al, 41h ; adapter information response? jne ic_j5 ; no jmp ic_adin ; yes ic_j5: jmp ir_x ; other: just ignore it ic_conc: push ds pop es ; es -> configure 82586 response segment mov si, offset icmd ; si -> command received mov di, offset rconc ; di -> configure 82586 response mov cx, 2 ; response length -> cx rep movsw ; move response mov fconc, 1 ; flag response received jmp ir_x ; go return from interrupt ic_geth: push ds pop es ; es -> Ethernet address response segment mov si, offset icmd ; si -> command received mov di, offset rgeth ; di -> Ethernet address response mov cx, 4 ; response length -> cx rep movsw ; move response mov fgeth, 1 ; flag response received jmp ir_x ; go return from interrupt ic_xmit: push ds pop es ; es -> transmit response segment mov si, offset icmd ; si -> command received mov di, offset rxmit ; di -> transmit response mov cx, 5 ; response length -> cx rep movsw ; move response mov fxmit, 1 ; flag response received jmp ir_x ; go return from interrupt ic_stat: push ds pop es ; es -> statistics response segment mov si, offset icmd ; si -> command received mov di, offset rstat ; di -> statistics response mov cx, 5 ; response length -> cx rep movsw ; move response mov fstat, 1 ; flag response received jmp ir_x ; go return from interrupt ic_rec: push ds pop es ; es -> receive response segment mov si, offset icmd ; si -> command received mov di, offset rr ; di -> receive response mov cx, 9 ; response length -> cx rep movsw ; move response push ds mov ds, ksegdata mov ax, _buflim ; buffer size -> ax sub ax, _bufbig ; - amount in use pop ds sub ax, rr_dmalen ; - size of new message jl ir_drop ; no room--drop it push ds mov ds, ksegdata les di, dword ptr _bufpt ; es/di -> buffer position cmp di, _bufend ; have we passed restart point jb icr_2 ; no mov di, _buforg ; yes: start over icr_2: pop ds mov ax, rr_dmalen ; message size -> ax inc ax ; + 1 to round up shr ax, 1 ; convert to words shl ax, 1 ; convert back to characters mov rr_dmalen, ax ; use it to update bufbig stosw ; store message length at front of message mov cx, ax ; message length -> cx shr cx, 1 ; convert to words mov al, lastcon ; last control -> al or al, EC_TO_HOST OR EC_FLAG1 ; set direction and acknowledge ; response mov lastcon, al ; save last control mov dx, econtrol ; dx -> control register out dx, al ; pass direction mov dx, estatus ; dx -> status register mov bx, edata ; bx -> data register icr_1: in al, dx ; get status test al, ES_DATA_READY ; is data ready? jz icr_1 ; no xchg dx, bx ; dx -> data register in ax, dx ; data word -> ax stosw ; store word in buffer xchg dx, bx ; dx -> status register dec cx ; count word jnz icr_1 ; loop if more words mov al, lastcon ; last control -> al and al, NOT (EC_TO_HOST OR EC_FLAG1) ; change direction to output mov lastcon, al ; save last control mov dx, econtrol ; dx -> control register out dx, al ; send control mov ax, rr_dmalen ; data length-> ax push ds mov ds, ksegdata mov _bufpt, di ; store pointer add ax, _bufbig ; + bytes in buffer add ax, 2 ; + 2 for size mov _bufbig, ax ; save buffer in use pop ds jmp ir_x ; go return from interrupt ir_drop: inc _c5_droptot ; count dropped message inc rdropnew ; count so another read gets started ; eat the message mov ax, rr_dmalen ; message size -> ax inc ax ; + 1 to round up shr ax, 1 ; convert to words shl ax, 1 ; convert back to characters mov rr_dmalen, ax ; use it to update bufbig stosw ; store message length at front ; of message mov cx, ax ; message length -> cx shr cx, 1 ; convert to words mov al, lastcon ; last control -> al or al, EC_TO_HOST OR EC_FLAG1 ; set direction and acknowledge ; response mov lastcon, al ; save last control mov dx, econtrol ; dx -> control register out dx, al ; pass direction mov dx, estatus ; dx -> status register mov bx, edata ; bx -> data register icr_3: in al, dx ; get status test al, ES_DATA_READY ; is data ready? jz icr_3 ; no xchg dx, bx ; dx -> data register in ax, dx ; data word -> ax xchg dx, bx ; dx -> status register dec cx ; count word jnz icr_3 ; loop if more words mov al, lastcon ; last control -> al and al, NOT (EC_TO_HOST OR EC_FLAG1) ; change direction to output mov lastcon, al ; save last control mov dx, econtrol ; dx -> control register out dx, al ; send control jmp ir_x ; go return from interrupt ic_seth: push ds pop es ; es -> set Ethernet address response segment mov si, offset icmd ; si -> command received mov di, offset rseth ; di -> set Ethernet address response mov cx, 2 ; response length -> cx rep movsw ; move response mov fseth, 1 ; flag response received jmp ir_x ; go return from interrupt ic_adin: push ds pop es ; es -> adapter information response segment mov si, offset icmd ; si -> command received mov di, offset radin ; di -> adapter information response mov cx, 5 ; response length -> cx rep movsw ; move response mov fadin, 1 ; flag response received jmp ir_x ; go return from interrupt ir_x: jmp icmdc ; look for another interrupt ir_y: mov ax, eoi2 ; EOI command for 8259 2 -> ax jz ir_1 ; branch if none mov dx, IOCWR2 ; dx -> 8259 2 command register out dx, al ; do end of interrupt 2 ir_1: mov ax, eoi1 ; EOI command for 8259 1 -> ax mov dx, IOCWR1 ; dx -> 8259 1 command register out dx, al ; do end of interrupt 1 pop dx pop cx pop bx pop ax pop bp pop di pop si pop es pop ds iret IINST endp _text ends end