version	equ	0
;History:1145,1

	.286

;  Copyright, 1988-1992, Russell Nelson, Crynwr Software

;   This program is free software; you can redistribute it and/or modify
;   it under the terms of the GNU General Public License as published by
;   the Free Software Foundation, version 1.
;
;   This program is distributed in the hope that it will be useful,
;   but WITHOUT ANY WARRANTY; without even the implied warranty of
;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;   GNU General Public License for more details.
;
;   You should have received a copy of the GNU General Public License
;   along with this program; if not, write to the Free Software
;   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

;-------------------------------------------------------------------------
; vA 4/3/93   Hacks for multiple ISA board support added, IJW @ Leeds Univ.
; vB 10/3/93  Added checks for legal packet size in rx, ijw.
;-------------------------------------------------------------------------

	include	defs.asm

code	segment	word public
	assume	cs:code, ds:code

;----------------------------------------------------------------------------;
;                                                                            ;
;       This source file is the property of 3Com Corporation and may not be  ;
;       copied or distributed in any isomorphic form without an appropriate  ;
;       prior licensing arrangement with 3Com Corporation.                   ;
;                                                                            ;
;       Copyright (c) 1988 3Com Corporation                                  ;
;                                                                            ;
;------ Equates -------------------------------------------------------------;
;
; Command code masks
;
CMD_CMDMASK         equ 0F800h                  ; command bits
CMD_ARGMASK         equ 007FFh                  ; argument bits
;
; Command codes, word form
;
CMD_GLOBALRESET     equ 00000b shl 11           ; global reset
CMD_SELECTWINDOW    equ 00001B shl 11           ; select register window
CMD_STARTINTXCVR    equ 00010b shl 11           ; start internal transciver
CMD_RXDISABLE       equ 00011b shl 11           ; rx disable
CMD_RXENABLE        equ 00100b shl 11           ; rx enable
CMD_RXRESET         equ 00101b shl 11           ; rx reset
CMD_RXDISCARD       equ 01000b shl 11           ; rx discard top packet
CMD_TXENABLE        equ 01001b shl 11           ; tx enable
CMD_TXDISABLE       equ 01010b shl 11           ; tx disable
CMD_TXRESET         equ 01011b shl 11           ; tx reset
CMD_REQUESTINT      equ 01100b shl 11           ; request interrupt
CMD_ACKNOWLEDGE     equ 01101b shl 11           ; acknowledge interrupt
CMD_SETINTMASK      equ 01110b shl 11           ; set interrupt mask
CMD_SETRZMASK       equ 01111b shl 11           ; set read zero mask
CMD_SETRXFILTER     equ 10000b shl 11           ; set rx filter
CMD_SETRXEARLY      equ 10001b shl 11           ; set rx early threshold
CMD_SETTXAVAILABLE  equ 10010b shl 11           ; set tx available threshold
CMD_SETTXSTART      equ 10011b shl 11           ; set tx start threshold
CMD_STATSENABLE     equ 10101b shl 11           ; statistics enable
CMD_STATSDISABLE    equ 10110b shl 11           ; statistics disable
CMD_STOPINTXCVR     equ 10111b shl 11           ; start internal transciver
;
; Command codes, hibyte form (commands without operands only)
;
CMDH_STARTINTXCVR   equ CMD_STARTINTXCVR shr 8
CMDH_RXDISABLE      equ CMD_RXDISABLE shr 8
CMDH_RXENABLE       equ CMD_RXENABLE shr 8
CMDH_RXDISCARD      equ CMD_RXDISCARD shr 8
CMDH_TXENABLE       equ CMD_TXENABLE shr 8
CMDH_TXDISABLE      equ CMD_TXDISABLE shr 8
CMDH_REQUESTINT     equ CMD_REQUESTINT shr 8
CMDH_STATSENABLE    equ CMD_STATSENABLE shr 8
CMDH_STATSDISABLE   equ CMD_STATSDISABLE shr 8
CMDH_STOPINTXCVR    equ CMD_STOPINTXCVR shr 8
;
; Status register bits (INT for interrupt sources, ST for the rest)
;
INT_LATCH           equ 00001h                  ; interrupt latch
INT_ADAPTERFAIL     equ 00002h                  ; adapter failure
INT_TXCOMPLETE      equ 00004h                  ; tx complete
INT_TXAVAILABLE     equ 00008h                  ; tx available
INT_RXCOMPLETE      equ 00010h                  ; rx complete
INT_RXEARLY         equ 00020h                  ; rx early
INT_REQUESTED       equ 00040h                  ; interrupt requested
INT_UPDATESTATS     equ 00080h                  ; update statistics
ST_FAILED           equ 00800h                  ; command failed
ST_BUSY             equ 01000h                  ; command busy
ST_WINDOW           equ 0E000h                  ; window bits (13-15)

STH_FAILED          equ ST_FAILED shr 8
STH_BUSY            equ ST_BUSY shr 8
STH_WINDOW          equ ST_WINDOW shr 8

;
; RxStatus register bits
;
RXS_INCOMPLETE      equ 8000h                   ; not completely received
RXS_ERROR           equ 4000h                   ; error in packet
RXS_LENGTH          equ 07FFh                   ; bytes in RxFIFO
RXS_ERRTYPE         equ 3800h                   ; Rx error type, bit 13-11
RXS_OVERRUN         equ 0000h                   ; overrun error
RXS_OVERSIZE        equ 0800h                   ; oversize packet error
RXS_DRIBBLE         equ 1000h                   ; dribble bit (not an error)
RXS_RUNT            equ 1800h                   ; runt packet error
RXS_CRC             equ 2800h                   ; CRC error
RXS_FRAMING         equ 2000h                   ; framing error

RXSH_INCOMPLETE     equ RXS_INCOMPLETE shr 8
RXSH_ERROR          equ RXS_ERROR shr 8
RXSH_ERRTYPE        equ RXS_ERRTYPE shr 8
RXSH_OVERRUN        equ RXS_OVERRUN shr 8
RXSH_DRIBBLE        equ RXS_DRIBBLE shr 8
RXSH_CRC            equ RXS_CRC shr 8
RXSH_RUNT           equ RXS_RUNT shr 8
RXSH_OVERSIZE       equ RXS_OVERSIZE shr 8
RXSH_FRAMING        equ RXS_FRAMING shr 8
;
; TxStatus register bits
;
TXS_COMPLETE        equ 80h                     ; tx completed
TXS_INTREQUESTED    equ 40h                     ; interrupt on successfull tx
TXS_ERRTYPE         equ 38h                     ; error bits
TXS_JABBERERROR     equ 20h                     ; jabber error
TXS_UNDERRUN        equ 10h                     ; tx underrun error
TXS_MAXCOLLISIONS   equ 08h                     ; max collisions error
TXS_STATUSOVERFLOW  equ 04h                     ; TX status stack is full
;
; Window Numbers
;
WNO_SETUP           equ 0                       ; setup/configuration
WNO_OPERATING       equ 1                       ; operating set
WNO_STATIONADDRESS  equ 2                       ; station address setup/read
WNO_FIFO            equ 3                       ; FIFO management
WNO_DIAGNOSTICS     equ 4                       ; diagnostics
WNO_READABLE        equ 5                       ; registers set by commands
WNO_STATISTICS      equ 6                       ; statistics
;
; Port offsets, Window 1 (WNO_OPERATING)
;
PORT_CmdStatus      equ 0Eh                     ; command/status
PORT_TxFree         equ 0Ch                     ; free transmit bytes
PORT_TxStatus       equ 0Bh                     ; transmit status (byte)
PORT_Timer          equ 0Ah                     ; latency timer (byte)
PORT_RxStatus       equ 08h                     ; receive status
PORT_RxFIFO         equ 00h                     ; RxFIFO read
PORT_TxFIFO         equ 00h                     ; TxFIFO write
;
; Port offsets, Window 0 (WNO_SETUP)
;
PORT_EEData         equ 0Ch                     ; EEProm data register
PORT_EECmd          equ 0Ah                     ; EEProm command register
PORT_CfgResource    equ 08h                     ; resource configuration
PORT_CfgAddress     equ 06h                     ; address configuration
PORT_CfgControl     equ 04h                     ; configuration control
PORT_ProductID      equ 02h                     ; product id (EISA)
PORT_Manufacturer   equ 00h                     ; Manufacturer code (EISA)
;
; Port offsets, Window 2 (WNO_STATIONADDRESS)
;
PORT_SA0_1          equ 00h                     ; station address bytes 0,1
PORT_SA2_3          equ 02h                     ; station address bytes 2,3
PORT_SA4_5          equ 04h                     ; station address bytes 4,5
;
; Port offsets, Window 3 (WNO_FIFO)
;
PORT_ALT_TxFree     equ 0Ch                     ; free transmit bytes (dup)
PORT_RxFree         equ 0Ah                     ; free receive bytes
;
; Port offsets, Window 4 (WNO_DIAGNOSTICS)
;
PORT_MediaStatus    equ 0Ah                     ; media type/status
PORT_SlingshotStatus equ 08h                    ; Slingshot status
PORT_NetDiagnostic  equ 06h                     ; net diagnostic
PORT_FIFODiagnostic equ 04h                     ; FIFO diagnostic
PORT_HostDiagnostic equ 02h                     ; host diagnostic
PORT_TxDiagnostic   equ 00h                     ; tx diagnostic
;
; Port offsets, Window 5 (WNO_READABLE)
;
PORT_RZMask         equ 0Ch                     ; read zero mask
PORT_IntMask        equ 0Ah                     ; interrupt mask
PORT_RxFilter       equ 08h                     ; receive filter
PORT_RxEarly        equ 06h                     ; rx early threshold
PORT_TxAvailable    equ 02h                     ; tx available threshold
PORT_TxStart        equ 00h                     ; tx start threshold
;
; Port offsets, Window 6 (WNO_STATISTICS)
;
PORT_TXBYTES        equ 0Ch                     ; tx bytes ok
PORT_RXBYTES        equ 0Ah                     ; rx bytes ok
PORT_TXDEFER        equ 08h                     ; tx frames deferred (byte)
PORT_RXFRAMES       equ 07h                     ; rx frames ok (byte)
PORT_TXFRAMES       equ 06h                     ; tx frames ok (byte)
PORT_RXDISCARDED    equ 05h                     ; rx frames discarded (byte)
PORT_TXLATE         equ 04h                     ; tx frames late coll. (byte)
PORT_TXSINGLE       equ 03h                     ; tx frames one coll. (byte)
PORT_TXMULTIPLE     equ 02h                     ; tx frames mult. coll. (byte)
PORT_TXNOCD         equ 01h                     ; tx frames no CDheartbt (byte)
PORT_TXCARRIERLOST  equ 00h                     ; tx frames carrier lost (byte)
;
; Various command arguments
;
INT_ALLDISABLED         equ 00000000000b            ; all interrupts disabled
INT_ALLENABLED          equ 00011111110b            ; all interrupts enabled

FILTER_INDIVIDUAL       equ 0001b                   ; individual address
FILTER_MULTICAST        equ 0010b                   ; multicast/group addresses
FILTER_BROADCAST        equ 0100b                   ; broadcast address
FILTER_PROMISCUOUS      equ 1000b                   ; promiscuous mode

RXEARLY_DISABLED        equ 2032                    ; RxEarly to disable

TXAVAIL_DISABLED        equ 2040                    ; TxAvailable to disable
TXAVAIL_MIN             equ 4

TXSTART_DISABLED        equ 2040                    ; TxStart to disable
TXSTART_MIN             equ 0
TXSTART_MAX             equ TXSTART_DISABLED

RXLENGTH_MAX            equ 1792                    ; maximum rxlength
;
; Transmit Preamble
;
PREAMBLESIZE            equ 4                       ; transmit preamble size
TXP_INTONSUCCESS        equ 8000h                   ; interrupt on successful tx
;
; Bits in various diagnostics registers
;
MEDIA_TP                equ 8000h                   ; TP transciever
MEDIA_BNC               equ 4000h                   ; Thinnet transciever
MEDIA_INTENDEC          equ 2000h                   ; internal encoder/decoder
MEDIA_SQE               equ 1000h                   ; SQE present
MEDIA_LBEAT             equ 0800h                   ; link beat ok (TP)
MEDIA_POLARITY          equ 0400h                   ; polarity (TP)
MEDIA_JABBER            equ 0200h                   ; jabber (TP)
MEDIA_UNSQUELCH         equ 0100h                   ; unsquelch (TP)
MEDIA_LBEATENABLE       equ 0080h                   ; link beat enable (TP)
MEDIA_JABBERENABLE      equ 0040h                   ; jabber enable (TP)
MEDIA_CRS               equ 0020h                   ; carrier sense
MEDIA_COLLISION         equ 0010h                   ; collision
MEDIA_SQEENABLE         equ 0008h                   ; enable SQE statistics

NETD_EXTLOOPBACK        equ 8000h                   ; TP external loopback
NETD_ENDECLOOPBACK      equ 4000h                   ; ENDEC loopback
NETD_CORELOOPBACK       equ 2000h                   ; ethernet core loopback
NETD_FIFOLOOPBACK       equ 1000h                   ; FIFO loopback
NETD_TXENABLED          equ 0800h                   ; tx enabled
NETD_RXENABLED          equ 0400h                   ; rx enabled
NETD_TXTRANSMITTING     equ 0200h                   ; tx transmitting
NETD_TXRESETREQD        equ 0100h                   ; tx reset required

FIFOD_RXRECEIVING       equ 8000h                   ; rx receiveing
FIFOD_RXUNDERRUN        equ 2000h                   ; rx underrun
FIFOD_RXSTATUSOVER      equ 1000h                   ; rx status overrun
FIFOD_RXOVERRUN         equ 0800h                   ; rx overrun
FIFOD_TXOVERRUN         equ 0400h                   ; tx overrun
FIFOD_BISTRESULTS       equ 00FFh                   ; BIST results (mask)

SLING_TXUNDERRUN        equ 2000h                   ; Slingshot TxUnderrun bit
;
; board identification codes, byte swapped in Rev 0
;
EISA_MANUFACTURER_ID    equ 06D50h                  ; EISA manufacturer code
ISA_PRODUCT_ID          equ 09050h                  ; Product ID for ISA board
PRODUCT_ID_MASK         equ 0F0FFh                  ; Mask off revision nibble
;
; EEProm access
;
EE_BUSY                     equ 8000h                   ; EEProm busy bit in EECmd
EE_TCOM_NODE_ADDR_WORD0     equ 00h
EE_TCOM_NODE_ADDR_WORD1     equ 01h
EE_TCOM_NODE_ADDR_WORD2     equ 02h
EE_VULCAN_PROD_ID           equ 03h
EE_MANUFACTURING_DATA       equ 04h
EE_SERIAL_NUMBER_WORD0      equ 05h
EE_SERIAL_NUMBER_WORD1      equ 06h
EE_MANUFACTURER_CODE        equ 07h
EE_ADDR_CONFIGURATION       equ 08h
EE_RESOURCE_CONFIGURATION   equ 09h
EE_OEM_NODE_ADDR_WORD0      equ 0Ah
EE_OEM_NODE_ADDR_WORD1      equ 0Bh
EE_OEM_NODE_ADDR_WORD2      equ 0Ch
EE_SOFTWARE_CONFIG_INFO     equ 0Dh
EE_CWORD                    equ 0Eh
;
; contention logic
;
READ_EEPROM             equ 080h
ID_GLOBAL_RESET		equ 0C0h
SET_TAG_REGISTER        equ 0D0h
TEST_TAG_REGISTER       equ 0D8h
ACTIVATE_AND_SET_IO     equ 0E0h
ACTIVATE_VULCAN         equ 0FFh
;
; Resource Configuration Register bits
;
RCONFIG_IRQ             equ 0F000h
;
; Address Configuration Register bits
;
ACONFIG_XCVR            equ 0C000h
ACONFIG_IOBASE          equ 0001Fh

IOBASE_EISA             equ 0001Fh

TP_XCVR                 equ 00000h
BNC_XCVR                equ 0C000h
AUI_XCVR                equ 04000h

MIN_IO_BASE_ADDR        equ 200h
MAX_IO_BASE_ADDR        equ 3F0h
REGISTER_SET_SIZE       equ 10h
;
; Software Configuration Register bits
;
SW_OPTIMIZE             equ 0030h
SW_MAXCLI               equ 3F00h
SW_LINKBEAT             equ 4000h
;
; Possibilities for SW_OPTIMIZE
;
OPTIMIZE_DOS_CLIENT     equ 0010h
OPTIMIZE_WINDOWS_CLIENT equ 0020h
OPTIMIZE_SERVER         equ 0030h
;
; Configuration Control Register bits
;
ENABLE_ADAPTER          equ 01h

setwin	macro	win
	setport	PORT_CmdStatus
	mov	ax,CMD_SELECTWINDOW+win
	out	dx,ax
	endm

	extrn	is_eisa: byte		;=0 if ISA, =1 if EISA
	extrn	is_186: byte		;=0 if 808[68], =1 if 80[1234]86.
	extrn	is_386: byte		;=0 if 80[12]8[68], =1 if 80[34]86.

	public	int_no, io_addr
int_no	db	0,0,0,0			;must be four bytes long for get_number.
io_addr	dw	0,0			;must be four bytes long for get_number.

	public	driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class	db	BLUEBOOK,IEEE8023,0	;null terminated list of classes.
driver_type	db	94		;assigned by FTP Software, <jbvb@ftp.com>
driver_name	db	'3c509',0	;name of the driver.
driver_function	db	2
parameter_list	label	byte
	db	1	;major rev of packet driver specification
	db	9	;minor rev of packet driver specification
	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
int_num	dw	0	;Interrupt # to hook for post-EOI
			;processing, 0 == none,

	public	rcv_modes
rcv_modes	dw	7		;number of receive modes in our table.
		dw	0               ;There is no mode zero
		dw	rcv_mode_1
		dw	rcv_mode_2
		dw	rcv_mode_3
		dw	0		;haven't set up perfect filtering yet.
		dw	rcv_mode_5
		dw	rcv_mode_6

	include	timeout.asm

	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 es:di->upcall routine, (0:0) if no upcall is desired.
;  (only if the high-performance bit is set in driver_function)
;enter with ds:si -> packet, cx = packet length.
;if we're a high-performance driver, es:di -> upcall.
;exit with nc if ok, or else cy if error, dh set to error number.
	assume	ds:nothing
	cmp	cx,GIANT		; Is this packet too large?
	ja	send_pkt_toobig

	loadport
	setport	PORT_TxStatus		;get the previous transmit status.
	in	al,dx
	setport	PORT_CmdStatus
	test	al,TXS_UNDERRUN or TXS_JABBERERROR	;do we need to reset transmitter?
	je	send_pkt_0
	push	ax
	mov	ax,CMD_TXRESET
	out	dx,ax
	pop	ax
send_pkt_0:

	test	al,TXS_ERRTYPE		;any errors?
	je	send_pkt_3		;no.
	call	count_out_err		;yes, count it.
	mov	ax,CMD_TXENABLE		;yes, re-enable the transmitter.
	out	dx,ax
send_pkt_3:

	mov	bx,cx			;round BX up to dword boundary.
	add	bx,3
	and	bx,not 3

	setport	PORT_TxFree		;wait for enough bytes in transmit buffer.
	mov	ax,18
	call	set_timeout
send_pkt_1:
	in	ax,dx
	cmp	ax,bx
	jae	send_pkt_2
	call	do_timeout
	jne	send_pkt_1
	mov	dh,CANT_SEND		;timed out, can't send.
	stc
	ret
send_pkt_toobig:
	mov	dh,NO_SPACE
	stc
	ret
send_pkt_2:

	setport	PORT_TxFIFO
	mov	ax,cx			;output the count
	out	dx,ax			;   (no interrupt requested)
	out	dx,ax			;output the second reserved word.
	mov	cx,bx			;output the rest of the packet.

	cmp	is_386,0		;can we output dwords?
	jne	send_pkt_7		;yes.
	shr	cx,1			;output 16 bits at a time.
	rep	outsw
	jmp	short send_pkt_6
send_pkt_7:
	.386
	shr	cx,2			;already rounded up.
	rep	outsd			;output 32 bits at a time.
	.286
send_pkt_6:

	clc
	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.
	assume	ds:nothing
	cmp	cx,EADDR_LEN		;ensure that their address is okay.
	je	set_address_4
	mov	dh,BAD_ADDRESS
	stc
	jmp	short set_address_done
set_address_4:

	loadport
	setwin	WNO_STATIONADDRESS
	setport	PORT_SA0_1
set_address_1:
	lodsb
	out	dx,al
	inc	dx
	loop	set_address_1
set_address_okay:
	mov	cx,EADDR_LEN		;return their address length.
	clc
set_address_done:
	push	cs
	pop	ds
	assume	ds:code
	loadport
	setwin	WNO_OPERATING
	ret


;skip past the following two bytes while destroying BX.
skip2	macro
	db	0bbh			;opcode of "mov bx,0000"
	endm

rcv_mode_1:
	mov	al,0			;receive nothing
	skip2
rcv_mode_2:
	mov	al,1			;receive individual address
	skip2
rcv_mode_3:
	mov	al,5			;receive individual address+broadcast
	skip2
rcv_mode_5:
	mov	al,3			;receive individual address+group addr(multicast)
	skip2
rcv_mode_6:
	mov	al,8			;receive all packets.
	mov	ah,CMD_SETRXFILTER shr 8	;set receive filter
	loadport
	setport	PORT_CmdStatus
	out	dx,ax
	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


	public	terminate
terminate:
	loadport
	setport	PORT_CmdStatus
	mov	ax,CMD_GLOBALRESET
	out	dx,ax
	ret


	public	reset_interface
reset_interface:
;reset the interface.
	assume	ds:code
	ret


;decide if we know this packet's type.
;enter with es:di -> packet type, dl = packet class.
;exit with nc if we know it, cy if not.
	extrn	recv_locate: near

;do the first upcall, get a pointer to the packet.
;enter with cx = packet length.
;exit with cx = packet length, es:di -> buffer for the packet.
	extrn	recv_found: near

;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.
	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

ether_buff	db	EADDR_LEN  dup(?)
		db	EADDR_LEN  dup(?)
ether_type	db	8 dup(?)
ETHER_BUFF_LEN	equ	$ - ether_buff
.erre	(ETHER_BUFF_LEN and 3) eq 0	;must be an even # of dwords.

early_bytes	dw	0		;the early byte gets the worm.

read_header:
;enter with dx -> PORT_RxFIFO
;exit with es:di -> packet type.

	mov	ax,ds
	mov	es,ax
	mov	di,offset ether_buff
	mov	cx,ETHER_BUFF_LEN/4
repinsd:
	shl	cx,1			;*** this gets changed into "rep insd"
	rep	insw			;***   "nop" on a 386 or 486.

	mov	di,offset ether_type

	mov	dl, BLUEBOOK		;assume bluebook Ethernet.
	mov	ax, es:[di]
	xchg	ah, al
	cmp 	ax, 1500
	ja	read_header_1
	inc	di			;set di to 802.2 header
	inc	di
	mov	dl, IEEE8023
read_header_1:
	ret


	public	recv
recv:
;called from the recv isr.  All registers have been saved, ds=cs,
;our interrupt has been acknowledged, and our interrupts have been
;masked at the interrupt controller.
	assume	ds:code
recv_another:
	loadport
	setport	PORT_CmdStatus
	in	ax,dx			;did we get a packet?
	or	ax,CMD_ACKNOWLEDGE
	out	dx,ax
	setport	PORT_RxStatus
	test	al,INT_RXCOMPLETE
	jne	recv_complete
	test	al,INT_RXEARLY		;are we getting it early?
	jne	recv_early
	jmp	recv_exit			;no.
recv_early:
	in	ax,dx			;get the amount we can read.
	and	ax,RXS_LENGTH
	cmp	ax,ETHER_BUFF_LEN	;do we have enough to read early?
	jb	recv_early_1		;no, give up.

	mov	early_bytes,ETHER_BUFF_LEN

	setport	PORT_RxFIFO
	call	read_header
	call	recv_locate		;see if this is a type we want.
	jnc	recv_early_1		;it is, just exit.
	jmp	recv_discard		;it isn't.

recv_early_1:
	jmp	recv_exit

;yes, this is dead code.  It's only in here to ensure that the setport macro
;has the right value.
	setport	PORT_RxStatus
recv_complete:
	in	ax,dx			;get the size.
	test	ax,RXS_ERROR		;any errors?
	je	recv_complete_2		;no, it's fine.

	and	ax,RXS_ERRTYPE		;get just the error type bits.
	cmp	ax,RXS_DRIBBLE		;dribble is just a warning.
	jne	recv_complete_1

recv_err:
	call	count_in_err
	jmp	recv_discard

recv_complete_1:
	in	ax,dx			;get the size again.
recv_complete_2:
;Put it on the receive queue
	and	ax,RXS_LENGTH
	mov	cx,ax

	cmp	early_bytes,0		;did we read the header in already?
	jne	recv_complete_3		;yes, we've already got it.

	cmp	cx,RUNT			;check legal packet size
	jb	recv_err
	cmp	cx,GIANT
	ja	recv_err

	push	cx
	setport	PORT_RxFIFO
	call	read_header
	pop	cx

	push	cx
	call	recv_find
	pop	cx
	jmp	short	recv_complete_4

recv_complete_3:
	add	cx,early_bytes		;add in the early bytes we got.

	cmp	cx,RUNT			;check legal packet size
	jb	recv_err
	cmp	cx,GIANT
	ja	recv_err

	push	cx
	call	recv_found		;do the first upcall.
	pop	cx

recv_complete_4:
	mov	ax,es			;is this pointer null?
	or	ax,di
	je	recv_discard		;yes - just free the frame.

	push	es			;remember where the buffer pointer is.
	push	di

	mov	bx,cx			;save the count.
	mov	cx,ETHER_BUFF_LEN/2	;move the data over.
	mov	si,offset ether_buff
	rep	movsw

	loadport			;restore the I/O port.
	setport	PORT_RxFIFO
	mov	cx,bx			;restore the count.
	sub	cx,ETHER_BUFF_LEN	;but leave off what we've already copied.

	cmp	is_386,0
	jne	io_input_386
io_input_286:
	push	cx
	shr	cx,1
	rep	insw
	pop	cx
	jnc	io_input_286_1		;go if the count was even.
	insb				;get that last byte.
	in	al,dx			;and get the pad byte.
	test	cx,2			;even number of words?
	jne	io_input_done		;no.
	in	ax,dx			;yes, get the pad word.
	jmp	short io_input_done
io_input_286_1:
	test	cx,2			;odd number of words?
	je	io_input_done		;no.
	in	ax,dx			;yes, get the pad word.
	jmp	short io_input_done

io_input_386:
	.386
	push	eax
	push	cx			;first, get all the full words.
	shr	cx,2
	rep	insd
	pop	cx
	test	cx,3			;even number of dwords?
	je	io_input_386_one_byte	;yes.
	in	eax,dx			;no, get the partial word.
	test	cx,2			;a full word to be stored?
	je	io_input_386_one_word
	stosw				;yes, store it,
	shr	eax,16			;and move over by a word.
io_input_386_one_word:

	test	cx,1			;a full byte to be stored?
	je	io_input_386_one_byte
	stosb				;yes, store it.
io_input_386_one_byte:
	pop	eax
	.286

io_input_done:

	mov	cx,bx			;restore the count.
	pop	si
	pop	ds
	assume	ds:nothing
	call	recv_copy		;tell them that we copied it.

	mov	ax,cs			;restore our ds.
	mov	ds,ax
	assume	ds:code

recv_discard:
	loadport
	setport	PORT_CmdStatus
	mov	ax,CMD_RXDISCARD
	out	dx,ax

recv_discard_1:
	in	ax,dx			;wait for the command to finish.
	test	ax,ST_BUSY
	jne	recv_discard_1

	mov	early_bytes,0

	jmp	recv_another
recv_exit:
	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.  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: 3c509 [options] <packet_int_no> "
		db      "[id_port]|[io_port]|[board_num]",CR,LF,'$'
	public	copyright_msg
copyright_msg	db	"Packet driver for a 3c509, version ",'0'+(majver / 10),'0'+(majver mod 10),".",'0'+version,CR,LF
		db	"Portions Copyright 1992, Crynwr Software",CR,LF,'$'
needs_186_msg	db	"Needs a 186 or higher-numbered processor",CR,LF,'$'
no_isa_msg	db	CR,LF
		db	"No 3c509 found.  Use a different id_port value.  Default is 0x110.",CR,LF,'$'
reading_msg	db	"Reading EEPROM.",'$'

multiple_msg	db	"Multiple 3C509s found, specify i/o port",CR,LF,'$'
wrong_port_msg	db	"No 3C509 board found at specified i/o port",CR,LF,'$'
eisa_in_isa_msg	db	"EISA configured board in ISA slot",CR,LF,'$'

int_no_name	db	"Interrupt number ",'$'
io_addr_name	db	"I/O port ",'$'
aui_xcvr_msg	db	"Using AUI transceiver",CR,LF,'$'
bnc_xcvr_msg	db	"Using BNC (10Base2) transceiver",CR,LF,'$'
tp_xcvr_msg	db	"Using Twisted Pair (10BaseT) transceiver",CR,LF,'$'
id_port_name	db	"ID port ",'$'

;called when you're ready to receive interrupts.
	extrn	set_recv_isr: near

;enter with si -> argument string, di -> dword to store.
;if there is no number, don't change the number.
	extrn	get_number: near

;enter with dx -> argument string, di -> dword to print.
	extrn	print_number: near

;-> the unique Ethernet address of the card.  Filled in by the etopen routine.
	extrn	rom_address: byte

;-> current address.  Normally the same as rom_address, unless changed
;by the set_address() call.
	extrn	my_address: byte

address_configuration	dw	?
resource_configuration	dw	?

is_10base2	db	0
is_10baseT	db	0

id_port		dw	110h,0
scratch		dw	0,0		;for multi-board support
board_number	db	0

;print the character in al.
	extrn	chrout: near

;print a crlf
	extrn	crlf: near

;parse_args is called with si -> first parameter (CR if none).
	public	parse_args
parse_args:
;exit with nc if all went well, cy otherwise.
	mov	di,offset scratch
	call	get_number		;also returns number in bx:cx
	jc	pa_ok
	cmp	cx,15			;board or slot?
	ja	pa_id
	mov	board_number,cl
	jmp	parse_args
pa_id:
	cmp	cx,200h			;id-port (100-1f0) or io-port (200-3e0) ?
	jae	pa_io
	cmp	cx,100h
	jb	pa_bad
	and	cx,1F0h
	mov	id_port,cx
	jmp	parse_args
pa_io:
	cmp	cx,3E0h
	ja	pa_bad			; bad value
	and	cx,3F0h
	mov	io_addr,cx
	jmp	parse_args		; get another arg
pa_ok:
	clc
	ret
pa_bad:
	stc
	ret


	public	etopen
etopen:
;initialize the driver.  Fill in rom_address with the assigned address of
;the board.  Exit with nc if all went well, or cy, dx -> $ terminated error msg.
;if all is okay,
	cmp	is_186,0		;this version requires a 186 or better.
	jne	etopen_1
	mov	dx,offset needs_186_msg
	stc
	ret
etopen_1:

	cmp	is_386,0		;can we do a real insd?
	je	etopen_2
;overlay the repinsd routine with a real "rep insd;nop"
	mov	word ptr repinsd+0,066h+0f3h*256
	mov	word ptr repinsd+2,06dh+090h*256
etopen_2:

	cmp	is_eisa,0
	jne	etopen_eisa
	jmp	etopen_isa
etopen_eisa:
	mov	cx,0fh
eisa_search:
	mov	dx,cx			;move it into the first nibble.
	shl	dx,12
	or	dx,0c80h
	in	ax,dx			;look for the manufacturer's ID
	cmp	ax,EISA_MANUFACTURER_ID
	jne	eisa_search_1
	inc	dx
	inc	dx
	in	ax,dx			;look for the product ID
	and	ax,PRODUCT_ID_MASK
	cmp	ax,ISA_PRODUCT_ID
	je	eisa_found
eisa_search_1:
	loop	eisa_search
	jmp	etopen_isa		;;; if it's not EISA-configured, try ISA.

eisa_found:
	and	dx,0f000h
	mov	io_addr,dx

	loadport
	setwin	WNO_SETUP

	setport	PORT_CfgAddress
	in	ax,dx
	mov	address_configuration,ax

	setport	PORT_CfgResource
	in	ax,dx
	mov	resource_configuration,ax

	setport	PORT_EECmd
	mov	si,offset read_ee_eisa
	call	read_eaddr

	jmp	have_configuration

;---------------------------------------------------------------------------
; etopen_ISA for multiple 3c509 boards, added 3/93, IJW.
;
etopen_isa:
	and	id_port,01F0h
	mov	dx,id_port
	xor	bl,bl

isa_1:
	cli
	call	write_id_pat
	or	bl,bl			; first board?
	jnz	isa_2
	mov	al,SET_TAG_REGISTER
	out	dx,al
isa_2:
	mov	al,EE_MANUFACTURER_CODE
	call	read_ee_isa
	cmp	ax,EISA_MANUFACTURER_ID	; board here ?
	jne	isa_3			; no

	mov	al,EE_OEM_NODE_ADDR_WORD0	; must read the address..
	call	read_ee_isa			; else it don't work ?!
	mov	al,EE_OEM_NODE_ADDR_WORD1	; address[1]
	call	read_ee_isa			; address[2]
	mov	al,EE_OEM_NODE_ADDR_WORD2
	call	read_ee_isa			; address[3]

	inc	bl			;count up a board.
	mov	al,bl
	or	al,SET_TAG_REGISTER
	out	dx,al
	xor	al,al
	out	dx,al			; reset
	sti
	jmp	isa_1			; see if any more boards

isa_3:
	xor	al,al
	out	dx,al			; reset
	sti
	or	bl,bl			; found any boards ?
	jnz	isa_4

not_found_isa:
	mov	dx,offset no_isa_msg
	stc
	ret
isa_multiple:
	mov	dx,offset multiple_msg
	stc
	ret

isa_4:
	xor	bl,bl
isa_5:
	cli
	call	write_id_pat
	inc	bl
	mov	al,bl
	add	al,TEST_TAG_REGISTER
	out	dx,al			; select the board
	mov	al,SET_TAG_REGISTER
	out	dx,al
	mov	al,EE_MANUFACTURER_CODE
	call	read_ee_isa
	cmp	ax,EISA_MANUFACTURER_ID	; board here ?
	jne	isa_8

	cmp	io_addr,0		; did user give an io_address ?
	je	isa_6			; no
	mov	al,EE_ADDR_CONFIGURATION
	call	read_ee_isa
	and	ax,1Fh			; make real io-address
	shl	ax,4
	add	ax,MIN_IO_BASE_ADDR
	cmp	ax,io_addr		; this board ?
	jne	isa_7			; no

isa_6:
	cmp	board_number,0
	jne	isa_multiple
	mov	board_number,bl

isa_7:
	mov	al,bl
	add	al,set_tag_register	; D0
	out	dx,al
	xor	al,al			; reset
	out	dx,al
	sti
	jmp	isa_5			; find the next board

isa_8:
	sti
	cmp	board_number,0		; found the board?
	jne	isa_9			; yes
	mov	dx,offset wrong_port_msg
	stc
	ret

isa_9:
	cli
	call	write_id_pat
	mov	al,TEST_TAG_REGISTER
	add	al,board_number
	out	dx,al
	mov	al,SET_TAG_REGISTER
	out	dx,al
	mov	al,EE_ADDR_CONFIGURATION
	call	read_ee_isa
	mov	address_configuration,ax
	cmp	io_addr,0		; know the base address?
	jne	isa_10			; yes
	and	ax,1Fh
	shl	ax,4
	add	ax,min_io_base_addr	; make realworld io addr
	mov	io_addr,ax

isa_10:
	mov	al,EE_RESOURCE_CONFIGURATION
	call	read_ee_isa
	mov	resource_configuration,ax

	mov	si,offset read_ee_isa
	call	read_eaddr

	mov	al,SET_TAG_REGISTER
	or	al,board_number
	out	dx,al
	sti

	cmp	io_addr,3F0h
	jne	isa_start
	mov	dx,offset eisa_in_isa_msg
	stc
	ret

isa_start:
	mov	al,ACTIVATE_VULCAN
	out	dx,al

have_configuration:
	mov	ax,address_configuration
	and	ax,BNC_XCVR or TP_XCVR or AUI_XCVR	;include all the bits.
	cmp	ax,BNC_XCVR		;does it match BNC?
	jne	not_10base2
	inc	is_10base2
not_10base2:
	cmp	ax,TP_XCVR		;does it match TP?
	jne	not_10baseT
	inc	is_10baseT
not_10baseT:

	mov	bx,resource_configuration
	mov	cl,12			;move it over where we need it.
	shr	bx,cl
	mov	int_no,bl

	loadport
	setwin	WNO_DIAGNOSTICS
	setport	PORT_MediaStatus
	in	ax,dx
	or	ax,MEDIA_LBEATENABLE or MEDIA_JABBERENABLE
	out	dx,ax

	setwin	WNO_SETUP		;select Window 0

	mov	ax,CMD_TXENABLE		;Enable the transmitter
	out	dx,ax

	mov	ax,CMD_RXENABLE		;Enable the receiver
	out	dx,ax

;Enable RX Complete interrupts
	mov	ax,CMD_SETINTMASK + INT_RXCOMPLETE + INT_RXEARLY
	out	dx,ax

	mov	ax,CMD_SETRZMASK + 0feh	;Enable all the status bits.
	out	dx,ax

	mov	ax,CMD_SETTXSTART + 0	;start transmitting after this many bytes.
	out	dx,ax

	mov	ax,CMD_SETRXEARLY + 0	;receive after this many bytes.
	out	dx,ax

	cmp	is_10base2,0		;coax?
	je	not_10base2_1		;no.
	mov	ax,CMD_STARTINTXCVR	;start internal transciever
	out	dx,ax
	call	delay_27_5ms
not_10base2_1:

	setport	PORT_CfgControl		;position to the CCR
	mov	al,ENABLE_ADAPTER	;Enable the adapter.
	out	dx,al

	call	rcv_mode_3

	mov	si,offset rom_address	;set our address.
	mov	cx,EADDR_LEN
	call	set_address_4
;sets the window to WNO_OPERATING.

	call	set_recv_isr

	mov	al, int_no		; Get board's interrupt vector
	add	al, 8
	cmp	al, 8+8			; Is it a slave 8259 interrupt?
	jb	set_int_num		; No.
	add	al, 70h - 8 - 8		; Map it to the real interrupt.
set_int_num:
	xor	ah, ah			; Clear high byte
	mov	int_num, ax		; Set parameter_list int num.

	clc
	ret
;if we got an error,
	stc
	ret


write_id_pat:
;write the 3c509 ID pattern to the ID port.
;enter with dx=id_port
	xor	al,al
	out	dx,al			;select the ID port.
	out	dx,al			;reset hardware pattern generator
	mov	cx,0ffh
	mov	al,0ffh
write_id_pat_1:
	out	dx,al			;keep writing matching values...
	shl	al,1
	jnc	write_id_pat_2
	xor	al,0cfh
write_id_pat_2:
	loop	write_id_pat_1
	ret

read_eaddr:
;enter with dx = eeprom register, si -> routine to read eeprom.
	push	cs
	pop	es
	mov	di,offset rom_address

	mov	al,EE_TCOM_NODE_ADDR_WORD0	;read the Ethernet address.
	call	si
	xchg	ah,al
	stosw
	mov	al,EE_TCOM_NODE_ADDR_WORD1
	call	si
	xchg	ah,al
	stosw
	mov	al,EE_TCOM_NODE_ADDR_WORD2
	call	si
	xchg	ah,al
	stosw
	ret


read_ee_eisa:
;enter with al = EEPROM address to read, dx = PORT_EECmd
;exit with ax = data.
	or	al,READ_EEPROM
	out	dx,al
read_ee_eisa_1:
	in	ax,dx
	test	ax,EE_BUSY
	jnz	read_ee_eisa_1
	add	dx,PORT_EEData - PORT_EECmd	;move to data register.
	in	ax,dx
	add	dx,PORT_EECmd - PORT_EEData	;move back to command register.
	ret


read_ee_isa:
;enter with al = EEPROM address to read, dx = address of ID port.
;exit with ax = data, cx = 0.
	push	bx
	or	al,READ_EEPROM
	out	dx,al
;wait 400 us here.
	call	delay_27_5ms
	mov	cx,16
read_ee_isa_1:
	in	al,dx
	shr	al,1			;put it into the carry.
	rcl	bx,1			;shift it into bx.
	loop	read_ee_isa_1
	mov	ax,bx
	pop	bx
	ret


delay_27_5ms:
;delay one timeout period, which is 27.5 ms.
	mov	ax,1
delay:
;delay AX timeout periods, each of which is 27.5 ms.
	call	set_timeout
delay_1:
	call	do_timeout
	jnz	delay_1
	ret


	public	print_parameters
print_parameters:
;echo our command-line parameters
	mov	di,offset int_no
	mov	dx,offset int_no_name
	call	print_number
	mov	di,offset io_addr
	mov	dx,offset io_addr_name
	call	print_number
	mov	dx,offset aui_xcvr_msg
	cmp	is_10base2,0		;coax?
	je	print_parameters_1
	mov	dx,offset bnc_xcvr_msg
print_parameters_1:
	cmp	is_10baseT,0		;tp?
	je	print_parameters_2
	mov	dx,offset tp_xcvr_msg
print_parameters_2:
	mov	ah,9
	int	21h
	mov	di,offset id_port
	mov	dx,offset id_port_name
	call	print_number
	ret

code	ends

	end
