; Packet driver for Novell's NE/2
; Written by:
;	Eric Henderson
;	Brigham Young University
;	
; Based on the "generic" packet driver by Russell Nelson with help
; from the western digital pd by Russell Nelson.
; 80[123]86 processor support lifted from 3com driver by permission
;	from Russell Nelson
;
;   Portions (C) Copyright 1990 BYU

version	equ	5

.286
	include	defs.asm

code	segment	byte public
	assume	cs:code, ds:code

;*****************************************************************************
;
;	NE/2 controller board offsets
;	IO port definition (BASE in io_addr)
;*****************************************************************************
ADDROM  EQU	10h			; LAN Address ROM
RACK	EQU	10h			; NE2 Port Window 
NERESET EQU	20h			; Issue a read for reset

; 8390 LAN Controller (page0) register offset for read and write 
CMDR	EQU	00h			; command register for read & write
CLDA0	EQU	01h			; current local dma addr 0 for read
PSTART	EQU	01h			; page start register for write
CLDA1	EQU	02h			; current local dma addr 1 for read
PSTOP	EQU	02h			; page stop register for write
BNRY	EQU	03h			; boundary reg for rd and wr
TSR	EQU	04h			; tx status reg for rd
TPSR	EQU	04h			; tx start page start reg for wr	
NCR	EQU	05h			; number of collision reg for rd
TBCR0	EQU	05h			; tx byte count 0 reg for wr
FIFO	EQU	06h			; FIFO for rd
TBCR1	EQU	06h			; tx byte count 1 reg for wr
ISR	EQU	07h			; interrupt status reg for rd and wr
CRDA0	EQU	08h			; current remote dma address 0 for rd
RSAR0	EQU	08h			; remote start address reg 0  for wr
CRDA1	EQU	09h			; current remote dma address 1 for rd
RSAR1	EQU	09h			; remote start address reg 1 for wr
RBCR0	EQU	0Ah			; remote byte count reg 0 for wr
RBCR1	EQU	0Bh			; remote byte count reg 1 for wr
RSR	EQU	0Ch			; rx status reg for rd
RCRWD	EQU	0Ch			; rx configuration reg for wr
CNTR0	EQU	0Dh			; tally cnt 0 for frm alg err for rd
TCR	EQU	0Dh			; tx configuration reg for wr
CNTR1	EQU	0Eh			; tally cnt 1 for crc err for rd
DCR	EQU	0Eh			; data configuration reg for wr
CNTR2	EQU	0Fh			; tally cnt 2 for missed pkt for rd
IMR	EQU	0Fh			; interrupt mask reg for wr
; 8390 LAN Controller (page1) register offset for read and write 
PAR0	EQU	01h 			; physical addr reg 0 for rd and wr
PAR1	EQU	02h 			; physical addr reg 1 for rd and wr
PAR2	EQU	03h 			; physical addr reg 2 for rd and wr
PAR3	EQU	04h 			; physical addr reg 3 for rd and wr
PAR4	EQU	05h 			; physical addr reg 4 for rd and wr
PAR5	EQU	06h 			; physical addr reg 5 for rd and wr
CURR	EQU	07h			; current page reg for rd and wr
MAR0	EQU	08h			; multicast addr reg 0 fro rd and WR
MAR1	EQU	09h			; multicast addr reg 1 fro rd and WR
MAR2	EQU	0Ah			; multicast addr reg 2 fro rd and WR
MAR3	EQU	0Bh			; multicast addr reg 3 fro rd and WR
MAR4	EQU	0Ch			; multicast addr reg 4 fro rd and WR
MAR5	EQU	0Dh			; multicast addr reg 5 fro rd and WR
MAR6	EQU	0Eh			; multicast addr reg 6 fro rd and WR
MAR7	EQU	0Fh			; multicast addr reg 7 fro rd and WR

;***********************************************************************
;
;	8003 control register operations
;***********************************************************************

MSK_RESET	EQU	80h	        ; reset LAN controller
MSK_ENASH	EQU	40h		; enable PC access to shared mem
MSK_DECOD	EQU	3Fh 		; ???? memory decode bits, corresponding
					; to SA 18-13. SA 19 assumed to be 1
;***********************************************************************
;
;	8390 CMDR MASK
;***********************************************************************

MSK_STP		EQU	01h		; software reset, take 8390 off line
MSK_STA		EQU	02h		; activate the 8390 NIC
MSK_TXP		EQU	26h		; initial txing of a frm  (With DMA)
MSK_RD2		EQU	20h		; abort remote DMA
MSK_PG0		EQU	00h		; select register page 0
MSK_PG1		EQU	40h		; select register page 1
MSK_PG2		EQU	80h		; select register page 2
MSK_DMA_RD	EQU	0ah		; start DMA read
MSK_DMA_WR	EQU	12h		; start DMA write

;***********************************************************************
;
;	8390 ISR & IMR MASK
;***********************************************************************

MSK_PRX  EQU	01h		; rx with no error
MSK_PTX  EQU	02h		; tx with no error
MSK_RXE  EQU	04h		; rx with error
MSK_TXE  EQU	08h		; tx with error
MSK_OVW  EQU	10h		; overwrite warning
MSK_CNT  EQU	20h		; MSB of one of the tally counters is set
MSK_RDC  EQU	40h		; remote dma completed
MSK_RST	 EQU	80h		; reset state indicator
MaskByte		equ	0
UnmaskByte		equ	1fh
InterruptMask		equ	0fh

;***********************************************************************
;
;	8390 DCR MASK
;***********************************************************************

MSK_WTS EQU	01h		; word transfer mode selection
MSK_BOS	EQU	02h		; byte order selection
MSK_LAS	EQU	04h		; long addr selection
MSK_BMS	EQU	08h		; burst mode selection
MSK_ARM	EQU	10h		; atuoinitialize remote
MSK_FT00 EQU	00h		; burst lrngth selection
MSK_FT01 EQU	20h		; burst lrngth selection
MSK_FT10 EQU	40h		; burst lrngth selection
MSK_FT11 EQU	60h		; burst lrngth selection

;***********************************************************************
;
;	8390 RCR MASK
;***********************************************************************

MSK_SEP EQU	01h		; save error pkts
MSK_AR 	EQU	02h		; accept runt pkt
MSK_AB 	EQU	04h		; accept broadcast 
MSK_AM 	EQU	08h		; accept multicast 
MSK_PRO	EQU	10h		; promiscuous physical
				; accept all pkt with physical adr
MSK_MON EQU	20h		; monitor mode

;***********************************************************************
;
;	8390 TCR MASK
;***********************************************************************

MSK_CRC EQU	01h		; inhibit CRC, do not append crc
MSK_LB01 EQU	06h		; encoded loopback control
MSK_ATD	EQU	08h		; auto tx disable
MSK_OFST EQU	10h		; collision offset enable 

;***********************************************************************
;
;	8390 RSR MASK
;***********************************************************************

SMK_PRX  EQU	01h		; rx without error
SMK_CRC  EQU	02h		; CRC error
SMK_FAE  EQU	04h		; frame alignment error
SMK_FO   EQU	08h		; FIFO overrun
SMK_MPA  EQU	10h		; missed pkt
SMK_PHY  EQU	20h		; physical/multicase address
SMK_DIS  EQU	40h		; receiver disable. set in monitor mode
SMK_DEF	 EQU	80h		; deferring

;***********************************************************************
;
;	8390 TSR MASK
;***********************************************************************

SMK_PTX  EQU	01h		; tx without error
SMK_DFR  EQU	02h		; non deferred tx
SMK_COL  EQU	04h		; tx collided
SMK_ABT  EQU	08h		; tx aboort because of excessive collisions
SMK_CRS  EQU	10h		; carrier sense lost
SMK_FU   EQU	20h		; FIFO underrun
SMK_CDH  EQU	40h		; collision detect heartbeat
SMK_OWC	 EQU	80h		; out of window collision

;***********************************************************************
;
;	on board memory constant definition
;***********************************************************************
; for rcv buff ring of onboard mem
START_PG EQU	46h	      		; start at page 46
STOP_PG  EQU	80h			; end at page 80 
; for tx buff of shr mem
TB_SIZE EQU	1			; number of tb buff in shr mem
TB_PGNO EQU	6			; number of pages in one tb buff

EIGHTBITSLOT	db	0		;8-bit machine flag
Path		dw	?
RxPath		dw	?

	public	int_no, io_addr
int_no		db	3,0,0,0		;must be four bytes long for get_number.
io_addr		dw	1000h,0		; I/O address for card (jumpers)
m_channel 	dw 	0		; micro channel flag	

rd_NE	MACRO	port
	mov	DX, CS:io_addr
	add	DX, port		; DX contains address of port
	in	AL, DX			; AL contains data read from port
	ENDM

wr_NE	MACRO	port
	mov	DX, CS:io_addr
	add	DX, port		; DX contains address of port
	out	DX, AL			; AL contains data to be written to port
	ENDM

ReceiveHeaderStructure	struc
   RReceiveStatus	db	?
   RNextBuffer		db	?
   RByteCount		dw	?

   RDestinationAddress	db	6 dup(?)
   RSourceAddress	db	6 dup(?)
   RPacketLength	dw	?
   RChecksum		dw	?
   RRPacketLength	dw	?
   RTranControl		db	?
   RHPacketType		db	?
   RDestinationNet	db	4 dup(?)
   RDestinationNode	db	6 dup(?)
   RDestinationSocket	dw	?
ReceiveHeaderStructure	ends

ReceiveHeader	ReceiveHeaderStructure	<>

Current				equ	InterruptStatus
CurrentDMA0			equ	RemoteStartAddress0
CurrentDMA1			equ	RemoteStartAddress1
ForRSTBit			equ	80h

NIC	struc
	Command			db	?
	PageStart		db	?
	PageStop		db	?
	Boundry			db	?
	TransmitStatus		db	?
	TransmitByteCount0	db	?
	TransmitByteCount1	db	?
	InterruptStatus		db	?
	RemoteStartAddress0	db	?
	RemoteStartAddress1	db	?
	RemoteByteCount0	db	?
	RemoteByteCount1	db	?
	ReceiveConfiguration	db	?
	TransmitConfiguration	db	?
	DataConfiguration	db	?
	IntMask			db	?
	DataPort		db	?
				db	15 dup (?)
	Reset			db	?
NIC	ends

BLUEBOOK	equ	1
IEEE8023	equ	11
	public	driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class	db	BLUEBOOK, IEEE8023,  0	;from the packet spec
driver_type	dw	0ffffh		;Wild card matches any type
driver_name	db	'NE/2',0	;name of the driver.
driver_function	db	2
parameter_list	label	byte
	db	1	;major rev of packet driver
	db	9	;minor rev of packet driver
	db	14	;length of parameter list
	db	EADDR_LEN	;length of MAC-layer address
	dw	GIANT	;MTU, including MAC headers
	dw	MAX_MULTICAST * EADDR_LEN	;buffer size of multicast addrs
	dw	0	;(# of back-to-back MTU rcvs) - 1
	dw	0	;(# of successive xmits) - 1
	dw	0	;Interrupt # to hook for post-EOI
			;processing, 0 == none,

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

	public	rcv_modes
rcv_modes	dw	7		;number of receive modes in our table.
		dw	0		;there is no mode 1.
		dw	rcv_mode_1
		dw	rcv_mode_2
		dw	rcv_mode_3
		dw	rcv_mode_4
		dw	rcv_mode_5
		dw	rcv_mode_6
rxcr_bits       db      MSK_AB		; Default to ours plus multicast


	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


	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
; get txblk length						  
	inc	cx
	and	cl, 0feh
	cmp	CX, RUNT
	jnb	length_ok
	mov	cx, RUNT
length_ok:
	cmp	cx, GIANT
	jbe	length1_ok
	mov	dh, NO_SPACE
	stc	
	jmp	count_out_err
length1_ok:
;new stuff
	mov	AX, CX
	wr_NE	TBCR0			; Transmit byte count
	mov	al, ah
	wr_NE	TBCR1
	mov	al, 0
	wr_NE	RSAR0
	mov	al, 40h
	wr_NE	RSAR1
	mov	ax, cx
	wr_NE	RBCR0			; Remote byte count
	mov	al, ah
	wr_NE	RBCR1

; Clear out DMA complete interrupt
	mov	al, MSK_PG0		
	wr_NE	CMDR
	mov	al, 40h
	wr_NE	ISR

	mov	al, MSK_DMA_WR
	wr_NE	CMDR

	mov	DX, CS:io_addr
	add	DX, RACK		; DX has address NE/2 Port window 

	shr	cx, 1
	rep	outsw

	xor	cx, cx			; Prevent infinite loop
WaitForDMAComplete:
	rd_NE	ISR
	test	al, 40h
	jnz	DMAComplete
	loop	WaitForDMAComplete

DMAComplete:
	mov	al, MSK_TXP
	wr_NE	CMDR
	clc
exit_now:
	ret


	public	set_address
set_address:
;enter with ds:si -> Ethernet address, CX = length of address.
;exit with nc if okay, or cy, dh=error if any errors.
;This proc will set the first CX bytes of the ethernet address, leaving
; the rest unchanged.
	assume	ds:nothing

	cmp	cx, 6
	jbe	set1
	mov	dh, BAD_ADDRESS
	stc
	ret
set1:
	mov	al, 61h
	wr_NE	CMDR
	mov	DX, CS:io_addr
	add	DX, PAR0		; DX has address 8390 Phys Address Reg
AddressToChip1:
	lodsb
	out	dx, al
	inc	dx
	in	al,61h			;wait a little while...
	loop	AddressToChip1
	pop	di
	pop	es

	mov 	al, 21h
	wr_NE 	CMDR
	clc
	ret

; Routines to set address filtering modes in the DS8390
;	This was lifted from R. Clements' WD PD
rcv_mode_1:     ; Turn off receiver
	mov al,	MSK_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,	MSK_AB     ; Set four ours plus broadcasts
	jmp short rcv_mode_set
rcv_mode_4:     ; Mode 3 plus selected multicast packets
	mov al,	MSK_AB+MSK_AM ; Ours, bcst, and filtered multicasts
	mov     mcast_all_flag,0
	jmp short rcv_mode_set
rcv_mode_5:     ; Mode 3 plus ALL multicast packets
	mov al,	MSK_AB+MSK_AM; 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,	MSK_AB+MSK_AM+MSK_PRO
	mov     mcast_all_flag,1
rcv_mode_set:
	push    ax              ; Hold mode until masks are right
	call    set_8390_multi  ; Set the multicast mask bits in chip
	pop     ax
	WR_NE	RCRWD
	mov     rxcr_bits,al    ; Save a copy of what we set it to
	ret

	public	set_multicast_list
set_multicast_list:
;enter with ds:si ->list of multicast addresses, ax = number of addresses,
;  cx = number of bytes.
;return nc if we set all of them, or cy,dh=error if we didn't.
	mov	dh,NO_MULTICAST
	stc
	ret

; Set the multicast filter mask bits in case promiscuous rcv wanted
;	This was lifted from R. Clements' WD PD
set_8390_multi:
	
	mov	al,MSK_RD2+MSK_PG1
	WR_NE	CMDR 
	mov	cx,8		; Eight bytes of multicast filter
	mov	si,offset mcast_list_bits  ; Where bits are, if not all ones
	push    cs
	pop     ds
	cli			; Protect from irq changing page bits

	mov	DX, CS:io_addr
	add	DX, MAR0
	mov	al,mcast_all_flag  ; Want all ones or just selected bits?
	or	al,al
	jz	set_mcast_2     ; z = just selected ones
	mov	al,0ffh		; Ones for filter
set_mcast_all:
	out	dx, al
	inc	dl		; Step to next one
	jmp	$+2		; limit chip access rate
	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
	jmp	$+2		; limit chip access rate
	loop	set_mcast_2
set_mcast_x:
	mov	al, MSK_RD2+MSK_PG0
	WR_NE	CMDR
	sti			; OK for interrupts now
	ret


	public	terminate
terminate:
	ret


	public	reset_interface
reset_interface:
;reset the interface.
	assume	ds:code
	mov	al, MSK_STP + MSK_RD2	
	wr_NE	CMDR
	mov al,	0ffh		; Clear all pending interrupts
	wr_NE	ISR
	xor al,	al		; Turn off all enables
	wr_NE	IMR
	wr_NE	NERESET		; Hard reset NE/2
	wr_NE	NERESET
	wr_NE	NERESET
	wr_NE	NERESET
	wr_NE	NERESET
	wr_NE	NERESET
	wr_NE	NERESET
	wr_NE	NERESET

	rd_NE	NERESET

	mov	al, 21h
	wr_NE	CMDR
	ret


;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.
	assume	ds:code

; read irq status register
	mov	al, MaskByte
	wr_NE	IMR
	sti
rd_isr:
	rd_NE	ISR			; read isr into AL
	and	AL, 3Fh			; check bit0-bit5, if all 1's no irq
	cmp	AL, 0			; if any irq
	jne	tst_ovw			; some irq
	cli
	mov	al, UnmaskByte
	wr_NE	IMR
	ret				; no more irq, exit

; process OVW	(OVW = 1)       
;    may report error here
tst_ovw:
	test	AL, MSK_OVW		; if OVW irq
	jnz	prcs_ov			; OVW (OVW = 1) 
	jmp	test_rx			; no OVW (OVW = 0) 

; **************************************************************
; follow the DP8390 datasheet addendum to handle the buff ring overflow
prcs_ov:
; 1. issue a STOP mode command
	mov	AL, MSK_STP + MSK_RD2
	wr_NE	CMDR
; 6. remove one packet from the ring
	rd_NE	BNRY			; BNRY in AL
	add	AL, 1			; start page of frm in AL
	cmp	AL, STOP_PG		; check boundary
        jne	get1
	mov	AL, START_PG		
; ring not empty
get1:
	mov	BH, AL			; BX has the rx_frm pointer
	mov	al, SIZE ReceiveHeader
	wr_NE	RBCR0
	xor	al, al
	wr_NE	RBCR1
	wr_NE	RSAR0
	mov	al, bh
	wr_NE	RSAR1
	mov	al, MSK_DMA_RD
	wr_NE	CMDR

	mov	DX, CS:io_addr
	add	DX, RACK		; DX has address NE/2 Port window 
	mov	di, OFFSET ReceiveHeader
	mov	ax, cs
	mov	es, ax
	mov	cx, SIZE ReceiveHeader
	shr	cx, 1	
Receive1:
rep	insw

SkipReceive1:
	mov	bx, offset ReceiveHeader
	mov	AL, CS:[BX]		; AL has the status byte
	test	AL, SMK_PRX		; if rx good
	jz	fd_bnr			; rx error, drop frm by forward bnry

; good frm, call _rcv_frm
	call 	_rcv_frm   		

fd_bnr:					;drop frm by forward BNRY
	mov	al, cs:ReceiveHeader.RNextBuffer	; al = next pointer 
	sub	AL, 1			; new BNRY in AL
	cmp	AL, START_PG		; check boundary
	jae	wrbnr			; unsigned arithmetic
	mov	AL, STOP_PG - 1		;
;	dec	AL			;
wrbnr:
	wr_NE	BNRY
; 2. clear the remote byte count registers (RBCR0,RBCR1)
	xor	AL, AL
	wr_NE	RBCR0
	wr_NE	RBCR1
; 3. poll the ISR for the RST bit
plisr:
	mov	cx, 0ffffh
	rd_NE	ISR
	test	AL, MSK_RST
	jnz	plisr_ok
	loop	plisr		; keep polling until the RST bit set
; 4. place the NIC in loopback mode (mode 1 or 2) by writing 02 or 04 to TCR
plisr_ok:
	mov	AL, 02h		; put it in mode 2 (internal loopback)
	wr_NE	TCR
; 5. issue start mode command
	mov	AL, MSK_STA + MSK_RD2
	wr_NE	CMDR
; 7. out from loopback mode by writing 00 to TCR
	xor	AL, AL
	wr_NE	TCR		; normal operation configuration
; clear OVW in ISR		      
	mov	AL, MSK_OVW 
	wr_NE	ISR			; clear OVW
	call	count_in_err		; increment overflow counter
	jmp	rd_isr			; back to the top


; end of the modification 
; *****************************************************
;	
;process PRX and RXE
;
test_rx:	    
	test 	AL, MSK_RXE		
	jnz	prcs_rxe		; RXE = 1
	test  	AL, MSK_PRX	
	jnz	prcs_rx	     		; PRX = 1
	jmp	test_tx

prcs_rxe:
	call	count_in_err
	mov	al, MSK_RXE
	wr_NE	ISR
	jmp	rd_isr
prcs_rx:
	mov	AL, MSK_PG1 + MSK_RD2	; read CURR reg
	wr_NE	CMDR
	rd_NE	CURR
	mov	BL, AL			; CURR in BL 
	mov	AL, MSK_PG0 + MSK_RD2	; read BNRY reg
	wr_NE  CMDR
	rd_NE	BNRY			; BNRY in AL
	add	AL, 1			; start page of frm in AL
	cmp	AL, STOP_PG	 	; check boundary
	jne	go_cmp
	mov	AL, START_PG 		; 		
go_cmp:
	cmp	AL, BL			
	jne	Ring_Not_Empty
	jmp	rd_isr			; buff ring empty

Ring_Not_Empty:
	wr_NE	RSAR1
	xor	al, al
	wr_NE	RBCR1
	wr_NE	RSAR0
	mov	al, SIZE ReceiveHeader
	wr_NE	RBCR0
	mov	al, MSK_DMA_RD
	wr_NE	CMDR

	mov	DX, CS:io_addr
	add	DX, RACK		; DX has address NE/2 Port window 
	mov	di, OFFSET ReceiveHeader
	mov	ax, cs
	mov	es, ax
	mov	cx, SIZE ReceiveHeader
	shr	cx, 1

Receive2:
rep	insw

SkipReceive2:
	mov	bx, offset ReceiveHeader
	mov	AL, CS:[BX]		; AL has the status byte
	test	AL, SMK_PRX		; if rx good
	jz	fd_bnry			; rx error, drop frm by forward bnry

; good frm, call _rcv_frm
   	call 	_rcv_frm   		

fd_bnry:				; drop frm by forward BNRY
	mov	al, CS:ReceiveHeader.RNextBuffer	; al = next pointer 
	sub	AL, 1			; new BNRY in AL
	cmp	AL, START_PG		; check boundary
	jae	wrbnry			; unsigned arithmetic
	mov	AL, STOP_PG - 1		;

wrbnry:
	wr_NE	BNRY
	mov	al, MSK_PRX
	wr_NE	ISR
	jmp	prcs_rx	 

;process PTX and TXE
test_tx:
	test  	AL, MSK_PTX	
	jnz	prc_ptx	     		; PTX = 1
	test 	AL, MSK_TXE		
	jnz	prc_txe			; TXE = 1
	jmp	test_cnt
      

; process tx good, update txok, lostcrs, collsn
prc_ptx:				; tx good 
	rd_NE	TSR
	test	AL, SMK_CRS		; is crs set in TSR
	jz	nocrs			; no		       
nocrs:	
	rd_NE	NCR			; read number of collision in AL
	mov	AL, MSK_PTX
	wr_NE	ISR			; clear PTX
	jmp	rd_isr

; process tx error, update .txbad .underrun
prc_txe:				; tx bad
	call	count_out_err	
	rd_NE	TSR
	test	AL, SMK_FU		; it fu set in TSR
	jz	nofu			; no		       
nofu:								  
    	mov	AL, MSK_TXE
	wr_NE	ISR			; clear PTX
	jmp	rd_isr

; process counter overflow, update .algerr .crcerr .???(missed pkt)
test_cnt:
	test  	AL, MSK_CNT	
	jnz	prc_cnt			; yes, process cnt
	jmp	rd_isr	     		; no CNT irq, back to the top
; process CNT			
prc_cnt:
	mov	AL, MSK_CNT
	wr_NE	ISR			; clear CNT
	jmp	rd_isr			; back to the top

; End of RECV

_rcv_frm:
	
; read byte count 
	mov 	cx, cs:ReceiveHeader.RByteCount	; Extract size of frame
	cmp	CX, 5dch
	jna	rxlen_ok
	jmp	rcv_ret
rxlen_ok:
	sub	CX, 4			; 4 control bytes
	mov	AX, cs
	mov	ES, AX
	mov 	di, offset cs:ReceiveHeader.RPacketLength
	mov	dl, BLUEBOOK		;default 
	mov	ax, es:[di]
	xchg	ah, al
	cmp 	ax, 1500
	ja	BlueBookPacket
	mov	di, offset cs:ReceiveHeader.RChecksum
	mov	dl, IEEE8023
BlueBookPacket:

	push	cx			; Save frame size
	push	es

	mov ax,	cs			; Set ds = code
	mov ds,	ax
	assume	ds:code
	call	recv_find		; See if type and size are wanted
					; 	CX = packet length
					;    ES:DI = packet type
					;    ES:DI = packet type

	pop	ds			; RX page pointer in ds now
	assume	ds:nothing
	pop	cx

	cld				; Copies below are forward
	mov ax,	es			; Did recv_find give us a null pointer?
	or  ax,	di			; ..
	je	no_buff			; If null, don't copy the data	
has_buf:  

;Tell DMA to copy the whole packet for us
	mov	ax, 4
	wr_NE	RSAR0		; Don't copy  4 8390 control bytes
	mov	ax, cx		; CX has byte count
	wr_NE	RBCR0		; LSB first
	mov	al, ah
	wr_NE	RBCR1		; Now MSB

	mov	al, MSK_DMA_RD  ; Issue DMA read command
	wr_NE	CMDR

; copy from NE/2 on board memory using the window RACK
; use IN and stosb to do the copy, IN -> AX -> ES:DI (CX has byte count)	       			 

copynow:
	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	DX, CS:io_addr
	add	DX, RACK		; DX has address NE/2 Port window (?)

	mov	si, cx
	shr	cx, 1
	rep	insw
	test	si, 1
	jz	call_rc
	in	ax, dx
	stosb
call_rc:
	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
	jmp	short rcv_ret

; no system buff availble to hold rx frm
no_buff:
rcv_ret:
	movseg	ds,cs
	assume	ds:code
	ret


	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


	public	usage_msg
usage_msg	db	"usage: NE2 [options] <packet_int_no>",CR,LF,'$'

	public	copyright_msg
copyright_msg	db	"Packet driver for NE/2 version "
		db	'0'+(majver / 10),'0'+(majver mod 10),".",'0'+version,CR,LF
		db	"Portions Copyright 1990, Eric Henderson, BYU",CR,LF,'$'
int_no_name	db	"Interrupt number ",'$'
io_addr_name	db	"I/O port ",'$'
no_board_msg:
	db	"NE/2 apparently not present at this IO address.",CR,LF,'$'
HardwareFailure:
	db	"The NE/2 is not responding.",CR,LF,'$'

	extrn	set_recv_isr: near


;enter with si -> argument string, di -> word to store.
;if there is no number, don't change the number.
;	extrn	decout: near
;	extrn	hexout: near

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

	public	parse_args
print_args:
	mov	dx,offset int_no_name
	mov	ah,9
	int	21h
	mov	di,offset int_no
	mov	ax,[di]			;print the number in hex.
	mov	dx,[di+2]
;	call	hexout
	mov	dl,' '
	mov	ah, 2
	int	21h
	mov	dl,'('
	mov	ah, 2
	int	21h
	mov	ax,[di]			;print the number in decimal.
	mov	dx,[di+2]
;	call	decout
	mov	dl,')'
	mov	ah, 2
	int	21h
	mov	dl,CR
	mov	ah, 2
	int	21h
	mov	dl,LF
	mov	ah, 2
	int	21h
	mov	dx,offset io_addr_name
	mov	ah,9
	int	21h
	mov	di,offset io_addr
	mov	ax,[di]			;print the number in hex.
	mov	dx,[di+2]
;	call	hexout
	mov	dl,' '
	mov	ah, 2
	int	21h
	mov	dl,'('
	mov	ah, 2
	int	21h
	mov	ax,[di]			;print the number in decimal.
	mov	dx,[di+2]
;	call	decout
	mov	dl,')'
	mov	ah, 2
	int	21h
	mov	dl,CR
	mov	ah, 2
	int	21h
	mov	dl,LF
	mov	ah, 2
	int	21h
parse_args:
	ret

	extrn	etopen_diagn: byte

BoardNotResponding:
	mov	dx, offset HardwareFailure
	mov	etopen_diagn, 35
	jmp	short error_wrt
bad_cksum:
no_memory:
	mov	dx,offset no_board_msg
	mov	etopen_diagn,37
error_wrt:
	stc
	ret

SlotSelectRegister		equ	96h
POS0				equ	100h
POS1				equ	101h
POS2				equ	102h
NE2ID				equ	7154h

IRQConfiguration		label	byte	;possible IRQ settings
				db	3
				db	4
				db	5
				db	9

IOConfiguration  		label	word	;possible I/O Base addresses
				dw	1000h
				dw	2020h
				dw	8020h
				dw	0A0A0h
				dw	0B0B0h
				dw	0C0C0h
				dw	0C3D0h

	public	etopen
etopen:
;if all is okay,
	cli					;Find the NE/2 NIC
	mov 	cl, 7				; channel position (8=1st slot
						; F=8th slot)
NextSlot:
	inc	cl
	cmp	cl,10h				;check 8 I/O slots
	jne	NoHardwareFailure
	jmp	BoardNotResponding
NoHardwareFailure:
	mov	al, cl
	out	SlotSelectRegister, al		;select card slot
	mov 	dx, POS1
	in	al, dx				;check for NE2 ID
	mov	ah, al
	mov	dx, POS0
	in	al, dx
	cmp	ax, NE2ID
	jne	NextSlot

	mov	dx, POS2			;read configuration port
	in	al, dx					

	test	al, 01				;test to see if card enable
	jz	NextSlot			; bit is set

	mov	bl, al
	and	bx, 000Eh			;mask all but I/O base addr.

	jz	NextSlot			;check next slot if no option
						; setting in POS 2
	dec	bx
	dec	bx
	mov	dx, IOConfiguration[bx]		; IO address
	mov	io_addr, dx			;save the base I/O address

	test	al, 10h				;if boot prom enabled then

SetIRQ:
	shr	al, 5				;load the hardware config.
	and	ax, 3				; table with the IRQ line
	mov	bx, ax				; value and string
	mov	dl, IRQConfiguration[bx]
	mov	int_no, dl			;save interrupt level
;	call	print_args

	call	set_recv_isr	; Put ourselves in interrupt chain

	; reset NE/2 board 
	cli
	wr_NE	NERESET
	wr_NE	NERESET
	wr_NE	NERESET
	wr_NE	NERESET
	wr_NE	NERESET
	wr_NE	NERESET
	wr_NE	NERESET
	wr_NE	NERESET

	rd_NE	NERESET

	mov	al, 21h
	wr_NE	CMDR

	; Test to see if we're OK
	rd_NE	CMDR
	cmp	al, 21h
	je	WeBeOK
	jmp	BoardNotResponding

WeBeOK:
	mov	al, 49h			;Word mode
	wr_NE	DCR		
	mov	al, 0
	wr_NE	RBCR0
	wr_NE	RBCR1
	mov	al, 4
	wr_NE	RCRWD
	mov	al, 02
	wr_NE	TCR		
	mov	al, START_PG
	wr_NE	PSTART		
	wr_NE	BNRY		
	mov	al, STOP_PG
	wr_NE	PSTOP		

	mov	al, 0ffh
	wr_NE	ISR		
	mov	al, 01fh
	wr_NE	IMR

	mov	al, 61h
	wr_NE	CMDR		
	mov	al, START_PG + 1
	wr_NE	CURR
	mov	al, 21h
	wr_NE	CMDR		



	mov	DX, CS:io_addr
	add	DX, RACK		; DX has address NE/2 Port window 

	mov 	bx, -1				;setup for Nic's memory test

	mov	bp, CS:io_addr
  	call	RAMTest  			;16 bit memory test

    	jz	memory_OK			;if error, report it
	jmp	no_memory

memory_OK:
	; set up rom_address from addr ROM.
	mov	al, 12			;read 6 bytes from PROM (word mode=12)
	wr_NE	RBCR0
	mov	al, 0
	wr_NE	RBCR1
	wr_NE	RSAR0
	wr_NE	RSAR1
	mov	al, MSK_DMA_RD
	wr_NE	CMDR

	mov	DX, CS:io_addr
	add	DX, RACK		; DX has address NE/2 Port window (?)
	mov	di, OFFSET rom_address
	mov	ax, cs
	mov	es, ax
	mov	cx, 6
GetEnetAddress:
	in	al, dx
	stosb
	loop	GetEnetAddress

ModeCheckDone:
	mov	ax, cs
	mov	ds, ax
	mov	si, OFFSET rom_address
	mov	cx, 6
	call	set_address

	mov 	al, 21h
	wr_NE 	CMDR

SetTXPage:
	mov     al,0C0h                		;C000h is used as TX
        wr_NE	TPSR				;page because of
        					;spurious writes to 
						;Ram during transmits
						;when collisions occur
	mov	al, 22h
	jmp	$+2
	wr_NE	CMDR	

	mov	al, 0
	wr_NE	TCR

	mov	al,0				;deselect all I/O channels
	out	SlotSelectRegister,al

	sti
	clc
	ret

;**********************************************************************
;
;	RAM Test
;
;	assumes:
;		BX	has the test value
;		BP	points to IOBase
;		NIC (8390) has been set to ignore activity on wire
;		  (loopback mode) and is set to page 0
;
;	returns:
;		Z flag set if no error
;		BP	preserved.
;		NIC set to page 0
;
;**********************************************************************

RAMTest	proc	near

	lea	dx, [bp].RemoteStartAddress0	;point at start of RAM
	xor	al, al
	out	dx, al
        inc     dx
	mov	al, 40h
	out	dx, al

	inc	dx				;set byte count=16k
	xor	al, al				; (both 8k RAM chips)
	out	dx, al
	inc	dx
	mov	al, 40h
	out	dx, al

	lea	dx, [bp].Command		;write in all RAM locations
	mov	al, MSK_DMA_WR			; what we send
	out	dx, al

	lea	dx, [bp].DataPort		;point at Nic's data port

	mov	cx, 2000h			;set count to 8k words

	mov	ax, bx				;read in test value

WriteLoop:
	inc	ax				;adjust test pattern
	out	dx, ax				;send it out to Nic
	loop	WriteLoop

						;now read back and compare
						; test pattern from NIC

	lea	dx, [bp].RemoteStartAddress0	;point at start of RAM
	xor	al, al
	out	dx, al
        inc     dx
	mov	al, 40h
	out	dx, al

	inc	dx				;set byte count = 16k
	xor	al, al
	out	dx, al
	inc	dx
	mov	al, 40h
	out	dx, al

	lea	dx, [bp].Command		;tell NIC to send back test
	mov	al, MSK_DMA_RD			; pattern
	out	dx, al

	lea	dx, [bp].DataPort		;point at Nic's data port

	mov	cx, 2000h			;setup loop

ReadLoop:
	in	ax, dx				;read in test pattern and
	inc	bx				; compare with original
	cmp	bx, ax

	loopz	ReadLoop

	ret					;zero flag set if memory error

RAMTest	endp

	public	print_parameters
print_parameters:
;echo our command-line parameters
	ret


code	ends

	end
