t7231_version	equ	2
  ifndef DAN
DAN	equ	4			; 4 seems to work best.
  endif

;
; Code that is common between T7231 implementations.
;

; Ported from Tim Krauskopf's micnet.asm, an assembly language
; driver for the MICOM-Interlan NI5210, by Russell Nelson.  Any bugs
; are due to Russell Nelson.
; Updated to version 1.08 Feb. 17, 1989 by Russell Nelson.
; Updated to support 1500 byte MTU April 27, 1989 By Brad Clements.
; copied and modified to support t7231 LanPACER controller by AT&T
; June 1, 1991 by Mark Darby.
; Updated to correct problems with higher speed CPU systems 
; by Mark Darby - NCR, October 12, 1993

; Copyright, 1988-90, 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.

;
;  Structure elements specific to the AT&T T7231 chip
;

; System Configuration Pointer
scp_struc	struc
scp_bus		db	?,?,?, ?,?,?	; bus use flag (0=16 bit, 1=8 bit).
scp_ptr		dd	?		; 24pointer to iscp
scp_struc	ends

; Intermediate System Configuration Pointer
iscp_struc	struc
iscp_busy	db	?,?		; busy flag (zeroed after init).
iscp_offset	dw	?		; 16pointer to iscp
iscp_base	dd	?		; base for all 16 pointers, lo, hi
iscp_struc	ends

; System Control Block
scb_struc	struc
scb_stat	dw	?		; status
scb_com		dw	?		; command
scb_cbl		dw	?		; 16pointer to command block list
scb_rfa		dw	?		; 16pointer to receive frame list
scb_serrs	dw	4 dup(?)	; 4 words of error counts
scb_struc	ends

; (Received) Frame Descriptor
fd_struc	struc
fd_status	dw	?		; status word for frame
fd_eol		dw	?		; end of FD list flag
fd_link		dw	?		; 16pointer to next FD
fd_ptr		dw	?		; 16pointer to list of RBD's
fd_dest		db	EADDR_LEN dup(?); 48 bits of destination
fd_source	db	EADDR_LEN dup(?); 48 bits of source
fd_cnt		dw	?		; length field of frame.
fd_struc	ends

; Receive Buffer Descriptor
rbd_struc	struc
rbd_status	dw	?		; status word in RBD
rbd_link	dw	?		; 16pointer to next RBD
rbd_ptr		dd	?		; 24pointer to actual buffer
rbd_size	dw	?		; size of the buffer
rbd_struc	ends

; Transmit Command Block
tcb_struc	struc
tcb_status	dw	?		; status word for xmit
tcb_com		dw	?		; command to transmit
tcb_link	dw	?		; 16pointer to next command
tcb_ptr		dw	?		; 16pointer to xmit TBD
tcb_addr	db	EADDR_LEN dup(?); destination address
tcb_len		dw	?
tcb_struc	ends

; Transmit Buffer Descriptor
tbd_struc	struc
tbd_status	dw	?		; bit 15=EOF, 13-0=actual count
tbd_link	dw	?		; 16pointer to next TBD
tbd_ptr		dd	?		; 24pointer to buffer
tbd_struc	ends

; all commands have at least the following:
cmd_struc	struc
cmd_status	dw	?		; status word
cmd_com		dw	?		; command word.
cmd_struc	ends

; MC-SETUP Command Block
mcb_struc	struc
mcb_status	dw	?		; status word for multicast
mcb_com		dw	?		; command to setup multicast
mcb_link	dw	?		; 16pointer to next command
mcb_cnt		dw	?		; number of multicast addresses.
mcb_struc	ends


;Memory allocation.

;SCPTR	EQU	0fff6h			; hardwired address for SCP
;ISCPTR	EQU	0ffeeh			; my address for ISCP, points to SCB
SCPTR	EQU	03ff6h			; hardwired address for SCP
ISCPTR	EQU	03feeh			; my address for ISCP, points to SCB
SCB	EQU	ISCPTR - 16		; system control block base
CCBPTR	EQU	SCB - 18		; offset of configure command block
TBDPTR	EQU	CCBPTR - 8		; xmit BD offset
TCBPTR	EQU	TBDPTR - 16		; xmit CB offset
TBUFPTR	EQU	TCBPTR - GIANT		; xmit buffer offset
;the receive buffers appear at lower addresses than TBUFPTR.
RBUFLEN	EQU	200
RBUF_TOTAL	equ	(size fd_struc) + (size rbd_struc) + RBUFLEN
FDBASE		equ	TBUFPTR - RBUF_TOTAL

memory_begin	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

firstfd		dw	FDBASE		; start of FD queue
lastfd		dw	0		; end of the FD chain
lastbd		dw	0		; end of the BD chain
flag		dw	0


;
; Configuration block for 82586, this comprises one config command
;  Parameters taken from MICOM driver
;
CBCONF	DW	0		; status word
	DW	8002H		; end of command list + configure command
	DW	0ffffh		; link to next command (not used)
	DW	080CH		; fifo=8, byte count=C
	DW	2E00H		; important! Addr (AL) not inserted on the fly!
	DW	6000H		; IFS = 60h
	DW	0F200H		; retry=F, slot time=200h
CBCONF_FLAGS	label	byte
	DW	0		; flags, set to 1 for promiscuous
CBCONF_MINLEN	label	byte
	DW	40H		; min frame length=40h


doca_wait:
;enter with ax = command to execute, es = base_addr.
;exit with nc if the command ran to completion.
;exit with cy if the command timed out.  Eventually we'll also reset the chip.
	mov	es:[SCB].scb_com,ax	;set the command.

	mov	si,es:[SCB].scb_cbl	;
	mov	es:[si].cmd_status,0	; status word of specific command
	and	ax,0700h
	cmp	ax,0100h		; is it an action command?
	jne	doca_wait_a		; no, any other

	call	doca

comment \
Quoting from the D-Step Errata Revision 2.0:

The value for the deadman timer should be greater than the longest
command execution time.  The command which can take the longest time
to execute is the transmit command, assuming maximum retries.  To
determine the maximum amount of time the transmit command may take,
one must use the following equation: 7143 ST + 14 f, where ST stands
for Slot Time and f = Maximum Frame Size + IFS + Preamble.  For
Ethernet/IEEE 802.3 where ST = 512 bits, f = 12144 bits, Preamble =
64 bits, IFS  96 bits, and one bit = 0.1 usec, the deadman timeout
should be greater than 0.369 seconds.

\

	mov	ax,14			;36.4 ticks / seconds * .369 seconds
	call	set_timeout
doca_wait_1:
	test	es:[si].cmd_status,8000h	; is command complete?
	jnz	doca_wait_2		;yes.
	call	do_timeout		;did we time out yet?
	jne	doca_wait_1		;not yet.
;reset the chip here, then Configure, IA-Setup, and MC-Setup.
	stc				;timeout -- uh-oh.
	ret
doca_wait_2:
	clc
	ret

doca_wait_a:
	call	doca
doca_wait_a_0:
	cmp	es:[SCB].scb_com,0	; has the command been accepted?
	jnz	doca_wait_a_0		; not yet.
	clc
	ret


	include	timeout.asm
	include	movemem.asm

	public bad_command_intercept
bad_command_intercept:
;called with ah=command, unknown to the skeleton.
;exit with nc if okay, cy, dh=error if not.
	mov	dh,BAD_COMMAND
	stc
	ret

	public	as_send_pkt
; The Asynchronous Transmit Packet routine.
; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
;   interrupts possibly enabled.
; Exit with nc if ok, or else cy if error, dh set to error number.
;   es:di and interrupt enable flag preserved on exit.
as_send_pkt:
	ret

	public	drop_pkt
; Drop a packet from the queue.
; Enter with es:di -> iocb.
drop_pkt:
	assume	ds:nothing
	ret

	public	xmit
; Process a transmit interrupt with the least possible latency to achieve
;   back-to-back packet transmissions.
; May only use ax and dx.
xmit:
	assume	ds:nothing
	ret


	public	send_pkt
send_pkt:
;enter with es:di->upcall routine, (0:0) if no upcall is desired.
;  (only if the high-performance bit is set in driver_function)
;enter with ds:si -> packet, cx = packet length.
;exit with nc if ok, or else cy if error, dh set to error number.
	assume	ds:nothing
	mov	es,base_addr		; base for board

	cmp	cx,GIANT		; Is this packet too large?
	ja	send_pkt_toobig

	mov	dx,cx			; save a copy, might be less than 60, ok

	cmp	dx,RUNT			; minimum length for Ether
	jnb	oklen
	mov	dx,RUNT			; make sure size at least RUNT
oklen:
	mov	di,TBUFPTR		; start of xmit buffer

;
;  check for previous xmit
;
xwait:
	mov	bx,es:[SCB].scb_com	; has previous command been accepted?
	or	bx,bx
	jnz	xwait			; not there yet, wait for it
wait_for_transmit_to_complete:
	test	word ptr es:[TCBPTR],4000h
	jnz	wait_for_transmit_to_complete
;
;  move the data using word moves.
;
	call	movemem
;
;  put the correct size into the TBD
;
	or	dx,08000h			; end of frame bit flag
	mov	es:[TBDPTR].tbd_status,dx	; store it
;	mov	es:[TCBPTR].tcb_status,0	; zero status wd
	mov	es:[TCBPTR].tcb_com,8004h	; xmit command in TCB
;	mov	es:[SCB].scb_com,0100h		; execute command
	mov	es:[SCB].scb_cbl,TCBPTR	; say where xmit command is

	mov	ax,100h
	call	doca_wait

	clc
	ret
send_pkt_toobig:
	mov	dh,NO_SPACE
	stc
	ret


rcv_mode_2:
	and	CBCONF_FLAGS,not 3
	or	CBCONF_FLAGS,2		;disable broadcasts.
	mov	CBCONF_MINLEN,40h
	jmp	short reconfigure
rcv_mode_4:
rcv_mode_3:
	and	CBCONF_FLAGS,not 3	;clear promiscuous mode.
	mov	CBCONF_MINLEN,40h
	jmp	short reconfigure
rcv_mode_6:
	and	CBCONF_FLAGS,not 3
	or	CBCONF_FLAGS,1		;set promiscuous mode.
	mov	CBCONF_MINLEN,0		;allow runts.
reconfigure:
	mov	es,base_addr		;get the base address for the board.
	mov	si,offset CBCONF	; configure command
	mov	di,CCBPTR		; where command will reside
	mov	cx,9
	rep	movsw			; copy to board
;
;  issue the configure command
;
	mov	es:[SCB].scb_cbl,CCBPTR	; where conf command is
	mov	es:[SCB].scb_serrs[0],0	; zero errs field
	mov	es:[SCB].scb_serrs[2],0	; zero errs field
	mov	es:[SCB].scb_serrs[4],0	; zero errs field
	mov	es:[SCB].scb_serrs[6],0	; zero errs field
	mov	ax,100h			; do-command command
	call	doca_wait
	ret


	public	set_multicast_list
set_multicast_list:
;enter with ds:si ->list of multicast addresses, ax = number of addresses,
;  cx = number of bytes.
;return nc if we set all of them, or cy,dh=error if we didn't.
	assume	ds:code
	mov	es,base_addr
	mov	es:[SCB].scb_cbl,TBUFPTR	;use the transmit buffer.

	mov	es:[TBUFPTR].mcb_status,0	;status word
	mov	es:[TBUFPTR].mcb_com,08003h	;command word for mc-setup + EL
	mov	es:[TBUFPTR].mcb_link,-1	;no command link.
	mov	di,TBUFPTR + mcb_cnt
	mov	ax,cx			;store the count.
	stosw
	rep	movsb

comment \ avoid deferred execution of a CU command during reception.
If a command is executed with a length of 18 to 20 bytes, and a frame
is received, the 82586 may deadlock with HOLD active.  We avoid this
problem by suspending the receiver. \

	mov	ax,30h			;suspend frame receiving.
	call	doca_wait

	mov	ax,100h			; do-command command
	call	doca_wait
	jnc	set_multicast_2

	mov	ax,20h			;resume frame receiving.
	call	doca_wait

	mov	dh,NO_MULTICAST		;for some reason we can't do multi's.
	stc
	ret
set_multicast_2:
	mov	ax,20h			;resume frame receiving.
	call	doca_wait

	clc
	ret


	public	terminate
terminate:
	assume	ds:code
	ret

	public	reset_interface
reset_interface:
;reset the interface.
;we don't do anything.
	assume	ds:nothing
retpoint:
	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

	public	recv
recv:
  ifdef IO_INTCLR
	loadport			;clear the interupt latch.
	setport	IO_INTCLR
	out	dx,al
  endif

	mov	flag, 1
;called from the recv isr.  All registers have been saved, and ds=cs.
;Upon exit, the interrupt will be acknowledged.
recv1:
	mov	ds,base_addr	; base for board
	assume	ds:nothing

	mov	ax,ds:[SCB].scb_stat	;get the status.
recv_isr_1:
	cmp	ds:[SCB].scb_com,0	;has previous command been accepted?
	jne	recv_isr_1		;no -- keep waiting.

	and	ax,0f000h		;isolate the ACK bits to make a
					;  command to ack the interrupt.
  if DAN and 1
	jz	recv_isr_2
  endif

	mov	ds:[SCB].scb_com,ax	;set the command.
	call	doca
recv_isr_2:
	cmp	ds:[SCB].scb_com,0	; has the command been accepted?
	jnz	recv_isr_2		; not yet.

;  Get whatever packets are on the board
;
	mov	bx,firstfd	; get addr of first FD in list
	mov	ax,[bx].fd_status	; status word of frame
	test	ax,08000h	; frame written?
	jnz	okframe

	jmp	ru_start	; no, restore receiver if necessary
frame_bad:
	call	count_in_err
ptrupdate_j_1:
	jmp	ptrupdate

;  we have a frame, read it in
;
okframe:
	test	ax,02000h		;check frame OK bit
	jz	frame_bad		;bad, fix it.
	mov	si,[bx].fd_ptr		;get pointer to buffer descriptor
	xor	cx,cx			;start with zero bytes.
countbuf:				;es:di is already set to receive packet
	mov	dx,si			;save a copy of current BD ptr
	mov	ax,[si].rbd_status	;get status and count word for BD
	test	ax,04000h		;is count field there?
	jz	ptrupdate_j_1		;no - we give up here.
	add	cl,al			;add the count into cx.
	adc	ch,0
	mov	si,[si].rbd_link	;go to next BD in list
	test	ax,8000h		;is this the last frame?
	je	countbuf		;no - keep counting.

	push	bx
	push	cx

	mov	ax,cs			;we need ds = code.
	mov	ds,ax
	assume	ds:code

	mov	es,base_addr		;get a pointer to their type.
	mov	di,es:[bx].fd_ptr	;get pointer to buffer descriptor
	mov	di,es:[di].rbd_ptr.offs	;get offset of data
	add	di,EADDR_LEN+EADDR_LEN	;skip the ethernet addreses and
					;  point to the packet type.

	mov	dl, BLUEBOOK		;assume bluebook Ethernet.
	mov	ax, es:[di]
	xchg	ah, al
	cmp 	ax, 1500
	ja	BlueBookPacket
	inc	di			;set di to 802.2 header
	inc	di
	mov	dl, IEEE8023
BlueBookPacket:
	call	recv_find		;look up our type.

	pop	cx
	pop	bx
	mov	ds,base_addr		;restore ds to the board.
	assume	ds:nothing

	mov	ax,es			;is this pointer null?
	or	ax,di
	je	ptrupdate		;yes - just free the frame.

	push	cx
	push	es			;remember where the buffer pointer is.
	push	di
  if DAN and 2
	push	cx
  endif

	mov	si,[bx].fd_ptr		;get pointer to buffer descriptor
copybuf:
	mov	dx,si			;save a copy of current BD ptr
	xor	ch,ch			;200 bytes is largest this can be
	mov	cl,byte ptr [si].rbd_status;get count word for BD
	mov	si,[si].rbd_ptr.offs	;get offset of data
  if DAN and 2
	pop	ax
	sub	ax, cx
	jc	copydone
	push	ax
  ENDIf
	call	movemem
	mov	si,dx			;get back current BD ptr
	test	[si].rbd_status,8000h	;check EOF bit
	mov	si,[si].rbd_link	;go to next BD in list
	jz	copybuf			;not done, keep copying it.

  if DAN and 2
	pop	cx
copydone:
  endif
	pop	si			;now give the frame to the client.
	pop	ds
	pop	cx
	assume	ds:nothing

	call	recv_copy
;
;  we are done with the frame, do the list management
;
ptrupdate:
	movseg	ds,cs
	assume	ds:code
	mov	es,base_addr		; reload board segment

	mov	si,es:[bx].fd_ptr	; first BD in frame list
nextbd:
	mov	cx,es:[si].rbd_status	; count word for BD, EOF bit
	test	cx,08000h		; EOF bit, if set, save si in lastbd
	jnz	dolastbd
	mov	es:[si].rbd_status,0	; clear status word, EOF bit
	cmp	si,lastbd		; see if we are wrapping
	jz	dolastbd		; yes, just undo it
	mov	si,es:[si].rbd_link	; follow link
	jmp	nextbd
dolastbd:
	mov	di,lastbd		; where end of BD list is now
	mov	lastbd,si		; store last known BD
	mov	es:[si].rbd_size,08000h+200; end of list here
	mov	es:[si].rbd_status,0	; clear status word, EOF bit
; size field for not end of list
	mov	es:[di].rbd_size,200	; remove old end-of-list

;
;  update the FD list flags, new end-of-list
;
	mov	es:[bx].fd_eol,08000h	; store new EOL
	mov	es:[bx].fd_status,0	; clear status word for frame
	mov	di,lastfd		; get old end-of-list
	mov	es:[di].fd_eol,0	; zero old one
	mov	lastfd,bx		; update stored pointer
	mov	si,es:[bx].fd_link	; where next fd is
	mov	firstfd,si		; store that info for next time
  if DAN and 4
	jmp	recv1
  endif

ru_start:
; re-start receive unit
;
;  check to see if the receiver went off because of no resources
;  and restart receiver if necessary
;
	movseg	ds,cs
	mov	es,base_addr
	mov	ax,es:[SCB].scb_stat	; status word for SCB
	and	ax,070h		; receiver status
	cmp	al,020h		; receiver has no resources
	jnz	hasres
  if DAN and 8
	cmp	flag, 1
	jnz	ru_start1
	mov	flag, 0
	jmp	recv1
  endif

ru_start1:
	call	count_out_err
;
;  setup lists for starting the RU on the chip
;  we know that there isn't anything in the buffer that we want
;

	mov	bx,firstfd		; get first FD on free list (assume free)
	mov	es:[SCB].scb_rfa,bx	; put into SCB
	mov	si,lastbd		; pointer to a BD, end of chain
	mov	ax,es:[si].rbd_link	; pointer to next BD
	mov	es:[bx].fd_ptr,ax	; set to start of BDs
;
;
;  Start the RU, doesn't need CB, only SCB parms.
;   command, to start receiving again
;
	mov	ax,10h			; start RU
	call	doca_wait
hasres:
;I don't we need to wait here because we haven't done anything to wait for.
	ret


	public	set_address
set_address:
	assume	ds:nothing
;enter with ds:si -> Ethernet address, CX = length of address.
;exit with nc if okay, or cy, dh=error if any errors.
	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:

;  Next step, load our address into the board
;     reuses the space that the configure command used, with different command
;
	mov	es,base_addr		; set to base address
	mov	es:[SCB].scb_cbl,CCBPTR	; say where conf command is

	mov	di,CCBPTR		; start of config command block
	xor	ax,ax
	stosw				; zero status word for commmand
	mov	ax,8001h		; IA setup command + EL
	stosw
	xor	ax,ax
	dec	ax
	stosw				; set link value to -1 (unused)

	rep	movsb			; move their ethernet address in.
;
;  start the IA setup command
;
	mov	ax,100h			; do-command command
	call	doca_wait
	jnc	set_address_okay
	mov	dh,-1			; no error in the list applies.
	jmp	short set_address_done
set_address_okay:
	mov	cx,EADDR_LEN		;return their address length.
	clc
set_address_done:
	movseg	ds,cs
	assume	ds:code
	ret


	public	timer_isr
timer_isr:
;if the first instruction is an iret, then the timer is not hooked
	iret

;any code after this will not be kept.  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

timeout_msg	db	"Timed out while initializing the board.",CR,LF,'$'
addr_bad_msg	db	"Memory address should be less than 65536.",CR,LF,'$'

security_msg	label	byte
db "Security is enabled.  Do not attempt to change the ethernet address or",CR,LF
db "set promiscuous mode on this board.  Unexpected results may occur.",CR,LF
db '$'

mem8_16		db	2		; 1 for 16k, 2 for 8k

	extrn	set_recv_isr: near
	extrn	maskint: near

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

;enter with dx -> name of word, di -> dword to print.
	extrn	print_number: near

;enter with ax = number to print.
	extrn	decout: near

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

timeout_error:
	mov	dx,offset timeout_msg
	jmp	short error
error:
	mov	ah,9
	int	21h
	stc
	ret

;
;  data for configuring and setting up the board
;
;  chip always looks at SCP for config info which points to ISCP for the
;  pointer to the CONTROL BLOCK which handles everything from there.
;  Kind of indirect, but it works.
;
SCP	DB	0			; bus use flag (0=16 bit, 1=8 bit).

	public	etopen
etopen:
	mov	al,int_no
	call	maskint			;disable these interrupts.

	cmp	base_addr.offs,0	;low word of segment can't be zero.
	je	etopen_1
	cmp	base_addr.segm,0	;high word of segment must be zero.
	je	etopen_2
etopen_1:
	mov	dx,offset addr_bad_msg
	stc
	ret
etopen_2:

;  Initialize the Ethernet board, set receive type.
;
	mov		es,base_addr
;
;	Check for the ATTLAN signature in shared memory
;
	mov		di,3ff9h
	cmp		byte ptr es:[di],'A'
	jnz		no_sig
	inc		di
	cmp		byte ptr es:[di],'T'
	jnz		no_sig
	inc		di
	cmp		byte ptr es:[di],'T'
	jnz		no_sig
	inc		di
	cmp		byte ptr es:[di],'L'
	jnz		no_sig
	inc		di
	cmp		byte ptr es:[di],'A'
	jnz		no_sig
	inc		di
	cmp		byte ptr es:[di],'N'
	jnz		no_sig
	mov		di,io_addr
	shr		di,1
	shr		di,1
	shr		di,1
	shr		di,1
	and		di,001fh
	mov		es:[3fffh],di
no_sig:

;
;  check for correct EPROM location
;
	call	check_board
	jnc	etopen_3
	ret
etopen_3:

;
;  Turn off interrupts, I don't want them
;
  ifdef IOINTOF
	loadport
	setport IOINTOF
	out	dx,al
  endif
;
;  Disconnect from network
;
  ifdef IODIS
	loadport
	setport	IODIS
	out	dx,al
  endif

;
;  Initialize the Ethernet board.
;
	mov	di,00000h		;our initial base address.
	mov	si,ISCPTR-2		;try the init down a little.

;
;  Now discern the end of memory by repeatedly re-initializing the board
;  until the BUSY flag in the ISCP gets reset.
;
re_discern:
	mov	es,base_addr		;remember where we think it starts.
	call	init_root		;did we find our memory size?
	jc	confbad			;no, keep trying.
	inc	si			;yes, see if we found the real one.
	inc	si
	call	init_root		;try initializing it in a different locn.
	jnc	confok			;it worked!  we've found the root.
confbad:
	sti
	jmp	timeout_error

confok:
	mov	memory_begin,di
	call	reconfigure
	jc	confbad
;  
;  We have to determine if security is enabled on the board.  This is done by
;  attempting to set the board into promiscuous mode (rcv_mode_6).  We will
;  then check to see if the "Command Abort" bit has been set.  If it has, then
;  security is enabled and we will notify the user.
;
	call	rcv_mode_6
	jc		confbad
	test	es:[CCBPTR].cmd_status,01000h
	jz		no_security
	mov		dx,offset security_msg
	mov		ah,9
	int		21h
	mov		word ptr rcv_modes+14,0000h
no_security:
	call	rcv_mode_3
	jc		confbad
;
;  Ask the board for the Ethernet address, and then use set_address to set it.
;
	movseg	es,ds
	mov	di,offset rom_address
	mov	cx,EADDR_LEN
	call	get_address

	mov	si,offset rom_address
	mov	cx,EADDR_LEN
	call	set_address
	jnc	store_address_2
	sti
	jmp	timeout_error
store_address_2:
;
;  IA sent, setup all of the other data structures on the board
;  start with xmit command descriptors
;
	mov	di,TCBPTR
	mov	es:[di].tcb_status,0
	mov	es:[di].tcb_com,08004h
	mov	es:[di].tcb_link,-1
	mov	es:[di].tcb_ptr,TBDPTR

	add	di,(size tcb_struc)

	mov	es:[di].tbd_status,0
	mov	es:[di].tbd_link,0
	mov	es:[di].tbd_ptr.offs,TBUFPTR
	mov	es:[di].tbd_ptr.segm,0

; Note that we allocate fd's, rbd's, and buffers all at the same time.  This
; doesn't mean that each pair of fd's and rbd's necessarily have anything to
; do with each other.  We just allocate them together because we want to have
; the same number of each, and it's easier to compute that way.

	mov	di,TBUFPTR		;get the last buffer.

	mov	ax,di			;compute the amount of free memory.
	sub	ax,memory_begin
	xor	dx,dx
	mov	bx,RBUF_TOTAL		;each buffer takes this much.
	div	bx
	mov	cx,ax			;put the number of buffers into cx.

init_rbuff_0:
	sub	di,RBUF_TOTAL		;back the pointer down by a little.

;init the FD.
	mov	es:[di].fd_status,0
	mov	es:[di].fd_eol,0
	mov	es:[di].fd_ptr,-1
	lea	ax,[di]-RBUF_TOTAL	;get the address of the next buffer.
	mov	es:[di].fd_link,ax

;init the BD.
	lea	bx,[di + (size fd_struc)]
	mov	es:[bx].rbd_status,0
	lea	ax,[bx-RBUF_TOTAL]	;make a pointer to the next BD
	mov	es:[bx].rbd_link,ax
	lea	ax,[bx+(size rbd_struc)]	;make a pointer to the buffer.
	mov	es:[bx].rbd_ptr.offs,ax
	mov	es:[bx].rbd_ptr.segm,0
	mov	es:[bx].rbd_size,RBUFLEN	;length of the buffer.

	loop	init_rbuff_0

init_rbuff_1:
;patch the parameters of the last FD and BD so they link around to the head.
	mov	es:[di].fd_eol,8000h
	mov	es:[di].fd_link,FDBASE
	mov	lastfd,di

	lea	bx,[di + (size fd_struc)]
	mov	es:[bx].rbd_link,FDBASE + (size fd_struc)
	mov	es:[bx].rbd_size,RBUFLEN + 8000h
	mov	lastbd,bx

;now put the location of the first rbd into the first fd.
	mov	es:[FDBASE].fd_ptr,FDBASE  + (size fd_struc)

	call	enable_network

;
;  Start the RU, doesn't need CB, only SCB parms.
;   command, to start receiving
;
	mov	es:[SCB].scb_rfa,FDBASE	; set to frame descriptors
	mov	ax,10h			; start RU
	call	doca_wait
;
; Now reset CX, FR, CNA, and RNR so that we don't get a spurious interrupt.
;
	mov	ax,es:[SCB].scb_stat	;get the status.
	and	ax,0f000h		;isolate the ACK bits.
	mov	es:[SCB].scb_com,ax	;make a command to
					;acknowledge the interrupt.
	call	doca
;
; Now hook in our interrupt
;
	call	set_recv_isr

	sti

	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


init_root:
;enter with es:di -> beginning of our system memory window,
;  si -> place to put ISC.
;exit with nc if it worked, cy if it didn't work.

	mov	al,SCP
	mov	es:[SCPTR].scp_bus,al
	mov	es:[SCPTR].scp_ptr.offs,si
	mov	es:[SCPTR].scp_ptr.segm,0

	mov	es:[si].iscp_busy,1		;set busy bit.
	mov	es:[si].iscp_offset,SCB		;point to the SCB.
	mov	es:[si].iscp_base.offs,0	;scb base.
	mov	es:[si].iscp_base.segm,0	;scb base.

	call	reset_586		; reset the 586, hardware-specific.
	push	ax
	in	al, 61h
	in	al, 61h
	in	al, 61h
	pop	ax

	mov		al,int_no
	cmp		al,2
	jnz		int_not2
	mov		al,9
int_not2:
	loadport
	setport	IOINTR
	out		dx,al

	mov		al,08h
	mov		ah,media_sel
	or 		ah,ah
	jz		only_aui
	add		al,04h
	mov		ah,li_enabl
	or		ah,ah
	jz		tp_noli
	add		al,02h

tp_noli:
only_aui:
	loadport
	setport IOMISC
	out dx,al

;	mov		ax,1
;	call	set_timeout
;time_delay:
;	call	do_timeout
;	jne		time_delay
	push	ax
	in	al, 61h
	in	al, 61h
	in	al, 61h
	pop	ax

;
;  Issue a CA to initialize the chip after reset
;
	call	doca
	push	ax
	in	al, 61h
	in	al, 61h
	in	al, 61h
	pop	ax

;	mov	ax,2			;don't wait too long.
;	call	set_timeout
;init_root_2:
	cmp	es:[si].iscp_busy,0	;did it clear the busy flag?
	je	init_root_1		;yes.
;	call	do_timeout
;	jne	init_root_2
	stc
	ret
init_root_1:
	clc
	ret
