Category : Network Files
Archive   : PKTD11A.ZIP
Filename : 8390.ASM

 
Output of file : 8390.ASM contained in archive : PKTD11A.ZIP

;History:538,1
; Ian Brabham 28 Apr 1993 Fix problems related to SMC version of 8390

dp8390_version equ 3 ;version number of the generic 8390 driver.

; Copyright, 1988-1992, Russell Nelson, Crynwr Software

; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, version 1.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

; This driver is the work of several people: Bob Clements, Eric Henderson,
; Dave Horne, Glenn Talbott, Russell Nelson, Jan Engvald, Paul Kranenburg,
; and Ian Brabham.

ife SM_RSTART_PG
%err SM_RSTART_PG cannot be zero because of a decrement/unsigned jump.
endif

;
; The longpause macro was originally written as this:
;
;longpause macro
; push cx
; mov cx,0
; loop $
; pop cx
;endm
;
; It was only used to stall while hard resetting the card. On my
; 25Mhz 486 longpause was taking more than 18ms, and almost forever
; on slower machines, much longer than necessary and not predictable.
;
; To be able to utilize longpause elsewhere and make it machine independent and
; predictable, I have re-written it to be a fixed time of 1.6ms, which just
; happens to be the time National recommends waiting for the NIC chip to
; stop sending or receiving after being commanded to stop.
;
; Based on the assumption that ISA specs mandate a 1.0 uS minimum I/O cycle
; Microchannel a 0.5uS minimum I/O cycle, and the NMI Status register (location
; 61h) is readable via I/O cycle on all machines, the longpause macro is now
; defined below. - gft - 901604
; (I realize that on slow machines this may take much longer, but the point
; is that on FAST machines it should never be faster than 1.6ms)

longpause macro
local lp_not_mc
push cx
push ax
mov cx,1600 ; 1.6ms = 1600*1.0us
test sys_features,SYS_MCA
je lp_not_mc
shl cx,1 ; twice as many loops for Microchannel
lp_not_mc:
in al,61h
loop lp_not_mc
pop ax
pop cx
endm

extrn sys_features: byte

sm_rstop_ptr db SM_RSTOP_PG

rxcr_bits db ENRXCR_BCST ; Default to ours plus multicast
ifdef board_features
is_overrun_690 db 0
endif


;-> the assigned Ethernet address of the card.
extrn rom_address: byte

;-> current address
extrn my_address: byte

public mcast_list_bits, mcast_all_flag
mcast_list_bits db 0,0,0,0,0,0,0,0 ;Bit mask from last set_multicast_list
mcast_all_flag db 0 ;Non-zero if hware should have all
; ones in mask rather than this list.

public rcv_modes
rcv_modes dw 7 ;number of receive modes in our table.
dw 0 ;There is no mode zero
dw rcv_mode_1
dw rcv_mode_2
dw rcv_mode_3
dw rcv_mode_4
dw rcv_mode_5
dw rcv_mode_6

;
; a temp buffer for the received header
;
RCV_HDR_SIZE equ 26 ; 2 ids @6 + protocol @2+8, + header @4
rcv_hdr db RCV_HDR_SIZE dup(0)

;
; The board data
;
public board_data
BOARD_DATA_SIZE equ 32
board_data db BOARD_DATA_SIZE dup(0)


; add public for soft errors (how were these extracted before? - gft - 910604

public soft_errors
public soft_tx_errors,soft_tx_err_bits,soft_tx_collisions
public soft_rx_errors,soft_rx_err_bits
public soft_rx_overruns,soft_rx_over_nd
;
; Re-arranged the order of these soft_xx_err things so that they can be
; accessed as a data structure (like the statistics structure defined
; in the packet driver spec. I don't know if it's necessary but I've always
; found data structures to be more portable if elements are size aligned.
; - gft - 910607
; Don't rearrange or insert things between these soft error words because
; they are accessed as a data structure (I don't think I violated my own
; admonition since they wern't public and I could find no references to
; them and only the NEx000 drivers reference the next previous thing,
; board_data, and they only us 16 bytes of that.)

soft_errors label dword
soft_tx_errors dw 0,0
soft_tx_collisions dw 0,0 ; added - gft - 910607 cause who ever heard
; of a CSMA/CD driver not counting
; collisions.
soft_rx_errors dw 0,0
soft_rx_overruns dw 0,0 ; added - gft - 910604 cause I just
; gotta track these so I can findout
; just when I'm pushing a driver or
; application to it's limits
soft_rx_over_nd dw 0,0 ; Also count when theres no data.
hard_tx_errors dw 0,0
soft_tx_err_bits db 0
soft_rx_err_bits db 0

;
; Next Packet Pointer (added - gft - 910603)
;
; Initialize to the same value as the current page pointer (start page 1).
; Update after each reception to be the value of the next packet pointer
; read from the NIC Header.
; Copy value -1 to boundry register after each update.
; Compare value with contents of current page pointer to verify that a
; packet has been received (don't trust ISR RXE/PRX bits). If !=, one
; or more packets have been received.

next_packet db 0
save_curr db 0

; Added flags and temp storage for new receive overrun processing
; - gft - 910604

rcv_ovr_resend db 0,0 ; flag to indicate resend needed
defer_upcall db 0,0 ; flag to indicate deferred upcall needed
defer_ds dw ? ; deferred upcall parameters
defer_si dw ?
defer_cx dw ?


ifdef deb2screen
; Event to screen debugger. Destroys no registers and requires just 3 bytes at
; each place called. Produces a one-line summary of event types that has ever
; occured and then some trace lines with the sequence of the last events./Jan E LDC

SHOWMIN equ 'a'
SHOWMAX equ 'l'
EVENTCOLOR equ 31h
EVENTLINE equ 17
TRACECOLOR equ 2eh

ShowEvent proc near
x = 0
rept (SHOWMAX-SHOWMIN+1)
push ax
mov al,x
jmp short ShowEventNum
x = x+1
endm

ShowEventNum:
pushf
push di
push es
mov ah,EVENTCOLOR
mov di,ax
shl di,1
add al,SHOWMIN
mov es,cs:EventPar
cld
stosw

mov ah,TRACECOLOR
mov es,cs:TracePar
cli
mov di,cs:TraceInd
stosw
and di,01ffh ; (1ff+1)/2 = 256 log entries
mov cs:TraceInd,di
mov al,01bh
not ah
stosw

pop es
pop di
popf
pop ax
ret
ShowEvent endp

EventPar dw 0b800h+(EVENTLINE-1)*10-2*EVENTCOLOR*16
TracePar dw 0b800h+EVENTLINE*10
TraceInd dw 0

SHOW_EVENT macro id
if id gt SHOWMAX or id lt SHOWMIN
.err
endif
call ShowEvent+((id-SHOWMIN)*(ShowEventNum-ShowEvent)/(SHOWMAX-SHOWMIN+1))
endm

else

SHOW_EVENT macro num
endm

endif ; deb2screen


ifdef debug ; Include a very useful logging mechanism.

; The log entry structure. Log entries include useful data such as
; a type (each place a log entry is made uses a different type), various
; chip status, ring buffer status, log entry dependent data, and optionally
; 8259 interrupt controller status.
logentry struc
le_type db 0 ; Log entry type
le_ccmd db ? ; Value of CCMD register
le_isr db ? ; Value of ISR register
le_tsr db ? ; Value of TSR register
le_tcur dw ? ; Value of sm_tcur
le_tboundary dw ? ; Value of sm_tboundary
le_tnum dw ? ; Value of sm_tnum
le_dw dw ? ; Log type specific dw data
ifndef mkle8259 ; Log 8259 status?
le_dd dd ? ; Log type specific dd data
else
le_irr1 db ? ; Value of 8259-1 IRR register
le_isr1 db ? ; Value of 8259-1 ISR register
le_irr2 db ? ; Value of 8259-2 IRR register
le_isr2 db ? ; Value of 8259-2 ISR register
endif
logentry ends

; The types of log entries.
LE_SP_E equ 0 ; send_pkt entry
LE_SP_X equ 1 ; send_pkt exit
LE_ASP_E equ 2 ; as_send_pkt entry
LE_ASP_X equ 3 ; as_send_pkt exit
LE_RBALLOC_E equ 4 ; tx_rballoc entry
LE_RBALLOC_X equ 5 ; tx_rballoc exit
LE_COPY_E equ 6 ; sm_copy entry
LE_COPY_X equ 7 ; sm_copy exit
LE_START_E equ 8 ; tx_start entry
LE_START_X equ 9 ; tx_start exit
LE_XMIT_E equ 0ah ; xmit entry
LE_XMIT_X equ 0bh ; xmit exit
LE_TXISR_E equ 0ch ; txisr entry
LE_TXISR_X equ 0dh ; txisr exit
LE_RECV_E equ 0eh ; recv entry
LE_RECV_X equ 0fh ; recv exit
LE_RCVFRM_E equ 10h ; rcv_frm entry
LE_RCVFRM_X equ 11h ; rcv_frm exit
LE_COPY_L equ 12h ; sm_copy loop
LE_TIMER_E equ 13h ; timer entry
LE_TIMER_X equ 14h ; timer exit

public log, log_index
log logentry 256 dup (<>) ; The log itself
log_index db 0 ; Index to current log entry

; The macro used to create log entries.
mkle macro letype, ledw, ledd, ledd2 ; Make an entry in the log
pushf ; Save interrupt enable state
cli ; Disable interrupts
push dx ; Save registers
push bx
push ax
mov bl, log_index ; Get current log_index
xor bh, bh ; Clear high byte
shl bx, 1 ; Multiply by sixteen
shl bx, 1
shl bx, 1
shl bx, 1
mov log[bx].le_type, letype ; Store log entry type
loadport ; Base of device
setport EN_CCMD ; Point at chip command register
in al, dx ; Get chip command state
mov log[bx].le_ccmd, al ; Store CCMD value
setport EN0_ISR ; Point at chip command register
in al, dx ; Get chip command state
mov log[bx].le_isr, al ; Store ISR value
setport EN0_TSR ; Point at chip command register
in al, dx ; Get chip command state
mov log[bx].le_tsr, al ; Store TSR value
mov ax, sm_tcur ; Get current sm_tcur
mov log[bx].le_tcur, ax ; Store sm_tcur value
mov ax, sm_tboundary ; Get current sm_tboundary
mov log[bx].le_tboundary, ax ; Store sm_tboundary value
mov ax, sm_tnum ; Get current sm_tnum
mov log[bx].le_tnum, ax ; Store sm_tnum value
mov log[bx].le_dw, ledw ; Store log entry dw
ifndef mkle8259 ; Include extra per-type data
mov word ptr log[bx].le_dd, ledd ; Store low word of log entry dd
mov word ptr log[bx].le_dd+2, ledd2 ; Store high word of log entry dd
else ; Include 8259 status
mov al,0ah ; read request register from
out 0a0h,al ; secondary 8259
pause_
in al,0a0h ; get it
mov log[bx].le_irr2, al
mov al,0bh ; read in-service register from
out 0a0h,al ; secondary 8259
pause_
in al,0a0h ; get it
mov log[bx].le_isr2, al
mov al,0ah ; read request register from
out 020h,al ; primary 8259
pause_
in al,020h ; get it
mov log[bx].le_irr1, al
mov al,0bh ; read in-service register from
out 020h,al ; primary 8259
pause_
in al,020h ; get it
mov log[bx].le_isr1, al
endif
ifdef screenlog ; Log the entry type to the screen too
push es
mov ax, 0b800h ; Color screen only...
mov es, ax
mov bl, log_index ; Get current log_index
xor bh, bh ; Clear high byte
shl bx, 1 ; Multiply by sixteen
add bx, 3360
mov byte ptr es:[bx-1], 07h
mov byte ptr es:[bx], letype+30h
mov byte ptr es:[bx+1], 70h
pop es
endif
inc log_index ;
pop ax ; Restore registers
pop bx
pop dx
popf ; Restore interrupt enable state
endm

else
mkle macro letype, ledw, ledd, ledd2 ; Define an empty macro
endm
endif

public bad_command_intercept
bad_command_intercept:
;called with ah=command, unknown to the skeleton.
;exit with nc if okay, cy, dh=error if not.
mov dh,BAD_COMMAND
stc
ret

public as_send_pkt
; The Asynchronous Transmit Packet routine.
; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
; interrupts possibly enabled.
; Exit with nc if ok, or else cy if error, dh set to error number.
; es:di and interrupt enable flag preserved on exit.
as_send_pkt:
ret

public drop_pkt
; Drop a packet from the queue.
; Enter with es:di -> iocb.
drop_pkt:
assume ds:nothing
ret

public xmit
; Process a transmit interrupt with the least possible latency to achieve
; back-to-back packet transmissions.
; May only use ax and dx.
xmit:
assume ds:nothing
ret


; The tx_wait loop had three problems that affected high load throughput.
; Most seriously, while we are waiting for the previous SEND to finnish,
; the chip can actually be RECEIVING one or even many packets! But because
; we were waiting with interrupts disabled, these packets were not emptied
; from the receive ring and we could get an overrun. We could put in code to
; test for pending receive interrupts, but that would not help for the
; third problem, see below. Instead interrupts are now on while waiting.
; Secondly, the wait loop was not long enough to allow for up to 16 collisions.
; Thirdly, for a router there are two or more drivers and the busy waiting
; in one of them prevented interrupt handling for the other(s), giving
; unnecessary low throughput. /Jan E LDC

tx_wait:
mov bx,1024*7 ; max coll time in Ethernet slot units
tx_wait_l1:
mov ah,51 ; assume 1 us IO
test sys_features,SYS_MCA
jz tx_wait_l2
shl ah,1 ; MCA IO is just 0.5 us
tx_wait_l2:
sti ; allow receive interrupts while waiting
loadport ; Point at chip command register
setport EN_CCMD ; ..
in al, dx ; Get chip command state
test al,ENC_TRANS ; Is transmitter still running?
cli ; the rest of the code may not work with EI (?)
jz tx_idle_0 ; Go if free

dec ah
jnz tx_wait_l2 ; wait 51.2 us (one ethernet slot time)

dec bx ; waited more than max collision time?
jnz tx_wait_l1 ; -no, probably not stuck yet

SHOW_EVENT 'b'
add2 hard_tx_errors,1 ;count hard errors.
call count_out_err ; Should count these error timeouts
; Maybe need to add recovery logic here
jmp short tx_idle_0


public send_pkt
send_pkt:
;enter with ds:si -> packet, cx = packet length.
;exit with nc if ok, or else cy if error, dh set to error number.
assume ds:nothing
mkle LE_SP_E, cx, si, ds

cli

;ne1000 checks the packet size at this point, which is probably more sensible.
loadport ; Point at chip command register
setport EN_CCMD ; ..
pause_
;ne1000 fails to check to see if the transmitter is still busy.
in al, dx ; Get chip command state
test al,ENC_TRANS ; Is transmitter still running?
ifdef debug
mov log_ccmd,al ; added - gft - 910607
endif
;
; Used to just go to tx_idle here if the transmitter was not running, however
; it is possible to get here with the transmission complete, but since
; interrupts are off when we get here it is also possible that a transmission
; JUST DID complete and has not yet had its interrupt acknowledge and errors
; recorded. Proceding here without checking will work, it just looses the
; error status from the last transmission if that transmission has not been
; acknowledged by the isr_tx code.
;
; Changed the jz tx_idle below to the following code - gft - 910607
;
; jz tx_idle ; Go if free

jnz tx_wait

; Check for recent TX completions in the interrupt status register

setport EN0_ISR ; Point at isr
pause_
in al,dx ; get state
ifdef debug
mov log_isr,al
endif
test al,ENISR_TX+ENISR_TX_ERR ; pending TX interupts?
jz tx_idle ; No, Go on with new TX

tx_idle_0: ; Added to capture TSR if tx not done on entry
; AND added all the below TX error detection down to tx_idle
; - gft - 910603

loadport
setport EN0_TSR ; point to TSR
pause_
in al, dx ; get state from prior TX
ifdef debug
mov log_tsr,al ; added - gft - 910603
endif

; Acknowledge the TX interrupt and take care of any TX errors from the
; previous transmission

mov ah,al ; save the TSR state

setport EN0_ISR ; Point at the interrupt status register
pause_
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+ENTSR_CRS
jz tx_idle ; No usefull errors, skip. See the called
; routine for explanation of the selection
; of TSR bits for consideration as errors.
call count_tx_err

tx_idle:
; If IPX + NETX ver 3.26 receives a CANT_SEND it will put a query on the
; screen if one should abort or retry. This is annoying, causes delay and
; destroys formatted screens. Even worse, the current NETX, ver 3.32, will
; hang for any of the replies.

cmp word ptr [si+2*EADDR_LEN+2],0ffffh ; Novell packet?
je tx_nov_noerr ; -yes, avoid CANT_SEND, causes hang 🙁

mov ax,soft_tx_errors ;return an error if the previous
or ax,hard_tx_errors ; packet failed.
jne send_pkt_err
tx_nov_noerr:

cmp cx,GIANT ; Is this packet too large?
ja send_pkt_toobig

cmp cx, RUNT ; Is the frame long enough?
jnb tx_oklen ; Go if OK
mov cx, RUNT ; Stretch frame to minimum allowed
tx_oklen:
push cx ; Hold count for later
loadport ; Set up for address
setport EN0_ISR
pause_
mov al,ENISR_RDC ; clear remote interrupt int.
out dx,al
setport EN0_TCNTLO ; Low byte of TX count
pause_
mov al, cl ; Get the count
out dx, al ; Tell card the count
setport EN0_TCNTHI ; High byte of TX count
pause_
mov al, ch ; Get the count
out dx, al ; Tell card the count
xor ax, ax ; Set up ax at base of tx buffer
mov ah, SM_TSTART_PG ; Where to put tx frame
pop cx ; Get back count to give to board
call block_output
jc tx_no_rdc
loadport
setport EN0_TPSR ; Transmit Page Start Register
pause_
mov al, SM_TSTART_PG
out dx, al ; Start the transmitter
setport EN_CCMD ; Chip command reg
pause_
mov al, ENC_TRANS+ENC_NODMA+ENC_START
out dx, al ; Start the transmitter
mkle LE_SP_X, cx, 1, 0

mov ax,soft_tx_errors ;return an error if the previous
or ax,hard_tx_errors ; packet failed.
jne send_pkt_err
clc ; Successfully started
ret ; End of transmit-start routine
send_pkt_toobig:
mov dh,NO_SPACE
stc
ret
send_pkt_err:
SHOW_EVENT 'e'
mov soft_tx_errors,0
mov soft_tx_collisions,0
mov hard_tx_errors,0
stc
mov dh,CANT_SEND
ret
tx_no_rdc:
mov dh,CANT_SEND
mkle LE_SP_X, cx, 0, 0
stc
ret

count_tx_err:
;
; Function to count hard and soft TX completion errors and collisions.
; Entered with ah containing the transmit status register state.
; - gft - 910607

test ah,ENTSR_PTX ;
jnz tx_err_02 ; NO hard tx errors go look for soft ones

; Handle hard tx errors, fifo underrun and abort

add2 hard_tx_errors,1 ;count hard errors.

test ah,ENTSR_COLL16 ; 16 collision abort?
jz tx_err_01 ; no skip
SHOW_EVENT 'c' ; this almost always occurs in pairs, is this
; routine called twice WHILE one collision
; error lasts??? /Jan E LDC
add2 soft_tx_collisions,16
tx_err_01:
call count_out_err ; only possible other hard error is FU, just
; count the hard error and skip the soft error
jmp tx_err_done

; Handle the tx soft errors and collisions
;
; National DP8390 Datasheet Addendum June 1990 says that Carrier Sense Lost
; (CRS) and Non Deferred TX (DFR) are useless in some or all 8390s, that
; leaves only out of window collision (OWC) and CD Heartbeat (CDH) as
; soft errors (I would hesitate to call collision an error of any kind).
; With who knows how may MAUs out there with SQE either disabled or not
; functional, I don't count CDs as errors either (If your MAU doesn't SQE
; you would count CD errors on EVERY transmission.) That only leaves OWC
; as a soft error.

tx_err_02:
test ah,ENTSR_OWC+ENTSR_COLL ; No soft errors or collisions?
jz tx_err_done ; return

test ah,ENTSR_OWC ; Out of window collision?
jz tx_err_03 ; No, skip
SHOW_EVENT 'f' ; this is not uncommon on a real net using
; some WD cards, other WD cards on same net
; don't report any OWC!?? /Jan E LDC
add2 soft_tx_errors,1

; Collison Counter

tx_err_03:
test ah,ENTSR_COLL ; Enumerated Collisions?
jz tx_err_done ; No, return

setport EN0_NCR ; point at the collision counter
pause_
in al,dx ; get the count
and al,0fh ; clear the unused bits
xor ah,ah ; clear the high byte
add2 soft_tx_collisions,ax

tx_err_done:
mkle LE_TX_ERR, ax, 0, 0
ret


public set_address
set_address:
assume ds:code
;enter with my_address,si -> Ethernet address, CX = length of address.
;exit with nc if okay, or cy, dh=error if any errors.
loadport
setport EN_CCMD ; Chip command register
pushf
cli ; Protect from irq changing page bits
mov al, ENC_NODMA+ENC_PAGE1 ;+ENC_STOP

out dx, al ; Switch to page one for writing eaddr
pause_
setport EN1_PHYS ; Where it goes in 8390
set_8390_1:
lodsb
out dx,al
pause_
inc dx
loop set_8390_1
loadport
setport EN_CCMD ; Chip command register
mov al, ENC_NODMA+ENC_PAGE0 ;+ENC_STOP
out dx, al ; Restore to page zero
pause_
popf
clc
ret

; Routines to set address filtering modes in the DS8390
rcv_mode_1: ; Turn off receiver
mov al, ENRXCR_MON ; Set to monitor for counts but accept none
jmp short rcv_mode_set
rcv_mode_2: ; Receive only packets to this interface
mov al, 0 ; Set for only our packets
jmp short rcv_mode_set
rcv_mode_3: ; Mode 2 plus broadcast packets (This is the default)
mov al, ENRXCR_BCST ; Set four ours plus broadcasts
jmp short rcv_mode_set
rcv_mode_4: ; Mode 3 plus selected multicast packets
mov al, ENRXCR_BCST+ENRXCR_MULTI ; Ours, bcst, and filtered multicasts
mov mcast_all_flag,0 ; need to do sw filter.
jmp short rcv_mode_set
rcv_mode_5: ; Mode 3 plus ALL multicast packets
mov al, ENRXCR_BCST+ENRXCR_MULTI ; Ours, bcst, and filtered multicasts
mov mcast_all_flag,1
jmp short rcv_mode_set
rcv_mode_6: ; Receive all packets (Promiscuous physical plus all multi)
mov al, ENRXCR_BCST+ENRXCR_MULTI+ENRXCR_PROMP
mov mcast_all_flag,1
rcv_mode_set:
push ax ; Hold mode until masks are right
call set_hw_multi ; Set the multicast mask bits in chip
pop ax
loadport
setport EN0_RXCR ; Set receiver to selected mode
pause_
out dx, al
mov rxcr_bits,al ; Save a copy of what we set it to
ret


; Set the multicast filter mask bits in case promiscuous rcv wanted
set_hw_multi:
push cs
pop ds
assume ds:code
loadport
setport EN_CCMD ; Chip command register
pause_
mov cx, 8 ; Eight bytes of multicast filter
mov si, offset mcast_list_bits ; Where bits are, if not all ones
cli ; Protect from irq changing page bits
mov al, ENC_NODMA+ENC_PAGE1+ENC_STOP
out dx, al ; Switch to page one for writing eaddr
setport EN1_MULT ; Where it goes in 8390
pause_
mov al, mcast_all_flag ; Want all ones or just selected bits?
or al, al
je set_mcast_2 ; Just selected ones
mov al, 0ffh ; Ones for filter
set_mcast_all:
out dx, al ; Write a mask byte
inc dl ; Step to next one
loop set_mcast_all ; ..
jmp short set_mcast_x

set_mcast_2:
lodsb ; Get a byte of mask bits
out dx, al ; Write a mask byte
inc dl ; Step to next I/O register
loop set_mcast_2 ; ..
set_mcast_x:
loadport
setport EN_CCMD ; Chip command register
pause_
mov al, ENC_NODMA+ENC_PAGE0+ENC_START
out dx, al ; Restore to page zero
ret


public reset_board
reset_board:
assume ds:nothing
reset_8390
setport EN_CCMD ; Chip command reg
pause_
mov al, ENC_STOP+ENC_NODMA
out dx, al ; Stop the DS8390

; Wait 1.6ms for the NIC to stop transmitting or receiving a packet. National
; says monitoring the ISR RST bit is not reliable, so a wait of the maximum
; packet time (1.2ms) plus some padding is required.

longpause
ret

public terminate
terminate:
terminate_board
ret

public reset_interface
reset_interface:
assume ds:code
call reset_board
loadport ; Base of I/O regs
setport EN0_ISR ; Interrupt status reg
pause_
mov al, 0ffh ; Clear all pending interrupts
out dx, al ; ..
setport EN0_IMR ; Interrupt mask reg
pause_
xor al, al ; Turn off all enables
out dx, al ; ..
ret

; Linkages to non-device-specific routines
;called when we want to determine what to do with a received packet.
;enter with cx = packet length, es:di -> packet type, dl = packet class.
;It returns with es:di = 0 if don't want this type or if no buffer available.
extrn recv_find: near

;called after we have copied the packet into the buffer.
;enter with ds:si ->the packet, cx = length of the packet.
extrn recv_copy: near

;call this routine to schedule a subroutine that gets run after the
;recv_isr. This is done by stuffing routine's address in place
;of the recv_isr iret's address. This routine should push the flags when it
;is entered, and should jump to recv_exiting_exit to leave.
;enter with ax = address of routine to run.
extrn schedule_exiting: near

;recv_exiting jumps here to exit, after pushing the flags.
extrn recv_exiting_exit: near

extrn count_in_err: near
extrn count_out_err: near

public recv
recv:
;called from the recv isr. All registers have been saved, and ds=cs.
;Actually, not just receive, but all interrupts come here.
;Upon exit, the interrupt will be acknowledged.
;ne1000 and ne2000 routines are identical to this point (except that ne2000
; masks off interrupts).
assume ds:code
mkle LE_RECV_E, 0, 0, 0

check_isr: ; Was there an interrupt from this card?
loadport ; Point at card's I/O port base
setport EN0_IMR ; point at interrupt masks
pause_ ; switch off, this way we can
mov al,0 ; leave the chip running.
out dx,al ; no interrupts please.
setport EN0_ISR ; Point at interrupt status register
pause_
in al, dx ; Get pending interrupts
and al, ENISR_ALL ; Any?
jnz isr_test_overrun
mkle LE_RECV_X, 0, 0, 0
jmp interrupt_done ; Go if none

;
; Revised receive overrun code which corresponds to the National DP8390
; Datasheet Addendum, June 1990.
;
; - gft - 910604
;

; Test for receive overrun in value from NIC ISR register

isr_test_overrun:
test al,ENISR_OVER ; Was there an overrun?
jnz recv_overrun ; Go if so.
jmp recv_no_overrun ; Go if not.

recv_overrun:
ifdef board_features
test board_features, BF_NIC_690
jz recv_overrun_2
mov is_overrun_690, 1
jmp recv_no_overrun

recv_overrun_2:
endif

; Count these things

add2 soft_rx_overruns,1

; and log them
SHOW_EVENT 'd'
mkle LE_RX_OVR_E, 0, 0, 0

; Get the command register TXP bit to test for incomplete transmission later

loadport ; Point at Chip's Command Reg
setport EN_CCMD ; ..
pause_
in al, dx
mov ah, al ; Save CR contents in ah for now

; Stop the NIC

pause_
mov al, ENC_STOP+ENC_NODMA
out dx, al ; Write "stop" to command register

; Wait 1.6ms for the NIC to stop transmitting or receiving a packet. National
; says monitoring the ISR RST bit is not reliable, so a wait of the maximum
; packet time (1.2ms) plus some padding is required.

longpause

; Clear the remote byte count registers

xor al,al
setport EN0_RCNTLO ; Point at byte count regs
out dx, al
setport EN0_RCNTHI
pause_
out dx, al

; check the saved state of the TXP bit in the command register

mov rcv_ovr_resend,al ; clear the resend flag
test ah,ENC_TRANS ; Was transmitter still running?
jz rcv_ovr_loopback ; Go if not running

; Transmitter was running, see if it finished or died

setport EN0_ISR ; point at the NIC ISR
pause_
in al,dx ; and get it
test al,ENISR_TX+ENISR_TX_ERR ; Did the transmitter finish?
jnz rcv_ovr_loopback ; one will be set if TX finished

; Transmitter did not complete, remember to resend the packet later.

mov rcv_ovr_resend,ah ; ah has at least ENC_TRANS set

; Put the NIC chip into loopback so It won't keep trying to receive into
; a full ring

rcv_ovr_loopback:
loadport ; resync setport
setport EN0_TXCR ; ..
mov al, ENTXCR_LOOP ; Put transmitter in loopback mode
pause_
out dx, al ; ..
setport EN_CCMD ; Point at Chip command reg
mov al, ENC_START+ENC_NODMA
pause_
out dx, al ; Start the chip running again

; Verify that there is really a packet to receive by fetching the current
; page pointer and comparing it to the next packet pointer.

mov al, ENC_NODMA+ENC_PAGE1 ; Set page one
pause_
out dx,al
setport EN1_CURPAG ; Get current page
pause_
in al,dx
pause_ ; Rewrite current page to fix SMC bug.
out dx,al
mov bl,al ; save it
mov save_curr,al
setport EN_CCMD ;
mov al, ENC_NODMA+ENC_PAGE0
pause_
out dx,al ; Back to page 0

mov al, next_packet ; get saved location for next packet

cmp al,bl ; Check if buffer emptry
jne rcv_ovr_rx_one ; OK go get the NIC header

; NO PACKET IN THE RING AFTER AN OVW INTERRUPT??? Can this ever happen?
; YES! if overrun happend between a receive interrupt and the when the
; current page register is read at the start of recv_frame.
; Count these things too.

add2 soft_rx_over_nd,1

jmp rcv_ovr_empty


; Get the NIC header for the received packet, and check it

rcv_ovr_rx_one:
mov ah,al ; make a byte address. e.g. page
mov bl,ah ; and save in bl
mov al,0 ; 46h becomes 4600h into buffer
mov cx,RCV_HDR_SIZE ; size of rcv_hdr
mov di,offset rcv_hdr ;point to header
movseg es,ds
call block_input
mov al, rcv_hdr+EN_RBUF_STAT ; Get the buffer status byte
test al,ENRSR_RXOK ; Is this frame any good?
jz rcv_ovr_ng ; Skip if not

;
; EVEN if the NIC header status is OK, I have seen garbaged NIC headers, so
; it doesn't hurt to range check the next packet pointer here.
;
mov al, rcv_hdr+EN_RBUF_NXT_PG ; Start of next frame
mov next_packet, al ; Save next packet pointer
cmp al,SM_RSTART_PG ; Below around the bottom?
jb rcv_ovr_ng ; YES - out of range
cmp al,sm_rstop_ptr ; Above the top?
jae rcv_ovr_ng ; YES - out of range

; ok to call rcv_frm

; This gets real tricky here. Because some wise user (like me) may attempt
; to call send_pkt from his receive interrupt service routine, you can't just
; blindly call rcv_frm from here because the NIC is now in LOOPBACK mode!
; to get around this problem I added a global flag that causes rcv_frm to
; make the first call to the users interrupt service routine, get a buffer,
; fill the buffer, BUT defer the second call until later. If a second call
; is required the flag will still be set after return from rcv_frm and you
; can make the upcall after the NIC has been taken out of loopback and has
; resent any required packet.

mov defer_upcall, 1 ; Defer upcalls until later. (may be cleared
; by rcv_frm)
call rcv_frm ; Yes, go accept it
jmp rcv_ovr_ok

rcv_ovr_ng:
or byte ptr soft_rx_err_bits,al
add2 soft_rx_errors,1
mkle LE_RX_ERR, 0, 0, 0 ; Log error packet - gft - 910521
;
; HAD TO ADD ERROR RECOVERY HERE. TO BLINDLY PROCEED AND ASSUME THE NEXT
; PACKET POINTR FROM THE NIC HEADER IS VALID IS INVITING DISASTER.
;
; Error recovery consists of killing and restarting the NIC. This drops all
; the packets in the ring, but thats better than winding up in the weeds!
;
; - gft - 910611

ifdef err_stop
call rcv_mode_1 ; for debugging, stop on error
jmp check_isr ; so we can dump the log
endif

; NO! no longer in memory
; call etopen_0 ; go back to the initial state
;
; Instead copy the last known current page pointer into the next packet pointer
; which will result in skipping all the packets from the errored one to where
; the NIC was storing them when we entered this ISR, but prevents us from
; trying to follow totally bogus next packet pointers through the card RAM
; space.
;
mov al, save_curr ; get the last known current page pointer
mov next_packet, al ; and use it as the next packet pointer
jmp check_isr ; then go handle more interrupts

rcv_ovr_ok:
; moved the next two instructions up to where I range check the
; next packet pointer above - gft - 910611
; mov al,rcv_hdr+EN_RBUF_NXT_PG ; Get pointer to next frame
; mov next_packet, al ; save it in next_packet - gft - 910603
mov al,next_packet ; Grap the next packet pointer
dec al ; Back up one page
cmp al,SM_RSTART_PG ; Did it wrap?
jae rcv_ovr_nwr2
mov al,sm_rstop_ptr ; Yes, back to end of ring
rcv_ovr_empty:
dec al
rcv_ovr_nwr2:
loadport ; Point at boundary reg
setport EN0_BOUNDARY ; ..
pause_
out dx, al ; Set the boundary
; When we get here we have either removed one packet from the ring and updated
; the boundary register, or determined that there really were no new packets
; in the ring.

; Clear the OVW bit in the ISR register.

loadport ; resync the setport macro
setport EN0_ISR ; point at the ISR
mov al, ENISR_OVER ; Clear the overrun interrupt bit
pause_
out dx, al

; Take the NIC out of loopback

setport EN0_TXCR ; Back to TX control reg
xor al, al ; Clear the loopback bit
pause_
out dx, al ; ..

; Any incomplete transmission to resend?

cmp rcv_ovr_resend,0
jz rcv_ovr_deferred ; no, go check for deferred upcall

; Yes, restart the transmission

setport EN_CCMD ; point at command register
mov al, ENC_TRANS+ENC_NODMA+ENC_START
pause_
out dx, al ; Start the transmitter

; If an upcall was deferred, make it now

rcv_ovr_deferred:
cmp defer_upcall,0
jz rcv_ovr_done
mov si,defer_si ; Recover pointer to destination
mov cx,defer_cx ; And it's this long
mov ds,defer_ds ; Tell client it's his source
assume ds:nothing
call recv_copy ; Give it to him
movseg ds,cs
assume ds:code

; log the end of overrun processing, with which flags were set

rcv_ovr_done:
ifdef debug
mov ax, WORD PTR rcv_ovr_resend
mov bx, WORD PTR defer_upcall
mkle LE_RX_OVR_X, ax, bx, 0
endif

mov defer_upcall,0 ; clear the defer upcall flag

; finally go back and check for more interrupts

jmp check_isr ; Done with the overrun case

; End of new receive overrun code

recv_no_overrun:
; Handle receive flags, normal and with error (but not overrun).
test al,ENISR_RX+ENISR_RX_ERR ; Frame received without overrun?
jnz recv_frame_0 ; Go if so.

ifdef board_features
cmp is_overrun_690, 0
jne recv_overrun_11
jmp recv_no_frame ; Go if not.
recv_overrun_11:
jmp recv_690_overrun
else
jmp recv_no_frame ; Go if not.
endif

;
; Move the label recv_frame down to below where the interrupts are cleared.
; This will cause the interrupts to be cleared only after being read in
; check_isr, instead of every time a packet is read from the ring. The way
; it used to work was find a RX or RX_ERR interrupt, clear both, check for
; packet really in ring (compare next packet pointer with current page reg)
; read packet and loop back to clear both ints. When all packets read from
; ring, loop back to check_isr. IF packets keep arriving as fast as or faster
; than we can read them, we never get back to check_isr to see if any higher
; priority interrupts occur (like OVW).
;
; The way it works now is find a RX or RX_ERR interrupt, clear both, check
; for packet really in ring and remember the current page register. Read
; packets from the ring until all packets received at the time the current
; page register was last read have been received, (comparing next packet
; pointer to the once read value of current page register). This eliminates
; both the (possibly unnecessary) resetting of the interrupts and the
; page switch and current page register read on a per packet basis. This should
; also eliminate possible problems with not doing the ring overflow processing
; in heavy traffic referred to in my comments below.
;
; - gft - 910611
;
recv_frame_0:

loadport ; Point at Chip's Command Reg
setport EN0_ISR ; Point at Interrupt status register
pause_
mov al, ENISR_RX+ENISR_RX_ERR
out dx, al ; Clear those requests
setport EN_CCMD ; ..
pause_
cli
mov al, ENC_NODMA+ENC_PAGE1+ENC_START
out dx, al ; Switch to page 1 registers
setport EN1_CURPAG ;Get current page of rcv ring
pause_
in al, dx ; ..
; mov ah, al ; Hold current page in AH
mov save_curr,al ; Hold last read current page register in
; memory instead - gft - 910611
setport EN_CCMD ; Back to page zero registers
pause_
mov al, ENC_NODMA+ENC_PAGE0+ENC_START
out dx, al ; Switch back to page 0 registers

; This becomes the loop back point to read packets from the ring.
; now only loop back and read until those packets received at the time
; the current page register is read above have been read.
; - gft - 910611

recv_frame:
mov al, next_packet ; Get saved pointer to next packet in ring
;
; change the next instruction to compare against the saved copy of the current
; page register and only read from the ring what was received up until the
; last read from the current page register - gft - 910611
;
; cmp al, ah ; Read all the frames?
cmp al, save_curr ; Read all the frames?
jne recv_more_frames; hacked jump code for addition of mkle
; macro below - gft -910521
; jmp recv_frame_break ; Finished them all
;
; changed jmp recv_frame_break to jmp check_isr after recv_frame_break was
; determined to be superfluous. See comments at recv_frame_break below.
; - gft - 910531
;
ifdef board_features
cmp is_overrun_690, 0
je recv_not_690_overrun

recv_690_overrun:
mov is_overrun_690, 0 ; clear overrun indicator
loadport
setport EN0_BOUNDARY ; rewrite bndry with itself
in al, dx
pause_
out dx, al

setport EN0_ISR ; Point at Interrupt status register
pause_
mov al, ENISR_OVER ; Clear overrun interrupt bit
out dx, al
call count_in_err ; Count the anomaly

recv_not_690_overrun:
endif
jmp check_isr ; Finished all receives, check for more
; interrupts.

recv_more_frames:

mov ah,al ; make a byte address. E.G. page
mov al,0 ; 46h becomes 4600h into buffer
mov bl,ah
mov cx,RCV_HDR_SIZE
mov di,offset rcv_hdr
movseg es,ds
call block_input
mov al, rcv_hdr+EN_RBUF_STAT ; Get the buffer status byte
test al,ENRSR_RXOK ; Good frame?
jz recv_err_no_rcv
;
; EVEN if the NIC header status is OK, I have seen garbaged NIC headers, so
; it doesn't hurt to range check the next packet pointer here.
;
mov al, rcv_hdr+EN_RBUF_NXT_PG ; Start of next frame
mov next_packet, al ; Save next packet pointer
cmp al,SM_RSTART_PG ; Below around the bottom?
jb recv_err_no_rcv ; YES - out of range
cmp al,sm_rstop_ptr ; Above the top?
jae recv_err_no_rcv ; YES - out of range

; ok to call rcv_frm

call rcv_frm ; Yes, go accept it
jmp recv_no_rcv
recv_err_no_rcv:
or byte ptr soft_rx_err_bits,al
add2 soft_rx_errors,1
;
; The code used to assume that after decoding the NIC header status
; byte as a receive error, the status, and next packet pointer values
; are actually valid. When using the NIC, this assumption may get one
; up that proverbial creek without a paddle. It has been my experience that
; IF bad status is read in the NIC header, the rest of the NIC header is
; not to be trusted. More likely you read a NIC header from the middle of a
; packet than actually receiving a frame, especially if the save error packet
; bit (SEP) in the receive configuration register is NOT set (it's NOT set in
; this driver).
;
; Error recovery consists of killing and restarting the NIC. This drops all
; the packets in the ring, but thats better than winding up in the weeds!
;
; - gft - 910611

ifdef err_stop
call rcv_mode_1 ; for debugging, stop on error
jmp check_isr ; so we can dump the log
endif

; NO! no longer in memory
; call etopen_0 ; go back to the initial state
;
; Instead copy the last known current page pointer into the next packet pointer
; which will result in skipping all the packets from the errored one to where
; the NIC was storing them when we entered this ISR, but prevents us from
; trying to follow totally bogus next packet pointers through the card RAM
; space.
;
mov al, save_curr ; get the last known current page pointer
mov next_packet, al ; and use it as the next packet pointer
jmp check_isr ; then go handle more interrupts

recv_no_rcv:
; moved the next two instructions up to where I range check the
; next packet pointer above - gft - 910611
; mov al, rcv_hdr+EN_RBUF_NXT_PG ; Start of next frame
; mov next_packet, al ; Save next packet pointer
mov al,next_packet ; Grap the next packet pointer
dec al ; Make previous page for new boundary

; Here's another little bug, which was exposed when when I expanded the
; recieve packet ring for HP 16 bit cards (from 06h-7fh to 06h-ffh).
; The jge instruction is for SIGNED comparisons and failed when the next
; packet pointer got into the range 81h-0feh. Changed the below jge to
; a jae for unsigned comparsions. - gft - 910610
; (Also scanned hppclan.asm and 8390.asm for improper use of signed
; comparisons and found no others.)

cmp al, SM_RSTART_PG ; Wrap around the bottom?
; jge rcv_nwrap4
jae rcv_nwrap4 ; - gft - 910610
mov al, sm_rstop_ptr ; Yes
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