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



  3 Responses to “Category : Network Files
Archive   : PKTD11A.ZIP
Filename : 8390.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/