*********************************************************************************
*										*
*	Low level part of STiNG port NEx000 driver for my ACSI-ISA interface	*
*	Copyright 2000-2001 Dr. Thomas Redelberger				*
*	Use it under the terms of the GNU General Public License		*
*	(See file COPYING.TXT)							*
*										*
*										*
* Features:									*
*	Packets are read/written directly from NEx000 to STinG datagram without	*
*	further buffering and memcpy						*
* Limitations:									*
*	Supports only one NEx000 at the ACSI bus.				*
*	Gross transfer rate for rx/tx is about 250KByte/sec but with aFTP 1.41	*
*	you end up with about 40KByte/sec overall for rx/tx to/from a RAM disk	*
*	on an 8 MHz ST								*
*										*
* Credits:									*
* Although written in 68000 assembler this source code is based on the source	*
* modules NE.C and 8390.C from Linux originally due to the authors 		*
* Donald Becker and Paul Gortmaker.						*
*										*
* Tabsize 8, developed with DEVPAC assembler 2.0.				*
*										*
*********************************************************************************
* $Id: ne.s 1.10 2001/02/25 15:06:14 Thomas Exp Thomas $
*
* descriptions:
*
* All functions called from upper layer software request and relinquish ACSI bus use
*
* - instMagiC	switches from our own (default) ACSI bus locking functions used
*		under TOS to MagiC's
*
* Call Tree:
* 	ne_probe1
*		ethdev_init
*			ether_setup
*
* 	ne_open
*		ei_open
*			NS8390_init
*				set_multicast_list
*
* 	ne_close
*		ei_close
*			NS8390_init
*
* 	ei_start_xmit
*		RAM2NE (macro)
*		ne_reset_8390
*		NS8390_init
*
* 	ei_interrupt
*		ei_rx_overrun
*			ei_receive
*				DumpPkt (macro)
*					retrieve_dgram
*						NE2RAM (macro)
*				process_arp (external)
*		ei_receive
*			...
*		ei_tx_intr
*		ei_tx_err
*
*
*
* Handling of 8390 interrupt mask register; where are interupts enabled/disbled?
* This is not relevant here because this driver works with polling
*
* - ei_start_xmit
*	disable
*	enable
*
* - NS8390_init
*	disable			(called from ei_close (called from ne_close))
*				(called from ne_probe1)
*	enable if "startp"	(called from ei_open  (called from ne_open ))
*
* - ne_probe1
*	disable			(first inline, then by calling NS8390_init)
*
*
*********************************************************************************
*

*
* development switches
*
RXDEBPRT	EQU	0		; Receive  debug printout level
					; 0=nothing except fatal stuff
					; 1=only hopeless
					; 2=all odd
					; 3=all packets
					; 4=full detail
TXDEBPRT	EQU	0		; Transmit debug printout level
					; 0=nothing except fatal stuff
					; 1=only hopeless
					; 2=all odd
					; 3=all packets
					; 4=full detail
MACAddDEBPRT	EQU	0		; <> 0 print NE's PROM

*
* configuration switches
*
STING		EQU	1
MAGIC_ACSI_LOCK	EQU	0		; use MagiC's ACSI locking functions
HWTEST		EQU	0		; check ACSI-ISA + NEx000 hardware

*
* code generation options
*
*		OPT	D+		; switch on symbol info
		OPT	O+		; optimize 0(an) to (an)
		OPT	W-		; warnings off
		OPT	M+		; macro expansion in listings on

		IFNE	STING
		 OUTPUT	\DEV\PRJ\ETHERNEA\SRC\NE.O
* entry points and references in this module
		 XDEF	instMagiC	; (); if MagiC is present, install its ACSI locking functions
		 XDEF	ne_probe1	; (); look for NEx000 hardware (super mode)
		 XDEF	ne_open		; (); switch hardware on (super mode)
		 XDEF	ne_close	; (); switch hardware off (super mode)
		 XDEF	ei_start_xmit	; (); tx an ethernet packet (super mode)
		 XDEF	ei_interrupt	; (); rx ethernet packets and housekeeping (super mode)
		 XDEF	get_stats	; (); access to struct enet_statistics
		 XDEF	prntStr		; (); debugging
		 XDEF	prntLong	; (); "
		 XDEF	prntWord	; (); "
		 XDEF	prntByte	; (); "
		 XDEF	prntSR		; (); "
* references into this module
		 XDEF	dev_dev_addr	; this is filled by ne_probe1 with the MAC address

* references into ethernea.c
		 XREF	tpl		; accessed from retrieve_dgram
		 XREF	stx		; "
		 XREF	my_port		; "
* entry point defined in ethernea.c
		 XREF	process_arp	; (); called from DumpPkt when an ARP packet was received
		ELSEIF
		 OUTPUT	B:\NE.PRG
		ENDC

*
* manifest constants
*
TRMAGIC		EQU	"TRNE"		; my XBRA ident

TheACh		EQU	$60		; ACSI channel of NE hardware (=3)
NilACh		EQU	0		; inactive ACSI channel used to deselect the
					; ACSI/ISA hardware
*
* addresses of system variables
*
evt_critic	EQU	$404		; (l) critical error handler vector (unused)
flock		EQU	$43e		; (w) semaphor to lock floppy usage of DMA
nvbls		EQU	$454		; (w) number of vbl slots
_vblqueue	EQU	$456		; (l) points to array of vbl slots
_hz_200		EQU	$4ba		; (l) 200Hz system tick
_dskbufp	EQU	$4c6		; (l) 1024 Byte BIOS buffer for disk I/O (unused)
pcookies	EQU	$5a0		; (l) points to cookie jar

*
* hardware addresses
*
gpip		EQU	$fffffa01	; (b) 68901 input register (unused)

diskctl		EQU	$ffff8604	; (w) disk controller data access
dmamodus	EQU	$ffff8606	; (w) DMA mode control
dmahigh		EQU	$ffff8609	; (b) DMA base address high (unused)
dmamid		EQU	$ffff860b	; (b) DMA base address medium (unused)
dmalow		EQU	$ffff860d	; (b) DMA base address low (unused)


HZ		EQU	200		; ST system timer; valid below?



**** helper macros **************************************************************

Alloc		MACRO			; allocate a number of bytes on stack
		IFLE	\1-8
		subq.l	#\1,sp
		ELSEIF
		lea	-\1(sp),sp
		ENDC
		ENDM

deAlloc		MACRO			; pop a number of bytes from stack
		IFLE	\1-8
		addq.l	#\1,sp
		ELSEIF
		lea	\1(sp),sp
		ENDC
		ENDM


mtst		MACRO			; bit test using a bit mask (masks taken from C code)
		IFNE	\1&$01
		btst	#0,\2
		ENDC
		IFNE	\1&$02
		btst	#1,\2
		ENDC
		IFNE	\1&$04
		btst	#2,\2
		ENDC
		IFNE	\1&$08
		btst	#3,\2
		ENDC
		IFNE	\1&$10
		btst	#4,\2
		ENDC
		IFNE	\1&$20
		btst	#5,\2
		ENDC
		IFNE	\1&$40
		btst	#6,\2
		ENDC
		IFNE	\1&$80
		btst	#7,\2
		ENDC
		ENDM


**** debugging macros ***********************************************************

myPling		MACRO
		move	#$0700,-(sp)		; string BELL\0
		pea	(sp)			; arg: address to this string
		bsr	prntStr
		addq.l	#6,sp			; pop arg and string
		ENDM

WaitKey		MACRO
		movem.l	d1-d2/a0-a2,-(sp)
		move.l	#$00020002,-(sp)	; bconin con
		trap	#13
		addq.l	#4,sp
		movem.l	(sp)+,d1-d2/a0-a2
		ENDM


PrA		MACRO
		pea	.mess\@(pc)
		bsr	prntStr
		addq.l	#4,sp
		bra.b	.cont\@
.mess\@		DC.B	\1,0
		EVEN
.cont\@
		ENDM

PrS		MACRO
		pea	\1
		bsr	prntStr
		addq.l	#4,sp
		ENDM

PrL		MACRO
		move.l	\1,-(sp)
		bsr	prntLong
		addq.l	#4,sp
		ENDM

PrW		MACRO
		move.w	\1,-(sp)
		bsr	prntWord
		addq.l	#2,sp
		ENDM

PrB		MACRO
		move.b	\1,-(sp)	; the 68K pushes an extra align byte
		bsr	prntByte
		addq.l	#2,sp		; the 68K pushes an extra align byte
		ENDM



****************************************************************************
* Macros to access register based hardware via the ACSI bus
*
* processor registers are used as follows:
*	d0:	as ACSI is w. and you mostly need b. d0 stores intermediate
*	d6.w:	will contain $88;	do not change it!
*	d7.w:	will contain $8a;	do not change it!
*
*	a5:	will point to dmamodus;	do not change it!
*	a6:	will point to diskctl;	do not change it!
*
****************************************************************************

lockACSI	MACRO
		move.l	pDMA_begin(pc),a0	; pointer to function
		jsr	(a0)			; exclude other tasks to use the ACSI bus
		tst.l	d0			; check for race about ACSI bus
		bne	.doNothing
		ENDM


unlockACSI	MACRO
		move.l	pDMA_end(pc),a0		; pointer to function
		jsr	(a0)			; let other tasks access ACSI bus
		ENDM


RcACSI		EQUR	a5
RdACSI		EQUR	a6
Ra1l		EQUR	d6
Ra1h		EQUR	d7


ldACSIRegs	MACRO				; for faster access to hardware
		lea	dmamodus\w,RcACSI
		lea	diskctl\w,RdACSI
* what has to be written to dmamodus before and after to write a byte with A1=low
		move	#%010001000,Ra1l	; =$088 A1=0 R_W=1
		move	#%010001010,Ra1h	; =$08a A1=1 R_W=1
;			  ||  || |
;			  ||  || A1
;			  ||  |0=flop, 1=DMA
;			  ||  0=flop,DMA, 1=SecCntReg
;			  |0=DMA enable, 1=DMA disable
;			  0=R_W=1 (read), 1=R_W=0 (write)
; R_W is only effective in DMA mode. Otherwise state of R_W line reflects direction of move.w
		ENDM


putACSI		MACRO
		move	Ra1l,(RcACSI)		; A1 gets low
		move	#TheACh+\2,(RdACSI)	; set register selection via ACSI channel
		move	Ra1h,(RcACSI)		; A1 gets high again
		move	Ra1h,(RdACSI)		; dummy write
		move	\1,(RdACSI)		; write to register
		ENDM


putMore		MACRO
		move	Ra1h,(RdACSI)		; dummy write
		move	\1,(RdACSI)		; write to register
		ENDM


getACSI		MACRO
		move	Ra1l,(RcACSI)		; A1 gets low
		move	#TheACh+\1,(RdACSI)	; set register selection via ACSI channel
		move	Ra1h,(RcACSI)		; A1 gets high again
		tst	(RdACSI)		; dummy read
		move	(RdACSI),\2		; read from register
		ENDM


getMore		MACRO				; arg 1 is not used!
		tst	(RdACSI)		; dummy read
		move	(RdACSI),\2		; read from register
		ENDM

*
* macro to deselect an interface by selecting ACSI device 0 
*
deselACSI	MACRO
		move	Ra1l,(RcACSI)		; A1 gets low
		move	#NilACh,(RdACSI)	; select unused ACSI channel to deselect The device
		move	Ra1h,(RcACSI)		; A1 gets hi again
		move	#$80,(RcACSI)		; prepare ACSI bus for floppy
		ENDM


		IFGT	HWTEST
**** the installation entry point ***********************************************

		bra	drv_init		; entry via Pexec or shell_write
		ENDC	; HWTEST


**** Auxiliary Code *************************************************************



**** Debugging code *************************************************************



******** prntLong *****************************************************
* print longword
*	cdecl void prntLong(long arg);
***********************************************************************

Rph		REG	d0-d2/d6/d7/a0-a2
RNph		EQU	8*4			; number of saved bytes

prntLong	movem.l	Rph,-(sp)		; save used regs.
		move.l	RNph+4(sp),d6		; get longword
		moveq.l	#7,d7
		bra.b	prntNibbles


******** prntWord *****************************************************
* print word
*	cdecl void prntWord(short arg);
***********************************************************************

prntWord	movem.l	Rph,-(sp)		; save used regs.
		move	RNph+4(sp),d6		; get word
		swap	d6			; in upper word
		moveq.l	#3,d7
		bra.b	prntNibbles


******** prntByte *****************************************************
* print word
*	cdecl void prntByte(char arg);
***********************************************************************

prntByte	movem.l	Rph,-(sp)		; save used regs.
		move.b	RNph+4(sp),d6		; get byte
		swap	d6			; in upper word
		lsl.l	#8,d6			; in upper byte
		moveq.l	#1,d7
*		fall thru to prntNibbles


prntNibbles	moveq	#0,d0			; clear word
		REPT	4			; get nibble in d0
		lsl.l	#1,d6			; in extend bit
		roxl.b	#1,d0			; from extend bit
		ENDR
		move.b	.HexTab(pc,d0.w),d0	; get hex character
		move	d0,-(sp)		; arg: the char
		move.l	#$00030002,-(sp)	; 2 args: VT52-console, Bconout
		trap	#13			; Bios
		addq.l	#6,sp			; pop args
		dbra	d7,prntNibbles

		movem.l	(sp)+,Rph
		rts

.HexTab		DC.B	"0123456789abcdef"

******** prntSR *******************************************************

prntSR		move	SR,-(sp)
		bsr.b	prntWord
		addq.l	#2,sp
		rts



******** prntStr ****************************************************************
* prints message on screen (VT52 codes)
*	cdecl void prntStr(char *message);
*********************************************************************************

Rps		REG	d0-d2/a0-a2/a6
RNps		EQU	4*7			; number of saved bytes

prntStr		movem.l	Rps,-(sp)
		move.l	RNps+4(sp),a6		; get pointer to string
		bra.b	.b1

.t1		move	d0,-(sp)		; arg: the char
		move.l	#$00030002,-(sp)	; 2 args: VT52-console, Bconout
		trap	#13			; Bios
		addq.l	#6,sp			; pop args
.b1		moveq	#0,d0			; unsigned extend
		move.b	(a6)+,d0
		bne.b	.t1

		movem.l	(sp)+,Rps
		rts



*********************************************************************************
******** taken from <linux/if.h> ************************************************

IFF_BROADCAST	EQU	$0002
IFF_PROMISC	EQU	$0100
IFF_ALLMULTI	EQU	$0200
IFF_MULTICAST	EQU	$1000


*********************************************************************************
******** taken from drivers/net/net_init.c **************************************

*** just what we need here
ether_setup
		IFNE	1
		move	#IFF_BROADCAST+IFF_MULTICAST,dev_flags
		ELSEIF
		move	#IFF_BROADCAST+IFF_MULTICAST+IFF_PROMISC,dev_flags
		ENDC
		rts


*********************************************************************************
******** Start of NS8390 specific code ******************************************

**** 8390 definitions taken from Linux 8390.h ***********************************

* Generic NS8390 register definitions.

* We only use one Tx slot. Should always use two Tx slots to get back-to-back transmits.

TX_PAGES	EQU	6
ETHER_ADDR_LEN	EQU	6


* The maximum number of 8390 interrupt service routines called per IRQ.
MAX_SERVICE	EQU	12

* The maximum time waited (in jiffies) before assuming a Tx failed. (1000ms)
TX_TIMEOUT	EQU	((1000*HZ)/1000)

* Some generic ethernet register configurations.
E8390_TX_IRQ_MASK	EQU	$0a		; For register EN0_ISR
E8390_RX_IRQ_MASK	EQU	$05
E8390_RXCONFIG		EQU	$04		; EN0_RXCR: broadcasts, no multicast,errors
E8390_RXOFF		EQU	$20		; EN0_RXCR: Accept no packets
E8390_TXCONFIG		EQU	$00		; EN0_TXCR: Normal transmit mode
E8390_TXOFF		EQU	$02		; EN0_TXCR: Transmitter off

*  Register accessed at EN_CMD, the 8390 base addr. 
E8390_STOP	EQU	$01		; Stop and reset the chip
E8390_START	EQU	$02		; Start the chip, clear reset
E8390_TRANS	EQU	$04		; Transmit a frame
E8390_RREAD	EQU	$08		; Remote read
E8390_RWRITE	EQU	$10		; Remote write
E8390_RSEND	EQU	$18		; Remote send
E8390_NODMA	EQU	$20		; Remote DMA
E8390_PAGE0	EQU	$00		; Select page chip registers
E8390_PAGE1	EQU	$40		; using the two high-order bits
E8390_PAGE2	EQU	$80		; Page 3 is invalid.

E8390_CMD	EQU	$00		; The command register (for all pages)
* Page 0 register offsets.
EN0_CLDALO	EQU	$01		; Low byte of current local dma addr  RD
EN0_STARTPG	EQU	$01		; Starting page of ring bfr WR
EN0_CLDAHI	EQU	$02		; High byte of current local dma addr  RD
EN0_STOPPG	EQU	$02		; Ending page +1 of ring bfr WR
EN0_BOUNDARY	EQU	$03		; Boundary page of ring bfr RD WR
EN0_TSR		EQU	$04		; Transmit status reg RD
EN0_TPSR	EQU	$04		; Transmit starting page WR
EN0_NCR		EQU	$05		; Number of collision reg RD
EN0_TCNTLO	EQU	$05		; Low  byte of tx byte count WR
EN0_FIFO	EQU	$06		; FIFO RD
EN0_TCNTHI	EQU	$06		; High byte of tx byte count WR
EN0_ISR		EQU	$07		; Interrupt status reg RD WR
EN0_CRDALO	EQU	$08		; low byte of current remote dma address RD
EN0_RSARLO	EQU	$08		; Remote start address reg 0
EN0_CRDAHI	EQU	$09		; high byte, current remote dma address RD
EN0_RSARHI	EQU	$09		; Remote start address reg 1
EN0_RCNTLO	EQU	$0a		; Remote byte count reg WR
EN0_RCNTHI	EQU	$0b		; Remote byte count reg WR
EN0_RSR		EQU	$0c		; rx status reg RD
EN0_RXCR	EQU	$0c		; RX configuration reg WR
EN0_TXCR	EQU	$0d		; TX configuration reg WR
EN0_COUNTER0	EQU	$0d		; Rcv alignment error counter RD
EN0_DCFG	EQU	$0e		; Data configuration reg WR
EN0_COUNTER1	EQU	$0e		; Rcv CRC error counter RD
EN0_IMR		EQU	$0f		; Interrupt mask reg WR
EN0_COUNTER2	EQU	$0f		; Rcv missed frame error counter RD

* Bits in EN0_ISR - Interrupt status register
ENISR_RX	EQU	$01		; Receiver, no error
ENISR_TX	EQU	$02		; Transmitter, no error
ENISR_RX_ERR	EQU	$04		; Receiver, with error
ENISR_TX_ERR	EQU	$08		; Transmitter, with error
ENISR_OVER	EQU	$10		; Receiver overwrote the ring
ENISR_COUNTERS	EQU	$20		; Counters need emptying
ENISR_RDC	EQU	$40		; remote dma complete
ENISR_RESET	EQU	$80		; Reset completed (this does never fire an int)
ENISR_ALL	EQU	$00		; no Interrupts ($3f would be normal)

* Bits in EN0_DCFG - Data config register
ENDCFG_WTS	EQU	$01		; word transfer mode selection

* Page 1 register offsets.
EN1_PHYS	EQU	$01		; This board's physical enet addr RD WR
EN1_CURPAG	EQU	$07		; Current memory page RD WR
EN1_MULT	EQU	$08		; Multicast filter mask array (8 bytes) RD WR

* Bits in received packet status byte and EN0_RSR
ENRSR_RXOK	EQU	$01		; Received a good packet
ENRSR_CRC	EQU	$02		; CRC error
ENRSR_FAE	EQU	$04		; frame alignment error
ENRSR_FO	EQU	$08		; FIFO overrun
ENRSR_MPA	EQU	$10		; missed pkt
ENRSR_PHY	EQU	$20		; physical/multicase address
ENRSR_DIS	EQU	$40		; receiver disable. set in monitor mode
ENRSR_DEF	EQU	$80		; deferring

* Transmitted packet status, EN0_TSR
ENTSR_PTX	EQU	$01		; Packet transmitted without error
ENTSR_ND	EQU	$02		; The transmit wasn't deferred.
ENTSR_COL	EQU	$04		; The transmit collided at least once.
ENTSR_ABT	EQU	$08		; The transmit collided 16 times, and was deferred.
ENTSR_CRS	EQU	$10		; The carrier sense was lost.
ENTSR_FU	EQU	$20		; A "FIFO underrun" occurred during transmit.
ENTSR_CDH	EQU	$40		; The collision detect "heartbeat" signal was lost.
ENTSR_OWC	EQU	$80		; There was an out-of-window collision.



******** ei_open ****************************************************************
* in
*
* out
*
* changed
*	Turbo/Pure-C convention d0-d2,a0-a1 may get changed
*********************************************************************************

Ron		REG	d3-d5/Ra1l/Ra1h/a2-a4/RcACSI/RdACSI


ei_open		movem.l	Ron,-(sp)
		lockACSI			; aquire ACSI bus (modifies a0)
		ldACSIRegs			; load registers to access ACSI bus

		moveq	#1,d0			; arg: startp
		bsr	NS8390_init
		st	dev_start

		deselACSI			; deselect ACSI interface
		unlockACSI			; relinquish ACSI bus (modifies a0)
.doNothing	movem.l	(sp)+,Ron
		rts



******** ei_close ***************************************************************
* in
*
* out
*
* changed
*	Turbo/Pure-C convention d0-d2,a0-a1 may get changed
*********************************************************************************

Rcl		REG	d3-d5/Ra1l/Ra1h/a2-a4/RcACSI/RdACSI


ei_close	movem.l	Rcl,-(sp)
		lockACSI			; aquire ACSI bus (modifies a0)
		ldACSIRegs			; load registers to access ACSI bus

		moveq	#0,d0			; arg: startp
		bsr	NS8390_init
		sf	dev_start

		deselACSI			; deselect ACSI interface
		unlockACSI			; relinquish ACSI bus (modifies a0)
.doNothing	movem.l	(sp)+,Rcl
		rts



******** RAM2NE *****************************************************************
* This is a macro for speed
* put # of bytes in register arg2 to NE from location pointed to by arg1.
*
* in:	arg1	address register source address
*	arg2	data register (w) # of bytes to put; must not be d0!
*
* both registers plus d0 get destroyed.
* Assembler inst. REPT does not work inside a macro, we repeate explicitly
	
RAM2NE		MACRO
		ext.l	\2			; clear upper word + test for 0
		beq.b	.doNothing\@
		subq.w	#2,\2			; first two puts are outside loop
		move.b	(\1)+,d0
		putACSI	d0,NE_DATAPORT		; this sets the NE IO address
		move.b	(\1)+,d0
		putMore	d0,NE_DATAPORT

* put the packet; optimized for speed, we do 8 bytes at once
		ror.l	#3,\2			; store remainder in upper word
		bra.b	.Rb1\@

.Rt1\@		move.b	(\1)+,d0		; a fool designed word access to ACSI port
		putMore	d0,NE_DATAPORT
		move.b	(\1)+,d0
		putMore	d0,NE_DATAPORT
		move.b	(\1)+,d0
		putMore	d0,NE_DATAPORT
		move.b	(\1)+,d0
		putMore	d0,NE_DATAPORT
		move.b	(\1)+,d0
		putMore	d0,NE_DATAPORT
		move.b	(\1)+,d0
		putMore	d0,NE_DATAPORT
		move.b	(\1)+,d0
		putMore	d0,NE_DATAPORT
		move.b	(\1)+,d0
		putMore	d0,NE_DATAPORT
.Rb1\@		dbra	\2,.Rt1\@

		clr.w	\2			; prepare for remainder
		rol.l	#3,\2			; restore remainder bits
		bra.b	.Rb2\@

.Rt2\@		move.b	(\1)+,d0
		putMore	d0,NE_DATAPORT		; put the remaining bytes
.Rb2\@		dbra	\2,.Rt2\@

.doNothing\@
		ENDM



******** ei_start_xmit **********************************************************
* Transfer a raw ethernet packet from RAM into the NEx000 and start the transmitter
* To minimize packet double buffering this function allows to specify two non
* contiguous parts of the packet which get catenated here on the fly. This eases
* to assemble an enet packet from STinGs dgram representation.
*
* long ei_start_xmit (char* buff1, short len1, char* buff2, short len2);
*
* in
*	a0.l:	points to packet to send first  portion
*	d0.w:	length of packet to send first  portion
*	a1.l:	points to packet to send second portion
*	d1.w:	length of packet to send second portion
*
* out
*	d0.l	0=OK
*		1=transmitter busy or other errors
* changed
*	Turbo/Pure-C convention d0-d2,a0-a1 may get changed
*
* N.b.: block_output and NS8390_trigger_send employed in the Linux code
* are inlined here to save cycles and to avoid the need for double buffering
*
* Only one tx buffer in use.
*
* Note that usually after calling ei_start_xmit (about a millisec)
* a Packet Transmitted interrupt would be fired ($02). Here this interrupt is dealt with
* by polling in ei_interrupt.
*
* Transmitting with NEx000 is a two step process:
* 1.	loading the RAM onboard the NE from the systems RAM by a remote
*	DMA write command
* 2.	kicking the NE so that it starts txing from its onboard RAM
*	out to the network
*
* There is a semaphore: dev_tbusy
* -	dev_tbusy is set at the end of this 2 step process
*	it is reset in ei_tx_intr
* Query:
* -	dev_tbusy is queried only in ei_start_xmit (this function)
*********************************************************************************

Rsx		REG	d3/Ra1l/Ra1h/a2/RcACSI/RdACSI


ei_start_xmit	movem.l	Rsx,-(sp)
		move.w	d0,d2			; save arg
		move.l	a0,a2			; save arg
	IFGE	TXDEBPRT-3
		PrL	_hz_200\w
		PrA	<" Tx l1: ">
		PrW	d0
		PrA	<" l2: ">
		PrW	d1
	ENDC
		lockACSI			; aquire ACSI bus (d0=rc, a0 modified)
						; jumps to .doNothing on fail to lock
		ldACSIRegs			; load registers to access ACSI bus

		tst.b	dev_tbusy		; is NE transmitting?
		beq.b	.c1

**** this section still misses printing why tx timed out
		getACSI	EN0_TSR,d0		; status of previous tx
		and	#ENTSR_PTX,d0		; was tx sucessful?
		bne.b	.c1			; yes, we can go on

		move.l	_hz_200\w,d0		; no, check how long ago
		sub.l	dev_trans_start,d0	; time since start of last xmit
		cmp.l	#TX_TIMEOUT,d0
		bls	.TxBusy			; not yet over, leave it alone
		
		PrS	.m1(pc)
		bsr	ne_reset_8390		; hard reset 8390 chip
		moveq	#1,d0			; arg: startp=1
		bsr	NS8390_init		; init 8390 chip
		move.l	_hz_200\w,dev_trans_start	; why here?
****						; and fall thru

.c1		move	d2,d3			; len1
		add	d1,d3			; + len2 = total length
		move	d3,d0
		putACSI	d0,EN0_RCNTLO		; DMA count
		lsr.w	#8,d0
		putACSI	d0,EN0_RCNTHI		; "
		putACSI	#0,EN0_RSARLO		; DMA destination
		putACSI	lcl_tx_start_page16,EN0_RSARHI	; 8390 page to tx from
* start remote DMA write
		putACSI	#E8390_RWRITE+E8390_START,NE_CMD

		RAM2NE	a2,d2			; put ethernet packet first  portion
		RAM2NE	a1,d1			; put ethernet packet second portion

		putACSI	#ENISR_RDC,EN0_ISR	; ack intr.

		getACSI	E8390_CMD,d0		; get status bits
		and	#E8390_TRANS,d0		; transmitting?
		beq.b	.c2			; no, go on

		PrS	.m3(pc)
		bra.b	.abortTx

.c2		putACSI	d3,EN0_TCNTLO		; transmit count
		lsr.w	#8,d3
		putACSI	d3,EN0_TCNTHI		; "
		; 8390 page to tx from was already set in NS8390_init
* start transmitter
		putACSI	#E8390_TRANS+E8390_START,E8390_CMD
	IFGE	TXDEBPRT-3
		PrA	<" start",13,10>
	ENDC

.abortTx	move.l	_hz_200\w,dev_trans_start	; save tx start time
		st	dev_tbusy		; flag tx is in progress

* free the packet here or caller should examine rc and do it !

		moveq	#0,d0			; OK

.quit		deselACSI			; deselect ACSI interface
		unlockACSI			; relinquish ACSI bus (modifies a0)
.doNothing	movem.l	(sp)+,Rsx		; restore used registers
		rts


.TxBusy		moveq	#1,d0			; rc
		addq.w	#1,lcl_es_tx_errors
		bra.b	.quit


.m1		DC.B	"TX timed out",13,10,0
.m3		DC.B	"Tried to transmit with the transmitter busy",13,10,0
		EVEN



******** ei_interrupt ***********************************************************
* The typical workload of the driver:
* Handle the ether interface interrupts. This is done by polling here.
*
* in:
*	nothing
*
* out:
*	nothing
*
* changed:
*	d0-d2,a0-a1 do change and are not saved because of TC,PC calling convention
*	we save *all* the other regs. so that saving does not need to be done by the childs
*
* uses
*	d0,d1, d5, Ra1l,Ra1h, a0, RcACSI,RdACSI
*	plus childs	
*
*********************************************************************************

Rit		REG	d3-d5/Ra1l/Ra1h/a2-a4/RcACSI/RdACSI	; saved registers on entry

* local variables in registers
RitInts		EQUR	d5		; copy of interrupt register; to be conserved by lower levels!
RitBS		EQUR	a4		; pointer to global vars.

* local variables in memory
		RSRESET
itNSrvd		RS.W	1		; max # of interrupts served at once
itNLcl		EQU	__RS


ei_interrupt	movem.l	Rit,-(sp)		; save all used registers
		lockACSI			; aquire ACSI bus (d0=rc, a0 modified)
						; jumps to .doNothing on fail to lock
		Alloc	itNLcl			; local vars
		ldACSIRegs			; load registers to access ACSI bus
		lea	BS,RitBS		; allows faster access to global vars.

		tst.b	dev_interrupt		; test for reentrancy (should never happen)
		beq.b	.c0

		PrS	.m1(pc)
		bra	.quit

.c0		st	dev_interrupt		; we are in it
		move	#MAX_SERVICE,itNSrvd(sp)	; only so many interrupts max.
		bra	.b1



.t1		mtst	ENISR_OVER,RitInts
		beq.b	.c1

	IFGE	RXDEBPRT-4
		PrL	_hz_200\w
		PrA	<" IRxOver Cnt: ">
		PrW	itNSrvd(sp)
		PrA	<" ISR: ">
		PrB	RitInts
		PrS	crlf(pc)
	ENDC
		bsr	ei_rx_overrun
		bra	.c2			; this did ei_receive already


.c1		mtst	ENISR_RX,RitInts	; got a good packet?
		beq	.c11

	IFGE	RXDEBPRT-4
		PrL	_hz_200\w
		PrA	<" IRxRecv Cnt: ">
		PrW	itNSrvd(sp)
		PrA	<" ISR: ">
		PrB	RitInts
		PrA	<" RSR: ">
		getACSI	EN0_RSR,d0
		PrB	d0
		PrS	crlf(pc)
	ENDC
		bsr	ei_receive


.c11		mtst	ENISR_RX_ERR,RitInts	; RX with error?
		beq	.c2

	IFGE	RXDEBPRT-1
		PrL	_hz_200\w
		PrA	<" IRxErr  Cnt: ">
		PrW	itNSrvd(sp)
		PrA	<" ISR: ">
		PrB	RitInts
		PrA	<" RSR: ">
		getACSI	EN0_RSR,d0
		PrB	d0
		PrS	crlf(pc)
	ENDC
		addq.w	#1,lcl_es_rx_errors-BS(RitBS)	; that is all we do
		putACSI	#ENISR_RX_ERR,EN0_ISR	; ack intr.


.c2		mtst	ENISR_TX,RitInts	; TX finished
		beq.b	.c3

	IFGE	TXDEBPRT-4
		PrL	_hz_200\w
		PrA	<" ITxDone Cnt: ">
		PrW	itNSrvd(sp)
		PrA	<" ISR: ">
		PrB	RitInts
		PrS	crlf(pc)
	ENDC
		bsr	ei_tx_intr
***		bra.b	.c4			; TX and TX_ERR are mut. exclusive


.c3		mtst	ENISR_TX_ERR,RitInts
		beq.b	.c4

	IFGE	TXDEBPRT-4
		PrL	_hz_200\w
		PrA	<" ITxErr  Cnt: ">
		PrW	itNSrvd(sp)
		PrA	<" ISR: ">
		PrB	RitInts
		PrS	crlf(pc)
	ENDC
		bsr	ei_tx_err


.c4		mtst	ENISR_COUNTERS,RitInts
		beq.b	.c5

		move	#$ff,d1
		getACSI	EN0_COUNTER0,d0
		and	d1,d0			; suppress junk bits
		add	d0,lcl_es_rx_frame_errors-BS(RitBS)
		getACSI	EN0_COUNTER1,d0
		and	d1,d0
		add	d0,lcl_es_rx_crc_errors-BS(RitBS)
		getACSI	EN0_COUNTER2,d0
		and	d1,d0
		add	d0,lcl_es_rx_missed_errors-BS(RitBS)

		putACSI	#ENISR_COUNTERS,EN0_ISR	; ack intr.


.c5		mtst	ENISR_RDC,RitInts
		beq.b	.c6

	IFGE	RXDEBPRT-4
		PrL	_hz_200\w
		PrA	<" IRxRDC  Cnt: ">
		PrW	itNSrvd(sp)
		PrA	<" ISR: ">
		PrB	RitInts
		PrS	crlf(pc)
	ENDC
		putACSI	#ENISR_RDC,EN0_ISR	; ignore RDC interrupts that make it here

.c6
.b1		move	sr,d1
		ori	#$700,sr		; disable all ints.
		getACSI	EN0_ISR,RitInts		; look what is (still) up
		getMore	EN0_ISR,d0		; again to minimize spike risk
		move	d1,sr			; reenable ints.
		cmp.b	d0,RitInts		; should always be the same
		bne.b	.b1
		tst.b	RitInts			; more interrupts to do?
		beq.b	.exit			; exit if all interrupts done

		subq.w	#1,itNSrvd(sp)		; count down max # if interrupts to serve
		bne	.t1			; do more if >0



		PrS	.m3(pc)			; loop counter expired
* we do not do ack. rather leave int. for the next iteration (polling!)
*		putACSI	#$7f,EN0_ISR		; Ack. all intrs

.exit
.quit		sf	dev_interrupt		; we leave it
		deselACSI			; deselect ACSI interface
		unlockACSI			; relinquish ACSI bus (modifies a0)
		deAlloc	itNLcl			; pop local vars
.doNothing	movem.l	(sp)+,Rit		; restore used registers
		rts


.m1		DC.B	"Reentering interrupt handler",13,10,0
.m3		DC.B	"Too much work at interrupts",13,10,0
		EVEN



******** ei_tx_err **************************************************************
* A transmitter error has happened. Most likely excess collisions (which
* is a fairly normal condition). If the error is one where the Tx will
* have been aborted, we do not try to send the packet sitting in the NE buffer
* again because tx would not be finished before the next tx is attempted by 
* ei_start_xmit
*
* in
*	nothing
*
* out
*	nothing
*
* changed
*	d0,d3, a0
*********************************************************************************

* local variables in registers
RteTxsr		EQUR	d3		; temporary

ei_tx_err	getACSI	EN0_TSR,RteTxsr
		putACSI	#ENISR_TX_ERR,EN0_ISR		; Ack intr.
		sf	dev_tbusy			; NE is available for next tx

		mtst	ENTSR_ABT,RteTxsr
		beq.b	.c1

		addq.w	#1,lcl_es_tx_aborted_errors-BS(RitBS)
		IFGE	TXDEBPRT-1
		PrS	.m1(pc)
		ENDC

.c1		mtst	ENTSR_ND,RteTxsr
		beq.b	.c2

		IFGE	TXDEBPRT-1
		PrS	.m2(pc)
		ENDC

.c2		mtst	ENTSR_CRS,RteTxsr
		beq.b	.c3

		addq.w	#1,lcl_es_tx_carrier_errors-BS(RitBS)
		IFGE	TXDEBPRT-1
		PrS	.m3(pc)
		ENDC

.c3		mtst	ENTSR_FU,RteTxsr
		beq.b	.c4

		addq.w	#1,lcl_es_tx_fifo_errors-BS(RitBS)
		IFGE	TXDEBPRT-1
		PrS	.m4(pc)
		ENDC

.c4		mtst	ENTSR_CDH,RteTxsr
		beq.b	.c5

		addq.w	#1,lcl_es_tx_hartbeat_errors-BS(RitBS)
		IFGE	TXDEBPRT-1
		PrS	.m5(pc)
		ENDC

.c5		mtst	ENTSR_OWC,RteTxsr
		beq.b	.c6

		addq.w	#1,lcl_es_tx_window_errors-BS(RitBS)
		IFGE	TXDEBPRT-1
		PrS	.m6(pc)
		ENDC

.c6
		IFGE	TXDEBPRT-1
		PrS	crlf(pc)
		ENDC
		rts


		IFGE	TXDEBPRT-1
.m1		DC.B	"excess-collisions ",0
.m2		DC.B	"non-deferral ",0
.m3		DC.B	"lost-carrier ",0
.m4		DC.B	"FIFO-underrun ",0
.m5		DC.B	"lost-heartbeat ",0
.m6		DC.B	"window ",0
		EVEN
		ENDC



******** ei_tx_intr *************************************************************
* We have finished a transmit: check for errors
* We can not trigger another send because we use only one tx buffer and tx would
* not be finished before next attempt by ei_start_xmit
*
* in
*	nothing
*
* out
*	nothing
*
* changed
*	d0,d3
*********************************************************************************

* local variables in registers
RtiStts		EQUR	d3		; temporary


ei_tx_intr	getACSI	EN0_TSR,RtiStts		; first get status for this one
		putACSI	#ENISR_TX,EN0_ISR	; Ack intr.
		sf	dev_tbusy		; NE is available for next tx

* Minimize Tx latency: update the statistics after we restart TXing

* do we have a collision?
		mtst	ENTSR_COL,RtiStts
		beq.b	.c1
		addq.w	#1,lcl_es_collisions-BS(RitBS)

* do we have a packet transmitted?
.c1		mtst	ENTSR_PTX,RtiStts
		beq.b	.c2

		addq.w	#1,lcl_es_tx_packets-BS(RitBS)
		bra.b	.c3

.c2		PrA	<"TX Err in tx_intr?",13,10,0>

.c3		rts



*********************************************************************************
* The following interfacing code, retrieve_dgram, was taken from ethernea.c,
* converted to assembler and optimized.
* It links STinG specifics with NEx000 hardware specifics.
* Therefore it references the external IP packet representation as well as STinGs
* internal IP packet represention.
* It accesses the transport layer (tpl) as well as the port layer (stx).
*
* Although this is an ugly design it is necessary to do away with packet double
* buffering and instead copy directly from the NE memory to the STinG dgram
*********************************************************************************

*
* function pointer offsets in  tpl  and  stx  and  my_port. Because we
* use only a few functions full blown structure declaration is not 
* warranted
*
tpKRmalloc	EQU	$0c
tpKRfree	EQU	$10

sxSet_dgram_ttl	EQU	$0c
sxIP_discard	EQU	$2c

ptStat_rcv_data	EQU	$20
ptReceive	EQU	$24
ptStat_dropped	EQU	$28

*
*
* IP packet header
*
		RSRESET
ipVersionHd_len	RS.B	1		; 4-7: IP Version, 0-3: Internet Header Length
ipTos		RS.B	1		; Type of Service
ipLength	RS.W	1		; Total of all header, options and data
ipIdent		RS.W	1		; Identification for fragmentation
ipFragments	RS.W	1		; 15: Reserved : Must be zero
					; 14: Don't fragment flag
					; 13: More fragments flag
					; 0-12: Fragment offset
ipTtl		RS.B	1		; Time to live
ipProtocol	RS.B	1		; Protocol
ipHdr_chksum	RS.W	1		; Header checksum
ipIp_src	RS.L	1		; Source IP address
ipIp_dest	RS.L	1		; Destination IP address
ipHdrLen	EQU	__RS



*
* STinG/STik Internal IP packet representation
*
		RSRESET
sgIpHdr		RS.B	ipHdrLen	; Header of IP packet
sgOptions	RS.L	1		; Options data block
sgOpt_length	RS.W	1		; Length of options data block
sgPkt_data	RS.L	1		; IP packet data block
sgPkt_length	RS.W	1		; Length of IP packet data block
sgTimeout	RS.L	1		; Timeout of packet life
sgIp_gateway	RS.L	1		; Gateway for forwarding this packet
sgRecvd		RS.L	1		; Receiving port
sgNext		RS.L	1		; Next IP packet in IP packet queue
sgIp_DgramLen	EQU	__RS



******** NE2RAM *****************************************************************
* This is a macro for speed
* get # of bytes in register arg2 to RAM location pointed to by arg1.
* A first getACSI must already have been done!
*
* in:	arg1	address register destination address
*	arg2	data register (w) # of bytes to get; must not be d0!
*
* both registers plus d0 get destroyed.
* Assembler inst. REPT does not work inside a macro, we repeate explicitly

NE2RAM		MACRO
* get the Packet; optimized for speed, we do 8 bytes at once.
		ext.l	\2			; clear upper word
		ror.l	#3,\2			; store remainder in upper word
		bra.b	.Nb1\@

.Nt1\@		getMore	NE_DATAPORT,d0		; a fool designed word access to ACSI port
		move.b	d0,(\1)+
		getMore	NE_DATAPORT,d0
		move.b	d0,(\1)+
		getMore	NE_DATAPORT,d0
		move.b	d0,(\1)+
		getMore	NE_DATAPORT,d0
		move.b	d0,(\1)+
		getMore	NE_DATAPORT,d0
		move.b	d0,(\1)+
		getMore	NE_DATAPORT,d0
		move.b	d0,(\1)+
		getMore	NE_DATAPORT,d0
		move.b	d0,(\1)+
		getMore	NE_DATAPORT,d0
		move.b	d0,(\1)+
.Nb1\@		dbra	\2,.Nt1\@

		clr.w	\2				; prepare for remainder
		rol.l	#3,\2				; restore remainder bits
		bra.b	.Nb2\@

.Nt2\@		getMore	NE_DATAPORT,d0			; get the remaining bytes
		move.b	d0,(\1)+
.Nb2\@		dbra	\2,.Nt2\@

		ENDM



******** retrieve_dgram  ********************************************************
* As this function is called only (statically) once from ei_receive it does 
* special parameter passing different from Turbo-C/Pure-C or cdecl:
*
* in
*	RrxPktLen:	(d2) length of raw ethernet packet incl. ethernet CRC
*
* out
*	d0.l:	0=OK, <>0 error
*
* changed
*	d0-d3	we do save d2-d3 because ei_receive uses them
*	a0-a3	we do not save any of them because ei_interrupt does save all
*		we must not use d6,d7,a5,a6 (getMore)
*********************************************************************************

RrxPktLen	EQUR	d2			; must be defined already here
RrxPushed	EQU	4			; bytes pushed on stack (d2,d3)


retrieve_dgram	move	d3,-(a7)		; save used reg.
		move	RrxPktLen,-(a7)		; save used reg.
		move.l	tpl,a3			; access to transport layer functions

		pea	sgIp_DgramLen\w		; arg1: a long!
		movea.l	tpKRmalloc(a3),a0	; allocate mem for IP header
		jsr	(a0)
		addq.w	#4,a7			; pop arg1

		movea.l	d0,a2			; address of header
		tst.l	d0
		beq	.err1			; KRmalloc failed?

		movea.l	a2,a0
		REPT	20			; ipHdrLen
		getMore	NE_DATAPORT,d0
		move.b	d0,(a0)+		; get IP header
		ENDR

* check consistency of IP header
		move	(a7),d0			; saved RrxPktLen
		cmp.w	ipLength(a2),d0
		bcs	.err2			; error if IP length > raw packet

		move	ipLength(a2),IPLength-BS(RitBS)	; save IP packet length

		move.b	ipVersionHd_len(a2),d3
		and.w	#$000f,d3		; left with Hd_len (is in longs)
		lsl.w	#2,d3			; to bytes
		cmp.w	#ipHdrLen,d3
		bcs	.err2			; error if smaller than minimum header

		cmp.w	(a7),d3
		bcc	.err2			; error if eq or larger than total raw packet

		move.w	d3,d0			; hd_len in bytes
		sub.w	#ipHdrLen,d0		; -IP Header length
		move.w	d0,sgOpt_length(a2)	; = options length in bytes
		ext.l	d0
		move.l	d0,-(a7)		; arg: length
		movea.l	tpKRmalloc(a3),a0	; allocate mem for options
		jsr	(a0)
		addq.w	#4,a7			; pop arg
		move.l	d0,sgOptions(a2)	; attach options data block
		beq	.err3			; KRmalloc failed?

		movea.l	d0,a0			; ^options data
		move.w	sgOpt_length(a2),d1	; options length in bytes
		lsr.w	#2,d1			; length in longs
		bra.b	.b1

.t1		REPT	4
		getMore	NE_DATAPORT,d0
		move.b	d0,(a0)+		; get IP options
		ENDR
.b1		dbra	d1,.t1

		move.w	ipLength(a2),d0
		sub.w	d3,d0			; length-hd_len
		move.w	d0,sgPkt_length(a2)
		ext.l	d0
		move.l	d0,-(a7)		; arg: length in bytes
		movea.l	tpKRmalloc(a3),a0	; allocate mem for data
		jsr	(a0)
		addq.w	#4,a7			; pop arg
		move.l	d0,sgPkt_data(a2)	; attach pkt data block
		beq	.err3			; KRmalloc failed?

		movea.l	d0,a0			; ^pkt_data
		move.w	sgPkt_length(a2),d1	; pkt_length in bytes

		NE2RAM	a0,d1			; get IP data

		putACSI	#ENISR_RDC,EN0_ISR	; reset remote DMA ready bit

		lea	my_port,a3
		move.l	a3,sgRecvd(a2)		; recvd = &my_port
		moveq	#0,d0
		move.l	d0,sgNext(a2)		; nothing hanging
		move.l	a2,-(a7)		; arg ^dgram
		movea.l	stx,a0
		movea.l	sxSet_dgram_ttl(a0),a0
		jsr	(a0)
		addq.w	#4,a7			; pop arg

* append new dgram at the end of the dgram queue hanging from my_port.receive
		lea	ptReceive(a3),a0	; address of pointer to first dgram in queue
		bra.b	.b4

.t4		move.l	d0,a0
		lea	sgNext(a0),a0		; address of pointer to next dgram in queue
.b4		move.l	(a0),d0			; first dgram hanging and then next dgram in queue
		bne.b	.t4			; branch if not last one

		move.l	a2,(a0)			; attach the new dgram

		move	(a7)+,RrxPktLen		; restore used reg.
		ext.l	RrxPktLen
		add.l	RrxPktLen,ptStat_rcv_data(a3)	; update STinGs statistics
		addq.w	#1,lcl_es_rx_packets-BS(RitBS)	; update driver statistics-BS(RitBS)
		moveq	#0,d0			; rc=OK

.quit		move	(a7)+,d3		; restore used reg.
		rts


.err1		moveq	#-1,d0
.stat		move	(a7)+,RrxPktLen		; restore used reg.
		addq.w	#1,my_port+ptStat_dropped	; update STinG  statistics
		addq.w	#1,lcl_es_rx_dropped-BS(RitBS)	; update driver statistics
		bra.b	.quit

.err2		move.l	a2,-(a7)		; arg: free space again
		movea.l	tpKRfree(a3),a0
		jsr	(a0)
		addq.w	#4,a7			; pop args
		moveq	#-2,d0
		bra.b	.stat

.err3		move.w	#1,-(a7)		; arg2: flag
		move.l	a2,-(a7)		; arg1: dgram
		movea.l	stx,a0
		movea.l	sxIP_discard(a0),a0	; discard the whole packet
		jsr	(a0)
		addq.w	#6,a7			; pop args
		moveq	#-3,d0
		bra.b	.stat



******** declarations for ethernet **********************************************
		RSRESET
EthDst		RS.B	6			; Ethernet destination address (unused)
EthSrc		RS.B	6			; Ethernet source address (unused)
EthType		RS.W	1			; Ethernet packet type
EthCTypeIP		EQU	$0800		; packet type IP
EthCTypeARP		EQU	$0806		; packet type ARP
EthCtypeRARP		EQU	$8035		; packet type reverse ARP (unused)
EthCTypeIPARPHi		EQU	$08		; Hi byte for both IP and ARP
EthCTypeIPLo		EQU	$00		; Lo byte IP
EthCTypeARPLo		EQU	$06		; Lo byte ARP
EthN		EQU	__RS
NArpPkt		EQU	64-EthN			; length of Arp packet without ethernet header
						; but with padding and ethernet CRC



******** DumpPkt ****************************************************************
* This is a macro for speed
* in:	RrxPktLen	(d2) The raw ethernet packet length
*
* This code effects to get the packet out of the NEx000 card into the ST RAM
*
*	registers d3-d5,Ra1l,Ra1h,RcACSI,RdACSI must not be changed!
*
	
DumpPkt		MACRO
	IFGE	RXDEBPRT-999
		PrW	RrxPktLen
		PrA	<" RrxPktLen",13,10>
	ENDC
* we do not need the two leading ethernet MAC addresses (12 bytes) and the trailing 4 CRC bytes
* thus we adjust the # of bytes to read
		move	RrxPktLen,d0
		sub	#EthType+NCRC,d0		; enet dst & src (12) bytes + 4 CRC less
		putACSI	d0,EN0_RCNTLO
		lsr.w	#8,d0
		putACSI	d0,EN0_RCNTHI
* hence we skip the 8390 header and the 2 MAC addreses
		putACSI	#N8390Hdr+EthType,EN0_RSARLO	; skip 8390 header (4) + enet dst & src (12)
		putACSI	RrxReadPg,EN0_RSARHI
* thus we start remote DMA read command starting at the 2 ethernet type bytes
		putACSI	#E8390_RREAD+E8390_START,E8390_CMD

* note that the data is shifted by one byte in case of a junk header, we need to do one more read
		tst.b	rxJnk8990(sp)
		beq.b	.Mc1
		getACSI	NE_DATAPORT,d0			; dummy read

.Mc1		moveq	#-1,d0				; default is error
		getACSI	NE_DATAPORT,d0			; get type hi byte
		cmp.b	#EthCTypeIPARPHi,d0
		bne	.Merr
		getMore	NE_DATAPORT,d0			; get type lo byte
		cmp.b	#EthCTypeIPLo,d0		; IP packet?
		beq	.MIP
		cmp.b	#EthCTypeARPLo,d0		; ARP packet?
		beq	.MARP				; if not it is an error


.Merr		addq.w	#1,lcl_es_rx_errors-BS(RitBS)	; fall thru
.Merr1
	IFGE	RXDEBPRT-1
		PrA	<"rc ">
		PrL	d0
		PrA	<" ">
		getACSI	NE_DATAPORT,d0
		PrB	d0
		PrA	<" ">
		getACSI	NE_DATAPORT,d0
		PrB	d0
		PrA	<" ">
		getACSI	NE_DATAPORT,d0
		PrB	d0
		PrS	crlf(pc)
	ENDC
		putACSI	#E8390_NODMA+E8390_PAGE0,E8390_CMD	; abort remote DMA
		getACSI	NE_DATAPORT,d0			; only this makes for a proper abort !!!
		putACSI	#ENISR_RDC,EN0_ISR		; reset remote DMA ready bit
		bra	.Mexit


.MARP		moveq	#-11,d0
		move	#NArpPkt,IPLength-BS(RitBS)	; save ARP packet length
		move	RrxPktLen,d1			; raw ethernet packet length
		sub	#EthN+NCRC,d1			; ethernet header + 4 CRC less
		cmp	#NArpPkt,d1			; must fit in buffer
		bhi	.Merr
		lea	ArpPktBuff,a0			; destination
		NE2RAM	a0,d1				; both regs get destroyed!
		putACSI	#ENISR_RDC,EN0_ISR		; reset remote DMA ready bit
		lea	ArpPktBuff,a0			; arg1:
		move	RrxPktLen,d0			; arg2:
		jsr	process_arp
		addq.w	#1,lcl_es_rx_packets-BS(RitBS)	; update driver statistics
		bra	.Mexit


.MIP		bsr	retrieve_dgram			; implied arg1: RrxPktLen	
		tst.l	d0
		bne	.Merr1
*		bra.b	.Mexit				; fall thru to exit

.Mexit
		ENDM



******** ei_receive *************************************************************
* We have a good packet(s), get it/them out of the buffers.
*
* N.b.: get_8390_hdr and block_input employed in the Linux code are not used here.
* Rather an inlined remote DMA Read packet technique is used
*
* in
*	nothing
*
* out
*	nothing
*
* changed
*	d0-d3, a0-a3
*
********
* The 8390 header problem:
* Was observed with 1 NE1000 clone with Winbond W89C90 chip
* 1 NE2000 clone with Winbond W89C90 chip
* 1 NE2000 clone with UMC UM9090 chip
* Symptoms:
* The Status field (rxHdrSts) contains garbage
* The Next Frame field (RrxNextFrm) looks like it contains the Status value
* The Lo Byte Count field looks like it contains the Next Frame value
* The Hi Byte Count field looks like it contains the Lo Byte Count
* We recover accordingly
* This renders 8390 Remote DMA Send useless and necessitates Remote DMA Read
* This also defeats use of the error bits in the 8390 header
*********************************************************************************

N8390Hdr	EQU	4		; the chip saves a 4 bytes header preceeding the packet
NCRC		EQU	4		; 4 trailing CRC bytes

* local variables in registers
RrxReadPg	EQUR	d4
RrxNextFrm	EQUR	d3
;RrxPktLen	EQUR	d2			; * defined already in retrieve_dgram above!

* local variables in memory
		RSRESET
rxHdrSts16	RS.B	1			; to allow for word ACSI reads
rxHdrSts8	RS.B	1			; status in 8390 Header
rxPktCnt	RS.W	1			; max. # of packets to slurp
rxJnk8990	RS.B	1			; Header is junk flag (8990, 9090 symptom)
rxJnk8019	RS.B	1			; Header is junk flag (RTL8019 symptom)
rxNLcl		EQU	__RS


ei_receive	Alloc	rxNLcl				; locals vars
* Smoother operation if ack is done *before* remote DMA Read
		putACSI	#ENISR_RX+ENISR_RX_ERR,EN0_ISR
		move	#20,rxPktCnt(sp)			; # packets maximum

		move.b	lcl_current_page8-BS(RitBS),RrxReadPg	; start reading where writing should have commenced

		putACSI	#E8390_PAGE1,E8390_CMD			; switch to page 1
		move	sr,d1					; save status register
		ori	#$700,sr				; disable all ints.
.b1		getACSI	EN1_CURPAG,lcl_current_page16-BS(RitBS)	; get the rx page (incoming packet pointer)
		getACSI	EN1_CURPAG,d0				; again
		cmp.b	lcl_current_page8-BS(RitBS),d0		; should be equal
		bne.b	.b1
		move	d1,sr					; restore status
		putACSI	#E8390_PAGE0,E8390_CMD			; revert to page 0



* when we have read up to CurrPg we are done
.t1		cmp.b	lcl_current_page8-BS(RitBS),RrxReadPg	; Read all the frames?
		beq	.exit				; Done for now

* get the header the NIC stored at the beginning of the frame
		moveq	#0,d0
		move	d0,rxJnk8990(sp)		; clear Jnk8990 AND Jnk8019
		putACSI	#N8390Hdr,EN0_RCNTLO
		putACSI	d0,EN0_RCNTHI
		putACSI	d0,EN0_RSARLO			; Hdr is on page boundary
		putACSI	RrxReadPg,EN0_RSARHI
		putACSI	#E8390_RREAD+E8390_START,E8390_CMD ; start remote DMA Read

		getACSI	NE_DATAPORT,rxHdrSts16(sp)	; first: status byte
		getMore	NE_DATAPORT,RrxNextFrm		; next:  next page
		getMore	NE_DATAPORT,d0			; next:  Count Lo
		getMore	NE_DATAPORT,RrxPktLen		; next:  Count Hi
		putACSI	#ENISR_RDC,EN0_ISR		; reset remote DMA ready bit
		lsl.w	#8,RrxPktLen			; Count Hi in upper byte
		move.b	d0,RrxPktLen			; merge in Count Lo byte
* RrxPktLen is the length of the packet data to *follow* the header (incl. CRC)

* check status (only $01 and $21 is good)
		move.b	rxHdrSts8(sp),d0
		and.b	#$ff-ENRSR_PHY-ENRSR_DEF,d0	; do not care phys/multi., defer.
		cmp.b	#ENRSR_RXOK,d0			; only his should be set
		bne.b	.e1

* check if next frame is within start and stop
		cmp.b	lcl_rx_start_page-BS(RitBS),RrxNextFrm
		bcs.b	.e1
		cmp.b	lcl_stop_page-BS(RitBS),RrxNextFrm
		bhi.b	.e1

* we should also check here if CountHi is consistent with NextFrm and ThisFrm (sic!)

* check for good ethernet packet length
		cmp	#64,RrxPktLen			; check for bogus length
		bcs.b	.e1
		cmp	#1518,RrxPktLen			; 6(eth)+6(eth)+2(type)+1500+4(crc)
		bls	.getPkt				; get the packet if tests passed sucessfully


.e1		; when we get here we have the 8390 header writing problem
		; or we got crap from the middle of a frame
	IFGE	RXDEBPRT-2
		PrA	<9,"RxX">
		bsr	eirDumpState
	ENDC
* check status (only $01 and $21 is good) again
		move.b	RrxNextFrm,d0			; the misguided status
		and.b	#$ff-ENRSR_PHY-ENRSR_DEF,d0	; do not care phys/multi., defer.
		cmp.b	#ENRSR_RXOK,d0			; only his should be set
		bne.b	.e2				; hopeless

		; here there is still a chance
		move.b	RrxPktLen,RrxNextFrm		; fix Next
		sub.b	RrxReadPg,RrxPktLen		; calculate Count Hi Byte
		subq.b	#1,RrxPktLen			; rounding ???
		bge.b	.c1
	
		add.b	lcl_stop_page-BS(RitBS),RrxPktLen		; adjust for wrap
		sub.b	lcl_rx_start_page-BS(RitBS),RrxPktLen

.c1		ror.w	#8,RrxPktLen			; swap bytes
		tst.b	RrxPktLen			; if Lo Count==0 ...
		bne.b	.c2
		add.w	#$0100,RrxPktLen		; ...one more block
.c2
	IFGE	RXDEBPRT-2
		PrA	<9,"RxY">
		bsr	eirDumpState
	ENDC

* check if next frame is within start and stop again
		cmp.b	lcl_rx_start_page-BS(RitBS),RrxNextFrm
		bcs.b	.e2
		cmp.b	lcl_stop_page-BS(RitBS),RrxNextFrm
		bhi.b	.e2

* check for good ethernet packet length again
		cmp	#64,RrxPktLen			; check for bogus length
		bcs.b	.e2
		cmp	#1518,RrxPktLen			; 6+6+2(type)+1500+4(crc)
		bhi.b	.e2

		; when we get here we have the 8390 header problem sucessfully fixed
		; and go for normal extraction of the packet
		st	rxJnk8990(sp)			; take note of Hdr problem
		bra.b	.getPkt				; go get it


.e2		; when we get here we may have the 8019 header problem
		; we suppose the largest possible packet
		move	#1518,RrxPktLen			; 6+6+2(type)+1500+4(crc)
		st	rxJnk8019(sp)			; take note of Hdr problem
							; fall through


.getPkt		; here we assume things are OK
	IFGE	RXDEBPRT-3
		PrA	<9,"Rx ">
		bsr	eirDumpState
	ENDC

		DumpPkt

		tst.b	rxJnk8019(sp)				; did we assume RTL8019 Header problem?
		beq.b	.c4

		IFNE	1
		bra	.resync					; nail Next to Current
		ELSEIF
* try to caclulate NextFrm from the IP or ARP packet length
		move	IPLength-BS(RitBS),RrxNextFrm		; get IP or ARP packet length
		cmp	#46,RrxNextFrm				; check for good IP lenght
		bcs	.resync
		cmp	#1500,RrxNextFrm			; "
		bhi	.resync

		add	#N8390Hdr+EthN+NCRC+255,RrxNextFrm	; this shall be the raw enet packet incl. Hdr incl. CRC
		lsr.w	#8,RrxNextFrm				; +255 / 256 = # of pages needed
		add.b	RrxReadPg,RrxNextFrm
		cmp.b	lcl_stop_page-BS(RitBS),RrxNextFrm	; wrap around?
		bcs.b	.c4
		sub.b	lcl_stop_page-BS(RitBS),RrxNextFrm
		add.b	lcl_rx_start_page-BS(RitBS),RrxNextFrm
		ENDC		


* set boundary one page behind up to including the page we read
.c4		move.b	RrxNextFrm,RrxReadPg			; start of next packet to read
		cmp.b	lcl_rx_start_page-BS(RitBS),RrxNextFrm	; if Next is at the start
		bne.b	.c5
		move.b	lcl_stop_page-BS(RitBS),RrxNextFrm	; ...boundary must be stop-1
.c5		subq.b	#1,RrxNextFrm
		putACSI	RrxNextFrm,EN0_BOUNDARY			; update boundary

		subq.w	#1,rxPktCnt(sp)			; make sure not tied up to much
		bne	.t1				; next packet


	IFGE	RXDEBPRT-2
		PrA	<9,"Too much to receive",10,13,0>
	ENDC


.exit		deAlloc	rxNLcl				; pop local vars
		rts



.resync		; when we get here we can not recover; we try a clean restart
		addq.w	#1,lcl_es_rx_errors-BS(RitBS)
	IFGE	RXDEBPRT-1
		PrA	<9,"RxZ">
		bsr	eirDumpState
	ENDC
		move.b	lcl_current_page8-BS(RitBS),d0
		cmp.b	lcl_rx_start_page-BS(RitBS),d0	; handle wrap around
		bne.b	.rc1
		move.b	lcl_stop_page-BS(RitBS),d0
.rc1		subq.b	#1,d0
		putACSI	d0,EN0_BOUNDARY
		bra.b	.exit				; ...and abort receive


eirDumpState	
		PrA	<" Cnt: ">
		PrW	rxPktCnt+4(sp)
		PrA	<" Ths: ">
		PrB	RrxReadPg
		PrA	<" Nxt: ">
		PrB	RrxNextFrm
		PrA	<" Cur: ">
		PrB	lcl_current_page8-BS(RitBS)
		PrA	<" Sts: ">
		PrB	rxHdrSts8+4(sp)
		PrA	<" Siz: ">
		PrW	RrxPktLen
		PrS	crlf(pc)
		rts


******** ei_rx_overrun **********************************************************
* We have a receiver overrun: we have to kick the 8390 to get it started
* again. Problem is that you have to kick it exactly as NS prescribes in
* the updated datasheets, or "the NIC may act in an unpredictable manner."
* This includes causing "the NIC to defer indefinitely when it is stopped
* on a busy network."  Ugh.
*
* in
*	nothing
*
* out
*	nothing
*
* changed
*
*********************************************************************************

* local variables in memory; we must use them here instead of registers because 
* ei_receive (called below) would destroy those registers
		RSRESET
roWasTxing	RS.W	1
roMustResend	RS.w	1
roNLcl		EQU	__RS


ei_rx_overrun	Alloc	roNLcl				; locals vars
		clr	roMustResend(sp)

		getACSI	E8390_CMD,d0
		and	#E8390_TRANS,d0
		move	d0,roWasTxing(sp)		; find out if transmit is in progress
		
		putACSI	#E8390_NODMA+E8390_PAGE0+E8390_STOP,E8390_CMD	; stop

	IFGE	RXDEBPRT-1
		PrA	<"Receiver overrun",13,10>
	ENDC
		addq.w	#1,lcl_es_rx_over_errors-BS(RitBS)

* Wait a full Tx time (1.2ms) + some guard time, NS says 1.6ms total.
* Early datasheets said to poll the reset bit, but now they say that
* it "is not a reliable indicator and subsequently should be ignored."

		move.l	ticks2ms-BS(RitBS),d0	; wait 2ms
		moveq	#2,d1			; irrelevant here
		bsr	ADelay

* Reset RBCR[01] back to zero as per magic incantation.
		moveq	#0,d0
		putACSI	d0,EN0_RCNTLO
		putACSI	d0,EN0_RCNTHI

* See if any Tx was interrupted or not. According to NS, this
* step is vital, and skipping it will cause no end of havoc.
		tst	roWasTxing(sp)
		beq.b	.c1

		getACSI	EN0_ISR,d0
		and	#ENISR_TX+ENISR_TX_ERR,d0	; completed if non zero
		seq	roMustResend(sp)		; if zero must resend

* Have to enter loopback mode and then restart the NIC before
* you are allowed to slurp packets up off the ring.
.c1		putACSI	#E8390_TXOFF,EN0_TXCR
		putACSI	#E8390_NODMA+E8390_PAGE0+E8390_START,E8390_CMD

* Clear the Rx ring of all the debris, and ack the interrupt.
		bsr	ei_receive
		putACSI	#ENISR_OVER,EN0_ISR

* Leave loopback mode, and resend any packet that got stopped.
		putACSI	#E8390_TXCONFIG,EN0_TXCR 

		tst	roMustResend(sp)
		beq.b	.c2
 
		putACSI	#E8390_NODMA+E8390_PAGE0+E8390_START+E8390_TRANS,E8390_CMD

.c2		deAlloc	roNLcl			; pop local vars
		rts




*********************************************************************************
* update statics counters and get access to them
*********************************************************************************

Rgs		REG	Ra1l/Ra1h/RcACSI/RdACSI


get_stats	movem.l	Rgs,-(sp)
		lockACSI			; aquire ACSI bus (d0=rc, a0 modified)
						; jumps to .doNothing on fail to lock
		ldACSIRegs			; load registers to access ACSI bus

		tst.b	dev_start		; device active
		beq.b	.c1			; if not, just return pointer

* if device is running, update statistics
		move	#$ff,d1
		getACSI	EN0_COUNTER0,d0
		and	d1,d0			; suppress junk bits
		add	d0,lcl_es_rx_frame_errors
		getACSI	EN0_COUNTER1,d0
		and	d1,d0
		add	d0,lcl_es_rx_crc_errors
		getACSI	EN0_COUNTER2,d0
		and	d1,d0
		add	d0,lcl_es_rx_missed_errors
	
.c1		move.l	#lcl_stats,d0		; OK

		deselACSI			; deselect ACSI interface
		unlockACSI			; relinquish ACSI bus (modifies a0)
.quit		movem.l	(sp)+,Rgs		; restore used registers
		rts

.doNothing	moveq	#0,d0
		bra.b	.quit


******** set_multicast_list *****************************************************
*	Set or clear the multicast filter for this adaptor.
*********************************************************************************

set_multicast_list
		move	dev_flags,d0
		and	#IFF_PROMISC,d0
		beq.b	.c1
		putACSI	#E8390_RXCONFIG+$18,EN0_RXCR
		bra.b	.c3

.c1		move	dev_flags,d0
		and	#IFF_ALLMULTI,d0
		or	dev_mc_list,d0
		beq.b	.c2
* The multicast-accept list is initialized to accept-all, and we rely on 
* higher-level filtering for now.
		putACSI	#E8390_RXCONFIG+$08,EN0_RXCR
		bra.b	.c3

.c2		putACSI	#E8390_RXCONFIG,EN0_RXCR

.c3		rts



******** ethdev_init ************************************************************
* Initialize the rest of the 8390 device structure.
*********************************************************************************

ethdev_init	bsr	ether_setup
		rts



******** NS8390_init ************************************************************
* This page of functions should be 8390 generic
* Follow National Semi's recommendations for initializing the "NIC"
*
* in:
*	d0.w	if <> 0 initialize more
* out:
*	nothing
*
* changed:
*	d0, a0
*********************************************************************************

NS8390_init	move	d0,-(sp)		; save argument "startp"

* Follow National Semi's recommendations for initing the DP83902
		putACSI	#E8390_NODMA+E8390_PAGE0+E8390_STOP,E8390_CMD
* we always access byte-wise: $48, never word-wise: $49
		putACSI	#$48,EN0_DCFG
* Clear the remote byte count registers
		moveq	#0,d0
		putACSI	d0,EN0_RCNTLO
		putACSI	d0,EN0_RCNTHI
* Set to monitor and loopback mode -- this is vital!
		putACSI	#E8390_RXOFF,EN0_RXCR
		putACSI	#E8390_TXOFF,EN0_TXCR
* Set the transmit page and receive ring
		putACSI	lcl_tx_start_page16,EN0_TPSR
		move.b	lcl_rx_start_page,d0
		putACSI	d0,EN0_BOUNDARY
		putACSI	d0,EN0_STARTPG
		move.b	lcl_stop_page,d0
		putACSI	d0,EN0_STOPPG
* Clear the pending interrupts and mask
		putACSI	#$ff,EN0_ISR		; acknoledge all
		putACSI	#$00,EN0_IMR		; and disable all interrupts
    
* Copy the station address into the DS8390 registers, and set the 
* multicast hash bitmap to receive all multicasts
		putACSI	#E8390_NODMA+E8390_PAGE1+E8390_STOP,E8390_CMD
		lea	dev_dev_addr,a0
		move.b	(a0)+,d0
		putACSI	d0,EN1_PHYS+0
		move.b	(a0)+,d0
		putACSI	d0,EN1_PHYS+1
		move.b	(a0)+,d0
		putACSI	d0,EN1_PHYS+2
		move.b	(a0)+,d0
		putACSI	d0,EN1_PHYS+3
		move.b	(a0)+,d0
		putACSI	d0,EN1_PHYS+4
		move.b	(a0)+,d0
		putACSI	d0,EN1_PHYS+5

* Initialize the multicast list to accept-all. If we enable multicast the 
* higher levels can do the filtering
		moveq	#$ff,d0
		putACSI	d0,EN1_MULT+0
		putACSI	d0,EN1_MULT+1
		putACSI	d0,EN1_MULT+2
		putACSI	d0,EN1_MULT+3
		putACSI	d0,EN1_MULT+4
		putACSI	d0,EN1_MULT+5
		putACSI	d0,EN1_MULT+6
		putACSI	d0,EN1_MULT+7

		move.b	lcl_rx_start_page,d0
		addq.b	#1,d0					; boundary one behind current
		putACSI	d0,EN1_CURPAG
		move.b	d0,lcl_current_page8			; mirror
		putACSI	#E8390_NODMA+E8390_PAGE0+E8390_STOP,E8390_CMD	; back to page 0

		sf	dev_tbusy
		sf	dev_interrupt

		move	(sp)+,d0		; restore argument "startp"
		beq.b	.c1
		putACSI	#$ff,EN0_ISR		; clear all interrupts
* for this application (polling) we leave all intr masked.
*		putACSI	#ENISR_ALL,EN0_IMR	; and enable all
		putACSI	#E8390_NODMA+E8390_PAGE0+E8390_START,E8390_CMD
		putACSI	#E8390_TXCONFIG,EN0_TXCR	; xmit on
* 3c503 TechMan says rxconfig only after the NIC is started
		putACSI	#E8390_RXCONFIG,EN0_RXCR	; rx on 
* Get the multicast status right if this was a reset
		bsr	set_multicast_list

.c1		rts




******** End of the NS8390 chip specific code ***********************************
*********************************************************************************
******** Start of the NEx000 and clones board specific code *********************


*NE_BASE	EQU	 (dev->base_addr)	; that is fixed here
NE_CMD		EQU	$00
NE_DATAPORT	EQU	$10		; NatSemi-defined port window offset
NE_RESET	EQU	$1f		; Issue a read to reset, a write to clear
NE_IO_EXTENT	EQU	$20

NE1SM_START_PG	EQU	$20		; First page of TX buffer
	IFNE	0
NE1SM_STOP_PG	EQU	$40		; Last page +1 of RX ring (NE1000 with 8K)
	ELSEIF
NE1SM_STOP_PG	EQU	$60		; Last page +1 of RX ring (NE1000 with 16K)
	ENDC

NESM_START_PG	EQU	$40		; First page of TX buffer
	IFNE	1
NESM_STOP_PG	EQU	$80		; Last page +1 of RX ring
	ELSEIF
* some NE2000 cards in 8bit mode seem to use only half of memory (use only 8 from 16 bits internally?)
NESM_STOP_PG	EQU	$60		; Last page +1 of RX ring
	ENDC



**** Delay function specific to Ataris ******************************************
* in:
*	d0.l maximum delay time in processor specific units
*	d1.l maximum delay time in 200Hz timer ticks = 5ms
* out:
*	d0 time left in processor specific units

ADelay		lea	_hz_200\w,a0
		add.l	(a0),d1			; time to quit at

.t1		subq.l	#1,d0			; time has come?
		beq.b	.exit
		cmp.l	(a0),d1			; time has come?
		bhi.b	.t1

.exit		rts



******** ne_probe1 **************************************************************
* we do not have a ne_probe from which this gets called
*
* in:
*	nothing
* out:
*	nothing
*
* changed:
*	a lot
*********************************************************************************

NBSA_prom	EQU	32

* local variables in memory
		RSRESET
pbSA_prom	RS.B	NBSA_prom
pbWordLen	RS.W	1
pbNEx000	RS.B	1
pbFiller	RS.B	1
pbNLcl		EQU	__RS


ne_probe1	Alloc	pbNLcl			; allocate locals vars
		move	#2,pbWordLen(sp)
		lockACSI			; aquire ACSI bus (modifies a0)
		ldACSIRegs			; load registers to access ACSI bus

* first we calibrate 2ms for this specific machine
		move.l	#0,d0			; indefinitely
		move.l	#26,d1			; 26*5ms=130ms according to _hz_200
		bsr.b	ADelay
		neg.l	d0			; so many machine ticks for 130ms
		asr.l	#6,d0			; 2ms = 130ms/64
		addq.l	#1,d0			; always round up
		move.l	d0,ticks2ms		; store for use in the driver

		getACSI	NE_RESET,d1
		putACSI	d1,NE_RESET

*		move.l	ticks2ms,d0		; wait 2ms
		moveq	#2,d1			; wait 5-10ms maximum
		bsr.b	ADelay

		getACSI	EN0_ISR,d1		; read isr
		and.b	#ENISR_RESET,d1		; test for reset bit
		bne.b	.c1

		PrS	.m1(pc)
		bra	.quit

.c1		putACSI	#$ff,EN0_ISR		; ack all interrupts

		moveq	#0,d0
		putACSI	#E8390_NODMA+E8390_PAGE0+E8390_STOP,E8390_CMD	; select page 0
		putACSI	#$48,EN0_DCFG		; set byte-wide access
		putACSI	d0,EN0_RCNTLO		; clear the count regs.
		putACSI	d0,EN0_RCNTHI
		putACSI	d0,EN0_IMR		; mask completion iqr
		putACSI	#$ff,EN0_ISR
		putACSI	#E8390_RXOFF,EN0_RXCR	; $20 set to monitor
		putACSI	#E8390_TXOFF,EN0_TXCR	; $02 and loopback mode
.again		putACSI	#NBSA_prom&$ff,EN0_RCNTLO
		putACSI	#NBSA_prom>>8,EN0_RCNTHI
		putACSI	d0,EN0_RSARLO		; DMA starting at $0000
		putACSI	d0,EN0_RSARHI
		putACSI	#E8390_RREAD+E8390_START,E8390_CMD	; go

		move	#NBSA_prom/2-1,d2
		lea	pbSA_prom(sp),a0

.t2		getACSI	NE_DATAPORT,d0
		move.b	d0,(a0)+
		getMore	NE_DATAPORT,d1
		move.b	d1,(a0)+
		cmp.b	d0,d1			; check for doubled up values
		beq.b	.c2
		move	#1,pbWordLen(sp)		
.c2		dbra	d2,.t2
	IFNE 0
		move.l	#$00010002,-(sp)	; 2 args: VT52-console, Bconstat
		trap	#13
		addq.l	#4,sp
		tst	d0
		beq	.again

		WaitKey
	ENDC

	IFNE	MACAddDEBPRT
		lea	pbSA_prom(sp),a0
		moveq	#NBSA_prom/4-1,d2
		PrA	<$1b,"H">

.t3		PrL	(a0)+
		PrS	crlf(pc)
		dbra	d2,.t3

		PrA	<$1b,"H">
*		WaitKey
	ENDC	; MACAddDEBPRT

		cmp	#2,pbWordLen(sp)
		bne.b	.c21

* we have a NE2000 or clone, reorder image of PROM contents
		moveq	#15,d2
		lea	pbSA_prom(sp),a0
		move.l	a0,a1

.t31		move.b	(a0)+,(a1)+	; SA_prom[i]=SA_prom[i+i];
		addq.l	#1,a0
		dbra	d2,.t31

		move.b	#NESM_START_PG,lcl_tx_start_page8
		move.b	#NESM_STOP_PG,lcl_stop_page
		bra.b	.c22

* we have a NE1000 or clone
.c21		move.b	#NE1SM_START_PG,lcl_tx_start_page8
		move.b	#NE1SM_STOP_PG,lcl_stop_page


.c22		move.b	lcl_tx_start_page8,lcl_rx_start_page
		add.b	#TX_PAGES,lcl_rx_start_page

* check this
		cmp.b	#$57,pbSA_prom+14(sp)
		seq	d0
		cmp.b	#$57,pbSA_prom+15(sp)
		seq	pbNEx000(sp)
		and.b	d0,pbNEx000(sp)
		beq.b	.c3

		lea	.m2(pc),a0
		bra.b	.c4

.c3		lea	.m3(pc),a0

.c4		move.l	a0,lcl_name

		bsr	ethdev_init

* store devices ethernet MAC address in struct device
		lea	pbSA_prom(sp),a0		
		lea	dev_dev_addr,a1
		moveq	#ETHER_ADDR_LEN-1,d0

.t4		move.b	(a0)+,(a1)+
		dbra	d0,.t4

		moveq	#0,d0		; arg:
		bsr	NS8390_init

.quit		deselACSI			; deselect ACSI interface
		unlockACSI			; relinquish ACSI bus (modifies a0)
.doNothing	deAlloc	pbNLcl			; pop local vars
		rts


.m1		DC.B	"Reset Bit not set. Fatal",13,10,0
.m2		DC.B	"NE1000",0
.m3		DC.B	"No NE1000",0
		EVEN


******** ne_open ****************************************************************
*
*********************************************************************************

ne_open		bsr	ei_open
		rts


******** ne_close ***************************************************************
*
*********************************************************************************

ne_close	bsr	ei_close
		rts



******** ne_reset_8390 **********************************************************
* Hard reset the card.  This used to pause for the same period that a
* 8390 reset command required, but that shouldn't be necessary
*********************************************************************************

ne_reset_8390	
* DON'T change these to inb_p/outb_p or reset will fail on clones
		getACSI	NE_RESET,d1
		putACSI	d1,NE_RESET

		move.l	ticks2ms-BS(RitBS),d0	; wait 2ms
		moveq	#2,d1			; wait 5-10ms maximum (irrelevant here)
		bsr	ADelay

* This check _should_not_ be necessary, omit eventually
		getACSI	EN0_ISR,d1		; read isr
		and.b	#ENISR_RESET,d1		; test for reset bit
		bne.b	.c1

		PrS	.m1(pc)

.c1		putACSI	#ENISR_RESET,EN0_ISR	; ack interrupt

		rts


.m1		DC.B	"ne_reset_8390 failed",13,10,0
		EVEN




*********************************************************************************
* Simple semaphor routines to be used in a Single-TOS environment
* to be overriden under MagiC
*********************************************************************************

myDMABegin	tas	flock\w			; check, set flock and
		bne.b	.conflict		; if somebody owns the bus we flag
		tas	flock+1\w		; Holzauge: we do *both* bytes
		bne.b	.conflict
		moveq	#0,d0			; OK
		rts

.conflict	moveq	#-1,d0			; signal conflict
		rts


myDMAEnd	clr.w	flock\w			; allow floppy
		rts


		IFNE	MAGIC_ACSI_LOCK
**** instMagiC ******************************************************************
* tests presence of MagiC and overrides DMA semaphor functions 
*********************************************************************************

* structure pointed to by the MagX cookie
		RSRESET
mcConfig	RS.L	1	; unused here
mcDOSvars	RS.L	1	; unused here
mcAESvars	RS.L	1	; unused here
mcRes1		RS.L	1	; unused here
mcHdf		RS.L	1

* function table pointed to by mcHdf
		RSRESET
mhDMA_begin	RS.L	1	; (mcHdf points here)
mhDMA_end	RS.L	1	; 
mhDMA_wait	RS.L	1	; unused here
mhNCR_begin	RS.L	1	; unused here
mhNCR_end	RS.L	1	; unused here
mhNCR_wait	RS.L	1	; unused here



instMagiC	movea.l	pcookies\w,a0		; get pointer to jar (array)
		move.l	a0,d0
		beq.b	.quit			; if NULL pointer quit

.t1		move.l	(a0)+,d0		; get cookie tag
		beq.b	.quit			; last one
		movea.l	(a0)+,a1		; get cookie value
		cmp.l	#'MagX',d0		; MagiC cookie?
		bne.b	.t1			; no, again

		move.l	a1,d0			; value=pointer to MagX structure
		beq.b	.quit			; this should never happen

		move.l	mcHdf(a1),d0		; get hdf pointer
		beq.b	.quit			; this should never happen

		move.l	d0,a0
		move.l	mhDMA_begin(a0),pDMA_begin	; override my pointer to function
		move.l	mhDMA_end(a0),pDMA_end		; override my pointer to function
		PrS	.m1(pc)			; show success

.quit		rts


.m1		DC.B	8,8," MagiC support, ",0
		EVEN

		ELSEIF

instMagiC	rts				; do nothing

		ENDC	; MAGIC_ACSI_LOCK


		IFGT	HWTEST
		IFNE	0
*********************************************************************************
* hardware test from terminal 
*********************************************************************************

instDriver	bsr	ne_probe1
		bsr	ne_open

.t1		WaitKey
		cmp.b	#'x',d0
		beq.b	.quit
		cmp.b	#'r',d0
		beq.b	.c1
		cmp.b	#'t',d0
		beq.b	.c2
		bra.b	.t1

.c1		lockACSI
		bsr	ei_interrupt
		unlockACSI
.doNothing	bra.b	.t1

.c2		move	#NtstPacket,d0
		lea	tstPacket,a0
		bsr	ei_start_xmit
		bra.b	.t1
		
.quit		bsr	ne_close
		rts


		ELSEIF


*********************************************************************************
* hardware test from VBL interrupt 
*********************************************************************************

instDriver	bsr	ne_probe1
		bsr	ne_open

* install receiver in vertical blank queue
		move.l	_vblqueue\w,a0		; get address of first slot
		move	nvbls,d0		; total number of slots
		subq.w	#2,d0			; first slot is reserved for GEM

.t0		addq.l	#4,a0			; first slot is reserved for GEM
		tst.l	(a0)			; free slot?
		dbeq	d0,.t0

		bne.b	.err			; no free slot, quit

		move.l	#ei_interrupt,(a0)	; install my VBL handler


* simulate transmitting
.t1		WaitKey
		cmp.b	#'x',d0
		beq.b	.finish
		cmp.b	#'t',d0
		beq.b	.c2
		bra.b	.t1

.c2		move	#NtstPacket,d0
		lea	tstPacket,a0
		bsr	ei_start_xmit
		bra.b	.t1


* deinstall my VBL handler
.finish		move.l	_vblqueue\w,a0		; get address of first slot
		move	nvbls,d0		; total number of slots
		subq.w	#2,d0			; first slot is reserved for GEM

.t2		addq.l	#4,a0			; first slot is reserved for GEM
		cmp.l	#ei_interrupt,(a0)	; found my guy?
		bne.b	.b2
		clr.l	(a0)			; yes, deinstall it
.b2		dbra	d0,.t2

.quit		bsr	ne_close
		rts


.err		PrA	<"No free VBL slot, quit",13,10>
		bra.b	.quit
		ENDC	; 0/1


******** drv_init ***************************************************************
* driver installation for Pexec or shell_write callers, in user mode as usual
*********************************************************************************

drv_init
* set stack and give back unused memory
		move.l	4(sp),a0		; get pointer to basepage
		move.l	$18(a0),d0		; get start of BSS
		add.l	$1c(a0),d0		; +BSS length=points beyond BSS
		move.l	d0,sp			; here starts our stack
		sub.l	a0,d0			; =total length
		move.l	d0,-(sp)		; reserve this much memory
		move.l	a0,-(sp)		; from there
		clr	-(sp)			; dummy word
		move	#$4a,-(sp)		; Mshrink
		trap	#1			; GemDos
		lea	12(sp),sp		; pop args

		PrS	.m1(pc)			; TaTa

		pea	instMagiC(pc)		; must execute in super mode
		move.w	#$26,-(sp)		; Supexec
		trap	#14
		addq.l	#6,sp

		pea	instDriver(pc)		; must execute in super mode
		move.w	#$26,-(sp)		; Supexec
		trap	#14
		addq.l	#6,sp

	IFNE	0	
* give back unused memory
		move.l	4(sp),a0		; get pointer to basepage
		move.l	$18(a0),d0		; get start of BSS
		add.l	$1c(a0),d0		; +BSS length=points beyond BSS
		sub.l	a0,d0			; =total length
		clr	-(sp)			; OK return code
		move.l	d0,-(sp)		; reserve this much memory
		move	#$31,-(sp)		; Keep Process = Ptermres
	ELSEIF
		clr	-(sp)			; Pterm0
	ENDC
		trap	#1			; Gemdos
		illegal				; should never get here


.m1		DC.B	$1b,"pDriver for ThR ACSI-ISA interface",$1b,"q",13,10
		DC.B	"Version 1999-04-20, (C)1999 Dr. Thomas Redelberger",13,10,0
		EVEN
		ENDC	; HWTEST



******** Initialised data *******************************************************
crlf		DC.B	13,10,0
		EVEN

* These point to the ACSI bus semaphor functions. They point to our functions.
* to be used when MagiC is not present. If MagiC is present they get overidden
pDMA_begin	DC.L	myDMABegin
pDMA_end	DC.L	myDMAEnd


		IFGT	HWTEST
tstPacket	DC.B	$00,$60,$97,$97,$93,$ff
;;;		DC.B	$ff,$ff,$ff,$ff,$ff,$ff
		DC.B	$00,$00,$21,$23,$34,$87
		DC.W	$0800
		REPT	100	
		DC.B	0
		ENDR
NtstPacket	EQU	*-tstPacket
		ENDC	; HWTEST

******** data initialised to zero ***********************************************

;;;		SECTION BSS	; otherwise lcl_xxx-BS(RitBS) will not work
BS			EQU	*
ticks2ms		DS.L	1
IPLength		DS.W	1	; temporary

* this is only a small subset of struct dev from linux' netdevice.h

dev_start		DS.B	1	; start an operation
dev_interrupt		DS.B	1	; interrupt arrived
dev_tbusy		DS.B	1	; transmitter busy 
			DS.B	1	; filler
dev_trans_start		DS.L	1	; Time (in jiffies) of last Tx
dev_flags		DS.W	1	; interface flags (a la BSD)
dev_dev_addr		DS.B	6	; MAC address
dev_mc_list		DS.L	1	; pointer to Multicast MAC addresses (ThR: who fills this?)

* this is only a small subset of struct ei_device from linux' 8390.h
lcl_name		DS.L	1	; points to device name string
;lcl_Bits		DS.B	1	; bit defs for lcl_Bits below
;Bopen			EQU	0
;Bword16		EQU	1	; We have the 16-bit (vs 8-bit) version of the card
;Btxing			EQU	2	; Transmit Active (unused)
;Birqlock		EQU	3	; 8390's intrs disabled when '1' (unused)
;Bdmaing		EQU	4	; Remote DMA Active (unused)
;dummy1			DS.B	1	; word align
lcl_tx_start_page16	DS.B	1	; dummy to allow for direct ACSI get/put
lcl_tx_start_page8	DS.B	1	; first tx RAM page
lcl_rx_start_page	DS.B	1	; first rx RAM page = point past last rx RAM page
lcl_stop_page		DS.B	1	; point past last rx RAM page
lcl_current_page16	DS.B	1	; dummy to allow for direct ACSI get/put
lcl_current_page8	DS.B	1	; Previous write pointer in buffer

* The new statistics table
lcl_stats			EQU	*
lcl_es_rx_packets		DS.W	1
lcl_es_tx_packets		DS.W	1
lcl_es_rx_errors		DS.W	1
lcl_es_tx_errors		DS.W	1
lcl_es_rx_dropped		DS.W	1
lcl_es_tx_dropped		DS.W	1
lcl_es_multicast		DS.W	1
lcl_es_collisions		DS.W	1

* detailed rx_errors
lcl_es_rx_length_errors		DS.W	1
lcl_es_rx_over_errors		DS.W	1
lcl_es_rx_crc_errors		DS.W	1
lcl_es_rx_frame_errors		DS.W	1
lcl_es_rx_fifo_errors		DS.W	1
lcl_es_rx_missed_errors		DS.W	1

* detailed tx_errors
lcl_es_tx_aborted_errors	DS.W	1
lcl_es_tx_carrier_errors	DS.W	1
lcl_es_tx_fifo_errors		DS.W	1
lcl_es_tx_hartbeat_errors	DS.W	1
lcl_es_tx_window_errors		DS.W	1

		SECTION	BSS

ArpPktBuff	DS.B	NArpPkt

		IFGT	HWTEST
		DS.B	256		; my stack area
		ENDC	; HWTEST
******** end of ne.s ************************************************************
