Metropoli BBS
VIEWER: exp16.asm MODE: TEXT (ASCII)
version	equ 3
DEBUG   equ 0
;History:1518,1

;  Copyright 1991 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

deb            macro    xxx
if DEBUG
               push bx
               push ds

               mov bx,0b800h
               mov ds,bx
               mov bx,word ptr xxx
               add bx,40
               shl bx,1
               shl bx,1
               inc byte ptr ds:[bx]
               pop ds
               pop bx
endif
               endm


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 barts 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)

;	82586 Structure definitions

SysConfigPtr	struc
	SCP_SystemBus		dw	?
				dw	2 dup (?)
	SCP_ISCP_Ptr_low	dw	?
	SCP_ISCP_Ptr_high	dw	?
SysConfigPtr	ends


;----------------------------------------------------------------
; Note: The @ addresses are the I/O address offsets from [bp].
;----------------------------------------------------------------

; The SCP is at location XXX6H.  Since the
; SMB pointers must have the low order 5 bits
; zero, we assume that the SMP pointer will
; be set to XXE0H
@SysConfigPtr	struc
				db	8000h dup (?)
				dw	?
				dw	?
				dw	?
	@SCP_SystemBus		dw	?
				dw	2 dup (?)
	@SCP_ISCP_Ptr_low	dw	?
	@SCP_ISCP_Ptr_high	dw	?
@SysConfigPtr	ends


ISysConfigPtr	struc
	ISCP_Busy		dw	?
	ISCP_SCB_Offset		dw	?
	ISCP_Ptr_low		dw	?
	ISCP_Ptr_high		dw	?
ISysConfigPtr	ends


; NOTE: Access to the SCB through the shadow
; registers is done by setting the SMB_PTR to
; the ISCP base address and then using this
; structure.  This is because the SCB address
; does not start on a 32 byte boundary as
; as required for the SMB_PTR.

@ISysConfigPtr	struc
				db	4000h dup (?)
	@ISCP_Busy		dw	?
	@ISCP_SCB_Offset	dw	?
	@ISCP_SCB_Ptr_low	dw	?
	@ISCP_SCB_Ptr_high	dw	?
	@SCB_Status		dw	?
	@SCB_Command		dw	?
	@SCB_CommandList	dw	?
	@SCB_RecBlockList	dw	?
				db	4000h - 16 dup (?)
	@SCB_CRC_Errors	dw	?
	@SCB_ALN_Errors	dw	?
	@SCB_RSC_Errors	dw	?
	@SCB_OVR_Errors	dw	?
@ISysConfigPtr	ends


SystemControlBlock	struc
	SCB_Status		dw	?
	SCB_Command		dw	?
	SCB_CommandList 	dw	?
	SCB_RecBlockList	dw	?
	SCB_CRC_Errors		dw	?
	SCB_ALN_Errors		dw	?
	SCB_RSC_Errors		dw	?
	SCB_OVR_Errors		dw	?
SystemControlBlock	ends

; NOTE: The SCB shadow register structure is
; part of the ISysConfigPtr shadow register
; structure.

ReceiveBlock	struc
	FD_Status		dw	?
	FD_Command		dw	?
	FD_Link		        dw	?
	FD_RBD_Offset		dw	?
	RBD_ByteCount		dw	?
	RBD_Link		dw	?
	RBD_RB_Address_low	dw	?
	RBD_RB_Address_high	dw	?
	RBD_Size		dw	?
	RB_Data		db	RxBufferSize dup (?)
ReceiveBlock	ends

ReceiveBlock_data	struc
	IPX_FD_Status			dw	?
	IPX_FD_Command			dw	?
	IPX_FD_Link			dw	?
	IPX_FD_RBD_Offset		dw	?
	IPX_RBD_ByteCount		dw	?
	IPX_RBD_Link			dw	?
	IPX_RBD_RB_Address_low 	        dw	?
	IPX_RBD_RB_Address_high	        dw	?
	IPX_RBD_Size			dw	?
	IPX_DestAddr_HIGH		dw	?
	IPX_DestAddr_MID		dw	?
	IPX_DestAddr_LOW		dw	?
	IPX_SourceAddr_HIGH 		dw	?
	IPX_SourceAddr_MID		dw	?
	IPX_SourceAddr_LOW		dw	?
	IPX_LengthTypeField 		dw	?
ReceiveBlock_data	ends


@ReceiveBlock	struc
				db	4000h dup (?)
	@FD_Status		dw	?
	@FD_Command		dw	?
	@FD_Link		dw	?
	@FD_RBD_Offset		dw	?
	@RBD_ByteCount		dw	?
	@RBD_Link		dw	?
	@RBD_RB_Address_low	dw	?
	@RBD_RB_Address_high	dw	?
				db	4000h - 16 dup (?)
	@RBD_Size		dw	?
; Ethernet Packet Data
	@DestAddr_HIGH	        dw	?
	@DestAddr_MID		dw	?
	@DestAddr_LOW		dw	?
	@SourceAddr_HIGH 	dw	?
	@SourceAddr_MID	        dw	?
	@SourceAddr_LOW	        dw	?
	@LengthTypeField 	dw	?
@ReceiveBlock	ends

SendBlock	struc
	TxCB_Status		dw	?
	TxCB_Command		dw	?
	TxCB_Link		dw	?
	TxCB_TBD_Offset	        dw	?
	TBD_ByteCount		dw	?
	TBD_Link		dw	?
	TBD_TB_Address_low	dw	?
	TBD_TB_Address_high	dw	?
	TB_Data		db	TxBufferSize dup (?)
SendBlock	ends


@SendBlock	struc
				db	4000h dup (?)
	@TxCB_Status		dw	?
	@TxCB_Command		dw	?
	@TxCB_Link		dw	?
	@TxCB_TBD_Offset	dw	?
	@TBD_ByteCount	dw	?
	@TBD_Link		dw	?
	@TBD_TB_Address_low	dw	?
	@TBD_TB_Address_high	dw	?
				db	4000h - 16 dup (?)
	@TB_Data		db	TxBufferSize dup (?)
@SendBlock	ends


CommandBlock	struc			 		;Set command block
	CB_Status	dw	?			; Size for largest
	CB_Command	dw	?			; command.  The Dump
	CB_Link	dw	?				; command needs a 170
	CB_Param0	dw	?			; byte area.  The 14
			db	170+14 dup (?)	 	; extra bytes are
CommandBlock	ends					; padding to make the
							; size of this struc
							; boundary.
@CommandBlock	struc
			db	4000h dup (?)		;Set command block
	@CB_Status	dw	?			; Size for largest
	@CB_Command	dw	?			; command.  The Dump
	@CB_Link	dw	?			; command needs a 170
	@CB_Parm0	dw	?			; byte area.  The 14
			db 	170+14 dup (?)  	; extra bytes are
@CommandBlock	ends					; padding to make the
							; size of this struc
							; align on a 16 byte
SCPS			equ	SysConfigPtr
ISCPS			equ	ISysConfigPtr
SCBS			equ	SystemControlBlock

; System ControlBlock.

Bit_CX		equ	8000h
Bit_FR		equ	4000h
Bit_CNA		equ	2000h
Bit_RNR		equ	1000h

;  System Control Block's control unit statuses

CUS_Idle		equ	0000h
CUS_Suspended		equ	0100h
CUS_Active		equ	0200h
CUS_Mask		equ	0300h

;  System Control Block's receive unit statuses.
RUS_Idle		equ	0000h
RUS_Suspend		equ	0010h
RUS_No_Resource		equ	0020h
RUS_Ready		equ	0040h

;  System Control Block's control unit commands
CUC_NOP			equ	0000h
CUC_Start		equ	0100h
CUC_Resume		equ	0200h
CUC_Suspend		equ	0300h
CUC_Abort		equ	0400h

;  System Control Block's receive unit commands
RUC_NOP			equ	0000h
RUC_Start		equ	0010h
RUC_Resume		equ	0020h
RUC_Suspend		equ	0030h
RUC_Abort		equ	0040h


; General Action Command Block.

GA_NOP			equ	0000h
GA_IA_Setup		equ	0001h
GA_Configure		equ	0002h
GA_MC_Setup		equ	0003h
GA_Transmit		equ	0004h
GA_TDR			equ	0005h
GA_Dump		 	equ	0006h
GA_Diagnose		equ	0007h

lock_bit_address	EQU	01H
lock_bit_mask		EQU	0000000000000001B
iochrdy_mask		EQU	00010000B
iochrdy_test_mask	EQU	00100000B
iochrdy_test_result	EQU	01000000B
iochrdy_early		EQU	00000000B
iochrdy_late		EQU	00010000B


; Common Bits.

Bit_C			equ	8000h	; Command complete.
Bit_B			equ	4000h	; Busy executing command.
Bit_OK			equ	2000h	; Error free completion.
Bit_A			equ	1000h	; Command aborted.

Bit_EL			equ	8000h	; End of list.
Bit_EOF			equ	8000h	; End of frame.
byte_Bit_EOF	 	equ	80h	; End of frame.
Bit_S			equ	4000h	; Suspend after completion.
Bit_I			equ	2000h	; Interrupt after completion.

Bit_S11                 equ     0800h
Bit_S10			equ	0400h
Bit_S9			equ	0200h
Bit_S8			equ	0100h
Bit_S7			equ	0080h
Bit_S6			equ	0040h
Bit_S5			equ	0020h

ACK_INT_Mask 		equ	0f000h
MaxCollisions		equ	0020h
Ready			equ	0040h

LNK_OK                  equ     8000h
XCVR_PRB                equ     4000h
ET_OPN                  equ     2000h
ET_SRT                  equ     1000h
TDR_TIME                equ     07ffh

;
; equates and definitions:
;
EOI			equ	020h	;End-of-interrupt command.
Master_PIC		equ	020h	;1st Interrupt-Control port.
Slave_PIC		equ	0A0h	;2nd Interrupt-Control port.

Bart_Board_ID		equ	0BABAH

Number_of_Tx_Buffers	equ	1

_586_Reset		equ	0080H
ASIC_Reset		equ	0040H

bus_width		equ	0	;16 bit, use for initialize
initialize_586		equ	1

;
; Timer equates
;
one_mil 	EQU	02387
ten_mils	EQU	23866


;
;  EEPROM equATES
;
EE_SK		equ	00000001B	;EEPROM shift clock (1 = high, 0 = low)
EE_CS		equ	00000010B	;EEPROM chip select (1 = high, 0 = low)
EE_DI		equ	00000100B	;EEPROM data in (set to 1 for writing data to EEPROM)
EE_DO		equ	00001000B	;EEPROM data out
EE_tick		equ	10

EEPROM_read_opcode 	equ	110B
EEPROM_write_opcode	equ	101B
EEPROM_erase_opcode	equ	111B
EEPROM_EWEN_opcode 	equ	10011B		;Erase/write enable
EEPROM_EWDS_opcode 	equ	10000B		;Erase/write disable

TIME_OUT_VALUE	equ	0FFFFh

loopback_enable	equ	00000010B

_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.
connection_type		db	?

;Memory Sizes
mem_size_address	equ	00H
_32K			equ	00000h
_64K			equ	00001h

connection_address	equ	00H
connection_field	equ	0001000000000000B

TPE_address		equ	05H
TPE_type_field		equ	0000000000000001B

BNC			equ	00000h
AUI			equ	00001h
TPE			equ	00002h

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

;	CPU types
_8088_CPU		equ	00000h
Not_8088_CPU		equ	NOT _8088_CPU

;	Slot Width
slot_width_mask	equ	04h
_16_bit_slot		equ	0000h
_8_bit_slot		equ	0001h

_16_bit_override_bit    equ     08h


;
;	BART base port structure
;
@BARTBasePorts	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	?
@BARTBasePorts	ends


@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 BART.
;	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	0,0		; I/O address for card
encoded_int_no	db	?		;encoded for bart.

	public	driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class	db	BLUEBOOK, IEEE8023, 0		;from the packet spec
driver_type	db	255		;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
pro      db 0
pro1     db 0

	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	;Default is 8
				DB	08H

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

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	?

our_type	dw	8 dup(?)

is_186		db	0		;=0 if 808[68], =1 if 80[123]86.
rom_address	db	EADDR_LEN dup(?)

	include	io16.asm

	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:
	inc	cx		;round size up to next even number.
	and	cx,not 1
;
; 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

	call	repoutsw
;.286
;        rep outsb

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,255
        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	get_address
get_address:
;get the address of the interface.
;enter with es:di -> place to get the address, cx = size of address buffer.
;exit with nc, cx = actual size of address, or cy if buffer not big enough.
; Give caller the one currently in the 8390, not necessarily the one in PROM.
	assume ds:code
	cmp cx,	EADDR_LEN		; Caller wants a reasonable length?
	jb	get_addr_x		; No, fail.
	mov cx,	EADDR_LEN		; Move one ethernet address from our copy
	mov si, offset rom_address	; Copy from most recent setting
	rep     movsb
	mov cx,	EADDR_LEN		; Tell caller how many bytes we fed him
	clc				; Carry off says success
	ret
get_addr_x:
	stc				; Tell caller our addr is too big for him
	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:
        push    bp
	push	ds
	push	si

	push	cs
	pop	ds
;
; 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
	pop	ds

;
; 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
        pop     bp
        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
        pop     bp
set_address_done:
	push	cs
	pop	ds
	assume	ds:code
	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     pro,0
	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     pro,0
	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     pro,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
	clc
	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
	jnz	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	AX, OFFSET _586_not_responding_msg
	OR	AX, AX

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, cx = number of addresses.
;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:
        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

	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 bart.  Also disable barts 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 bart 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:
deb 160
;
; 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.
;
deb 1
	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     pro,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
       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
       jmp     ProcessRx_1

ProcessRx_1:
deb 2
;
; 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_LengthTypeField
	out	dx, ax
	mov	dx, bp			;@Data_Reg

	mov	ax,cs
	mov	es,ax
	mov	di,offset our_type
	mov	cx,8
	call	repinsw
;.286
;        rep insb

;
; 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

	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:
deb 3
	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
deb 4
;
; 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
	lea	ax, [BX].IPX_DestAddr_HIGH
	out	dx, ax
	mov	dx, bp			;@Data_Reg
;
; Now read the packet into es:di
;
	call	repinsw
;        rep insb

	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

        mov	bp,io_addr              ; just in case
deb 5
recv_isr_9:

;
; 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



	public	recv_exiting
recv_exiting:
;called from the recv isr after interrupts have been acknowledged.
;Only ds and ax have been saved.
	assume	ds:nothing
	ret

; 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


;any code after this will not be kept after initialization.
end_resident	label	byte


	public	usage_msg
usage_msg	db	"usage: exp16 [-n] [-d] [-w] <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 Intel Corp",CR,LF,'$'
using_186_msg	db	"Using 80[123]86 I/O instructions.",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


	public	etopen
etopen:
;initialize the driver.
	assume	ds:code
;Determine the processor type.  The 8088 and 8086 will actually shift ax
;over by 33 bits, while the 80[123]86 use a shift count mod 32.
	mov	cl,33
	mov	ax,0ffffh
	shl	ax,cl
	jz	not_186
	mov	is_186,1
	mov	dx,offset using_186_msg
	mov	ah,9
	int	21h
not_186:

;
; Get board information (Node address, etc.).  Returns with BP
; set to the board IO base address (if not errors).
;
	call	get_system_info
	jnz	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.
	jae	driver_init_error_exit
check_config_exit:

;
; Initialize the 586 and the 586 data structures.
;
	call	init_586
	jnz	driver_init_error_exit

; test iochrdy
        call    iochrdy_test
;
; Set up Interrupt line, start the receive unit, and Enable barts interrupt.
;
	call	set_recv_isr
	call	ru_start
	enable_board_ints

	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.

	mov	dx,offset end_resident
	clc
	ret

driver_init_error_exit:
	stc
	ret


; int number to IRQ translate table.
IRQ_xlat_table	LABEL	BYTE
		DB	00H			;IRQ 0	Invalid IRQ
		DB	00H			;IRQ 1	Invalid IRQ
		DB	01H			;IRQ 2	  Valid IRQ
		DB	02H			;IRQ 3 	  Valid IRQ
		DB	03H			;IRQ 4 	  Valid IRQ
		DB	04H			;IRQ 5 	  Valid IRQ
		DB	00H			;IRQ 6 	Invalid IRQ
		DB	00H			;IRQ 7 	Invalid IRQ
		DB	00H			;IRQ 8 	Invalid IRQ
		DB	01H			;IRQ 9	  Valid IRQ
		DB	05H			;IRQ 10   Valid IRQ
		DB	06H			;IRQ 11   Valid IRQ
max_IRQ		EQU	$-IRQ_xlat_table

io_addresses label word
               dw  300h,310h,320h,330h,340h,350h,360h,370h
               dw  200h,210h,220h,230h,240h,250h,260h,270h
               dw  0

	public	parse_args
parse_args:
;	mov	di,offset int_no
;	call	get_number
	mov	di,offset io_addr
	call	get_number

;	mov	al,int_no		;ensure that it's in our table.
;	cmp	al,max_IRQ
;	ja	get_encoded_irq_error_exit
;
;	mov	bx,offset irq_xlat_table ;encode it into the number bart wants.
;	xlat
;
;	or	al, al			;if zero, bart doesn't want it.
;	je	get_encoded_irq_error_exit
;	mov	encoded_int_no,al
;
	clc
	ret

;get_encoded_irq_error_exit:
;	stc
;	ret

bnc_connector_msg	db	"Configured for the BNC connector",CR,LF,'$'
aui_connector_msg	db	"Configured for the AUI connector",CR,LF,'$'
tpe_connector_msg	db	"Configured for 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	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,BNC
	je	print_parameters_1
	mov	dx,offset aui_connector_msg
	cmp	connection_type,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
; AX will point to error message.
;
	CALL	init_cmd
	JNZ	init_bart_ram_exit

	CALL	configure_command
	JNZ	init_bart_ram_exit

	CALL	diagnose_command
	JNZ	init_bart_ram_exit

	push	cs
	pop	es
	mov	si,offset rom_address
	mov	cx,EADDR_LEN
	call	set_address
        jnc     sa_ok

        mov     dx,offset badset_msg
        mov     ah,0
        int 21h
sa_ok:
;
; Disable loopback.
;
	LEA	DX, [BP].@Config
	IN	AX, DX
	AND	AL, NOT loopback_enable
	OUT	DX, AL

	XOR	AX, AX

init_bart_ram_exit:

	OR	AX, AX
	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
	CALL	check_for_bart_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	AX, 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_bart_hardware:
;
; Set DX to the auto-id port.
;
	LEA	DX, [BP].@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:
;
; 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.
;
	MOV	BX, AX
	MOV	AX, OFFSET _586_diagnostic_failure

	TEST	BX, 0800H			;Test failure bit.  If set,
	JNZ	diagnose_exit			; then error exit.

	XOR	AX, AX				;Set AX and zero bit to
						; indicate no error.
diagnose_exit:

	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_bart_here:
        mov     bp,[bx]
        cmp     bp,0
        je      no_barts
        inc     bx
        inc     bx
        push    bx
        call    reset_board
        pop     bx
        jc      no_bart_here

        clc
        ret

no_barts:
        stc
        ret

get_system_info:
        cmp     io_addr,0
        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 bart 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 bart board, but we error exit instead.
;
bad_or_no_board:

	MOV	AX, OFFSET board_not_installed
	JMP	SHORT get_system_info_exit

board_id_ok:
        mov     io_addr,bp

;
; 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.
;
	CALL	check_eeprom
	JC	get_system_info_exit
;
; Get the amount of memory on the adapter.  If carry clear, then AX
; has memory size.  Else AX has error code.
;
	CALL	test_buffer_memory
	JC	get_system_info_exit

	MOV	_64K_not_32K,al
;
; 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
        mov     cl,0
kg:
        cmp     [bx],al
        je      found_it
        inc     bx
        inc     cl
        jmp     kg
found_it:
	mov	int_no,cl

;
; 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

	xor	ax, AX

get_system_info_exit:

	OR	AX, AX
	RET

;
;
; Return connection type in AL with carry clear.  Otherwise return AX with
; unable_to_read_eeprom error code and carry set.
;
;
get_connection_type:

;
; 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, 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,bnc
	TEST	AX, TPE_type_field
	JZ	get_connection_type_ret

;
; Here if TPE type connection.
;
	MOV	BL, TPE

get_connection_type_ret:

	MOV	AL, BL

	CLC
	RET


;
;
; Do a checksum on the EEPROM.  Retrun carry set and AX pointing to an error
; message if checksum is bad.  Otherwise return carry clear.
;
; The checksum is the same as the board ID.
;
check_eeprom:

	mov	cx, 40h
	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	ax, offset eeprom_checksum_error
	stc
	ret


test_buffer_memory:
;enter with bp=io_addr
;exit with nc 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
	je	restart_memory_tests

	jmp	short 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
; Bart board memory is bad.
;
	CMP	SI, 32 * (1024/2)
	JNE	restart_memory_tests

	MOV	AX, 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


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


; 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      10,13,"IOCHRDY Problem.  Bart forced into 8 Bit Mode.$"
buffer_memory_error		DB	10,13,"Memory error on the Bart board$"

reset_error			DB	10,13,"ASIC reset failure on Bart board$"

_586_diagnostic_failure	DB	10,13,"82586 diagnostic failure on the "
				DB	      "Bart board$"

_586_not_responding_msg	DB	10,13,"82586 did not respond to command "
				DB	      "on the Bart board$"

command_unit_not_idle	DB	10,13,"82586 command unit is not "
				DB	      "responding on the Bart board$"

invalid_int_no		LABEL	WORD
invalid_int_number		DB	10,13,"Bart board IRQ/Interrupt number "
				DB	      "not specified correctly$"

eeprom_checksum_error	DB	10,13,"EEPROM failed checksum$"

Board_Not_Installed		DB	10,13,"Bart Board not found$"

irq_config_error		DB	10,13,"IRQ selection is for 16 bit slot "
				DB	      "only.$"

code	ends

	end
[ RETURN TO DIRECTORY ]