📄 8390.asm
字号:
dec al
rcv_nwrap4:
loadport ; Point at the Boundary Reg again
setport EN0_BOUNDARY ; ..
pause_
out dx, al ; Set new boundary
; This is real bizarre. The NIC DP8390 Datasheet Addendum, June 1990, states
; that:
;
; In heavily loaded networks which cause overflows of the Recieve Buffer
; Ring, the NIC may disable the local DMA and suspend further receptions
; even if the Boundary register is advanced beyond the Current register.
; In the event that the Network Interface Controller (DP8390 NIC) should
; encounter a receiver buffer overflow, it is necessary to implement the
; following routine. A receive buffer overflow is indicated by the NIC's
; assertion of the overflow bit (OVW) in the interrupt status register
; (ISR).
;
; If this routine is not adhered to, the NIC may act in an unpredictable
; manner. It should also be noted that it is not permissible to service
; an overflow interrupt by continuing to empty packets from the receive
; buffer without implementing the prescribed overflow routine.
; ...
;
; The overflow routine is the one implemented at recv_overrun.
;
; The funny thing is that the way this code is written, in a heavily loaded
; network, you could NEVER NOTICE THAT AN OVERRUN OCCURED. If the NIC does
; NOT suspend further receptions even though the Boundary register is advanced
; (like is says the NIC MAY do above), you will simply receive each frame,
; advance the boundary pointer (allowing the NIC to receive another frame),
; and jump from here back to recv_frame to read the next packet, NEVER CHECKING
; ISR for OVW. If the NIC NEVER stops, and the network is heavily loaded
; enough you could go round and round forever.
;
; So what's the problem you ask? If the NIC DOES stop receiving, you will
; process every packet in the ring before you notice that there is an overrun!
; Instead of dropping a few packets here and a few packets there you will drop
; large blocks of packets because you have taken the time to empty the ring
; completely before turning the NIC back on.
;
; The solution is to check here for an OVW after each pass and jump to
; recv_overrun if required.
;
; setport EN0_ISR ; Point at interrupt status register
; pause_
; in al, dx ; Get pending interrupts
;ifdef debug
; mov log_isr,al
;endif
; test al,ENISR_OVER ; Was there an overrun?
; jz recv_frame_loop ; Go if not.
; jmp recv_overrun ; Go if so.
;recv_frame_loop:
;
; But that's a performance hit and it may not be necessaary. I have not yet
; been able to tell if the NIC is stopping (like National says it MAY do), or
; if it keeps on receiving as soon as the boundary pointer is advanced. It
; SEEMS to keep on working fine.
;
; Therefore I'm not going to put that overrun check in here and I'll
; live with this routine as long as it seems to be working fine.
;
; One more thought, if this code is implemented, it shoud be moved to the
; beginning of recv_frame before the RX interrupts are cleared. Recv_overrun
; only receives ONE packet and jumps back to check_isr, and if you don't move
; this routine you could exit with packets in the ring and not know it because
; you cleared the interrupt bits.
;
; - gft - 910606
;
; New thoughts and fixes - gft - 910611
;
; A compromise fix which also MINIMIZES NIC accesses is to remember the
; contents of the current page register after entry to recv_frame and then
; remove from the ring only those packets received at that point. Then go
; back to check_isr and catch any new packets or receive overruns. See comments
; at the start of recv_frame.
;
jmp recv_frame ; See if any more frames
;
; The next bit of code following recv_frame_break: is superfluous.
; 1. recv_frame_break: is not public, it can't be reached from outside.
; 2. a grep of all the Clarkson packet driver collection reveals only
; one jump to recv_frame_break.
; 3. Just prior to that jump, the command register was set to
; ENC_NODMA+ENC_PAGE0+ENC_START, followed by a setport to EN0_BOUNDARY
; and a fetch of the boundry register.
; Therefore I am commenting out the following bit of code, and changing the
; jmp recv_frame_break (formerly je recv_frame_break) to a jmp check_isr.
; - gft - 910531
;
;recv_frame_break:
; loadport ; Point at Command register
; setport EN_CCMD ; ..
; pause_
; mov al, ENC_NODMA+ENC_PAGE0+ENC_START
; out dx,al
; jmp check_isr ; See if any other interrupts pending
;
recv_no_frame: ; Handle transmit flags.
test al,ENISR_TX+ENISR_TX_ERR ; Frame transmitted?
jnz isr_tx ; Go if so.
jmp isr_no_tx ; Go if not.
isr_tx:
mov ah, al ; Hold interrupt status bits
loadport ; Point at Transmit Status Reg
setport EN0_TSR ; ..
pause_
in al, dx ; ..
; New TX interrupt acknowledge code follows:
mov ah,al ; save the TSR state
setport EN0_ISR ; Point at the interrupt status register
pause_
; Since transmissions happen synchronously to this driver (even if as_send_pkt
; is implemented, their still synchronous to the driver), only one of the two
; possible TX interrupts may occur at any one time. Therefore it is OK to
; zap both TX and TX_ERR bits in the interrupt status register here.
mov al,ENISR_TX+ENISR_TX_ERR ; clear either possible TX int bit
out dx,al
test ah,ENTSR_COLL+ENTSR_COLL16+ENTSR_FU+ENTSR_OWC
jz isr_tx_done ; No usefull errors, skip. See the called
; routine for explanation of the selection
; of TSR bits for consideration as errors.
call count_tx_err
isr_tx_done:
; If TX queue and/or TX shared memory ring buffer were being
; used, logic to step through them would go here. However,
; in this version, we just clear the flags for background to notice.
jmp check_isr ; See if any other interrupts on
isr_no_tx:
; Now check to see if any counters are getting full
test al,ENISR_COUNTERS ; Interrupt to handle counters?
jnz isr_stat ; Go if so.
jmp isr_no_stat ; Go if not.
isr_stat:
; We have to read the counters to clear them and to clear the interrupt.
; Version 1 of the PC/FTP driver spec doesn't give us
; anything useful to do with the data, though.
; Fix this up for V2 one of these days.
loadport ; Point at first counter
setport EN0_COUNTER0 ; ..
pause_
in al, dx ; Read the count, ignore it.
setport EN0_COUNTER1
pause_
in al, dx ; Read the count, ignore it.
setport EN0_COUNTER2
pause_
in al, dx ; Read the count, ignore it.
setport EN0_ISR ; Clear the statistics completion flag
pause_
mov al, ENISR_COUNTERS ; ..
out dx, al ; ..
isr_no_stat:
jmp check_isr ; Anything else to do?
interrupt_done:
loadport
setport EN0_IMR ; Tell card it can cause these interrupts
pause_
mov al, ENISR_ALL
out dx, al
ret
; Do the work of copying out a receive frame.
; Called with bl/ the page number of the frame header in shared memory
public rcv_frm
rcv_frm:
mkle LE_RCVFRM_E, 0, 0, 0
; Set cx to length of this frame.
mov ch, rcv_hdr+EN_RBUF_SIZE_HI ; Extract size of frame
mov cl, rcv_hdr+EN_RBUF_SIZE_LO ; Extract size of frame
sub cx, EN_RBUF_NHDR ; Less the header stuff
; Set es:di to point to Ethernet type field.
mov di, offset rcv_hdr+EN_RBUF_NHDR+EADDR_LEN+EADDR_LEN
push bx ; save page.
push cx ; Save frame size
push es
mov ax, cs ; Set ds = code
mov ds, ax
mov es,ax
assume ds:code
mov dl, BLUEBOOK ;assume bluebook Ethernet.
mov ax, es:[di]
xchg ah, al
cmp ax, 1500
ja BlueBookPacket
inc di ;set di to 802.2 header
inc di
mov dl, IEEE8023
BlueBookPacket:
call recv_find ; See if type and size are wanted
pop ds ; RX page pointer in ds now
assume ds:nothing
pop cx
pop bx
cld ; Copies below are forward, please
mov ax, es ; Did recv_find give us a null pointer?
or ax, di ; ..
je rcv_no_copy ; If null, don't copy the data
push cx ; We will want the count and pointer
push es ; to hand to client after copying,
push di ; so save them at this point
mov ah,bl ; set ax to page to start from
mov al,EN_RBUF_NHDR ; skip the header stuff
call block_input
;
; Added defer upcall code here in support of new ring overrun code.
; If defer_upcall is set, don't call recv_copy, just save si,ds,cx
; and the receive ring overrun code will call recv_copy later.
; - gft - 910604
;
cmp defer_upcall,0 ; is deferral required?
jz rcv_copy_ok ; No, finish of the copy.
pop ax ; Yes, save the parameters for recv_copy
mov defer_si,ax ; to use later.
pop ax
mov defer_ds,ax
pop ax
mov defer_cx,ax
jmp rcv_copy_deferred
rcv_copy_ok:
pop si ; Recover pointer to destination
pop ds ; Tell client it's his source
pop cx ; And it's this long
assume ds:nothing
call recv_copy ; Give it to him
rcv_no_copy:
movseg ds,cs
assume ds:code
mov defer_upcall,0 ; clear the defer upcall flag - gft - 910604
rcv_copy_deferred:
mkle LE_RCVFRM_X, 0, 0, 0
ret ; That's it for rcv_frm
include multicrc.asm
include timeout.asm
public timer_isr
timer_isr:
;if the first instruction is an iret, then the timer is not hooked
iret
;any code after this will not be kept after initialization. Buffers
;used by the program, if any, are allocated from the memory between
;end_resident and end_free_mem.
public end_resident,end_free_mem
end_resident label byte
end_free_mem label byte
;standard EN0_DCFG contents:
endcfg db 048h ; Set burst mode, 8 deep FIFO
; Called once to initialize the card
public etopen
etopen: ; Initialize interface
;Step 1. Reset and stop the 8390.
call reset_board
;Step 2. Init the Data Config Reg.
loadport
mov al,endcfg
setport EN0_DCFG
pause_
out dx,al
;Step 2a. Config the Command register to page 0.
loadport
mov al, ENC_PAGE0 + ENC_NODMA + ENC_STOP
setport EN_CCMD
pause_
out dx,al
;Step 3. Clear Remote Byte Count Regs.
mov al, 0
setport EN0_RCNTLO
pause_
out dx,al
setport EN0_RCNTHI
pause_
out dx,al
;Step 4. Set receiver to monitor mode
mov al, ENRXCR_MON
setport EN0_RXCR
pause_
out dx,al
;Step 5. Place NIC into Loopback Mode 1.
mov al,ENTXCR_LOOP
setport EN0_TXCR
pause_
out dx,al
;Step 6. Do anything special that the card needs. Read the Ethernet address
;into rom_address.
call init_card ;init the card as needed.
jnc etopen_1 ;go if it worked.
ret
etopen_1:
;Step 7. Re-init endcfg in case they put it into word mode.
loadport
mov al,endcfg
setport EN0_DCFG
pause_
out dx,al
;Step 8. Init EN0_STARTPG to same value as EN0_BOUNDARY
mov al,SM_RSTART_PG
setport EN0_STARTPG
pause_
out dx,al
if 1 ;Paul Kranenburg suggests that this should be set to zero.
mov al,SM_RSTART_PG
else
mov al,sm_rstop_ptr
dec al
endif
setport EN0_BOUNDARY
pause_
out dx,al
mov al,sm_rstop_ptr
setport EN0_STOPPG
pause_
out dx,al
;Step 9. Write 1's to all bits of EN0_ISR to clear pending interrupts.
mov al, 0ffh
setport EN0_ISR
pause_
out dx,al
;Step 10. Init EN0_IMR as desired.
mov al, ENISR_ALL
setport EN0_IMR
pause_
out dx,al
;Step 11. Init the Ethernet address and multicast filters.
mov si,offset rom_address
mov cx,EADDR_LEN
call set_address ; Now set the address in the 8390 chip
call set_hw_multi ; Put the right stuff into 8390's multicast masks
;Step 12. Program EN_CCMD for page 1.
loadport
mov al, ENC_PAGE1 + ENC_NODMA + ENC_STOP
setport EN_CCMD
pause_
out dx,al
;Step 13. Program the Current Page Register to same value as Boundary Pointer.
; THIS IS WRONG WRONG WRONG inspite of some self-contradicting National
; documentation. If the Current Page Register is initialized to the same
; value as the Boundary Pointer, the first ever packet received will be lost or
; trashed because the driver always expects packets to be received at Boundrary
; pointer PLUS ONE!
mov al,SM_RSTART_PG
inc al ; To fix the bug! - gft - 910523
setport EN1_CURPAG
pause_
out dx,al
mov save_curr,al ; added in conjunction with fixes above
; - gft - 910611
mov next_packet, al ; initialize next_packet to the value in
; current - gft - 910603
;Step 14. Program EN_CCMD back to page 0, and start it.
mov al, ENC_NODMA + ENC_START + ENC_PAGE0
setport EN_CCMD
pause_
out dx,al
mov al, 0 ;set transmitter mode to normal.
setport EN0_TXCR
pause_
out dx,al
if 0
mov al, ENRXCR_BCST
setport EN0_RXCR
pause_
out dx,al
endif
call set_recv_isr ; Put ourselves in interrupt chain
mov al, int_no ; Get board's interrupt vector
add al, 8
cmp al, 8+8 ; Is it a slave 8259 interrupt?
jb set_int_num ; No.
add al, 70h - 8 - 8 ; Map it to the real interrupt.
set_int_num:
xor ah, ah ; Clear high byte
mov int_num, ax ; Set parameter_list int num.
clc ; Say no error
ret ; Back to common code
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -