Metropoli BBS
VIEWER: de600.asm MODE: TEXT (ASCII)
PAGE  ,132
   .286c
version equ     0

	include defs.asm        ;SEE ENCLOSED COPYRIGHT MESSAGE

;/* PC/FTP Packet Driver source, conforming to version 1.09 of the spec
;*  Portions (C) Copyright 1990 D-Link, Inc.
;*
;*  Permission is granted to any individual or institution to use, copy,
;*  modify, or redistribute this software and its documentation provided
;*  this notice and the copyright notices are retained.  This software may
;*  not be distributed for profit, either in original form or in derivative
;*  works.  D-Link, inc. makes no representations about the suitability
;*  of this software for any purpose.  D-LINK GIVES NO WARRANTY,
;*  EITHER EXPRESS OR IMPLIED, FOR THE PROGRAM AND/OR DOCUMENTATION
;*  PROVIDED, INCLUDING, WITHOUT LIMITATION, WARRANTY OF MERCHANTABILITY
;*  AND WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE.
;*/

BIT0            EQU     01H
BIT1            EQU     02H
BIT2            EQU     04H
BIT3            EQU     08H
BIT4            EQU     10H
BIT5            EQU     20H
BIT6            EQU     40H
BIT7            EQU     80H

code    segment byte public
	assume  cs:code, ds:code

; DE-600's I/O port Table
DAT             equ     0
STAT            equ     1
CMD             equ     2

; DE-600's DATA port Command
WRITE           equ     0004h   ;write memory
READ            equ     0104h   ;read  memory
STATUS          equ     0204h   ;read  status register
COMMAND         equ     0304h   ;write command register
NUL_CMD         equ     0ch     ;null command
RX_LEN          equ     0504h   ;read  Rx packet length
TX_ADR          equ     0604h   ;write Tx address
RW_ADR          equ     0704h   ;write memory address

;< COMMAND   bits 7-0 >
RXEN            equ     08h    ; bit 3
TXEN            equ     04h    ; bit 2
LOOPBACK        equ     0Ch    ; RXEN=1, TXEN=1
RX_NONE         equ     00h    ; M1=0, M0=0 (bit 1,0)
RX_ALL          equ     01h    ; M1=0, M0=1
RX_BP           equ     02h    ; M1=1, M0=0
RX_MBP          equ     03h    ; M1=1, M0=1
RESET           equ     80h    ; set bit 7 high
STOP_RESET      equ     00h    ; set bit 7 low
;  bit 6   -- IRQ inverse
;  bit 5,4 -- Rx Page Number  ( RA12=1, RA11=0 or 1 )

;< TX_ADR   bit 7, bit 4 >
;  bit 7   -- Tx Page Number  ( TA11=0 or 1 )
;  bit 4   -- Tx Page Number  ( TA12=0 )
PAGE0           equ     00h
PAGE1           equ     08h
PAGE2           equ     10h
PAGE3           equ     18h

;< RW_ADR   bit 7, bit 5,4 >
;  bit 7   -- RW Page Number  ( H11 =? )
;  bit 4   -- RW Page Number  ( H12 =? )
;  bit 5   -- Address Maping  ( HA13=0 => Memory, HA13=1 => Node Number )
HA13            equ     020h

; DE-600's CMD port Command
SLT_NIC         equ     004h  ;select Network Interface Card
SLT_PRN         equ     01Ch  ;select Printer
NML_PRN         equ     0ECh  ;normal Printer situation
IRQEN           equ     010h  ;enable IRQ line

; DE-600's STAT port bits 7-4
RXBUSY          equ     80h
GOOD            equ     40h
RESET_FLAG      equ     20h
T16             equ     10h
TXBUSY          equ     08h

BFRSIZ          equ     2048    ;number of bytes in a buffer
PRNTABADD       equ     408h    ;DOS printer table address
RX_MIN_LEN      equ     18      ;= EADDR_LEN + EADDR_LEN + TYPE_LEN + CRC

write_sub_delay	macro	reg
	mov     al,reg			;output the low nibble.
	shl     al,cl			;cl must be four.
	or      al,ch
	xor     al,08h			;raise the write line.
	out     dx,al
	call    delay

	xor     al,08h			;lower the write line
	out     dx,al
	call    delay

	mov     al,reg			;output the high nibble.
	and     al,not 0fh		;get us some zero bits.
	or      al,ch
	out     dx,al			;(write line is low).
	call    delay

	xor     al,08h			;raise the write line.
	out     dx,al
	endm

write_sub_fast	macro	reg
	mov     al,reg			;output the low nibble.
	shl     al,cl			;cl must be four.
	or      al,ch
	out     dx,al

	mov     al,reg			;output the high nibble.
	and     al,not 0fh		;get us some zero bits.
	or      al,ch
	xor     al,08h			;raise the write line.
	out     dx,al			;(write line is low).
	endm

write_sub_slow	macro	reg
	mov     al,reg			;output the low nibble.
	shl     al,cl			;cl must be four.
	or      al,ch
	out     dx,al
	call	delay

	mov     al,reg			;output the high nibble.
	and     al,not 0fh		;get us some zero bits.
	or      al,ch
	xor     al,08h			;raise the write line.
	out     dx,al			;(write line is low).
	endm

read_sub_fast	macro	reg
	setport DAT
	mov     al,ch
	out     dx,al
	pause

	setport STAT
	in      al,dx
	mov     reg,al
	setport DAT
	mov     al,ch
	xor     al,08h
	out     dx,al
	pause

	setport STAT
	in      al,dx
	shr     reg,cl
	and     al,0f0h
	or      reg,al
	endm


read_sub_slow	macro	reg
	setport DAT
	mov     al,ch
	out     dx,al
	call    delay

	setport STAT
	in      al,dx
	mov     reg,al
	setport DAT
	mov     al,ch
	xor     al,08h
	out     dx,al
	call    delay

	setport STAT
	in      al,dx
	shr     reg,cl
	and     al,0f0h
	or      reg,al
	endm


read_sub_delay	macro	reg, first
	setport DAT
	mov     al,ch
  if first
	xor     al,08h
  endif
	out     dx,al
	call    delay

  if first
	xor     al,08h
	out     dx,al
	call    delay
  endif

	setport STAT
	in      al,dx
	mov     reg,al
	setport DAT
	mov     al,ch
	xor     al,08h
	out     dx,al
	call    delay

	setport STAT
	in      al,dx
	shr     reg,cl
	and     al,0f0h
	or      reg,al
	endm


pause	macro
	jmp	$+2
	endm

	public  int_no
int_no          db      7,0,0,0         ; IRQ interrupt number
io_addr         dw      03bch,0         ; I/O address for card (jumpers)

	public	driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class    db      BLUEBOOK, IEEE8023, 0 ;from the packet spec
driver_type     db      31              ;from the packet spec
driver_name     db      'DE600',0      ;name of the driver
driver_function	db	2
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,

	public	rcv_modes
rcv_modes	dw	7		;number of receive modes in our table.
		dw	0               ;There is no mode zero
		dw	rcv_mode_1
		dw	0
		dw	rcv_mode_3
		dw	0
		dw	rcv_mode_5
		dw	rcv_mode_6

CurTxPage       db      08h             ;the BL value when OUT DATA,TX_ADR
CurRxPage       db      20h             ;the BL value when OUT DATA,COMMAND
TxStartAdd      dw      ?
RxStartAdd      dw      ?
InitRxTxReg     dw      ?
RxPktLen        dw      ?
TxPktLen        dw      ?
Mode_RxPg       db      ?
Mode            db      RX_BP
IRQinverse      db      0               ; = 40h for XT printer adapter
NICstatus       db      0
In_ISR          db      0
In_Tx           db      0
printer         dw      408h
PS2             db      0

our_type        dw      ?
our_address     db      EADDR_LEN + EADDR_LEN dup(0) ;temporarily hold our address


	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 ds:si -> packet, cx = packet length.
;exit with nc if ok, or else cy if error, dh set to error number.
	assume  ds:nothing
;select DE-600
	mov     al,SLT_NIC
;*** CMD sub ***
	loadport
	setport CMD
	out     dx,al
;*** End CMD sub ***
	mov     In_Tx,1
	call    delay

	cmp     cx,RUNT         ; minimum length for Ether
	jae     LengthOK
	mov     cx,RUNT         ; make sure size at least RUNT
LengthOK:
	inc     cx
	and     cx,not 1

	mov     di,cx
;change Tx page to another free buffer
	xor     CurTxPage,08h

	mov     bx,offset send_pkt_pointer
	jmp     cs:[bx]

send_pkt_pointer        dw      offset send_pkt0

send_pkt0:
;set Tx Pointer for moving packet
	mov     ax,BFRSIZ
	sub     ax,cx           ;AX = the pointer to TX
	or      ah,CurTxPage
	mov     TxStartAdd,ax   ;save Current Tx Packet Start Address
	mov     bx,ax           ;write memory address
	mov     cx,RW_ADR
	setport DAT
	write_sub_fast	bl
	write_sub_fast	bh
	cld
	mov     cx,WRITE        ;write packet into memory
	mov     ah,ch
	xor     ah,08h
write_mem:
	lodsb
	mov     bl,al
	shl     al,cl
	or      al,ch
	out     dx,al

	mov     al,bl
	and     al,0f0h
	or      al,ah
	dec     di
	out     dx,al
	jnz     write_mem

	mov     cx,4000h
	setport STAT
wait_Tx_idle:
	in      al,dx
	test    al,TXBUSY       ; Is the previous Tx successful ?
	jz      command_to_Tx   ; Yes, TXBUSY is low. Then could Tx next packet.
	loop    wait_Tx_idle

command_to_Tx:
;set Tx Pointer at beginning of packet
	mov     bx,TxStartAdd
	mov     cx,TX_ADR
	loadport
	setport DAT
	write_sub_fast	bl
	write_sub_fast	bh

;Enable interrupt and start Tx
	mov     bl,Mode_RxPg
	mov     cx,COMMAND
	write_sub_fast	bl
	or      bl,TXEN
	write_sub_fast	bl

Exit_Send_Packet:
	mov     In_Tx,0

	cmp     In_ISR,0
	jne     using_NIC_now
	mov     al,SLT_PRN
	loadport
	setport CMD
	out     dx,al

	cmp     PS2,0
	jnz     using_NIC_now
	setport STAT
	in      al,dx
	and     al,40h
	xor     al,IRQinverse
	jz      using_NIC_now
	call    trigger_int
using_NIC_now:
	clc
	ret


send_pkt1:
;set Tx Pointer for moving packet
	mov     ax,BFRSIZ
	sub     ax,cx           ;AX = the pointer to TX
	or      ah,CurTxPage
	mov     TxStartAdd,ax   ;save Current Tx Packet Start Address
	mov     bx,ax           ;write memory address
	mov     cx,RW_ADR
	loadport
	setport DAT
	write_sub_slow	bl
	call	delay
	write_sub_slow	bh

	cld
	mov     cx,WRITE        ;write packet into memory
	mov     ah,ch
	xor     ah,08h
write_mem1:
	lodsb
	mov     bl,al
	shl     al,cl
	or      al,ch
	out     dx,al
	call    delay

	mov     al,bl
	and     al,0f0h
	or      al,ah
	dec     di
	out     dx,al
	call    delay
	jnz     write_mem1

	mov     cx,4000h
	setport STAT
wait_Tx_idle1:
	in      al,dx
	test    al,TXBUSY       ; Is the previous Tx successful ?
	jz      command_to_Tx1  ; Yes, TXBUSY is low. Then could Tx next packet.
	loop    wait_Tx_idle1

command_to_Tx1:
;set Tx Pointer at beginning of packet
	mov     bx,TxStartAdd
	mov     cx,TX_ADR
	loadport
	setport DAT
	write_sub_slow	bl
	call	delay
	write_sub_slow	bh
	call    delay

;Enable interrupt and start Tx
	mov     bl,Mode_RxPg
	mov     cx,COMMAND
	write_sub_slow	bl
	call    delay
	or      bl,TXEN
	write_sub_slow	bl
	jmp     Exit_Send_Packet


send_pkt2:
;set Tx Pointer for moving packet
	mov     ax,BFRSIZ
	sub     ax,cx           ;AX = the pointer to TX
	or      ah,CurTxPage
	mov     TxStartAdd,ax   ;save Current Tx Packet Start Address
	mov     bx,ax           ;write memory address
	mov     cx,RW_ADR
	loadport
	setport DAT
	write_sub_delay	bl
	call	delay
	write_sub_delay	bh
	cld
	mov     cx,WRITE        ;write packet into memory
write_mem2:
	lodsb
	mov     bl,al			;except for this line,
	shl     al,cl			;  it's write_sub_delay
	or      al,ch
	xor     al,08h
	out     dx,al
	call    delay

	xor     al,08h
	out     dx,al
	call    delay

	mov     al,bl
	and     al,0f0h
	or      al,ch
	out     dx,al
	call    delay

	xor     al,08h
	out     dx,al
	call    delay
	dec     di
	jnz     write_mem2

	mov     cx,4000h
	setport STAT
wait_Tx_idle2:
	in      al,dx
	test    al,TXBUSY       ; Is the previous Tx successful ?
	jz      command_to_Tx2  ; Yes, TXBUSY is low. Then could Tx next packet.
	loop    wait_Tx_idle2

command_to_Tx2:
;set Tx Pointer at beginning of packet
	mov     bx,TxStartAdd
	mov     cx,TX_ADR
	loadport
	setport DAT
	write_sub_delay	bl
	call	delay
	write_sub_delay	bh
	call    delay

;Enable interrupt and start Tx
	mov     bl,Mode_RxPg
	mov     cx,COMMAND
	write_sub_delay	bl
	call    delay
	or      bl,TXEN
	write_sub_delay	bl
	jmp     Exit_Send_Packet


trigger_int:
	mov     al,SLT_NIC
	call    CMD_sub

	mov     bl,Mode_RxPg
	xor     bl,BIT6
	mov     cx,COMMAND
	call    Write_sub

	mov     al,SLT_PRN
	call    CMD_sub

	call    delay
	call    delay

	mov     al,SLT_NIC
	call    CMD_sub

	mov     bl,Mode_RxPg
	mov     cx,COMMAND
	call    Write_sub
	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.
	assume  ds:code
	cmp     cx,EADDR_LEN            ;make sure that we have enough room.
	jb      cant_get_address

;select DE-600
	cld
	mov     si,offset our_address
	rep     movsb
	mov     cx,EADDR_LEN
	clc
	ret
cant_get_address:
	stc
	ret


rcv_mode_1:
	mov     cl,RX_NONE
	jmp     short set_RXCR
rcv_mode_3:
	mov     cl,RX_BP
	jmp     short set_RxCR
rcv_mode_5:
	mov     cl,RX_MBP
	jmp     short set_RxCR
rcv_mode_6:
	mov     cl,RX_ALL
set_RxCR:
	mov     Mode,cl
	mov     bl,cl
	or      bl,CurRxPage            ; Add original Rx Page
	mov     Mode_RxPg,bl            ; Save Rx Mode & Rx Page
;select DE-600
	loadport
	setport CMD
	in      al,dx
	pause
	test    al,BIT4
	jz      in_IC_mode
;not active, we have to put a wrapper around it.
	mov     al,int_no
	call    maskint

	mov     al,SLT_NIC		;turn on the NIC.
	call    CMD_sub
	call	in_IC_mode		;now we're in the right mode.
	mov     al,SLT_PRN		;turn on the PRN.
	call    CMD_sub

	mov     al,int_no
	call    unmaskint
	ret

in_IC_mode:
	mov     cx,COMMAND              ; Set new Rx Mode
	call    Write_sub
	ret


	extrn   maskint : near
	extrn   unmaskint : near

	public  set_address
set_address:
;Set Ethernet address on controller
;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            ;make sure that we have enough room.
	je      can_set_address
	mov     dh,BAD_ADDRESS
	stc
	jmp     set_address_none

;select DE-600
can_set_address:
	loadport
	setport CMD
	in      al,dx
	push    ax
	test    al,BIT4
	jz      IC_mode

	mov     al,int_no
	call    maskint
	mov     al,SLT_NIC
	call    CMD_sub
IC_mode:
	mov     cx,RW_ADR
	xor     bl,bl
	call    Write_sub
	or      bl,HA13
	call    Write_sub

	cld
	mov     bp,cs
	mov     es,bp
	mov     bp,si

	mov     cx,EADDR_LEN
	mov     di,offset our_address
	rep     movsb

	mov     si,bp
	mov     di,EADDR_LEN
	mov     cx,WRITE
set_our_address:
	lodsb
	mov     bl,al
	call    Write_sub
	dec     di
	jnz     set_our_address

	pop     ax
	test    al,BIT4
	jz      IC_mode1

	mov     al,SLT_PRN
	call    CMD_sub
	mov     al,int_no
	call    unmaskint
IC_mode1:
	clc
set_address_none:
	push    cs
	pop     ds
	assume  ds:code
	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:
	ret


	public  reset_interface
reset_interface:
	mov     al,int_no
	call    maskint
;select DE-600

	mov     al,SLT_NIC
	call    CMD_sub

; Pulse IE_RESET
	mov     bl,RESET
	mov     cx,COMMAND
	call    Write_sub
	mov     bl,STOP_RESET
	call    Write_sub

; Initialize Rx buffer pointer, and start receive
	mov     bl,Mode_RxPg
	mov     cx,COMMAND
	call    Write_sub
	or      bl,RXEN
	call    Write_sub

; Enable Printer Adapter IRQ line
	mov     al,SLT_PRN
	call    CMD_sub

	mov     al,int_no
	call    unmaskint
	ret


	assume  ds:nothing
Write_sub:
	loadport
	setport DAT
	write_sub_delay	bl
	ret

Read_sub:
	loadport
	setport DAT
	mov     al,ch
	xor     al,08h
	out     dx,al
	call    delay

	xor     al,08h
	out     dx,al
	call    delay

	setport STAT
	in      al,dx
	mov     bl,al
	test    ch,BIT1
	jz      not_READ_STATUS
	dec     dx
	mov     al,NUL_CMD
	xor     al,08h
	out     dx,al
	call    delay

	xor     al,08h
	out     dx,al
	jmp     short End_read
not_READ_STATUS:
	setport DAT
	mov     al,ch
	xor     al,08h
	out     dx,al
	call    delay

	setport STAT
	in      al,dx
	shr     bl,cl
	and     al,0f0h
	or      bl,al
End_read:
	ret

CMD_sub:
	loadport
	setport CMD
	out     dx,al
	ret

delay:
	nop     ; pointer 0
	nop     ;         1
	nop     ;         2
	nop     ;         3
	nop     ;         4
	nop     ;         5
	nop     ;         6
	nop     ;         7
	nop     ;         8
	nop     ;         9
	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

recv_pointer    dw      offset recv0

	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
;select DE-600
	mov     al,SLT_NIC
;*** CMD sub ***
	loadport
	setport CMD
	out     dx,al
;*** End CMD sub ***
;set watch dog
	mov     In_ISR,1
	call    delay

;Check the interrupt source, Rx or Tx ?
	mov     cx,STATUS               ; Read NIC Status Register
;*** Read sub ***
	setport DAT
	mov     al,ch
	out     dx,al
	pause
	setport STAT
	in      al,dx
;*** End Read sub ***

	jmp	recv_pointer

recv0:
	mov     NICstatus,al            ; save NIC status
	setport DAT
	test    al,GOOD                 ; Is Rx generating interrupt ?
	mov     al,NUL_CMD
	out     dx,al
	jnz     Rx_Good_Pkt             ; Yes, take care of this situation.
	mov     al,NICstatus
	test    al,RXBUSY
	jz      Enable_Rx
	jmp     CheckTx

Enable_Rx:
;change Rx page & enable NIC to Rx
	mov     bl,Mode_RxPg
	mov     cx,COMMAND
	setport DAT
	write_sub_fast	bl
	or      bl,RXEN
	write_sub_fast	bl
	jmp     CheckTx

Rx_Good_Pkt:
;Put it on the receive queue
	mov     cx,RX_LEN               ; read Rx Packet Length
	loadport
	read_sub_fast	bl
	read_sub_fast	bh

	sub     bx,4                    ;subtrate 4 CRC Byte Count
	mov     RxPktLen,bx             ;save Rx Packet Length

;change Rx page & enable NIC to Rx
	xor     Mode_RxPg,10h
	mov     bl,Mode_RxPg
	mov     cx,COMMAND
	setport DAT
	write_sub_fast	bl
	or      bl,RXEN
	write_sub_fast	bl
	xor     bx,bx
	mov     bh,CurRxPage            ;BL = Current Rx Page
	xor     CurRxPage,10h           ;change to next page for Rx
	shr     bx,1                    ;shift BX to real memory address
	mov     RxStartAdd,bx           ;save just Rx Packet Start Address

	add     bx,EADDR_LEN+EADDR_LEN  ;seek to the TYPE word
	mov     cx,RW_ADR
	write_sub_fast	bl
	write_sub_fast	bh
	pause
	mov     cx,READ                 ;read the TYPE word
	read_sub_fast	bl
	read_sub_fast	bh

	mov     our_type,bx             ;save the TYPE word

	mov     ax,ds
	mov     es,ax
	mov     di,offset our_type
	mov     cx,RxPktLen

	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               ;request a Rx buffer to store Rx data

	mov     ax,es                   ;is this pointer null?
	or      ax,di
	jnz     find_buffer
	jmp     short CheckTx                 ;yes - just free the frame.
find_buffer:
	push    es
	push    di                   ;remember where the buffer pointer is.
	assume  ds:nothing
	mov     bx,RxStartAdd
	mov     cx,RW_ADR
	loadport
	setport DAT
	write_sub_fast	bl
	write_sub_fast	bh

	cld
	mov     bp,RxPktLen             ;CX = the byte count of Rx Packet
	setport STAT
	mov     cx,READ
	mov     ah,ch
	xor     ah,08h
read_mem:
	setport	DAT
	mov     al,ch			;output our read request.
	out     dx,al
	pause

	setport	STAT
	in      al,dx			;input four bits into bl.
	mov     bl,al
	setport	DAT			;now output the inverse read request.
	mov     al,ah
	out     dx,al
	pause

	setport	STAT
	in      al,dx			;input the high four bits.
	shr     bl,cl
	and     al,0f0h
	or      al,bl			;combine the two nibbles.
	stosb
	dec     bp
	jnz     read_mem

RxCopy_CheckTx:
	pop     si
	pop     ds
	mov     cx,RxPktLen

	call    recv_copy               ;tell them that we copied it.

	mov     ax,cs                   ;restore our ds.
	mov     ds,ax
	assume  ds:code

CheckTx:
	test    NICstatus,T16           ; Is pending a Tx Packet ?
	jz      return                  ; No, then return.
	                                ; Yes, send this packet.
;set Tx Pointer at beginning of packet
	mov     bx,TxStartAdd
	mov     cx,TX_ADR
	call    Write_sub
	xchg    bh,bl
	call    Write_sub

;Enable interrupt and start Tx
	mov     bl,Mode_RxPg
	mov     cx,COMMAND
	call    Write_sub
	or      bl,TXEN
	call    Write_sub
return:
	mov     al,SLT_PRN
;*** CMD sub ***
	loadport
	setport CMD
	out     dx,al
;*** End CMD sub ***

	cmp     PS2,0
	jnz     Rx_another_pkt
	setport STAT
	in      al,dx
	and     al,40h
	xor     al,IRQinverse
	jz      Rx_another_pkt
	call    trigger_int
Rx_another_pkt:
	mov     In_ISR,0
	ret


recv1:
	assume  ds:code
	mov     NICstatus,al            ; save NIC status
	loadport
	setport DAT
	test    al,GOOD                 ; Is Rx generating interrupt ?
	mov     al,NUL_CMD
	out     dx,al
	jnz     Rx_Good_Pkt1             ; Yes, take care of this situation.
	mov     al,NICstatus
	test    al,RXBUSY
	jz      Enable_Rx1
	jmp     CheckTx                 ; No, go to check Tx.

Enable_Rx1:
;change Rx page & enable NIC to Rx
	mov     bl,Mode_RxPg
	mov     cx,COMMAND
	setport DAT
	write_sub_slow	bl
	call    delay
	or      bl,RXEN
	write_sub_slow	bl
	jmp     CheckTx

Rx_Good_Pkt1:
;Put it on the receive queue
	mov     cx,RX_LEN               ; read Rx Packet Length
	loadport
	read_sub_slow	bl
	read_sub_slow	bh

	sub     bx,4                    ;subtrate 4 CRC Byte Count
	mov     RxPktLen,bx             ;save Rx Packet Length

;change Rx page & enable NIC to Rx
	xor     Mode_RxPg,10h
	mov     bl,Mode_RxPg
	mov     cx,COMMAND
	setport DAT
	write_sub_slow	bl
	call    delay
	or      bl,RXEN
	write_sub_slow	bl
	call    delay
	xor     bx,bx
	mov     bh,CurRxPage            ;BL = Current Rx Page
	xor     CurRxPage,10h           ;change to next page for Rx
	shr     bx,1                    ;shift BX to real memory address
	mov     RxStartAdd,bx           ;save just Rx Packet Start Address

	add     bx,EADDR_LEN+EADDR_LEN  ;seek to the TYPE word
	mov     cx,RW_ADR
	write_sub_slow	bl
	call    delay
	write_sub_slow	bh
	call    delay
	mov     cx,READ                 ;read the TYPE word

	read_sub_slow	bl
	read_sub_slow	bh

	mov     our_type,bx             ;save the TYPE word

	mov     ax,ds
	mov     es,ax
	mov     di,offset our_type
	mov     cx,RxPktLen

	call    recv_find               ;request a Rx buffer to store Rx data

	mov     ax,es                   ;is this pointer null?
	or      ax,di
	jnz     find_buffer1
	jmp     CheckTx                 ;yes - just free the frame.
find_buffer1:
	push    es
	push    di                   ;remember where the buffer pointer is.
	assume  ds:nothing
	mov     bx,RxStartAdd
	mov     cx,RW_ADR
	loadport
	setport DAT
	write_sub_slow	bl
	call    delay
	write_sub_slow	bh
	call    delay

	cld
	mov     bp,RxPktLen             ;CX = the byte count of Rx Packet
	setport STAT
	mov     cx,READ
	mov     ah,ch
	xor     ah,08h
read_mem1:
	dec     dx
	mov     al,ch
	out     dx,al
	call    delay

	inc     dx
	in      al,dx
	mov     bl,al
	dec     dx
	mov     al,ah
	out     dx,al
	call    delay

	inc     dx
	in      al,dx
	shr     bl,cl
	and     al,0f0h
	or      al,bl
	stosb
	dec     bp
	jnz     read_mem1
	jmp     RxCopy_CheckTx


recv2:
	assume  ds:code
	mov     NICstatus,al            ; save NIC status
	loadport
	setport DAT
	test    al,GOOD                 ; Is Rx generating interrupt ?
	mov     al,NUL_CMD
	out     dx,al
	jnz     Rx_Good_Pkt2             ; Yes, take care of this situation.
	mov     al,NICstatus
	test    al,RXBUSY
	jz      Enable_Rx2
	jmp     CheckTx                 ; No, go to check Tx.

Enable_Rx2:
;change Rx page & enable NIC to Rx
	mov     bl,Mode_RxPg
	mov     cx,COMMAND
	setport DAT
	write_sub_delay	bl
	call    delay
	or      bl,RXEN
  if 0
	write_sub_delay	bl
  else
	mov     al,bl
	shl     al,cl
	or      al,ch
	xor     al,08h
	out     dx,al
	call    delay

	xor     al,08h
	out     dx,al
	call    delay

	mov     al,bl
	and     al,0f0h
	or      al,ch
	xor     al,08h
	out     dx,al
  endif
	jmp     CheckTx

Rx_Good_Pkt2:
;Put it on the receive queue
	mov     cx,RX_LEN               ; read Rx Packet Length
	loadport

	read_sub_delay	bl, 1
	read_sub_delay	bh, 0

	sub     bx,4                    ;subtrate 4 CRC Byte Count
	mov     RxPktLen,bx             ;save Rx Packet Length

;change Rx page & enable NIC to Rx
	xor     Mode_RxPg,10h
	mov     bl,Mode_RxPg
	mov     cx,COMMAND
	setport DAT
	write_sub_delay	bl
	call    delay
	or      bl,RXEN
	write_sub_delay	bl
	call    delay

	xor     bx,bx
	mov     bh,CurRxPage            ;BL = Current Rx Page
	xor     CurRxPage,10h           ;change to next page for Rx
	shr     bx,1                    ;shift BX to real memory address
	mov     RxStartAdd,bx           ;save just Rx Packet Start Address

	add     bx,EADDR_LEN+EADDR_LEN  ;seek to the TYPE word
	mov     cx,RW_ADR
	write_sub_delay	bl
	call    delay
	write_sub_delay	bh
	call    delay

	mov     cx,READ                 ;read the TYPE word

	read_sub_delay	bl, 1
	read_sub_delay	bh, 0
	mov     our_type,bx             ;save the TYPE word

	mov     ax,ds
	mov     es,ax
	mov     di,offset our_type
	mov     cx,RxPktLen

	call    recv_find               ;request a Rx buffer to store Rx data

	mov     ax,es                   ;is this pointer null?
	or      ax,di
	jnz     find_buffer2
	jmp     CheckTx                 ;yes - just free the frame.
find_buffer2:
	push    es
	push    di                   ;remember where the buffer pointer is.
	assume  ds:nothing
	mov     bx,RxStartAdd
	mov     cx,RW_ADR
	loadport
	setport DAT
	write_sub_delay	bl
	call    delay
	write_sub_delay	bh
	call    delay

	cld
	mov     bp,RxPktLen             ;CX = the byte count of Rx Packet
	setport STAT
	mov     cx,READ
	mov     ah,ch
	xor     ah,08h
read_mem2:
	dec     dx
	mov     al,ch
	out     dx,al
	call    delay

	inc     dx
	in      al,dx
	mov     bl,al
	dec     dx
	mov     al,ah
	out     dx,al
	call    delay

	inc     dx
	in      al,dx
	shr     bl,cl
	and     al,0f0h
	or      al,bl
	stosb
	dec     bp
	jnz     read_mem2
	jmp     RxCopy_CheckTx


	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


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

	public  usage_msg
usage_msg       db      "usage: DE600PD <packet_int_no>",CR,LF,'$'

	public  copyright_msg
copyright_msg   db      "Packet driver for the D-Link DE-600, "
	        db      "version ",'0'+(majver / 10),'0'+(majver mod 10),".",'0'+version,CR,LF,'$'
	        db      "Portions Copyright 1988, Robert C. Clements, K1BC"
	        db      CR,LF,'$'

CableErr        db      "Bad cable connection.",07,CR,LF,'$'
mem_error_msg   db      "Adapter memory buffer failure, or bad printer "
	        db      "port connection.",07,CR,LF,'$'
irq_error_msg   db      "IRQ unavailable, please check other hardware in "
	        db      "computer.",07,CR,LF,'$'
no_NIC_err      db      "Adapter not found, or AC adapter power is off.",07,CR,LF,'$'

	extrn   set_recv_isr: 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

	assume  ds:code
	public  parse_args
parse_args:
	ret


	public  print_parameters
print_parameters:
	ret

cable_err:
	mov     dx,offset CableErr
	jmp     short show_msg
IRQ_error:
	mov     dx,offset irq_error_msg
	jmp     short show_msg
no_our_NIC:
	mov     dx,offset no_NIC_err
	jmp     short show_msg
mem_error:
	mov     dx,offset mem_error_msg
show_msg:
	mov     ah,9
	int     21h
	stc
	ret

	public  etopen
etopen:
;  Initialize the Ethernet board.
	call    check_PS2

	xor     ax,ax
	mov     es,ax
	mov     si,PRNTABADD-2          ; point to printer table at low memory
next_prn_port:
	add     si,2
	mov     bx,es:[si]              ; get LPTx's I/O Base
	or      bx,bx                   ; Does LPTx really exist ?
	jz      Chk_out_of_range
	mov     io_addr,bx              ; save the I/O Base number
	mov     printer,si              ; memorize it's LPTx now
	call    Check_DE600             ; Yes, BX = I/O Base, then check.
	jnc     IO_good                 ; If carry flag is clear, so go to
	mov     al,NML_PRN
	call    CMD_sub
Chk_out_of_range:
	cmp     si,PRNTABADD+6          ; We still miss our card from LPT1 to
	jb      next_prn_port
	jmp     short no_our_NIC        ; We still miss our card from LPT1 to
	                                ; LPT4. We give it up.
IO_good:
; Copy our Ethernet address from PROM into DE600.
	push    ds
	pop     es
	mov     di,offset our_address
	mov     cx,RW_ADR
	xor     bl,bl
	call    Write_sub
	or      bl,HA13
	call    Write_sub

	cld
	mov     bp,EADDR_LEN
	mov     cx,READ
get_our_address:
	call    Read_sub
	mov     al,bl
	stosb
	dec     bp
	jnz     get_our_address

	mov     si,offset our_address	;make sure it's got the right magic
	cmp     word ptr es:[si],0de00h	;  number.
	jne     no_our_NIC
	cmp     byte ptr es:[si+2],15h
	jne     no_our_NIC

	mov     word ptr es:[si],8000h	;now modify it to the address assigned
	mov     byte ptr es:[si+2],0c8h	;  by Xerox.
	and     byte ptr es:[si+3],0fh
	or      byte ptr es:[si+3],070h

	mov     cx,EADDR_LEN
	call    set_address

; Check DE600's IRQ enviroment
	call    Check_IRQ
	cmp     bx,1
	je      cable_OK
	jmp     cable_err

cable_OK:
	mov     bx,offset delay
	mov     byte ptr [bx],0c3h     ;  ret

; test 8 KBytes memory
;********** write mode 1 *************
	mov     cx,700h
	mov     al,PAGE3
	call    Write_LoopBack_Data
	xor     bx,bx
	mov     si,bx
write_next_page:
	call    Tx_Data
	push    bx
	mov     cx,RW_ADR
	loadport
	setport DAT
	write_sub_fast	bl
	write_sub_fast	bh

	mov     bx,si
	mov     bp,800h
	mov     cx,WRITE
	mov     ah,ch
	xor     ah,08h
wr_this_page:
	mov     al,bl
	shl     al,cl
	or      al,ch
	out     dx,al
	mov     al,bl
	and     al,0f0h
	or      al,ah
	out     dx,al
	inc     bl
	dec     bp
	jnz     wr_this_page
	inc     si
	pop     bx
	add     bx,800h
	cmp     bx,1800h
	ja      read_memory
	jmp     short write_next_page

;************ read mode 1 ************
read_memory:
	loadport
	setport DAT
	xor     bx,bx
	mov     si,bx
read_next_page:
	push    bx
	mov     cx,RW_ADR
	write_sub_fast	bl
	write_sub_fast	bh

	mov     bx,si
	mov     bp,800h
	mov     cx,READ
	mov     ah,ch
	xor     ah,08h
rd_this_page:
	mov     al,ch
	out     dx,al
	pause
	inc     dx
	in      al,dx
	mov     bh,al
	dec     dx
	mov     al,ah
	out     dx,al
	pause
	inc     dx
	in      al,dx
	shr     bh,cl
	and     al,0f0h
	or      bh,al
	cmp     bh,bl
	jne     memory_test
	dec     dx
	inc     bl
	dec     bp
	jnz     rd_this_page
	inc     si
	pop     bx
	add     bx,800h
	cmp     bx,1800h
	ja      mem_is_OK
	push    si
	push    bx
	mov     cx,700h
	mov     al,PAGE0
	call    Write_LoopBack_Data
	call    Tx_Data
	pop     bx
	pop     si
	jmp     short read_next_page
mem_is_OK:
	jmp     mem_OK

;************ write mode 2 ***************
memory_test:
	pop     ax
memory_test1:
	mov     cx,700h
	mov     al,PAGE3
	call    Write_LoopBack_Data
	xor     bx,bx
	mov     si,bx
write_next_page1:
	call    Tx_Data
	push    bx
	mov     cx,RW_ADR
	loadport
	setport DAT
	write_sub_slow	bl
	call    delay
	write_sub_slow	bh
	call    delay

	mov     bx,si
	mov     bp,800h
	mov     cx,WRITE
	mov     ah,ch
	xor     ah,08h
wr_this_page1:
	mov     al,bl
	shl     al,cl
	or      al,ch
	out     dx,al
	call    delay

	mov     al,bl
	and     al,0f0h
	or      al,ah
	out     dx,al
	call    delay
	inc     bl
	dec     bp
	jnz     wr_this_page1
	inc     si
	pop     bx
	add     bx,800h
	cmp     bx,1800h
	ja      read_memory1
	jmp     short write_next_page1

;************* read mode 2 **************
read_memory1:
	loadport
	setport DAT
	xor     bx,bx
	mov     si,bx
read_next_page1:
	push    bx
	mov     cx,RW_ADR
	write_sub_slow	bl
	call    delay
	write_sub_slow	bh
	call    delay

	mov     bx,si
	mov     bp,800h
	mov     cx,READ
	mov     ah,ch
	xor     ah,08h
rd_this_page1:
	mov     al,ch
	out     dx,al
	call    delay

	inc     dx
	in      al,dx
	mov     bh,al
	dec     dx
	mov     al,ah
	out     dx,al
	call    delay

	inc     dx
	in      al,dx
	shr     bh,cl
	and     al,0f0h
	or      bh,al
	cmp     bh,bl
	jne     memory_test2
	dec     dx
	inc     bl
	dec     bp
	jnz     rd_this_page1
	inc     si
	pop     bx
	add     bx,800h
	cmp     bx,1800h
	ja      mem_is_OK1
	push    si
	push    bx
	mov     cx,700h
	mov     al,PAGE0
	call    Write_LoopBack_Data
	call    Tx_Data
	pop     bx
	pop     si
	jmp     read_next_page1
mem_is_OK1:
	jmp     mem_OK1

;************* write mode 3 ***************
memory_test2:
	pop     ax
memory_test3:
	mov     cx,700h
	mov     al,PAGE3
	call    Write_LoopBack_Data
	xor     bx,bx
	mov     si,bx
write_next_page3:
	call    Tx_Data
	push    bx
	mov     cx,RW_ADR
	loadport
	setport DAT
	write_sub_delay	bl
	call    delay
	write_sub_delay	bh
	call    delay

	mov     bx,si
	mov     bp,800h
	mov     cx,WRITE
wr_this_page3:
	mov     al,bl
	shl     al,cl
	or      al,ch
	xor     al,08h
	out     dx,al
	call    delay

	xor     al,08h
	out     dx,al
	call    delay

	mov     al,bl
	and     al,0f0h
	or      al,ch
	out     dx,al
	call    delay

	xor     al,08h
	out     dx,al
	call    delay
	inc     bl
	dec     bp
	jnz     wr_this_page3
	inc     si
	pop     bx
	add     bx,800h
	cmp     bx,1800h
	ja      read_memory3
	jmp     write_next_page3

;************* read mode 3 ***************
read_memory3:
	loadport
	setport DAT
	xor     bx,bx
	mov     si,bx
read_next_page3:
	push    bx
	mov     cx,RW_ADR
	write_sub_delay	bl
	call    delay
	write_sub_delay	bh

	call    delay

	mov     bx,si
	mov     bp,800h
	mov     cx,READ
	mov     ah,ch
	xor     ah,08h

	mov     al,ch
	xor     al,08h
	out     dx,al
	call    delay
rd_this_page3:
	mov     al,ch
	out     dx,al
	call    delay

	setport STAT
	in      al,dx
	mov     bh,al
	setport DAT
	mov     al,ah
	out     dx,al
	call    delay

	setport STAT
	in      al,dx
	shr     bh,cl
	and     al,0f0h
	or      bh,al
	cmp     bh,bl
	jne     mem_err
	setport	DAT
	inc     bl
	dec     bp
	jnz     rd_this_page3
	inc     si
	pop     bx
	add     bx,800h
	cmp     bx,1800h
	ja      mem_OK2
	push    si
	push    bx
	mov     cx,700h
	mov     al,PAGE0
	call    Write_LoopBack_Data
	call    Tx_Data
	pop     bx
	pop     si
	jmp     read_next_page3

pointer         dw      0

mem_err:
	pop     bx
	cmp     pointer,9		;too slow?  Must not be working.
	ja      mem_real_err
	mov     bx,offset delay		;append another NOP and RET in.
	add     bx,pointer
	mov     [bx],0c390h
	inc     pointer			;slow it down a little more and try
	jmp     memory_test3		;  again.
mem_real_err:
	jmp     mem_error

change_routine:
	mov     ax,offset recv1
	mov     recv_pointer,ax
	mov     ax,offset send_pkt1
	mov     send_pkt_pointer,ax
	ret

change_routine2:
	mov     ax,offset recv2
	mov     recv_pointer,ax
	mov     ax,offset send_pkt2
	mov     send_pkt_pointer,ax
	ret

;********** memory test passed ****************
mem_OK1:
	call    change_routine
	jmp     short mem_OK
mem_OK2:
	call    change_routine2
mem_OK:
	call    speed_test

	push    es
	xor     ax,ax
	mov     es,ax
	mov     si,printer
	mov     word ptr es:[si],ax     ; Zero-out Printer Port
	pop     es

; Initialize Rx buffer pointer, and start receive
	mov     bl,Mode
	or      bl,IRQinverse
	or      bl,CurRXPage
	mov     Mode_RxPg,bl
	mov     cx,COMMAND
	call    Write_sub
	or      bl,RXEN
	call    Write_sub

; Put our Receive routine in interrupt chain
	call    set_recv_isr

; We didn't need to enable the receive & transmit interrupts, they were
; set by hardware already. (accept GOOD, SUC & T16 to generate interrupt)

; Enable Printer Adapter IRQ line
	mov     al,SLT_PRN
	call    CMD_sub

	mov     dx,offset end_resident
	clc
	ret


;*********** sub-routine *************
; Check DE-600 routine
Check_DE600:
	mov     al,SLT_NIC
	call    CMD_sub
	call    delay

	loadport
	setport DAT
	mov     al,NUL_CMD
	out     dx,al
	call    delay

	mov     bl,RESET
	mov     cx,COMMAND
	call    Write_sub
	call    delay
	mov     bl,STOP_RESET
	call    Write_sub
	call    delay

	mov     cx,STATUS
	call    Read_sub
	test    bl,0f0h
	jz      Check_OK
	stc
	ret
Check_OK:
	clc
	ret

OldIRQ5 dd      0
OldIRQ7 dd      0

NewIRQ5:
	push    ax
	push    bx
	push    cx
	push    dx
	push    ds
	mov     al,20h
	out     20h,al
	mov     ax,cs
	mov     ds,ax
	cmp     LB,0
	jz      DisCare_IRQ5
	mov     bh,5
	call    Clear_int
DisCare_IRQ5:
	pop     ds
	pop     dx
	pop     cx
	pop     bx
	pop     ax
	iret

NewIRQ7:
	push    ax
	push    bx
	push    cx
	push    dx
	push    ds
	mov     al,20h
	out     20h,al
	mov     ax,cs
	mov     ds,ax
	cmp     LB,0
	jz      DisCare_IRQ7
	mov     bh,7
	call    Clear_int
DisCare_IRQ7:
	pop     ds
	pop     dx
	pop     cx
	pop     bx
	pop     ax
	iret

replace_IRQ5_7:
	xor     cx,cx
	mov     es,cx
	mov     di,034h
	mov     ax,es:[di]                      ;save old interrupt vector
	mov     word ptr OldIRQ5,ax
	mov     ax,es:[di]+2
	mov     word ptr OldIRQ5+2,ax
	mov     ax,offset NewIRQ5
	stosw
	mov     ax,cs
	stosw

	mov     di,03ch
	mov     ax,es:[di]                      ;save old interrupt vector
	mov     word ptr OldIRQ7,ax
	mov     ax,es:[di]+2
	mov     word ptr OldIRQ7+2,ax
	mov     ax,offset NewIRQ7
	stosw
	mov     ax,cs
	stosw

	in      al,21h
	mov     intmask,al
	pause
	pause
	and     al,5fh
	out     21h,al
	ret

intmask         db      0
INT_come        db      0
T16_flag        db      0
LB              db      0

restore_IRQ5_7:
	xor     cx,cx
	mov     es,cx
	mov     di,034h
	mov     ax,word ptr OldIRQ5
	mov     es:[di],ax                      ;save old interrupt vector
	mov     ax,word ptr OldIRQ5+2
	mov     es:[di]+2,ax

	mov     di,03ch
	mov     ax,word ptr OldIRQ7
	mov     es:[di],ax                      ;save old interrupt vector
	mov     ax,word ptr OldIRQ7+2
	mov     es:[di]+2,ax

	mov     al,intmask
	out     21h,al
	ret

Write_LoopBack_Data:
	mov     si,offset our_address
	mov     di,si
;set Tx Pointer for moving packet
	mov     bx,BFRSIZ
	sub     bx,cx           ;CX= Packet Length
	or      bh,al           ;AL= Page Number
	mov     TxStartAdd,bx   ;BX= the pointer to TX
	mov     cx,RW_ADR       ;write memory address
	call    Write_sub
	mov     bl,bh
	call    Write_sub
	cld
	loadport
	setport DAT
	mov     bp,12
	mov     cx,WRITE
write_our_node_ID:
	lodsb
	mov     bl,al
	call    Write_sub
	cmp     bp,7
	jne     not_second_ID
	mov     si,di
not_second_ID:
	dec     bp
	jnz     write_our_node_ID
	ret

Tx_Data:
;Check TXIDLE, if high then wait for previous Tx end, if low then Tx it
	mov     cx,800h        ; Avoid infinite loop
	loadport
	setport STAT
wait_Txidle0:
	in      al,dx
	test    al,TXBUSY       ; Is the previous Tx successful ?
	jz      Tx_next0        ; Yes, TXBUSY is low. Then could Tx next packet.
	loop    wait_Txidle0
Tx_next0:
;set Tx Pointer at beginning of packet
	push    bx
	mov     cx,TX_ADR
	mov     bx,TxStartAdd
	call    Write_sub
	mov     bl,bh
	call    Write_sub
;Enable interrupt and start Tx
	mov     cx,COMMAND
	mov     bl,RX_NONE
	call    Write_sub
	or      bl,TXEN
	call    Write_sub
	pop     bx
	ret

LoopBack_Tx:
;set Tx Pointer at beginning of packet
	mov     bx,TxStartAdd
	mov     cx,TX_ADR
	call    Write_sub
	mov     bl,bh
	call    Write_sub
;Enable interrupt and start Tx
	mov     bl,RX_BP
	or      bl,IRQinverse
	mov     cx,COMMAND
	call    Write_sub
	or      bl,LOOPBACK
	call    Write_sub

	mov     LB,1
	mov     al,SLT_PRN
	call    CMD_sub

	xor     bx,bx
	mov     cx,8000h
wait_int:
	cmp     INT_come,0
	jz      have_T16
	mov     bx,1
	jmp     short exit_LoopBack
have_T16:
	cmp     T16_flag,0
	jz      still_wait
	mov     bx,-1
	jmp     short exit_LoopBack
still_wait:
	loop    wait_int

exit_LoopBack:
	mov     LB,0
	mov     al,SLT_NIC
	call    CMD_sub

	push    bx
	mov     cx,STATUS
	call    Read_sub
	pop     bx
	ret

Clear_int:
	mov     al,SLT_NIC
	call    CMD_sub
	pause
	mov     cx,STATUS
	call    Read_sub

	mov     T16_flag,0
	test    bl,GOOD         ; Is Rx generating interrupt ?
	jz      chk_T16
	mov     INT_come,1
	mov     int_no,bh
	jmp     short exit_Clear_int
chk_T16:
	test    bl,T16          ; Is pending a Tx Packet ?
	jz      exit_Clear_int
	mov     T16_flag,1
exit_Clear_int:
	ret

Check_IRQ:
	call    replace_IRQ5_7
	sti
	mov     cx,RUNT
	mov     al,PAGE0
	call    Write_LoopBack_Data
	call    LoopBack_Tx     ; check IRQ= 7 or 5 but IRQ not inverse
	cmp     bx,0
	jnz     IRQ_OK

;Check TXIDLE, if high then wait for previous Tx end, if low then Tx it
	mov     cx,800h        ; Avoid infinite loop
	loadport
	setport STAT
wait_Txidle:
	in      al,dx
	test    al,TXBUSY       ; Is the previous Tx successful ?
	jz      Tx_next         ; Yes, TXBUSY is low. Then could Tx next packet.
	loop    wait_Txidle
Tx_next:
	mov     IRQinverse,40h  ; check IRQ= 7 or 5 but IRQ inverse
	mov     PS2,0
	call    LoopBack_Tx
IRQ_OK:
	cli
	call    restore_IRQ5_7
	ret

check_PS2:
	mov     ax,0c400h
	int     15h
	jc      not_PS2
	mov     PS2,1
not_PS2:
	ret

speed_test:
	xor     ax,ax
	mov     es,ax
	mov     si,20h
	mov     ax, es:[si]
	mov     cs:old_int8, ax
	mov     ax, es:[si+2]
	mov     cs:old_int8[2], ax
	cli
	mov     ax,offset new_int8
	mov     es:[si],ax
	mov     es:[si+2],cs
	sti

next_test1:
	mov     ticks_start,0
next_test:
	cmp     ticks_start,0
	jz      next_test
	mov     ticks,0
	xor     bx,bx
loop_again:
	mov     cx,6
	loop    $
	cmp     ticks,2
	jae     End_count
	inc     bx
	jmp     short loop_again

End_count:
	cli
	xor     ax,ax
	mov     es,ax
	mov     si,20h
	mov     ax,old_int8
	mov     es:[si],ax
	mov     ax,old_int8[2]
	mov     es:[si+2],ax
	sti
	cmp     bx,0a000h
	jb      low_speed
	mov     ax,offset recv1         ; special for high speed EISA
	mov     recv_pointer,ax
low_speed:
	ret

ticks_start     db      0
ticks           db      0
old_int8        dw      ?
	        dw      ?
new_int8:
	inc     ticks
	inc     ticks_start
	jmp     dword ptr cs:old_int8

	code    ends
	end
[ RETURN TO DIRECTORY ]