version		equ	5
DEBUG		equ	0
;History:31,1

;  Copyright 1991, 1992, 1993 Russell Nelson

;   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.

	include	defs.asm

code	segment	word public
	assume	cs:code, ds:code

;
;	Waits for SCB command unit to become idle
;
;	MUST NOT TRASH CX OR THE ISSUE_COMMAND PROCEDURE WILL FAIL
;cmd_clear exits with cy if the command didn't become idle in one millisecond.

cmd_clear	macro
	local	exit

	lea	dx, [bp].@SCB_CMD		;BP -> I/O Base

	in	ax, dx				;Read SCB command
	or	ax, ax				;Wait for command accepted
	jz	exit

	call	cmd_wait
exit:
	endm

;
; Drives channel attention to the 586.  We don't care whats in AX, the write
; will cause the ASIC to drive CA to the 586.
;
	issue_ca	MACRO
	lea	dx, [bp].@CA_Ctrl
	out	dx, al
	endm


;
;
;  Enable and disable exp16s interrupts.
;
enable_board_ints	macro
	lea	dx, [bp].@Sel_IRQ
	mov	al, encoded_int_no
	or	al, 08h
	out	dx, al
	endm

disable_board_ints	macro
	lea	dx, [bp].@Sel_IRQ
	mov	al, encoded_int_no
	out	dx, al
	endm


RxBufferSize	equ	1518+14+18	;Max Rx packet+MAC header+
						; 18 (alignment)
TxBufferSize	equ	1518+14+20	;Max Tx packet+MAC header+
						; 20 (alignment)

	include	exp16.inc

_64K_not_32K	db	0	;<>0 if we have 64K of memory.
_16_not_8_bit_slot	db	0	;<>0 if we're in a 16-bit slot.

;possible values for connection_type:
CONN_BNC	equ	0
CONN_AUI	equ	1
CONN_TPE	equ	2
CONN_AUTO	equ	3
connection_type	db	?

;Memory Sizes
mem_size_address	equ	00H
_32K		equ	00000h
_64K		equ	00001h

CONNECTION_ADDRESS	equ	00H
CONNECTION_FIELD	equ	0001000000000000B

AUTO_CON_ADDRESS	EQU	01H
AUTO_CON_MASK	EQU	10000000B

TPE_address	equ	05H
TPE_type_field	equ	0000000000000001B

int_num_address	equ	00H
int_num_field	equ	1110000000000000B
int_field_shift	equ	13

EE_ETHERNET_ADD_LOW	equ	2
EE_ETHERNET_ADD_MID	equ	3
EE_ETHERNET_ADD_HIGH	equ	4
EE_INT		equ	0
EE_SHIFT	equ	13

;	Slot Width
slot_width_mask	equ	04h
_16_bit_slot	equ	0000h
_8_bit_slot	equ	0001h

_16_bit_override_bit	equ	08h


;
;	EXP16 base port structure
;
@EXP16BasePorts	struc
@Data_Reg	dw	?		;Data Transfer Register.
@Write_Ptr	dw	?		;Write Address Pointer.
@Read_Ptr	dw	?		;Read Address Pointer.
@CA_Ctrl	db	?		;Channel Attention Control.
@Sel_IRQ	db	?		;IRQ Select.
@SMB_Ptr	dw	?		;Shadow Memory Bank Pointer.
	db	?
@MEM_Ctrl	db	?
@MEM_Page_Ctrl	db	?
@Config		db	?
@EEPROM_Ctrl	db	?
@ID_Port	db	?
@EXP16BasePorts	ends

ECR1		equ	300eh

@ISR_Ports	struc
	db	0C008h dup(?)
@SCB_STAT	dw	?
@SCB_CMD	dw	?
@SCB_CBL	dw	?
@SCB_RFA	dw	?
;        @SCB_CRCERRS    dw      ?
;        @SCB_ALNERRS    dw      ?
;        @SCB_RSCERRS    dw      ?
;        @SCB_OVRNERRS   dw      ?
@ISR_Ports	ends


	@memory_page_struc	STRUC
	DB	4000H DUP (?)
	@mem_loc_0	DW	?
	@mem_loc_2	DW	?
	@mem_loc_4	DW	?
	@mem_loc_6	DW	?
	@mem_loc_8	DW	?
	@mem_loc_10	DW	?
	@mem_loc_12	DW	?
	@mem_loc_14	DW	?
	DB	4000H - 16 DUP (?)
	@mem_loc_16	DW	?
	@mem_loc_18	DW	?
	@mem_loc_20	DW	?
	@mem_loc_22	DW	?
	@mem_loc_24	DW	?
	@mem_loc_26	DW	?
	@mem_loc_28	DW	?
	@mem_loc_30	DW	?
	@memory_page_struc	ENDS

;
;
;	TData Segment
;
;	This segment template represents the 64-KB segment that the 82586
;	can address.  Shared memory can be either 32K or 64K for the EXP16.
;	All structures must be quad-word aligned.
;
;	Offset	Type of Block	Block Size	Cnt
;	------	-------------	----------	---
;	0000h	ISCP		8		1
;	0008h	SCB		16		1
;	0020h	CommandBlock	104		1
;	00E0h	SendBlock(s)	1568		variable
;	????h	ReceiveBlock(s)	1568		variable
;	FFF6h	SCP		10		1
;

TData_64	segment at 0

	ISCP	ISysConfigPtr  <>
	SCB	SystemControlBlock <>

	ORG	20H

	CB	CommandBlock	<>

	Send_Blocks	DB	SIZE SendBlock * Number_of_Tx_Buffers dup (?)
	Receive_Blocks	LABEL	WORD


	ORG	(0FFFFh - SIZE SCPS + 1) AND 0FFE0H
	SCP	LABEL	BYTE

	ORG	0FFFFh - SIZE SCPS + 1
	THeapTop	LABEL	BYTE			;Top of available memory

end_of_send_blocks	=	20h + SIZE CommandBlock + (Number_of_Tx_Buffers * SIZE SendBlock)
num_rx_buf_32k	=	(07FFFh - SIZE SCPS + 1 - end_of_send_blocks) / (SIZE ReceiveBlock)
num_rx_buf_64k	=	(0FFFFh - SIZE SCPS + 1 - end_of_send_blocks) / (SIZE ReceiveBlock)

	TData_64	ENDS


	public	int_no
int_no		db	3,0,0,0		;must be four bytes long for get_number.
io_addr		dw	-1,-1		; I/O address for card
encoded_int_no	db	?		;encoded for exp16.

	public	driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class	db	BLUEBOOK, IEEE8023, 0		;from the packet spec
driver_type	db	91		;from the packet spec
driver_name	db	'EtherExpress16',0	;name of the driver.
driver_function	db	26
parameter_list	label	byte
	db	1	;major rev of packet driver
	db	9	;minor rev of packet driver
	db	14	;length of parameter list
	db	EADDR_LEN	;length of MAC-layer address
	dw	GIANT	;MTU, including MAC headers
	dw	MAX_MULTICAST * EADDR_LEN	;buffer size of multicast addrs
	dw	0	;(# of back-to-back MTU rcvs) - 1
	dw	0	;(# of successive xmits) - 1
int_num		dw	0	;Interrupt # to hook for post-EOI
			;processing, 0 == none,
save_err	db	0		;error bits on promiscuous recieve
pro0		db	0
pro1		db	0

	BART_Board_ID	EQU	0BABAH
	BUD_board_ID	EQU	0BABBH
	mca_hp_board_ID	EQU	0BABCH

board_id	dw	?

	public	rcv_modes
rcv_modes	dw	7		;number of receive modes in our table.
	dw	0			;There is no mode zero
	dw	0
	dw	rcv_mode_2
	dw	rcv_mode_3
	dw	rcv_mode_4	;haven't set up perfect filtering yet.
	dw	0
	dw	rcv_mode_6

;
;	82586 Configuration Parameters
;
	config_params	LABEL	WORD
	num_config_params	LABEL	BYTE	;12 BYTEs
	DB	0CH

	fifo_limit	LABEL	BYTE
	DB	14

	srdy	LABEL	BYTE	;srdy = 1
	save_bad_frame	LABEL	BYTE	;do not save bad frame
	DB	40H

	address_length	LABEL	BYTE	;6 BYTEs
	auto_insert_address	LABEL	BYTE	;auto insert off (1)
	preamble_length	LABEL	BYTE	;2 BYTEs
	internal_loopback	LABEL	BYTE	;0 = OFF  1 = ON
	external_loopback	LABEL	BYTE	;0 = OFF  1 = ON
	DB	2EH	;26h in IPXWS

	linear_priority	LABEL	BYTE	;default is 0
	accel_contention_resolution	LABEL	BYTE	;default is 0
	exp_backoff_method	LABEL	BYTE	;0 = 802.3  1 = Alternate
	DB	00H

	interframe_spacing	LABEL	BYTE	;default is 60H (96 bits)
	DB	60H

	slot_time_low	LABEL	BYTE	;0
	DB	00H

	slot_time_high	LABEL	BYTE	;2
	retry_num	LABEL	BYTE	;15
	DB	0F2H

	promiscuous_mode	LABEL	BYTE	;0 = OFF  1 = ON
	broadcast_disable	LABEL	BYTE	;0 = OFF  1 = ON
	encode_decode	LABEL	BYTE	;0 = NRZ  1 = MANCHESTER
	transmit_on_no_carrier	LABEL	BYTE	;0 = STOP 1 = CONTINUE
	no_crc_insertion	LABEL	BYTE	;0 = OFF  1 = ON
	crc_type	LABEL	BYTE	;0 = 32 bit autodin II
						;1 = 16 bit CCITT
	bit_stuffing	LABEL	BYTE	;0 = 802.3  1 = HDLC
	padding	LABEL	BYTE	;0 = OFF  1 = ON
	DB	00H

	carrier_sense_filter	LABEL	BYTE	;0 = OFF  1 = 0N
	carrier_sense_source	LABEL	BYTE	;0 = external  1 = internal
	collision_detect_filter	LABEL	BYTE	;default = 0
	collision_detect_source	LABEL	BYTE	;0 = external  1 = internal
	DB	00H

	min_frame_length	LABEL	BYTE	;60 BYTEs
	DB	60
	DB	00H	;undefined


RxBufferCount	dw	?
TxBufferCount	dw	?

	Receive_Head	DW	?
	Receive_Tail	DW	?

HEADER_LEN	equ	EADDR_LEN + EADDR_LEN + MAX_P_LEN
	even
our_type	db	HEADER_LEN dup(?)

	extrn	is_186: byte		;=0 if 808[68], =1 if 80[123]86.
	extrn	sys_features: byte

	include	io16.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.
	cmp	ah,26
	jne	bad_command_intercept_1
	jmp	do_tdr
bad_command_intercept_1:
	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	pro1,1
	je	no_size_check1

	cmp	cx,GIANT		; Is this packet too large?
	jna	no_size_check1
	jmp	send_pkt_toobig

no_size_check1:

	push	bp
	mov	bp,io_addr

	disable_board_ints
	cmd_clear	; wait for command processor to clear

	cmp	pro1,1			; on pro drive do not ajust size
	je	oklen

	cmp	cx,RUNT		; minimum length for Ether
	jae	oklen
	mov	cx,RUNT		; make sure size at least RUNT
oklen:
;
; Set SMB pointer to the transmit buffer.
;
	lea	dx,[BP].@SMB_Ptr
	mov	ax,offset send_blocks
	out	dx,ax
;
; Write evenized, padded packet length (>= RUNT bytes) to 586 TBD byte
; count field.  OR on the End of Frame bit.
;
	lea	dx,[BP].@TBD_ByteCount
	mov	ax,cx
	or	ah,byte_BIT_EOF
	out	dx,ax
;
; Set the TxCB command field for transmit.  Also set the EL bit (End of List).
;
	lea	dx, [BP].@TxCB_Command
	mov	ax, BIT_EL+GA_Transmit
	out	dx, ax
;
; Set write pointer to the data buffer for current send block.  Also
; set DX to the data register.
;
	lea	dx, [BP].@Write_Ptr
	mov	ax, offset send_blocks
	add	ax, offset TB_Data
	out	dx, ax
	mov	dx, bp			;@Data_Reg

	inc	cx		;round size up to next even number.
	and	cx,not 1
	call	repoutsw

send_continue:
;
; Set the SCBs command block pointer to this send packet.
;
	lea	dx, [BP].@SCB_CBL
	mov	ax, offset send_blocks
	out	dx, ax

;
; Set the SCB command to start the command unit.
;
	lea	dx, [BP].@SCB_CMD
	mov	ax, CUC_START
	out	dx, ax
	issue_ca

	cmd_clear	; wait for frame transmit to start
	lea	dx,[BP].@SMB_Ptr
	mov	ax,offset send_blocks
	out	dx,ax

	lea	dx,[BP].@TxCB_status
transmiting:
	in	ax,dx

	test	ax,BIT_C		; check status of transmit
	jz	transmiting

	test	ax,BIT_OK		; how was the x-mit
	jnz	tx_ok

	enable_board_ints
	pop	bp
	mov	dh,CANT_SEND
	jmp	tx_bad

tx_ok:
	enable_board_ints

	pop	bp

	clc
	ret

send_pkt_toobig:
	mov	dh,NO_SPACE
tx_bad:
	stc
	ret


;	ReadTickCounter
;
;	Read the 16 bit timer tick count register (system board timer 0).
;	The count register decrements by 2 (even numbers) every 838ns.
;
;	Assumes:	Interrupts disabled
;
;	Returns:	AX with the current count
;			Interrupts disabled

ReadTickCounter:

	push	dx

	mov	dx, 43h				;Command 8254 timer to latch
	xor	al, al				; T0's current count
	out	dx, al

	mov	dx, 40h				;read the latched count
	in	al, dx				; LSB first
	mov	ah, al
	in	al, dx				; MSB next
	xchg	al, ah				;put count in proper order

	pop	dx
	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:code
	push	bp
	push	si
;
; Set up individual address command block.
;
	mov	bp,ds:io_addr
	cmd_clear
	mov	bx, BIT_EL+GA_IA_Setup
	call	setup_command_block
	pop	si

;
; Copy individual address command parameters to individual address
; command block.
;
	mov	cx, EADDR_LEN/2
move_node_address:
	lodsw
	out	dx, ax
	loop	move_node_address

;
; Signal 586 to execute individual address command.
;
	mov	ax, CUC_Start
	mov	bx, BIT_CNA
	call	issue_command

	lea	dx, [bp].@SMB_Ptr		;Move IO frame to the command
	mov	ax, offset cb			; command block.
	out	dx, ax

	lea	dx,[BP].@CB_Status
working1:
	in	ax,dx

	test	ax,BIT_C		; check status of set_ia
	jz	working1

	test	ax,BIT_OK
	jnz	set_ok
	stc
	jmp	set_address_done

set_ok:
	mov	cx,5000h		; on 486's if we do not delay this command does not work
set1:
	mov	bx,cx
	mov	ax,[bx]
	loop	set1			; just for delay

	mov	cx,EADDR_LEN		;return their address length.
	clc
set_address_done:
	pop	bp
	ret


rcv_mode_2:
	and	promiscuous_mode,not 3
	or	promiscuous_mode,2	;disable broadcasts.
	mov	min_frame_length,60
	and	save_bad_frame,not 80h
	mov	pro0,0
;;;	mov     pro1,0			;not present in NSA version
	jmp	short configure_command
rcv_mode_4:
rcv_mode_3:
	and	promiscuous_mode,not 3	;clear promiscuous mode.
	mov	min_frame_length,60
	and	save_bad_frame,not 80h
	mov	pro0,0
;;;	mov     pro1,0			;not present in NSA version
	jmp	short configure_command
rcv_mode_6:
	and	promiscuous_mode,not 3
	or	promiscuous_mode,1	;set promiscuous mode.
	mov	min_frame_length,60	;allow runt. but let it come in as a bad frame
	or	save_bad_frame,80h	; yes we want bad frames here
	mov	pro0,1
	mov	pro1,1
;
configure_command:
	mov	bp,io_addr
;
; Set up configure command block.
;
	mov	bx, bit_el + GA_Configure	;Set command block for a
	call	setup_command_block		; configure command. BIT_EL
						; means last command.
;
; Copy configure command parameters to configure command block.
;
	mov	si,offset config_params		;Set DS:SI to parameters and
	mov	cx, 6				; copy them to the command

move_config_params:

	lodsw
	out	dx, ax
	loop	move_config_params
;
; Signal 586 to execute configure command.
;
	mov	ax, CUC_Start
	mov	bx, BIT_CNA
	call	issue_command
	ret


;*****************************************************************
; TDR cable tester
; enter with es:di pointing to time int
;*****************************************************************

	public	do_tdr
do_tdr:
	push	bp
	push	es
	push	di
	mov	bp,io_addr
;
; Set up configure command block.
;
	mov	bx, bit_el + GA_TDR	;Set command block for a
	call	setup_command_block		; TDR command. BIT_EL
						; means last command.
; Signal 586 to execute configure command.
;
	mov	ax, CUC_Start
	mov	bx, BIT_CNA
	call	issue_command

	lea	dx, [bp].@SMB_Ptr		;Move IO frame to the command
	mov	ax, offset cb			; command block.
	out	dx, ax

	lea	dx,[BP].@CB_Status
working:
	in	ax,dx

	test	ax,BIT_C		; check status of transmit
	jz	working

	lea	dx,[bp].@CB_Parm0
	in	ax,dx

	pop	di
	pop	es
	pop	bp

	test	ax,LNK_OK
	jz	tdr_bad_cable
	clc
	ret

tdr_bad_cable:

	push	ax
	and	ax,TDR_TIME
	stosw	; store time
	pop	ax

	mov	cl,12
	shr	ax,cl
	and	ax,7
	mov	dh,al

	stc
	ret


;
;	BX = 586 command word
;
setup_command_block:

;
; Setup command block's command, status, and link fields.
;
	lea	dx, [bp].@SMB_Ptr		;Move IO frame to the command
	mov	ax, offset cb			; command block.
	out	dx, ax

	lea	dx, [bp].@CB_link		;Set command block's link to
	out	dx, ax				; itself.

	lea	dx, [bp].@SCB_CBL		;Point SCB command list
	out	dx, ax				; pointer to command block.

	lea	dx, [bp].@CB_command		;Set command block's command
	mov	ax, bx				; word to value passed in
	out	dx, ax				; BX.

;
; Return with the write pointer set to the command block parameter
; offset.
;
	lea	dx, [bp].@Write_Ptr		;Set write pointer to command
	lea	ax, CB.CB_Param0		; parameter field.
	out	dx, ax

	mov	dx, bp			;@Data_Reg
	ret


;
; Wait for 586 command register to be clear (  1 millisecond max)
; Issue command
; Wait for command to complete              (500 milliseconds max)
; Acknowledge command complete
;
;
issue_command:

;
; Make sure 586 command in SCB is clear.
;
	mov	cx, ax
	cmd_clear
	jc	issue_command_error_exit
	mov	ax, cx
;
; Put command code in AX into SCB command register and signal the
; 586 to look at it.  The cmd_clear macro leaves DX pointing to
; the SCB command register.
;
	out	dx, ax
	issue_ca

wait_for_commad_status_outer_loop:
;
; Wait here for 586 to complete the command.  586 should never take
; more than 500 Milliseconds to complete a command.
;
	call	readtickcounter
	mov	cx, ax
	lea	dx, [bp].@SCB_STAT

wait_for_commad_status:

	in	ax, dx
	cmp	ax, bx
	je	acknowledge_command_complete

	call	readtickcounter
	neg	ax
	add	ax, cx
	cmp	ax, ten_mils
	jb	wait_for_commad_status

_586_command_error:
;
; Signal error by pointing AX to error message, and setting
; condition codes.
;
	mov	dx,offset _586_not_responding_msg
	stc
issue_command_error_exit:
	ret

acknowledge_command_complete:
;
; Tell the 586 that we know its done by copying the status back to
; the command register's acknowledge fields.  BX has status.
;
	lea	dx, [bp].@SCB_CMD
	out	dx, ax
	issue_ca
;
; Signal no error.
;
	xor	ax, 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 hf we didn't.
	mov	dh,NO_MULTICAST
	stc
	ret


	public	terminate
terminate:
	push	bp
	mov	bp,io_addr

	cmd_clear	; wait until last command is done processing

	disable_board_ints	; shut off board ints

	lea	dx,[bp].@SCB_CMD
	mov	ax, RUC_SUSPEND		; suspend recieve unit
	out	dx, ax
	issue_ca

	pop	bp			; now ok to un-hook interrupts and release
	ret	; driver

	public	reset_interface
reset_interface:
;reset the interface.
	assume	ds:code
	ret


;called when we want to determine what to do with a received packet.
;enter with cx = packet length, es:di -> packet type, dl = packet class.
	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
;	extrn	resource_err_in:word

	public	recv
recv:
;called from the recv isr.  All registers have been saved, and ds=cs.
;Upon exit, the interrupt will be acknowledged.
	assume	ds:code
;
; Set BP to the base IO address of exp16.  Also disable exp16s ability
; to generate interrupts.
;
	mov	bp,io_addr
	disable_board_ints
;
; Make sure that command unit is clear before reading the status.
;
	cmd_clear
;
; Get the SCB status to see why exp16 interrupted us.  Also need to
; copy the status back to the command word to acknowledge the 586
; interrupt.  Issue channel attention to get the 586 to realize the
; interrupt aknowledge.
;
;	lea	dx, [bp].@SCB_RSCERRS
;	in	ax,dx
;	mov	word ptr resource_err_in,ax
;	lea	dx, [bp].@SCB_OVRNERRS
;	in	ax,dx
;	add	word ptr resource_err_in,ax

	lea	dx, [bp].@SCB_STAT
	in	ax, dx
	and	ax, ACK_INT_MASK

	lea	dx, [bp].@SCB_CMD
	out	dx, ax
	issue_ca	; AX is not changed.
;
; Check for frame receive status.  Jump if no frames received,
; otherwise processes the receive.
;
	test	AX, BIT_FR OR BIT_RNR
	jz	ExitDriverISR
	call	ProcessRx
ExitDriverISR:
;
; Here if there was a bad receive status.  Make sure the receive unit
; of the 586 is running.  Read in the SCB status and jump if we need
; to restart the receive unit.  We choose to have the jump to restart
; the RU since most of the time we will not need to do this.  This
; saves cycle times when exiting without a restart.
;
	lea	dx, [bp].@SCB_STAT
	in	ax, dx
	test	ax, READY
	jz	restart_RU

drvisr_continue:
	enable_board_ints
	ret


restart_RU:
;
; Need to restart the receive unit of the 586.  Set the SCB pointer to
; the receive frame area.
;
	lea	dx, [bp].@SCB_RFA
	mov	ax, Receive_Head
	out	dx, ax
;
; Make sure 586 is in a state to accept a command.
;
	cmd_clear
;
; Put the receive unit start command into the SCB command word and
; issue the channel attention.
;
; NOTE: cmd_clear macro leaves DX pointing to the SCB command word.
;
;	lea	dx, [bp].@SCB_CMD
	mov	ax, RUC_START
	out	dx, ax
	issue_ca
	jmp	drvisr_continue


ProcessRx:

;
; Prime the pump that services receives by setting AX to the receive
; head.  There could be over 30 packets received.
;
	mov	ax,Receive_Head

CheckNextReceiveStatus:

;
; Save receive head pointer in BX.  BX should not be altered during
; this loop.
;
	mov	bx, ax
;
; Set SMB pointer to the top of the receive frame.
;
	lea	dx, [bp].@SMB_Ptr
	out	dx, ax
;
; Get the status for this receive frame.  Check to make sure that
; it completed with no errors.  If not, then jump to check the
; 586 receive unit.
;
	lea	dx, [bp].@FD_Status
	in	ax, dx

	mov	save_err,1		; be sure we done accdently report an error

	test	ax, BIT_C		; command compleat
	jz	PRx_no
	test	ax,BIT_OK		; good frame
	jnz	ProcessRx_1

	call	count_in_err
	mov	save_err,0
	cmp	pro0,1
	je	PRx_bad			; if not in pro-mode lose bad frames and inc error
;	call	count_in_err
	jmp	recv_isr_9
PRx_no:
	ret

PRx_bad:
	mov	dh,0			;build save_err.
	test	ax,BIT_S7
	jz	PR_no_frag
	or	dh,2
PR_no_frag:
	test	ax,BIT_S11
	jz	PR_no_crc
	or	dh,4
PR_no_crc:
	test	ax,BIT_S10
	jz	PR_no_align
	or	dh,8
PR_no_align:
	test	ax,BIT_S8
	jz	PR_no_dma
	or	dh,16
PR_no_dma:
	test	ax,BIT_S9
	jz	PR_no_ru
	or	dh,32
PR_no_ru:
	mov	save_err,dh

ProcessRx_1:
;
; Set the read pointer to access the LengthTypeField.
;
; NOTE: BX was set to receive head at the beginning of this
;       procedure.
;
	lea	dx, [bp].@Read_Ptr
	lea	ax, [BX].IPX_DestAddr_HIGH
	out	dx, ax
	mov	dx, bp			;@Data_Reg

	mov	ax,cs
	mov	es,ax
	mov	di,offset our_type
	mov	cx,HEADER_LEN
	call	repinsw

;
; Get the packet size from the 586 data structure.
;
	lea	dx, [bp].@RBD_ByteCount
	in	ax, dx
	and	ah, 3fh
	mov	cx, ax

	mov	di,offset our_type+EADDR_LEN+EADDR_LEN

	mov	dl, BLUEBOOK		;assume bluebook Ethernet.
	mov	ax, es:[di]
	xchg	ah, al
	cmp	ax, 1518
	ja	BlueBookPacket
	inc	di			;set di to 802.2 header
	inc	di
	mov	dl, IEEE8023
BlueBookPacket:
	push	bx
	push	cx			; for some reison recv_find trashes cx
	mov	dh,save_err
	call	recv_find
	mov	bp,cs:io_addr		; just in case
	pop	cx
	pop	bx

	mov	ax,es			;is this pointer null?
	or	ax,di
	je	recv_isr_9		;yes - just free the frame.
	push	es			;remember where the buffer pointer is.
	push	di
	push	cx
  if 0
;
; Copy the packet's header into their buffer.
;
	push	cx
	mov	si,offset our_type
	mov	cx,HEADER_LEN/2
	rep	movsw
	pop	cx
	sub	cx,HEADER_LEN
  endif
;
; Set the read pointer to access the packet.
;
; NOTE: BX was set to receive head at the beginning of this
;       procedure.
;
	lea	dx, [bp].@Read_Ptr
  if 0
	lea	ax, [BX].IPX_DestAddr_HIGH + HEADER_LEN
  else
	lea	ax, [BX].IPX_DestAddr_HIGH
  endif
	out	dx, ax
	mov	dx, bp			;@Data_Reg
;
; Now read the packet into es:di
;
	call	repinsw

	pop	cx
	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_isr_9:
;
; Restore SMB pointer to the top of the receive frame.
;
	mov	bp,io_addr		; just in case
	mov	ax,bx
	lea	dx, [bp].@SMB_Ptr
	out	dx, ax

;
; Clear the current receive frames status.
;
	lea	dx, [bp].@FD_Status
	xor	ax, ax
	out	dx, ax
;
; Set the end of list bit for the current receive frame.
;
	lea	dx, [bp].@FD_command
	mov	ax, BIT_EL
	out	dx, ax
;
; Read in the pointer to the next receive frame.
;
	lea	dx, [bp].@FD_Link
	in	ax, dx
;
; Update our local pointer to the receive resources:
;
; Receive_tail <-- Receive head
; Receive_head <-- next receive frame.
;
	mov	BX, Receive_Head
	mov	Receive_Tail, BX
	mov	Receive_Head, AX
	mov	cx, ax
;
; Clear previous receive frame descriptors end of list bit to free up
; a receive resource.  We do this by moving the write pointer to the
; the command word in the previous receive frame and then writing a
; zero to it.
;
	lea	dx, [bp].@Write_Ptr
	lea	ax, [bx].FD_command
	out	dx, ax

	mov	dx, bp			;@Data_Reg
	xor	ax, ax
	out	dx, ax
;
; Check for another receive frame.
;
	mov	ax, cx
	jmp	CheckNextReceiveStatus



; This procedure is called from the cmd_clear macro after the macro determines
; that we must wait for a command to clear.
;
;	 MUST NOT TRASH CX OR THE ISSUE_COMMAND PROCEDURE WILL FAIL.
;
cmd_wait:
;enter with dx -> SCB_CMD
;exit with nc if it cleared in one millisecond, cy if not.

	call	readtickcounter			;Reference clock.  die after
	mov	bx, ax				; 1 ms.

wait_cmd_clear:

	in	ax, dx				;Read SCB command
	or	ax, ax				;Wait for command accepted
	jz	exit

	call	readtickcounter
	neg	ax
	add	ax, bx

	cmp	ax, one_mil
	jb	wait_cmd_clear

	stc
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 after initialization. Buffers
;used by the program, if any, are allocated from the memory between
;end_resident and end_free_mem.
	public	end_resident,end_free_mem
end_resident	label	byte
end_free_mem	label	byte


	public	usage_msg
usage_msg	db	"usage: exp16 [options] <packet_int_no> <io_addr>",CR,LF,'$'

	public	copyright_msg
copyright_msg	db	"Packet driver for the Intel EtherExpress 16, version ",'0'+(majver / 10),'0'+(majver mod 10),".",'0'+version,CR,LF
	db	"Copyright 1991 Intel Corp",CR,LF
	db	"Portions Copyright 1993 Crynwr Software",CR,LF,'$'

int_no_name	db	"Interrupt number ",'$'
io_addr_name	db	"I/O port ",'$'

	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 assigned Ethernet address of the card.
	extrn	rom_address: byte

chan_sel	db	0	;Channel select for SYS_MCA machines

	public	etopen
etopen:
;initialize the driver.
	assume	ds:code
;
; Get board information (Node address, etc.).  Returns with BP
; set to the board IO base address (if not errors).
;
	call	get_system_info
	jc	driver_init_error_exit

	disable_board_ints
;
; If they're in an 8-bit slot, make sure that they aren't using the slave PIC.
;
	cmp	_16_not_8_bit_slot,0	;are they using a 16-bit slot?
	jne	check_config_exit	;yes -- cool.

	mov	dx,offset irq_config_error
	cmp	int_no, 9		;no - don't let them use the upper IRQs.
	jb	check_config_exit
driver_init_error_exit:
	stc
	ret
check_config_exit:

;
; Initialize the 586 and the 586 data structures.
;
	call	init_586
	jc	driver_init_error_exit

; test iochrdy
	test	sys_features,SYS_MCA
	jnz	skip_iochrdy_test
	call	iochrdy_test
skip_iochrdy_test:

	cmp	connection_type,CONN_AUTO
	jne	no_auto_con
	call	auto_connector
	jnc	did_auto_con
	mov	dx,offset no_wire_msg
	mov	ah,9
	int	21h
	jmp	short	did_auto_con
no_auto_con:
	call	write_connector_setting_to_hardware
did_auto_con:

;
; Set up Interrupt line, start the receive unit, and Enable exp16s interrupt.
;
	call	set_recv_isr
	call	ru_start
	enable_board_ints	;;; only one left in broken version.

	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

write_connector_setting_to_hardware:
	assume	ds:code
	test	sys_features,SYS_MCA
	je	isa_connector_bart

	mov	bl,80h
	cmp	connection_type,CONN_TPE	;did they ask for TPE?
	je	mca_tpe_connector
	mov	bl,0
mca_tpe_connector:

	call	enable_slot
	mov	dx,102h			;stuff the AUI/Other bit in.
	in	al,dx
	and	al,not 04h
	cmp	connection_type,CONN_AUI
	je	mca_tpe_connector_1
	or	al,04h
mca_tpe_connector_1:
	out	dx,al

	mov	dx,103h			;stuff the BNC/TPE bit in.
	in	al,dx
	and	al,not 80h
	cmp	connection_type,CONN_BNC
	je	mca_tpe_connector_2
	or	al,80h			;<>0 means TPE
mca_tpe_connector_2:
	out	dx,al
	call	disable_slot

	jmp	no_connector_bart
isa_connector_bart:
	cmp	board_id,BART_Board_ID	;a real BART?
	je	no_connector_bart	;yes, it doesn't *have* an ECR1.
	mov	bp,io_addr
	lea	dx,ECR1[bp]
	in	al,dx
	and	al,not 82h		;turn off Other/not_AUI (read as
					;   MCA/ISA), and TPE/not_BNC
	cmp	connection_type,CONN_AUI
	je	set_connector_bart
	or	al,80h			;set Other/not_AUI
	cmp	connection_type,CONN_BNC
	je	set_connector_bart
	or	al,02h			;set TPE/not_BNC
set_connector_bart:
	out	dx,al
no_connector_bart:

	mov	cx,10			;10*10 milliseconds = 100 millis
	cmp	connection_type,CONN_AUI
	jne	set_connector_delay
	mov	cx,50			;50*10 milliseconds = 500 millis
set_connector_delay:
; Our delay procedure can not accept a value over 27 milliseconds,
; so we use a loop to delay the 100 milliseconds.
set_connector_delay_1:
	push	cx
	mov	di, ten_mils
	call	eeprom_delay
	pop	cx
	loop	set_connector_delay_1
	ret


; int number to IRQ translate table.
IRQ_xlat_table	LABEL	BYTE
	db	 12, 2, 3, 4, 5, 10, 11, 15

io_addresses	label word
	dw	300h,310h,320h,330h,340h,350h,360h,370h
	dw	200h,210h,220h,230h,240h,250h,260h,270h
	dw	0

enable_slot:
	mov	al,chan_sel
	out	96h, al			; select channel
	mov	al, 0ffh
	out	94h, al			; protect system board
	ret

disable_slot:
	xor	al,al			;disable pos
	out	96h,al
	ret

	public	parse_args
parse_args:
;exit with nc if all went well, cy otherwise.
	test	sys_features,SYS_MCA
	jne	parse_args_mc
	jmp	parse_args_1

parse_args_mc:
;The following code to read the POS registers is courtesy of Racal-Interlan.

; channel selector resides at io 96h
; POS register base is at io 100h
; EXP16/MC ID is 0628bh

; search thro' the slots for a EXP16/MC card
	mov	cx, 8			; for all channels(slots)

; channel select value for slots 0,1,2.. is 8,9,A etc
; start with slot 0, and then 7,6,5,4,3,2,1
get_05:
	mov	ax, cx			; channel number
	or	ax, 08h			; reg. select value
	mov	chan_sel,al		; save each one, the LAST will right

; read adapter id
	call	enable_slot
	mov	dx, 101h
	in	al, dx			; adapter id - ms byte
	mov	ah, al
	dec	dx
	in	al, dx			; adapter id - ls byte

; Check if EXP16/MC
	cmp	ax, 0628bh
	je	get_10
	loop	get_05

	call	disable_slot

	mov	dx,offset Board_Not_Installed
	stc
	ret

get_10:
; found our Adapter
	call	write_pos_to_eeprom

	clc
	ret

parse_args_1:
	mov	di,offset io_addr
	call	get_number
	clc
	ret

	include	exp16mca.asm

;-----------------------------------------------------------------------------
; It is safe to assume that the command unit is up and running ready
; for the first send.  And that there are at least two send buffers 
; available.
;
; This procedure works by sending a 64 byte packet to itself.  We do not turn
; on loopback, so we do not expect to see a receive.  Instead, we
; expect to see that the send completed OK.  If the send does not complete OK, 
; then this routine switches to other connectors and the test is repeated. 
;-----------------------------------------------------------------------------
auto_connector:

; Let's start doing the test send with AUI option first.  If we tried BNC
; first, and they were using AUI, it would seem like the BNC worked.  So
; we switch to AUI, which turns off the BNC power supply.
	MOV	connection_type, CONN_AUI
	CALL	write_connector_setting_to_hardware

; Test connection to see if it works.  Jump if it does.
	call	test_send
	clc
	je	auto_connector_exit

; Toggle AUI/Other setting and try to send a packet.  Jump if send 
; works.  Now try sending on BNC.
	MOV	connection_type, CONN_BNC
	CALL	write_connector_setting_to_hardware
	call	test_send
	clc
	je	auto_connector_exit

; Here if we just tried a send the BNC.  If this is 
; a three connector card, then we toggle the BNC/TPE setting.
; Jump if connection is found.
	MOV	connection_type, CONN_TPE
	CALL	write_connector_setting_to_hardware
	call	test_send
	clc
	je	auto_connector_exit
	stc
auto_connector_exit:
	ret


test_packet	label	byte
	db	EADDR_LEN dup(?)
	db	EADDR_LEN dup(?)
	db	00h,2eh			;A 46 in network order
	db	0,0			;DSAP=0 & SSAP=0 fields
	db	0f3h,0			;Control (Test Req + P bit set)

; This procedure sends a packet on the wire and wait for the send to
; complete.  It the checks to see if the send completed OK and returns
; the results.
test_send:

	mov	si,offset rom_address	;set the destination address.
	movseg	es,cs
	mov	di,offset test_packet
	repmov	EADDR_LEN
	mov	si,offset rom_address	;set the source address.
	repmov	EADDR_LEN

	mov	cx,60
	mov	si,offset test_packet
	call	send_pkt

	call	ReadTickCounter
	mov	cx, ax

	mov	bp,io_addr
	lea	dx, [bp].@TxCB_Status
send_test_wait:
	in	ax,dx
	test	ax,8000h		;did it finish?
	jne	send_test_done

; See if we have been here for a millisecond.  Jump if not.
	call	ReadTickCounter
	neg	ax
	add	ax, cx
	cmp	ax, one_mil
	jb	send_test_wait

	in	ax,dx			;get the transmit status back again.

send_test_done:

	mov	cx, ax			; Save status in CX.

	xor	ax, ax			; Clear the transmit command block status.
	out	dx, ax

; Send is complete.  Get the status and compare to OK send and return.
; Save flags for return.

	and	cx, 0f000h
	cmp	cx, 0a000h
	ret

no_wire_msg		db	"***Unable to detect a cable on any connector***",CR,LF,'$'
bnc_connector_msg	db	"Using the BNC connector",CR,LF,'$'
aui_connector_msg	db	"Using the AUI connector",CR,LF,'$'
tpe_connector_msg	db	"Using the twisted-pair connector",CR,LF,'$'

_8_bit_slot_msg	db	"The board is in an 8-bit slot",CR,LF,'$'
_16_bit_slot_msg	db	"The board is in a 16-bit slot",CR,LF,'$'

badset_msg	db	"Set Address Command Failed",CR,LF,'$'


	public	print_parameters
print_parameters:
	mov	di,offset int_no
	mov	dx,offset int_no_name
	call	print_number
	mov	io_addr.segm,0
	mov	di,offset io_addr
	mov	dx,offset io_addr_name
	call	print_number

	mov	dx,offset bnc_connector_msg	;print the connection type.
	cmp	connection_type,CONN_BNC
	je	print_parameters_1
	mov	dx,offset aui_connector_msg
	cmp	connection_type,CONN_AUI
	je	print_parameters_1
	mov	dx,offset tpe_connector_msg
print_parameters_1:
	mov	ah,9
	int	21h

	mov	dx,offset _16_bit_slot_msg	;print the slot size.
	cmp	_16_not_8_bit_slot,0
	jne	print_parameters_2
	mov	dx,offset _8_bit_slot_msg
print_parameters_2:
	mov	ah,9
	int	21h

	ret


init_586:
;enter with bp=io_addr, 586 reset.

;
; Set the number of transmit and receive buffers according to the
; amount of RAM on the adapter.
;
	MOV	AX, Number_of_Tx_Buffers
	MOV	BX, num_rx_buf_32k

	cmp	_64K_not_32K,0
	je	save_number_of_buffers

	MOV	BX, num_rx_buf_64k

save_number_of_buffers:

	MOV	TxBufferCount, AX
	MOV	RxBufferCount, BX
;
; initialize SCP.  Move IO frame to top of SCP.
;
	LEA	DX, [bp].@SMB_Ptr
	MOV	AX, OFFSET scp
	OUT	DX, AX
;
; Write initial SCP values:
;	bus width	0 = 16 bit   1 = 8 bit.
;	ISCP address	always found at address 000000.
;
	LEA	DX, [BP].@SCP_SystemBus
	MOV	AL, bus_width
	OUT	DX, AX

	LEA	DX, [BP].@SCP_ISCP_Ptr_low
	XOR	AX, AX
	OUT	DX, AX
	LEA	DX, [BP].@SCP_ISCP_Ptr_high
	OUT	DX, AX

;
; Initialize ISCP.  Move IO frame to top of ISCP.  NOTE: AX falls
; through as zero.
;
	LEA	DX, [bp].@SMB_Ptr
	OUT	DX, AX
;
; Write initial ISCP values:
;	ISCP BUSY	1 = Indicates 586 is in initialization
;			process.  586 sets this byte to 0 when
;			initialization is complete.
;
;	SCB address     Always 000008
;
	LEA	DX, [BP].@iscp_busy
	MOV	AX, initialize_586
	OUT	DX, AX

	LEA	DX, [BP].@ISCP_SCB_Ptr_low
	XOR	AX, AX
	OUT	DX, AX
	LEA	DX, [BP].@ISCP_SCB_Ptr_high
	OUT	DX, AX
	LEA	DX, [BP].@iscp_scb_offset
	MOV	AX, OFFSET scb
	OUT	DX, AX

;
; Initialize SCB.  Move Write Ptr to top of SCB.  NOTE: AX falls
; through as offset to SCB.
;
	XOR	AX, AX
	LEA	DX, [BP].@SCB_Status
	OUT	DX, AX
	LEA	DX, [BP].@SCB_Command
	OUT	DX, AX
	LEA	DX, [BP].@SCB_CommandList
	OUT	DX, AX
	LEA	DX, [BP].@SCB_RecBlockList
	OUT	DX, AX
	LEA	DX, [BP].@SCB_CRC_Errors
	OUT	DX, AX
	LEA	DX, [BP].@SCB_ALN_Errors
	OUT	DX, AX
	LEA	DX, [BP].@SCB_RSC_Errors
	OUT	DX, AX
	LEA	DX, [BP].@SCB_OVR_Errors
	OUT	DX, AX

;
; Initialize Receive Block(s).  First set head pointer and link from
; SCB to first frame descriptor.  NOTE: IO frame points to ISCB.
; The SCB link field can be addresses becasue the SCB is physically
; contiguous to the ISCB.
;
	mov	ax,offset Receive_Blocks
	lea	dx, [bp].@SCB_RecBlockList	;IO address of SCB RF link
	out	dx, ax

;
; Initialize the frame descriptor structures.  Move the Write ptr
; the top of the current frame descriptor.  AX falls through with the
; offset to the first frame descriptor.  DI is used to keep track of
; the address of the current frame descriptor.  The frame descriptor,
; receive buffer descriptor, and receive buffer are all contiguous in
; memory, and make up the frame descriptor structure.
;
	MOV	DI, AX				;DI is current frame descriptor
	MOV	RECEIVE_HEAD, DI
	MOV	CX, RxBufferCount		;CX is number of frame
						; descriptors to initialize.
init_receive_frames:

	LEA	DX, [bp].@SMB_Ptr
	MOV	AX, DI				; descriptor.
	OUT	DX, AX

;
; Init Frame Descriptor (FD).
;
	XOR	AX, AX				;Init frame descriptor status
	LEA	DX, [BP+4000H]
	OUT	DX, AX				; and command to zero.
	LEA	DX, [BP+4002H]
	OUT	DX, AX

	MOV	AX, RECEIVE_HEAD		;Init frame descriptors link
	CMP	CX, 1				; to next frame descriptor.
	JE	next_receive_link		; If this is the last receive
						; block, then set its link to
	LEA	AX, [DI+SIZE ReceiveBlock]	; the first receive block.

next_receive_link:

	LEA	DX, [BP+4004H]
	OUT	DX, AX

	LEA	AX, [DI+OFFSET RBD_ByteCount] ;Init frame descriptors
	LEA	DX, [BP+4006H]
	OUT	DX, AX			; link to its receive buffer descriptor.
;
; Init Receive Buffer Descriptor (RBD).
;
	LEA	DX, [BP+4008H]
	OUT	DX, AX			;Init the RBD Actual count to zero.

	MOV	AX, -1			;Init the RBD link to next RBD
	LEA	DX, [BP+400AH]
	OUT	DX, AX			; to minus 1 (unused link).

	LEA	AX, [DI+RB_Data]	;Init the RDB link to the data
	LEA	DX, [BP+400CH]
	OUT	DX, AX			; buffer.
	XOR	AX, AX
	LEA	DX, [BP+400EH]
	OUT	DX, AX

	MOV	AX, RxBufferSize OR BIT_EL	;Init the RBD count and status
	LEA	DX, [BP+8000H]
	OUT	DX, AX

	MOV	RECEIVE_TAIL, DI	;Set receive tail pointer.
	ADD	DI, SIZE ReceiveBlock	;Point DI to next frame
	LOOP	init_receive_frames	; descriptor.
;
; Initialize the send block structures.  Move the Write pointer the
; top of the current send block.  AX falls through with address of
; first send block.  DI is used to keep track of the address of the
; current send block.  The Transmit command block, transmit buffer
; descriptor, and transmit buffer are all contiguous in memory, and
; make up the send block structure.
;
	MOV	DI, OFFSET send_blocks
	MOV	CX, TxBufferCount

	LEA	DX, [BP].@SMB_Ptr
	XOR	AX, AX
	OUT	DX, AX

	MOV	AX, DI
	LEA	DX, [BP].@SCB_CommandList
	OUT	DX, AX

init_send_blocks:

	LEA	DX, [BP].@SMB_Ptr		;Set write pointer to send
	MOV	AX, DI				; block.
	OUT	DX, AX
;
; Init Transmit Control Block (TCB).
;
	XOR	AX, AX			;Init transmit control block
	LEA	DX, [BP+4000H]
	OUT	DX, AX			; status to zero.

	LEA	DX, [BP+4002H]
	OUT	DX, AX			;Init transmit control block
					; command to anything.  The
	MOV	AX, offset send_blocks	; Init transmit control block
	CMP	CX, 1			; link to next send block.
	JE	next_send_link		; If this is the last send
					; block, then point it to
	LEA	AX, [DI+SIZE SendBlock]	; the first block.

next_send_link:

	LEA	DX, [BP+4004H]
	OUT	DX, AX

	LEA	AX, [DI+OFFSET TBD_ByteCount]	;Init transmit control block
	LEA	DX, [BP+4006H]
	OUT	DX, AX				; link to Transmit Buffer
						; descriptor.
;
; Init Transmit Buffer Descriptor (TBD).
;
	LEA	DX, [BP+4008H]
	OUT	DX, AX			;Init Transmit Buffer Descriptor
					;  byte count to anything
	MOV	AX, -1			;Init Transmit Buffer Descriptor
	LEA	DX, [BP+400AH]
	OUT	DX, AX			;  link to next TBD.

	LEA	AX, [DI+OFFSET TB_Data]	;Init Transmit Buffer Descriptor
	LEA	DX, [BP+400CH]
	OUT	DX, AX			; link to transmit
	XOR	AX, AX			; buffer.  Low part of pointer
	LEA	DX, [BP+400EH]
	OUT	DX, AX			; is done first.

	ADD	DI, SIZE SendBlock		;Point DI to next send block
	LOOP	init_send_blocks		; and loop to initialize it.

;
; Enable loopback to insure nothing acidently hits the cable while
; the 586 gets initialized.
;
	LEA	DX, [BP].@Config
	IN	AX, DX
	OR	AL, loopback_enable
	OUT	DX, AL
;
; Free the 586 from reset.
;
	CALL	free_586_reset
;
; Initialize and configure the 586.  Carry flag set means error and
; dx will point to error message.
;
	CALL	init_cmd
	mov	dx,offset bad_init_msg
	jc	init_exp16_ram_exit

	CALL	configure_command
	mov	dx,offset bad_config_msg
	jc	init_exp16_ram_exit

	CALL	diagnose_command
	jc	init_exp16_ram_exit

	movseg	es,cs
	mov	si,offset rom_address
	mov	cx,EADDR_LEN
	call	set_address
	jnc	sa_ok

	mov	dx,offset badset_msg
	mov	ah,9
	int	21h
sa_ok:
;
; Disable loopback.
;
	LEA	DX, [BP].@Config
	IN	AX, DX
	AND	AL, NOT loopback_enable
	OUT	DX, AL

	clc
	ret

init_exp16_ram_exit:
	stc
	RET


; This procedure must wait until the ASIC finishes reset.  This will take
; around 240 uSec.  This loop will time out after 500 uSec.
;
	five_hundred_micros	EQU	1194
;
reset_board:
;enter with bp=io_addr

	LEA	DX, [BP].@EEPROM_Ctrl
	MOV	AL, ASIC_Reset
	OUT	DX, AL

	XOR	AL, AL
	OUT	DX, AL

;
; Get current tick count.  This loop will wait for 500 uSec to pass
; before failing.
;
	CALL	ReadTickCounter
	MOV	BX, AX

reset_delay:

	PUSH	BX
	LEA	DX, [BP].@ID_Port	; Set DX to the auto-id port.
	CALL	check_for_exp16_hardware
	POP	BX

	CMP	AX, BART_Board_ID
	JE	reset_board_exit

	CALL	ReadTickCounter
	NEG	AX
	ADD	AX, BX
	CMP	AX, five_hundred_micros
	JB	reset_delay

	mov	dx, offset reset_error
	stc
	RET

reset_board_exit:

	MOV	BX, AX
	clc
	RET


reset_586:
;enter with bp = io_addr
	lea	dx, [bp].@EEPROM_Ctrl
	mov	al, _586_Reset
	out	dx, al
	ret


free_586_reset:
;enter with bp = io_addr
	lea	dx, [bp].@EEPROM_Ctrl
	xor	al, al
	out	dx, al
	ret

check_for_exp16_hardware:
;enter with dx = ID port (or maybe shadow ID port).
;
; BX will have board ID when the loop is done.  CX is the loop count.
;
	XOR	BX, BX
	MOV	CX, 4

get_board_id_loop:
;
; Init registers for loop.  CX needs to be used in the loop, and AH
; could be set from the last loop.
;
	PUSH	CX
	XOR	AH, AH
;
; Read ID port.  See description above.
;
	IN	AL, DX
;
; Make nibble ID a shift count in CL.
;
	MOV	CL, AL
	AND	CL, 00000011B
	SHL	CL, 1
	SHL	CL, 1
;
; Move ID nibble to low order bits of AX, then shift then into place.  Put
; the board ID nibble into BX.  After four passes, BX will have board ID.
;
	SHR	AL, 1
	SHR	AL, 1
	SHR	AL, 1
	SHR	AL, 1
	SHL	AX, CL
	OR	BX, AX
;
; Recover loop count, and loop.
;
	POP	CX
	LOOP	get_board_id_loop
;
; Return board ID in AX.
;
	MOV	AX, BX
	RET


;
; Command #1:  Initialize
;
init_cmd:
	XOR	AX, AX				; Init command
	MOV	BX, BIT_CX + BIT_CNA		; status
	CALL	issue_command
	RET

diagnose_command:
;return nc if no problem, else cy,dx->error message.
;
; Set up individual address command block.
;
	MOV	BX, BIT_EL+GA_diagnose
	CALL	setup_command_block
;
; Execute the diagnose command.
;
	MOV	AX, CUC_Start
	MOV	BX, BIT_CNA
	CALL	issue_command
;
; Check diagnostics results.
;
	LEA	DX, [BP].@SMB_Ptr		;Move IO frame to the command
	MOV	AX, OFFSET cb			; command block.
	OUT	DX, AX

	LEA	DX, [BP].@mem_loc_0		;Read in the diagnose status
	IN	AX, DX				; word.
;
; Assume 586 failed the test.  Set AX to an error message and exit
; with the zero bit cleared.
;
	test	ax, 0800H			;Test failure bit.  If set,
	jnz	diagnose_error			; then error exit.
	clc
	ret
diagnose_error:
	mov	dx,offset _586_diagnostic_failure
	stc
	ret


;
; Command #4:  RU_START
;
ru_start:
;
; Set SCBs pointer to receive blocks at head of list.
;
	lea	dx, [bp].@SCB_RFA		;Set pointer to receive blocks
	mov	ax, receive_head		; to the head of the receive
	out	dx, ax				; block list.
;
; Signal 586 to start the receive unit.  cmd_clear leaves DX pointing
; to SCB command register.
;
	cmd_clear
	mov	ax, ruc_start
	out	dx, ax
	issue_CA
	ret


find_a_board:
	mov	bx,offset io_addresses
no_exp16_here:
	mov	bp,[bx]
	cmp	bp,0
	je	no_exp16s
	inc	bx
	inc	bx
	push	bx
	call	reset_board
	pop	bx
	jc	no_exp16_here

	clc
	ret

no_exp16s:
	stc
	ret

get_system_info:
;exit with zr if okay, or nz, dx -> error message if not okay.
	cmp	io_addr,-1
	jne	addr_override
	call	find_a_board
	jnc	board_id_ok
	jmp	bad_or_no_board

addr_override:

	MOV	bp, io_addr

;
; Next, get the exp16 board ID.  If the ID is not what we expect, then
; exit with error code in AX.
;
	call	reset_board
	jnc	board_id_ok
;
; Could search for exp16 board, but we error exit instead.
;
bad_or_no_board:

	mov	dx, offset board_not_installed
	stc
	JMP	SHORT get_system_info_exit

board_id_ok:
	mov	io_addr,bp

	LEA	DX, [BP].@ID_Port	; Set DX to the auto-id port.
	or	DX,3000h		;use the shadow port
	CALL	check_for_exp16_hardware
	mov	board_id,ax

;
; Since the software will be reading the EEPROM during the
; initialization, the 586 needs to be inactive.  This is because the
; control lines to the EEPROM are shared between the 586 and this
; software.  To keep from getting fouled up, the 586 reset line is
; asserted here (now that we have a base IO address, and we know that
; the hardware is present).  The 586 is not released from reset until
; after its data structures are intialized.
;
	CALL	reset_586
;
; Validate the EEPROM by doing a checksum.
;
	mov	cx, 40h
	CALL	check_eeprom
	JC	get_system_info_exit
;
; Get the amount of memory on the adapter.
;
	CALL	test_buffer_memory
	JC	get_system_info_exit

;
; Get the connection type.  If carry clear, then AL has connection
; type.  Otherwise AX has error code.
;
	CALL	get_connection_type
	JC	get_system_info_exit
	MOV	connection_type, AL
;
; Read the Ethernet address out of the EEPROM.
;
	MOV	AX, EE_ETHERNET_ADD_HIGH
	CALL	read_eeprom
	xchg	ah,al
	MOV	word ptr rom_address[0], AX

	MOV	AX, EE_ETHERNET_ADD_MID
	CALL	read_eeprom
	xchg	ah,al
	MOV	word ptr rom_address[2], AX

	MOV	AX, EE_ETHERNET_ADD_LOW
	CALL	read_eeprom
	xchg	ah,al
	MOV	word ptr rom_address[4], AX

	mov	ax,EE_INT
	call	read_eeprom
	mov	cl,EE_SHIFT
	shr	ax,cl
	mov	encoded_int_no,al
	mov	bx,offset irq_xlat_table
	xlat
	mov	int_no,al

;
; Get the Slot width.  If carry clear, then AL has the slot width.
; Otherwise AX has error code.
;
	LEA	DX, [BP].@Config
	IN	AL, DX
;
; Slot width is found in the adapters configuration register.
;
	and	al, slot_width_mask
	mov	_16_not_8_bit_slot, al

	clc
	ret

get_system_info_exit:
;we only ever get here with cy set.
	RET

;
;
; Return connection type in AL with carry clear.
;
;
get_connection_type:

	cmp	board_id,BART_Board_ID	;a real BART?
	je	get_auto_cnt_exit	;yes, it doesn't *have* an ECR1.

	MOV	AX, AUTO_CON_ADDRESS	;get the auto-connection bit.
	CALL	read_eeprom
	TEST	AX, AUTO_CON_MASK	;set?
	Je	get_auto_cnt_exit	;no, okay.
	mov	bl,CONN_AUTO		;yes, we should autosense the connector.
	jmp	short get_connection_type_ret
get_auto_cnt_exit:

;
; Read the connection address from the EEPROM.
;
	MOV	AX, connection_address
	CALL	read_eeprom		;Read_eeprom does not trash DI

;
; Assume AUI type connection.  Check connection fields to see if this
; is a BNC connection.  Jump if it is BNC.
;
	MOV	BL, CONN_AUI
	TEST	AX, CONNECTION_FIELD
	JZ	get_connection_type_ret

;
; Here if NOT BNC.  Must read another EEPROM word to tell if AUI or
; TPE.  Assume AUI and then check for TPE.
;
	MOV	AX, tpe_address
	CALL	read_eeprom		;Read_eeprom does not trash DI

;
; Check for AUI connection.
;
	mov	bl,CONN_BNC
	TEST	AX, TPE_type_field
	JZ	get_connection_type_ret

;
; Here if TPE type connection.
;
	MOV	BL, CONN_TPE

get_connection_type_ret:

	MOV	AL, BL

	CLC
	RET


;
;
; Do a checksum on the EEPROM.
;
; The checksum is the same as the board ID.
;
check_eeprom:
;enter with bp=io_addr, cx = count of bytes to sum starting at 0.
;exit with nc if okay, cy, dx -> error message if not.

	xor	bx, bx

checksum_loop:

	mov	ax, cx
	dec	ax
	call	read_eeprom

	add	bx, ax
	loop	checksum_loop

	cmp	bx, BART_Board_ID
	jne	checksum_error

	clc
	ret

checksum_error:
	mov	dx,offset eeprom_checksum_error
	stc
	ret


test_buffer_memory:
;enter with bp=io_addr
;exit with nc and al=_64k_not_32k if no error, or cy and di -> address in error.

; Set up SI with the maximum number of words to test.  If there is an
; error testing the high 32K of memory, then we will restart the
; test at 32K.  When the tests pass, the memory size is passed back
; in AX.
;
	mov	si, 64 * (1024/2)
;
; Warm up the buffer memory with 16 word writes.
;
	mov	cx, 16
	call	write_zeros

	cmp	cs:is_186,0
	jne	start_memory_tests

restart_memory_tests:
;
; Here if error testing memory and SI is set to 64K buffer size.
; We restart the tests for 32K buffer only.  If an error occurs
; with SI set to 32K, then a memory error is reported.
;
	mov	si, 32 * (1024/2)

start_memory_tests:
;
; Zero RAM.  Set write pointer to the base address and then fill the
; bufffer memory with zeros.
;
	mov	cx, si
	call	write_zeros

	call	word_memory_test_pattern
	jc	buffer_mem_error_exit

	cmp	cs:is_186,0
	je	_8088_quick_exit

	mov	cx, si
	call	write_zeros

	call	byte_memory_test_pattern
	jc	buffer_mem_error_exit

_8088_quick_exit:
;
; Zero RAM.  Set write pointer to the base address and then fill the
; bufffer memory with zeros.
;
	mov	cx, si
	call	write_zeros

;
; Set _64K_not_32K to the size of the memory that passed diagnostics.
;
	cmp	si, 32 * (1024/2)	;did we quit at 32K
	je	buffer_mem_exit		;yes, we only have 32K.

	mov	_64K_not_32K,1		;no, we must have 64K.

buffer_mem_exit:
	clc
	ret

buffer_mem_error_exit:
;
; If error occured, and SI is not set for 32K, then retry tests with
; SI set for 32K.  If the error occurs and the size is 32K, then the
; Exp16 board memory is bad.
;
	CMP	SI, 32 * (1024/2)
	JNE	restart_memory_tests

	MOV	DX, OFFSET buffer_memory_error
	STC	;Set carry to indicate error.
	RET


write_zeros:
;enter with bp=io_addr, cx=number of zero words to write.
;
; Move write pointer to the beginning of the buffer memory.
;
	LEA	DX, [BP].@Write_Ptr
	XOR	AX, AX
	OUT	DX, AX
;
; Set DX to the data register and write out zeros CX times.
;
	mov	dx, bp			;@Data_Reg
warm_up:
	out	dx, ax
	loop	warm_up
	ret


word_memory_test_pattern:
;enter with si = number of words to test.

;
; Set CX to number of words to test.  Set BX to beginning og pattern.
;
	MOV	CX, SI
	MOV	BX, 1

;
; Set DI to beginning of the buffer.
;
	XOR	DI, DI

word_inc_pattern:

	CALL	loop_set_up
	IN	AX, DX
	OR	AX, AX
	JNE	word_memory_test_pattern_error

;
; Write Test Pattern.
;
	MOV	AX, BX
	OUT	DX, AX

;
; Increment BX to next test pattern value.
;
	ADD	BX, 3

;
; Increment DI to next test memory location.
;
	ADD	DI, 2
	LOOP	word_inc_pattern

;
; Set Read pointer to beginning of buffer.
;
	LEA	DX, [BP].@read_ptr
	XOR	AX, AX
	OUT	DX, AX
	MOV	DX, BP			;@Data_Reg

;
; Set CX to number of words to test.  Set BX to beginning og pattern.
;
	MOV	CX, SI
	MOV	BX, 1

word_check_pattern:

	IN	AX, DX
	CMP	AX, BX
	JNE	word_memory_test_pattern_error

;
; Increment BX to next test pattern value.
;
	ADD	BX, 3

	LOOP	word_check_pattern

	CLC
	RET

word_memory_test_pattern_error:

	STC
	RET


byte_memory_test_pattern:
;enter with si = number of bytes to test.

;
; Set CX to number of bytes to test.  Set BX to beginning of pattern.
;
	MOV	CX, SI
	SHL	CX, 1
	MOV	BL, 1

;
; Set DI to beginning of the buffer.
;
	XOR	DI, DI

byte_inc_pattern:

	CALL	loop_set_up
	IN	AL, DX
	OR	AL, AL
	JNE	byte_memory_test_pattern_error

;
; Write Test Pattern.
;
	MOV	AL, BL
	OUT	DX, AL

;
; Increment BX to next test pattern value.
;
	ADD	BL, 3

;
; Increment DI to next test memory location.
;
	INC	DI

	LOOP	byte_inc_pattern

;
; Set Read pointer to beginning of buffer.
;
	LEA	DX, [BP].@read_ptr
	XOR	AX, AX
	OUT	DX, AX
	MOV	DX, BP			;@Data_Reg

;
; Set CX to number of bytes to test.  Set BX to beginning of pattern.
;
	MOV	CX, SI
	SHL	CX, 1
	MOV	BL, 1

byte_check_pattern:

	IN	AL, DX
	CMP	AL, BL
	JNE	byte_memory_test_pattern_error

;
; Increment BX to next test pattern value.
;
	add	bl, 3

	loop	byte_check_pattern

	clc
	ret

byte_memory_test_pattern_error:

	stc
	ret

loop_set_up:
;enter with bp=io_addr, di=buffer address.
;exit with ax=value at di, dx=I/O address to access buffer memory at DI

	PUSH	DI

	MOV	AX, DI			;SMB_Ptr must be on 16 byte
	AND	AX, 0FFE0H
	AND	DI, 0001FH

	LEA	DX, [BP].@SMB_Ptr		;Set IO page frame to the
	OUT	DX, AX


	TEST	DI, 0010H
	JZ	loop_set_up_1

	ADD	DI, @MEM_LOC_0

loop_set_up_1:
	AND	DI, 0FFEFH
	LEA	DX, [BP+DI].@MEM_LOC_0		;Set DX for IO from buffer and

	POP	DI

	RET


read_eeprom:
;enter with bp=io_addr, ax = EEPROM location.
;exit with ax = EEPROM contents.
;preserves di.
	push	di
	push	bx
	push	cx

;  Point to EEPROM control port.

	lea	dx, [bp].@eeprom_ctrl
	mov	bx, ax

; Select the EEPROM.  Mask off the ASIC and 586 reset bits and set
; the ee_cs bit in the EEPROM control register.

	in	al, dx
	and	al, 10110010b
	or	al, ee_cs
	out	dx, al

; Write out read opcode and EEPROM location.

	mov	ax, eeprom_read_opcode		;Set AX to READ opcode and
	mov	cx, 3				;Send it to the EEPROM circuit
	call	shift_bits_out

	mov	ax, bx				;Tell EEPROM which register is
	mov	cx, 6				; to be read.  6 bit address.
	call	shift_bits_out

	call	shift_bits_in			;AX gets EEPROM register

	call	eeprom_clean_up			;Leave EEPROM in known state.

	pop	cx
	pop	bx
	pop	di
	ret


;-----------------------------------------------------------------------------
;  Writes the specified value to the specified EEPROM register at the 
;  specified base I/O address.
;
;  Entry  - BP  Base IO.
;           AX  EEPROM location to write.
;	    BX  Value to write
;
;  Return - AX = 0 if no error
;	    AX = Pointer to Error message
;
;-----------------------------------------------------------------------------
write_eeprom:
;enter with bp=io_addr, ax = EEPROM location, bx = new contents.
;exit with ax = 0 if no error, ax -> error if error.
;preserves di.

	push	bx
	mov	bx, ax

	lea	dx, [bp].@EEPROM_Ctrl	;Point to EEPROM port.

	in	al, dx	  			;Select EEPROM
	and	al, 10110000b
	or	al, ee_cs
	out	dx, al

; Send Erase/write enable opcode to the EEPROM.
	mov	ax, EEPROM_EWEN_opcode	;Send erase/write enable
	mov	cx, 5			; command to the EEPROM.
	call	shift_bits_out

	mov	cx, 4			;Send 4 don't cares as
	call	shift_bits_out		; required by the eeprom

	call	stand_by

; Send the erase opcode to the EEPROM and wait for the command to 
; complete.
	mov	ax, EEPROM_erase_opcode	;Send Erase command to the
	mov	cx, 3			; EEPROM.
	call	shift_bits_out

	mov	ax, bx			;Send EEPROM location the the
	mov	cx, 6			; EEPROM.  6 bit address.
	call	shift_bits_out

	call	wait_eeprom_cmd_done	;wait for end-of-operation
	jc	write_fault_pop		; go if error

	call	stand_by

; Send the write opcode, location to write, and data to write to the
; EEPROM.  Wait for the write to complete.
	mov	ax, EEPROM_write_opcode	;Send write command to the 
	mov	cx, 3			; EEPROM.
	call	shift_bits_out

	mov	ax, bx			;Send the EEPROM location to
	mov	cx, 6			; the EEPROM.  5 bit address.
	call	shift_bits_out

	pop	ax			;Send data to write to the 
	mov	cx, 16			; EEPROM.  16 bits.
	call	shift_bits_out

	call	wait_eeprom_cmd_done	;Await end-of-command
	jc	write_fault		;go if error

	call	stand_by

; Send the erase write disable command to the EEPROM.
	mov	ax, EEPROM_EWDS_opcode		;Disable the Erase/write
	mov	cx, 5				; command previously sent to
	call	shift_bits_out			; EEPROM.

	mov	cx, 4	      			;Send 4 don't cares as
	call	shift_bits_out			; required by the eeprom

	call	eeprom_clean_up

	clc
	ret

write_fault_pop:
	add	sp, 2				;Get rid of data on stack
write_fault:
;;;	lea	ax, eeprom_write_error
	stc
	ret


shift_bits_out:
;enter with ax=data to be shifted, cx=# of bits to be shifted.

	push	bx

; Data bits are right justified in the AX register.  Move the data
; into BX and left justify it.  This will cause addresses to to
; be sent to the EEPROM high order bit first.

	mov	bx, ax
	mov	ch, 16
	sub	ch, cl
	xchg	cl, ch
	shl	bx, cl
	xchg	cl, ch
	xor	ch, ch

; Get the EEPROM control register into AL.  Mask of the ASIC asn 586
; reset bits.

	in	al, dx
	and	al, 10111111b

; Set or clear DI bit in EEPROM control register based on value of
; data in BX.

out_shift_loop:
	and	al, not ee_di			;Assume data bit will be zero

	rcl	bx, 1				;Is the data bit a one?
	jnc	out_with_it			;No
	or	al, ee_di			;Yes
out_with_it:
	out	dx, al				;Output a 0 or 1 on data pin

; Set up time for data is .4 Microseconds.  So to be safe (incase of
; this software is run on a cray), call delay.
	mov	di, 1
	call	eeprom_delay

	call	raise_eeprom_clock	; Clock the data into the EEPROM.
	call	lower_eeprom_clock
	loop	out_shift_loop		;Send next bit

	and	al, not ee_di		;Force data bit to zero
	out	dx, al			;Output a 0 on data pin

	pop	bx

	ret

shift_bits_in:
;exit with ax = register contents.
	push	bx
	push	cx

; BX will receive the 16 bits read from the EEPROM.  Data is valid in
; data out bit (DO) when clock is high.  There for, this procedure
; raises clock and waits a minimum amount of time.  DO is read, and
; clock is lowered.

	in	al, dx				;Init AL to eeprom control
	and	al, 10111111b			; register.

	xor	bx, bx				;Init holding register
	mov	cx, 16				;We'll shift in 16 bits

in_shift_loop:
	shl	bx, 1			;Adjust holding register for
					; next bit
	call	raise_eeprom_clock

	in	al, dx
	and	al, 10111111b

	test	al, ee_do		;Was the data bit a one?
	jz	in_eeprom_delay		;No

	or	bx, 1			;Yes, reflect data bit state
					; in holding register.

in_eeprom_delay:
	call	lower_eeprom_clock
	loop	in_shift_loop		;CONTINUE

	mov	ax, bx			;AX = data

	pop	cx
	pop	bx
	ret

raise_eeprom_clock:
	or	al, ee_sk		;Clock the bit out by raising
	jmp	short eeprom_clock_common
lower_eeprom_clock:
	and	al, not ee_sk			;Lower ee_sk
eeprom_clock_common:
	out	dx, al
	mov	di, ee_tick
	call	eeprom_delay			;Waste time
	ret


wait_eeprom_cmd_done:
;Wait for ee_do to go high, indicating end-of-write or end-of-erase
;operation.  Wait at most 20 ms (EEPROM needs at most 10).
;exit with nc if no error.

	push	bx
	push	cx

	call	stand_by

	call	readtickcounter
	mov	di, ax

ee_do_wait_loop:

	call	readtickcounter
	neg	ax
	add	ax, di

	cmp	ax, 28640		;12 Millisecond wait
	jb	ee_do_wait_loop

	in	al, dx			;Get EEPROM control register

	test	al, EE_DO		;ee_do high?
	jnz	ee_do_found

	stc
	jmp	short wait_eeprom_cmd_done_exit

ee_do_found:

	clc				;"clean" status (no timeout)

wait_eeprom_cmd_done_exit:

	pop	cx
	pop	bx
	ret


stand_by:
; lower chip select for 1 microsecond.
	in	al, dx	  			;de-select EEPROM
	and	al, (10111110b) and (not ee_cs)
	out	dx, al

	mov	di, 2
	call	eeprom_delay

	or	al, ee_cs
	out	dx, al

	ret

; Lower EEPROM chip select and DI.
; Clock EEPROM twice and leave clock low.

eeprom_clean_up:

	push	ax

	in	al, dx
	and	al, 10111111b
	and	al, not (ee_cs or ee_di)
	out	dx, al

	call	raise_eeprom_clock
	call	lower_eeprom_clock

	pop	ax
	ret

; DI has number of 838 Nanoseconds clock counts
eeprom_delay:

	push	ax
	push	bx
	push	dx

	call	readtickcounter
	mov	bx, ax

eeprom_delay_loop:

	call	readtickcounter
	neg	ax
	add	ax, bx
	cmp	ax, di
	jb	eeprom_delay_loop

	pop	dx
	pop	bx
	pop	ax
	ret

iochrdy_test:
; First test to see if the driver is supposed to run this test.
	MOV	AX, lock_bit_address
	CALL	read_eeprom

	TEST	AX, lock_bit_mask
	JNZ	iochrdy_test_exit
; Get the configuration register.

	LEA	DX, [BP].@config
	IN	AL, DX

; Set the iochrdy test bit with and set iochrdy to late.
	OR	AL, iochrdy_test_mask+iochrdy_late
	CLI
	OUT	DX, AL

; Test IOCHRDY with IO.
	LEA	DX, [BP].@mem_loc_0
	IN	AX, DX

; Read in the results of the test.  Save results in BL.
	LEA	DX, [BP].@config
	IN	AL, DX
	MOV	BL, AL

; Turn iochrdy test off.
	AND	AL, NOT iochrdy_test_mask
	OUT	DX, AL
	STI

; Test results.  Exit if IOCHRDY_LATE bit is set correctly.
	TEST	BL, iochrdy_test_result
	JZ	iochrdy_test_exit

; Here if test failed.  Clear the 16 bit override bit (force 8 bit
; transfers) and print a warning message.
	AND	AL, NOT _16_bit_override_bit
	OUT	DX, AL

	mov	dx, offset iochrdy_problem
	mov	ah,9
	int	21h


iochrdy_test_exit:

	RET



iochrdy_problem	db	"IOCHRDY Problem.  EtherExpress forced into 8 Bit Mode.",CR,LF,'$'
buffer_memory_error	db	"Memory error on the EtherExpress board",CR,LF,'$'

reset_error	db	"ASIC reset failure on EtherExpress board",CR,LF,'$'

_586_diagnostic_failure	db	"82586 diagnostic failure on the "
	db	"EtherExpress board",CR,LF,'$'

_586_not_responding_msg	db	"82586 did not respond to command "
	db	"on the EtherExpress board",CR,LF,'$'

command_unit_not_idle	db	"82586 command unit is not "
	db	"responding on the EtherExpress board",CR,LF,'$'

invalid_int_number	db	"EtherExpress board IRQ/Interrupt number "
	db	"not specified correctly",CR,LF,'$'

eeprom_checksum_error	db	"EEPROM failed checksum",CR,LF,'$'

Board_Not_Installed	db	"EtherExpress Board not found",CR,LF,'$'

irq_config_error	db	"IRQ selection is for 16 bit slot only.",CR,LF,'$'

bad_config_msg	db	"Could not configure the 82586",CR,LF,'$'

bad_init_msg	db	"Could not initialize the 82586",CR,LF,'$'

code	ends

	end
