;*****************************************************************************
;* Name : HEAD.ASM
;*
;* All rights reserved by DAVICOM, 1996
;*
;* Description :
;*
;* Functions :
;*
;* Author : Spenser Tsai
;*
;* Date : 05/17/96'
;*
;* Change Notes :
;* Date - Modifier
;*     Cause :
;*  Modified :
;*    Result :
;*
;
; Maintainers:
;
; John H.T. Ho		htho
;
;
; Revision History:
;
; Date	     Modfier.	Modification Descriptions
; ---------- ---------- ------------------------------------------
; 2000.6.27	htho	fix noise level setting bug
;
;*****************************************************************************
;  Copyright, 1996-1998, Spenser Tsai, DAVICOM

.model tiny
.386

include defs.asm

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

	public	phd_environ

	org	2ch

phd_environ	dw	?

	public	phd_dioa

	org	80h

phd_dioa	label	byte			;; Parameter location

;;******************************************
;; Driver Start point
;;******************************************
	org	100h

	extrn	start_1: near
start:
	jmp	start_1 			;; locate at tail.asm
	db	"PKT DRVR"                              ;;Packet driver the label of entry point

	extrn	branding_msg: byte

	dw	branding_msg

	even				;put the stack on a word boundary.

	dw	128 dup(?)		;128 words of stack.

	PUBLIC	our_stack
our_stack	label	byte

	extrn	int_no: byte

	public	entry_point, sys_features, flagword, is_186, is_286, is_386

entry_point	db	?,?,?,? 	; interrupt to communicate.

sys_features	db	0		; 2h = MC   40h = 2nd 8259

is_186		db	0		;=0 if 808[68], =1 if 80[123]86.

is_286		db	0		;=0 if 80[1]8[68], =1 if 80[234]86.

is_386		db	0		;=0 if 80[12]8[68], =1 if 80[34]86.

flagword	dw	0

original_mask	db	0		;=0 if interrupt was originally on.

;;IFDEF DISPLAY
;;
;;CR EQU 0Dh	  ; ASCII Carriage Return
;;LF EQU 0Ah	  ; ASCII Line Feed
;;
;;Show_isr_ok	      DB ' Software Interrupt happed.. '
;;		      DB CR,LF
;;Show_isr_ok_len     EQU $ - Show_isr_ok
;;
;;ENDIF   ;;DISPLAY

	even



functions	label	word
	dw	f_not_implemented	;0
	dw	f_driver_info		;1
	dw	f_access_type		;2
	dw	f_release_type		;3
	dw	f_send_pkt		;4
	dw	f_terminate		;5
	dw	f_get_address		;6
	dw	f_reset_interface	;7
	dw	f_stop			;8	;; Not in the Specification
	dw	f_not_implemented	;9
	dw	f_get_parameters	;10
;;	  dw	  f_not_implemented	  ;11
	dw	f_as_send_pkt		;11	;; Specification define 11, not 12
	dw	f_as_send_pkt		;12
	dw	f_drop_pkt		;13	;; Not in the Specification
	dw	f_not_implemented	;14
	dw	f_not_implemented	;15
	dw	f_not_implemented	;16
	dw	f_not_implemented	;17
	dw	f_not_implemented	;18
	dw	f_not_implemented	;19
	dw	f_set_rcv_mode		;20
	dw	f_get_rcv_mode		;21
	dw	f_set_multicast_list	;22
	dw	f_get_multicast_list	;23
	dw	f_get_statistics	;24
	dw	f_set_address		;25

;external data supplied by device-dependent module:
	extrn	driver_class: byte
	extrn	driver_type: byte
	extrn	driver_name: byte
	extrn	driver_function: byte
	extrn	parameter_list: byte
	extrn	rcv_modes: word 	;count of modes followed by mode handles.

;external code supplied by device-dependent module:
	extrn	send_pkt: near
	extrn	as_send_pkt: near
	extrn	drop_pkt: near
	extrn	terminate: near
;;	extrn	reset_interface: near
	extrn	xmit: near
;;	  extrn   recv: near
	extrn	etopen: near
;;	  extrn   set_multicast_list: near
	extrn	timer_isr: near

per_handle	struc
  in_use	  db	  0		  ;non-zero if this handle is in use.
  packet_type	  db	  MAX_P_LEN dup(0);associated packet type.
  packet_type_len dw	  0		  ;associated packet type length.
  receiver	  dd	  0		  ;receiver handler.
  receiver_sig	  db	  8 dup(?)	  ;signature at the receiver handler.
  class 	  db	  ?		  ;interface class
per_handle	ends

handles 	per_handle MAX_HANDLE dup(<>)	;; MAX_HANDLE = 10

end_handles	label	byte



	public	multicast_count, multicast_addrs, multicast_broad

multicast_count dw	0		;count of stored multicast addresses.

multicast_broad db	0ffh,0ffh,0ffh,0ffh,0ffh,0ffh	; entry for broadcast

multicast_addrs db	MAX_MULTICAST*EADDR_LEN dup(?)



;the device-dependent code reads the board's address from ROM in the

;initialization code.

	public	address_len, rom_address, my_address

address_len	dw	EADDR_LEN		;default to Ethernet.

rom_address	db	MAX_ADDR_LEN dup(?)	;our address in ROM.

my_address	db	MAX_ADDR_LEN dup(?)	;our current address.


	PUBLIC	rcv_mode_num
rcv_mode_num	dw	3



free_handle	dw	0		; temp, a handle not in use

found_handle	dw	0		; temp, handle for our packet

receive_ptr	dd	0		; the pkt receive service routine



	public	send_head, send_tail

send_head	dd	0		; head of transmit queue

send_tail	dd	0		; tail of transmit queue

	PUBLIC errors_in, bytes_out, packets_out, bytes_in, errors_out
statistics_list label	dword
  packets_in	  dd	  0
  packets_out	  dd	  0
  bytes_in	  dd	  0
  bytes_out	  dd	  0
  errors_in	  dd	  0
  errors_out	  dd	  0
  packets_dropped dd	  0		;dropped due to no type handler.


savespss	label	dword

savesp		dw	?		;saved during the stack swap.

savess		dw	?



their_recv_isr	dd	0		; original owner of board int

	PUBLIC	their_timer
their_timer	dd	0



;
; The following structure is used to access the registers pushed by the
; packet driver interrupt handler.  Don't change this structure without also
; changing the "bytes" structure given below.
;

regs	struc				; stack offsets of incoming regs
  _ES	  dw	  ?
  _DS	  dw	  ?
  _BP	  dw	  ?
  _DI	  dw	  ?
  _SI	  dw	  ?
  _DX	  dw	  ?
  _CX	  dw	  ?
  _BX	  dw	  ?
  _AX	  dw	  ?
  _IP	  dw	  ?
  _CS	  dw	  ?
  _F	  dw	  ?			  ; flags, Carry flag is bit 0
regs	ends



;

; bits in the _F register.

;

CY	equ	0001h

EI	equ	0200h





;
; This structure is a bytewise version of the "regs" structure above.
;

bytes	struc				  ; stack offsets of incoming regs
	  dw	  ?			  ; es, ds, bp, di, si are 16 bits
	  dw	  ?
	  dw	  ?
	  dw	  ?
	  dw	  ?
  _DL	  db	  ?
  _DH	  db	  ?
  _CL	  db	  ?
  _CH	  db	  ?
  _BL	  db	  ?
  _BH	  db	  ?
  _AL	  db	  ?
  _AH	  db	  ?
bytes	ends



	public	their_isr

their_isr	dd	0		; original owner of pkt driver int

;;******************************************
;; Upper layer called entry point
;;
	public	our_isr
our_isr:
	jmp	short our_isr_0 	;the required signature.
	nop
	db	'PKT DRVR',0

our_isr_open:
	push	ax			; save lots of registers
	push	bx
	push	cx
	push	dx
	push	si
	push	di
	push	bp
	push	ds
	push	es

	call	etopen			; init the card
	jc	our_isr_no_init

	mov	si,offset rom_address	;copy their original address to
	movseg	es,ds
	mov	di,offset my_address	;  their current address.
	mov	cx,MAX_ADDR_LEN/2
	rep	movsw

	cmp	rcv_modes+2[3*2],0	;does mode 3 exist?
	stc				;make sure we generate an error!
	je	our_isr_no_init 	;no.
	call	rcv_modes+2[3*2]	;  call it.
	clc

our_isr_no_init:
	pop	es			; restore lots of registers
	pop	ds
	pop	bp
	pop	di
	pop	si
	pop	dx
	pop	cx
	pop	bx
	pop	ax

	mov	dh,CANT_RESET		; (actually can't initialize)
	jc	our_isr_error
	or	flagword,CALLED_ETOPEN	; remember this fact
	jmp	short our_isr_cont

our_isr_0:

	assume	ds:nothing
	push	ax
	push	bx
	push	cx
	push	dx
	push	si
	push	di
	push	bp
	push	ds
	push	es

;;IFDEF DISPLAY
;;
;;EXTRN   PortPrintMessage : NEAR
;;	  mov	  ax, cs
;;	  push	  ds
;;	  mov	  ds, ax
;;	  mov	  si, OFFSET show_isr_ok
;;	  mov	  cx, Show_isr_ok_len
;;	  call	  PortPrintMessage
;;	  pop	  ds
;;	  jmp	  Our_isr_return
;;
;;ENDIF   ;;DISPLAY
	cld
	mov	bx,cs			;set up ds.
	mov	ds,bx
	assume	ds:code
	mov	bp,sp			;we use bp to access the original regs.
	and	_F[bp],not CY		;start by clearing the carry flag.

  if 0
	test	_F[bp],EI		;were interrupt on?
	jz	our_isr_ei		;no, don't turn them back on.
	sti				;yes, turn them back on.

our_isr_ei:

  endif

	test	flagword,CALLED_ETOPEN	; have we initialized the card?
	jz	our_isr_open		; no

our_isr_cont:
	mov	bl,ah			;jump to the correct function.
	xor	bh,bh
	cmp	bx,25			;only twenty five functions right now.
	ja	f_bad_command
	add	bx,bx			;*2

;
; The functions are called with all the original registers except
; BX, DH, and BP.  They do not need to preserve any of them.  If the
; function returns with cy clear, all is well.	Otherwise dh=error number.
;
	call	functions[bx]
	assume	ds:nothing
	jc	our_isr_error

our_isr_return:
	pop	es
	pop	ds
	pop	bp
	pop	di
	pop	si
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	iret

our_isr_error:
	assume	ds:nothing
	mov	bp,sp			;we use bp to access the original regs.
	mov	_DH[bp],dh
	or	_F[bp],CY		;return their carry flag.
	jmp	short our_isr_return

f_bad_command:
	assume	ds:code
	extrn	bad_command_intercept: near
	mov	bx,_BX[bp]
	call	bad_command_intercept
	mov	_BX[bp],bx
	mov	_DX[bp],dx
	jnc	our_isr_return
	jmp	our_isr_error

	public	re_enable_interrupts
re_enable_interrupts:
; Possibly re-enable interrupts.  We put this here so that other routines
; don't need to know how we put things on the stack.
	test	_F[bp], EI		; Were interrupts enabled on pkt driver entry?
	je	re_enable_interrupts_1	; No.
	sti				; Yes, re-enable interrupts now.

re_enable_interrupts_1:

	ret

f_not_implemented:
	mov	dh,BAD_COMMAND
	stc
	ret

;--------------------------------------------------------------------+
; Name : f_driver_info
;
; Description: Returns information about the interface.
;
; Enter : AH = 1, AL = 255, BX = handle (option).
;
; Return :
;	Non-errors :	Carry flag clear
;			Version 	BX
;			Clear		CH
;			Type		DX
;			Number		CL
;			Name		DS:SI	(Driver Name)
;			Functionality	AL
;						1 = Basic functions.
;						2 = Basic and Extended.
;						5 = Basic and High-performance.
;						6 = Basic, High-performance,
;						    Extended.
;					      255 = Not installed.
; Destroied registers : AX & BX.
;
;---------------------------------------------------------------------+

f_driver_info:
;	As of 1.08, the handle is optional, so we no longer verify it.
;	call	verify_handle

	cmp	_AL[bp],0ffh		; correct calling convention?
	jne	f_driver_info_1 	; ne = incorrect, fail

					;For enhanced PD, if they call
	cmp	_BX[bp],offset handles	;with a handle, give them the
					;class they think it is
	jb	default_handle
	cmp	_BX[bp],offset end_handles ;otherwise default to first class
	jae	default_handle

	mov	bx, _BX[bp]
	cmp	[bx].in_use,0		;if it's not in use, it's bad.
	je	default_handle
	mov	al, [bx].class
	mov	_CH[bp], al
	jmp	short got_handle

default_handle:
	mov	al,driver_class
	mov	_CH[bp],al

got_handle:
	mov	_BX[bp],majver		;version
	mov	al,driver_type
	cbw
	mov	_DX[bp],ax
	mov	_CL[bp],0		;number zero.
	mov	_DS[bp],ds		; point to our name in their ds:si
	mov	_SI[bp],offset driver_name
	mov	al,driver_function
	mov	_AL[bp],al
	clc
	ret

f_driver_info_1:
	stc
	ret
;;-------------------------------------------------------------------------

;--------------------------------------------------------------------+
; Name : f_set_rcv_mode
;
; Description: Set the receive mode on the interface associated with handle.
;		Can't use this function in the mutli-handle.
;
; Enter : AH = 20
;		BX = handle
;		CX = mode
;
; Return :
;	error return - carry flag set, DH = error code.
;
;	Non-error return - carry flag clear, AX = handle.
;
; Destroied registers :
;
;---------------------------------------------------------------------+
	public f_set_rcv_mode
f_set_rcv_mode:
	call	verify_handle
	cmp	cx,rcv_mode_num 	;are we already using that mode?
	je	f_set_rcv_mode_4	;yes, no need to check anything.

	mov	dx,bx			;remember our handle.
	call	count_handles		;is ours the only open handle?
					;; Return cx = Number of open handle
	cmp	cl,1
	jne	f_set_rcv_mode_1	;no, don't change the receive mode.

	mov	cx,_CX[bp]		;get the desired receive mode.
	cmp	cx,rcv_modes		;do they have this many modes?
	jae	f_set_rcv_mode_1	;no - must be a bad mode for us.

	mov	bx,cx
	shl	bx, 1			;we're accessing words, not bytes.
;;	  add	  bx,bx 		  ;we're accessing words, not bytes.
	mov	ax,rcv_modes[bx]+2	;get the handler for this mode.
	or	ax,ax			;do they have one?
	je	f_set_rcv_mode_1	;no - must be a bad mode for us.

	mov	rcv_mode_num,cx 	;yes - remember the number and
	call	ax			;  call it.
f_set_rcv_mode_4:
	clc
	ret

f_set_rcv_mode_1:
	mov	dh,BAD_MODE
	stc
	ret
;;--------------------------------------------------------------------------

;--------------------------------------------------------------------+
; Name : f_get_rcv_mode
;
; Description: Return the current receive mode of the interface associated
;		with handle.
;
; Enter : AH = 21
;		BX = handle
;
; Return :
;	error return - carry flag set, DH = error code.
;
;	Non-error return - carry flag clear, AX = mode number.
;
; Destroied registers :
;
;---------------------------------------------------------------------+
	PUBLIC	f_get_rcv_mode
f_get_rcv_mode:
	call	verify_handle
	mov	ax,rcv_mode_num 	;return the current receive mode.
	mov	_AX[bp],ax
	clc
	ret
;;--------------------------------------------------------------------------

EXTRN	MulticastTable : BYTE
EXTRN	AddMulticastAddress : NEAR

;--------------------------------------------------------------------+
; Name : f_set_multicast_list
;
; Description:
;
; Enter : AH = 22
;	     ES:DI = pointer of multicast table
;		CX = length of bytes
;
; Return :
;	error return - carry flag set, DH = error code.
;
;	Non-error return - carry flag clear.
;
; Destroied registers :
;
;---------------------------------------------------------------------+
	PUBLIC	f_set_multicast_list
f_set_multicast_list:
;following instruction not needed because cx hasn't been changed.
;	mov	cx,_CX[bp]		;Tell them how much room they have.

;verify that they supplied an even number of EADDR's.
	mov	ax,cx
	xor	dx,dx
	mov	bx,EADDR_LEN
	div	bx
	or	dx,dx			;zero remainder?
	jne	f_set_multicast_list_2	;no, we don't have an even number of
					;  addresses.
	mov	bx, ax			;; Save number of address
	mov	cx, ax
multi_bit_chk:
	mov  al, BYTE PTR es:[di]	; Get first byte of address
	and  al, 01h			; Check least significant bit
	jz	f_set_multicast_list_2
	add	di, EADDR_LEN
	loop	multi_bit_chk

;;	  cmp	  ax,MAX_MULTICAST	  ;is this too many?
;;	  ja	  f_set_multicast_list_3  ;yes - return NO_SPACE
; Verify Multicast Table space.
	mov	dx, MulticastTable.table_maxnum
	sub	dx, MulticastTable.table_current
	cmp	ax, dx
	ja	f_set_multicast_list_3	;yes - return NO_SPACE

f_set_multicast_list_1:
;;	  mov	  multicast_count,ax	  ;remember the number of addresses.
;;	  movseg  es,cs
;;	  mov	  di,offset multicast_addrs
;;	  push	  ds
;;	  mov	  ds,_ES[bp]		  ; get ds:si -> new list.
;;	  mov	  si,_DI[bp]
;;	  push	  cx
;;	  rep	  movsb
;;	  pop	  cx
;;	  pop	  ds

	mov	si,_DI[bp]		;; ES:SI -> new address list
	call	AddMulticastAddress	;; BX = number of address to added
;;	  call	  set_multicast_list
	ret

f_set_multicast_list_2:
	mov	dh,BAD_ADDRESS
	stc
	ret

f_set_multicast_list_3:
	mov	dh,NO_SPACE
	stc
	ret
;;----------------------------------------------------------------------------


;--------------------------------------------------------------------+
; Name : f_get_multicast_list
;
; Description: address list point to length bytes of multicast address.
;
; Enter : AH = 23
;
; Return :
;	error return - carry flag set, DH = error code.
;
;	Non-error return : carry flag clear.
;				ES:DI = address list.
;				   CX = Length of bytes
;
; Destroied registers : BX, AX, CX, SI, DI, ES
;
;---------------------------------------------------------------------+
	PUBLIC	f_get_multicast_list
f_get_multicast_list:
	mov	bx, OFFSET MulticastTable
	mov	si, bx
	add	si, 4			     ;; Point to first address
	mov	ax, ds
	mov	es, ax				;; ES = DS
	mov	di, OFFSET multicast_addrs
	mov	cx, MulticastTable.table_current	;; Number of multicast address
	mov	bx, cx				;; save number of address
	cmp	cx, 0			;; Don't move data if no multicast addr.
	je	gmt_return

multicast_move:
	movsw
	movsw
	movsw
	add	si, 10				;; Skip to next address
	loop	multicast_move

gmt_return:
	mov	_ES[bp],ds		;return what we have remembered.
	mov	_DI[bp],offset multicast_addrs
	mov	ax,EADDR_LEN		;multiply the count by the length.
;;	  mul	  multicast_count
	mul	bx
	mov	_CX[bp],ax		;because they want total bytes.
	clc
	ret
;;--------------------------------------------------------------------------

;--------------------------------------------------------------------+
; Name : f_get_statistics
;
; Description: Returns a pointer to a statistics structure for the interface.
;
; Enter : AH = 24
;		BX = handle
;
; Return :
;	error return - carry flag set, DH = error code.
;
;	Non-error return : carry flag clear.
;				DS:SI = statistics sturcture.
;
; Destroied registers :
;
;---------------------------------------------------------------------+

f_get_statistics:
	call	verify_handle		;just in case.
	mov	_DS[bp],ds
	mov	_SI[bp],offset statistics_list
	clc
	ret
;;-------------------------------------------------------------------------




;; Called by f_access_type
access_type_class:
	mov	dh,NO_CLASS
	stc
	ret



;; Called by f_access_type
access_type_type:
	mov	dh,NO_TYPE
	stc
	ret


;; Called by f_access_type
access_type_number:
	mov	dh,NO_NUMBER
	stc
	ret


;; Called by f_access_type
access_type_bad:
	mov	dh,BAD_TYPE
	stc
	ret



;register caller of pkt TYPE

;--------------------------------------------------------------------+
; Name : f_access_type
;
; Description: Initiate access to packets of the specified type.
;		This could get Handle & Receiver function entry point.
;
; Enter : AH = 2
;		AL:	Class
;		BX:	Type
;		DL:	Number
;	     DS:SI:	Type Name
;		CX:	type length
;	     ES:DI:	Receiver function
;
; Return :
;	error return - carry flag set, DH = error code.
;
;	Non-error return - carry flag clear, AX = handle.
;
; Destroied registers : AX, BX, CX, DX, SI, DI, DS, ES
;
;---------------------------------------------------------------------+
	PUBLIC f_access_type
f_access_type:
	mov	bx, offset driver_class

access_type_9:
	mov	al, [bx]		;get the next class.
	inc	bx
	or	al,al			;end of the list?
	je	access_type_class	;class failed (story of my life)
;;-- Check Class
	cmp	_AL[bp],al		;our class?
	jne	access_type_9		;no, try again

access_type_1:
	cmp	_BX[bp],-1		;generic type?
	je	access_type_2		;yes.
	mov	al,driver_type
	cbw
	cmp	_BX[bp],ax		;our type?
	jne	access_type_type	;no.

access_type_2:
	cmp	_DL[bp],0		;generic number?
	je	access_type_3
	cmp	_DL[bp],1		;our number?
	jne	access_type_number

access_type_3:
	cmp	_CX[bp],MAX_P_LEN	;is the type length too long? (8)
	ja	access_type_bad 	;yes - can't be ours.

; now we do two things--look for an open handle, and check the existing
; handles to see if they're replicating a packet type.
	mov	free_handle,0		;remember no free handle yet.
	mov	bx,offset handles

access_type_4:		;; Serach start
	cmp	[bx].in_use,0		;is this handle in use?
	je	access_type_5		;no - don't check the type.
	mov	al, _AL[bp]		;is this handle the same class as
	cmp	al, [bx].class		;  they're want?
	jne	short access_type_6
	mov	es,_DS[bp]		;get a pointer to their type
	mov	di,_SI[bp]		;  from their ds:si to our es:di
	mov	cx,_CX[bp]		;get the minimum of their length
					;  and our length.  As currently
					;  implemented, only one receiver
					;  gets the packets, so we have to
					;  ensure that the shortest prefix
					;  is unique.

	cmp	cx,[bx].packet_type_len ;Are we less specific than they are?
	jb	access_type_8		;no.
	mov	cx,[bx].packet_type_len ;yes - use their count.

access_type_8:
	lea	si,[bx].packet_type
	or	cx,cx			; pass-all TYPE? (zero TYPE length)
	jne	access_type_7		; ne = no
	mov	bx,offset handles+(MAX_HANDLE-1)*(size per_handle)
	jmp	short access_type_5	; put pass-all last

access_type_7:
	repe	cmpsb
	jne	short access_type_6	;go look at the next one.

access_type_inuse:
	mov	dh,TYPE_INUSE		;a handle has been assigned for TYPE
	stc				;and we can't assign another
	ret

access_type_5:				;handle is not in use
	cmp	free_handle,0		;found a free handle yet?
	jne	access_type_6		;yes.
	mov	free_handle,bx		;remember a free handle

access_type_6:
	add	bx,(size per_handle)	;go to the next handle.
	cmp	bx,offset end_handles	;examined all handles?
	jb	access_type_4		;no, continue.
	mov	bx,free_handle		;did we find a free handle?
	or	bx,bx
	je	access_type_space	;no - return error.
	mov	[bx].in_use,1		;remember that we're using it.
	mov	ax,_DI[bp]		;remember the receiver type.
	mov	[bx].receiver.offs,ax
	mov	ax,_ES[bp]
	mov	[bx].receiver.segm,ax

	push	ds

	mov	ax,ds		;; ES = DS
	mov	es,ax
	mov	ds,_DS[bp]		;remember their type.
	mov	si,_SI[bp]
	mov	cx,_CX[bp]
	mov	es:[bx].packet_type_len,cx	; remember the TYPE length
	lea	di,[bx].packet_type
	rep	movsb

	lds	si,es:[bx].receiver	;copy the first 8 bytes
	lea	di,[bx].receiver_sig	; to the receiver signature.
	mov	cx,8/2
	rep	movsw

	pop	ds

	mov	al, _AL[bp]
	mov	[bx].class, al
	mov	_AX[bp],bx		;return the handle to them.

	clc

	ret
;;--------------------------------------------------------------------------

;; Called by f_access_type
access_type_space:
	mov	dh,NO_SPACE
	stc
	ret


;--------------------------------------------------------------------+
; Name : f_release_type
;
; Description: This function ends access to packets associated with
;		a handle.
;
; Enter : AH = 3
;		BX = Handle
;
; Return :
;		Error - carry flag set, DH = error code.
;	    Non-error - carry flag clear.
;
; Destroied registers :
;
;---------------------------------------------------------------------+
	PUBLIC f_release_type
f_release_type:
	call	verify_handle		;mark this handle as being unused.
	mov	[bx].in_use,0

	call	count_handles		;All handles gone now?

	cmp	cl,0
	jne	f_release_type_1	;no, don't change the receive mode.

  if 0
	cmp	rcv_modes+2[3*2],0	;does mode 3 exist?
	je	f_release_type_1	;no.
	mov	rcv_mode_num,3		;yes - remember the number and
	call	rcv_modes+2[3*2]	;  call it.
  endif

f_release_type_1:
	clc
	ret
;;--------------------------------------------------------------------------



;--------------------------------------------------------------------+
; Name : f_send_pkt
;
; Description: Transmit legth bytes of data, starting at buffer.
;
; Enter : AH = 4
;		CX = length.
;	     DS:SI = buffer
;
; Return :
;		Error - carry flag set, DH = error code.
;	    Non-error - carry flag clear.
;
; Destroied registers :
;
;---------------------------------------------------------------------+
IFDEF OK
Start_send_msg	DB	'DM9100 Debug: Send starting'
		DB	CR, LF
Start_send_msg_len  EQU     $ - Start_send_msg

End_send	DB	'DM9100 Debug: Send Ending'
		DB	CR, LF
End_send_len	EQU	$ - End_send
ENDIF	;; OK

EXTRN	PortPrintMessage : NEAR

	PUBLIC	f_send_pkt
f_send_pkt:

; Enter with ds:si -> buffer, cx = length
; XXX Should re-enable interrupts here, but some drivers are broken.
; Possibly re-enable interrupts.
;	test _F[bp], EI 	; Were interrupts enabled on pkt driver entry?
;	je	f_send_pkt_1	; No.
;	sti			; Yes, re-enable interrupts now.
;f_send_pkt_1:

;following two instructions not needed because si and cx haven't been changed.
;	mov	si,_SI[bp]
;	mov	cx,_CX[bp]	; count of bytes in the packet.

.386
;;	  inc	 packets_out


;;	  xor	  eax, eax
;;	  mov	  ax, cx
;;	  add	 bytes_out,eax		  ;add up the received bytes.
	push	ds
	mov	ds,_DS[bp]	; address of buffer from caller's ds.
	assume	ds:nothing, es:nothing

IFDEF OK
;;-- Show send start
	push	si
	push	cx
	mov	si, OFFSET Start_send_msg
	mov	cx, Start_send_msg_len
	call	PortPrintMessage
	pop	cx
	pop	si
ENDIF	;; OK

; If -n option take Ethernet encapsulated Novell IPX packets (from BYU's
; PDSHELL) and change them to be IEEE 802.3 encapsulated.

EPROT_OFF	equ	EADDR_LEN*2

	test	cs:flagword,N_OPTION
	jnz	f_send_pkt_2
	call	send_pkt
	pop	ds

;;-- Delay
;;	  pusha
;;	  popa

IFDEF OK
;;-- Show send end
	push	si
	push	cx
	mov	si, OFFSET End_send
	mov	cx, End_send_len
	call	PortPrintMessage
	pop	cx
	pop	si
ENDIF	;; OK

	ret

f_send_pkt_2:
	cmp	ds:[si].EPROT_OFF,3781h ; if not Novell (prot 8137)
	jne	f_send_pkt_3		;  don't tread on it
	push	ax			; get scratch reg
	mov	ax,[si].EPROT_OFF+4	; get len
	xchg	ah,al
	inc	ax			; make even (rounding up)
	and	al,0feh
	xchg	ah,al
	mov	ds:[si].EPROT_OFF,ax	; save in prot field
	pop	ax			; restore old contents

f_send_pkt_3:
	call	send_pkt
	pop	ds
	ret
;;---------------------------------------------------------------------------

;--------------------------------------------------------------------+
; Name : f_as_send_pkt
;
; Description: Transmit legth bytes of data, starting at buffer.
;		When the application's data has been copied out of the buffer,
;		and the application can safely modify or re-use the buffer.
;
;
; Enter : AH = 11 or 12
;	     ES:DI = iocb structure
;     * following are described in the specification.
;     * 	CX = length.
;     *      DS:SI = buffer
;     *      ES:DI = upcall function
;
; Return :
;		Error - carry flag set, DH = error code.
;	    Non-error - carry flag clear.
;
; Destroied registers :
;
; Notes:
;	04/24/96 - as_send_pkt is not implemented yet.
;
;---------------------------------------------------------------------+
	assume	ds:code

f_as_send_pkt:

;es:di -> iocb.
	test	driver_function,4	; is this a high-performance driver? (2)
	jz	f_as_send_pkt_2 	; no. ;; not 5 or 6

; Possibly re-enable interrupts.
	test _F[bp], EI 		; Were interrupts enabled on pkt driver entry?
	je	f_as_send_pkt_1 	; No.
	sti				; Yes, re-enable interrupts now.

f_as_send_pkt_1:
	push	ds			; set up proper ds for the buffer
	lds	si,es:[di].buffer	; ds:si -> buffer(upper layer)
	assume	ds:nothing
	mov	cx,es:[di].len		; cx = length
	inc    packets_out
	movzx	eax, cx
	add    bytes_out,eax		; add up the received bytes.

;ds:si -> buffer, cx = length, es:di -> iocb.
	call	as_send_pkt		;; Not yet implements
	pop	ds
	assume	ds:code
	ret

f_as_send_pkt_2:
	mov dh, BAD_COMMAND		; return an error.
	stc
	ret
;;------------------------------------------------------------------------



;--------------------------------------------------------------------+
; Name : f_drop_pkt
;
; Description: This function is not described in the specification.
;
;
; Enter : AH = 13
;
; Return :
;		Error - carry flag set, DH = error code.
;	    Non-error - carry flag clear.
;
; Destroied registers :
;
;---------------------------------------------------------------------+

f_drop_pkt:

; es:di -> iocb.
	test	driver_function,4	; is this a high-performance driver?
	jz	f_as_send_pkt_2 	; no.
	push	ds			; Preserve ds
	mov	si,offset send_head	; Get head offset

dp_loop:
	mov	ax,ds:[si]		; Get offset
	mov	dx,ds:[si+2]		; Get segment
	mov	bx,ax
	or	bx,dx			; End of list?
	je	dp_endlist		; Yes

	cmp	ax,di			; Offsets equal?
	jne	dp_getnext		; No

	mov	bx,es
	cmp	dx,bx			; Segments equal?
	jne	dp_getnext		; No

	call	drop_pkt		; Pass to driver ;; Not implement yet
	les	di,es:[di].next 	; Get next segment:offset
	mov	ds:[si],di		; Set next offset
	mov	ds:[si+2],es		; Set next segment
	pop	ds			; Restore ds
	clc
	ret

dp_getnext:
	mov	ds,dx			; Get next segment
	mov	si,ax			; Get next iocb offset
	lea	si,ds:[si].next 	; Get next iocb next ptr offset
	jmp	dp_loop 		; Try again

dp_endlist:
	pop	ds			; Restore ds
	mov	dh,BAD_IOCB		; Return error
	stc				; Set carry
	ret
;;--------------------------------------------------------------------------



;--------------------------------------------------------------------+
; Name : f_terminate
;
; Description: Terminate the driver associated with handle.
;		Release memory.
;
; Enter : AH = 5
;		BX = Handle.
;
; Return :
;		Error - carry flag set, DH = error code.
;	    Non-error - carry flag clear.
;
; Destroied registers :
;
;---------------------------------------------------------------------+

f_terminate:
	call	verify_handle		; must have a handle
	mov	[bx].in_use,0		; mark handle as free
	call	count_handles		; all handles gone?
					; return CL = Number of handles
	or	cl,cl
	jne	f_terminate_4		; no, can't exit completely

;;;;++, 2000.6.27, htho, fix noise level bug
	extrn	Reset_MAC_PHY: near
	call	Reset_MAC_PHY
;;;;++
;
; Now disable interrupts
;
	mov	al,int_no
	or	al,al			;are they using a hardware interrupt?
	je	f_terminate_no_irq	;no.
	cmp	original_mask,0 	;was it enabled?
	je	f_terminate_no_mask	;yes, don't mask it now.
	call	maskint

f_terminate_no_mask:
;
; Now return the interrupt to their handler.
;
	mov	ah,25h			;get the old interrupt into es:bx
	mov	al,int_no
	add	al,8
	cmp	al,8+8			;is it a slave 8259 interrupt?
	jb	f_terminate_3		;no.
	add	al,70h - (8+8)		;map it to the real interrupt.

f_terminate_3:
	push	ds
	lds	dx,their_recv_isr
	int	21h
	pop	ds

f_terminate_no_irq:
	cmp	their_timer.segm,0	;did we hook the timer interrupt?
	je	f_terminate_no_timer
;; -+, 971230, Hu-Tiong, inherent bug of public domain code
	mov	ax,2508h		;restore the timer interrupt.
;;;;	mov	ax,251Ch		;restore the timer interrupt.
;; -+
	push	ds
	lds	dx,their_timer
	int	21h
	pop	ds

f_terminate_no_timer:
	call	terminate		;terminate the hardware.
					;; Do nothing
	mov	al,entry_point	;release our_isr.
	mov	ah,25h
	push	ds
	lds	dx,their_isr
	int	21h
	pop	ds
;
; Now free our memory
;
	movseg	es,cs
	mov	ah,49h
	int	21h
	clc
	ret

f_terminate_4:
	mov	dh, CANT_TERMINATE
	stc
	ret
;;--------------------------------------------------------------------------



;--------------------------------------------------------------------+
; Name : f_get_address
;
; Description: Get the current local net address.
;
; Enter : AH = 6
;		BX = Handle.
;	     ES:DI = store address buffer.
;		CX = buffer size
;
; Return :
;		Error - carry flag set, DH = error code.
;	    Non-error - carry flag clear, CX = copied length.
;
; Destroied registers :
;
;---------------------------------------------------------------------+

f_get_address:
;	call	verify_handle
;	mov	es,_ES[bp]		; get new one
;	mov	di,_DI[bp]		; get pointer, es:di is ready
;	mov	cx,_CX[bp]		;Tell them how much room they have.

	cmp	cx,address_len		;is there enough room for our address?
	jb	get_address_space	;no.
	mov	cx,address_len		;yes - get our address length.
	mov	_CX[bp],cx		;Tell them how long our address is.
	mov	si,offset my_address	;copy it into their area.
	rep	movsb
	clc
	ret

get_address_space:
	mov	dh,NO_SPACE
	stc
	ret

;;----------------------------------------------------------------------------


EXTRN	PortUpdateReceiveFilter : NEAR
;--------------------------------------------------------------------+
; Name : f_set_address
;
; Description:
;
; Enter : AH = 25
;		ES:DI = address.
;		   CX = length
;
; Return :
;		Error - carry flag set, DH = error code.
;	    Non-error - carry flag clear, CX = length.
;
; Destroied registers :
;
;---------------------------------------------------------------------+

f_set_address:
	call	count_handles
	cmp	cl,1			;more than one handle in use?
	ja	f_set_address_inuse	;yes - we can't set the address

	mov	cx,_CX[bp]		;get the desired address length.
	cmp	ch,0
	cmp	cl,parameter_list[3]	;is it the right length?
	ja	f_set_address_too_long	;no.

	mov	ds,_ES[bp]		; set new one
	assume	ds:nothing
	mov	si,_DI[bp]		; set pointer, ds:si is ready
	mov	ax,cs
	mov	es,ax
	mov	di,offset my_address
	rep	movsb
	mov	ds,ax			;restore ds.
	assume	ds:code
	mov	cx,_CX[bp]		;get the desired address length.
	mov	si,offset my_address

;;	call	set_address
	call	PortUpdateReceiveFilter

	jc	f_set_address_err	;Did it work?
	mov	cl,parameter_list[3]
	xor	ch,ch
	mov	_CX[bp],cx		;yes - return our address length.
	clc
	ret

f_set_address_inuse:
	mov	dh,CANT_SET
	stc
	ret

f_set_address_too_long:
	mov	dh,NO_SPACE
	stc
	ret

f_set_address_err:

;we get here with cy set - leave it set.
	mov	si,offset rom_address	;we can't set the address, restore
	mov	di,offset my_address	;  to original.
	mov	cx,MAX_ADDR_LEN/2
	rep	movsw
	ret
;;--------------------------------------------------------------------------



;--------------------------------------------------------------------+
; Name : f_reset_interface
;
; Description: Resets the interface associated with handle to a known
;		state, aborting any transmits in process and reinitializing
;		the receiver.
;	       (1)The local net address is reset to the default(from ROM),
;		(2)the multicast list is cleared, and (3)the receive mode
;		is set to 3(own address & broadcasts).
;	       (4)If multiple handles are open, these actions might seriously
;		disrupt other applications using the interface,
;		so CANT_RESET should be returned.
;
; Enter : AH = 7
;		BX = Handle.
;
; Return :
;		Error - carry flag set, DH = error code.
;	    Non-error - carry flag clear, CX = copied length.
;
; Destroied registers :
;
;---------------------------------------------------------------------+

f_reset_interface:
	call	verify_handle		;; Handle is real.
	jc	reset_bad_handle
	call	count_handles		;; (4)
	cmp	cl, 1
	ja	multihandle

	call	PortUpdateReceiveFilter ;; (1), (2), (3)
;;	call	reset_interface
	clc
	ret

reset_bad_handle:
	ret

multihandle:
	stc
	mov	dh, CANT_RESET
	ret
;;-------------------------------------------------------------------------




;--------------------------------------------------------------------+
; Name : f_stop
;
; Description: This function is not specified in the PC/TCP Version 1.09
;		Packet Driver Specification.
;	       Stop the packet driver doing upcalls.
;	       Also a following terminate will always succed
;	       (no in use handles any longer).
;
; Enter : AH = 8
;
; Return :
;	    carry flag clear.
;
; Destroied registers :
;
;---------------------------------------------------------------------+

f_stop:
	mov	bx,offset handles

f_stop_2:
	mov	[bx].in_use,0
	add	bx,(size per_handle)	; next handle
	cmp	bx,offset end_handles
	jb	f_stop_2
	clc
	ret
;;--------------------------------------------------------------------------


;--------------------------------------------------------------------+
; Name : f_get_parameters
;
; Description: High-performance driver function.
;
;
; Enter : AH = 10
;
; Return :
;	    carry flag clear.
;
; Destroied registers :
;
;---------------------------------------------------------------------+
f_get_parameters:

;strictly speaking, this function only works for high-performance drivers.
	test	driver_function,4	;is this a high-performance driver?
	jne	f_get_parameters_1	;yes.
	mov	dh,BAD_COMMAND		;no - return an error.
	stc
	ret

f_get_parameters_1:
	mov	_ES[bp],cs
	mov	_DI[bp],offset parameter_list
	clc
	ret
;;---------------------------------------------------------------------------




;--------------------------------------------------------------------+
; Name : count_handles
;
; Description: Count handles currently in use.
;
; Enter :
;
; Return : CL = Number of handles currently in use.
;
; Destroied registers :
;
;---------------------------------------------------------------------+
count_handles:
;exit with cl = number of handles currently in use.
	mov	bx,offset handles
	mov	cl,0			;number of handles in use.

count_handles_1:
	add	cl,[bx].in_use		;is this handle in use?
	add	bx,(size per_handle)	;go to the next handle.
	cmp	bx,offset end_handles
	jb	count_handles_1

	ret
;;--------------------------------------------------------------------



;--------------------------------------------------------------------+
; Name : verify_handle
;
; Description: Ensure that handle is real.
;
; Enter :
; Return : Error - carry flag set.
;
; Destroied registers :
;
;---------------------------------------------------------------------+

verify_handle:
;Ensure that their handle is real.  If it isn't, we pop off our return
;address, and return to *their* return address with cy set.
	mov	bx,_BX[bp]		;get the handle they gave us
	cmp	bx,offset handles
	jb	verify_handle_bad	;no - must be bad.
	cmp	bx,offset end_handles
	jae	verify_handle_bad	;no - must be bad.
	cmp	[bx].in_use,1		;if it's not in use, it's bad.
	jne	verify_handle_bad
	ret

verify_handle_bad:
	mov	dh,BAD_HANDLE
	add	sp,2			;pop off our return address.
	stc
	ret

	public	set_recv_isr
set_recv_isr:
	mov	ah,35h			;get the old interrupt into es:bx
	mov	al,int_no		; board's interrupt vector
	or	al,al
	je	set_isr_no_irq
	add	al,8
	cmp	al,8+8			;is it a slave 8259 interrupt?
	jb	set_recv_isr_1		;no.
	add	al,70h - 8 - 8		;map it to the real interrupt.

EXTRN	InterruptMain : NEAR

set_recv_isr_1:
	int	21h
	mov	their_recv_isr.offs,bx	;remember the old seg:off.
	mov	their_recv_isr.segm,es

	mov	ah,25h			;now set our recv interrupt.
;;	  mov	  dx,offset recv_isr
	mov	dx, OFFSET InterruptMain
	int	21h

	cmp	byte ptr timer_isr,0cfh ;is there just an iret at their handler?
	je	set_isr_no_timer	;yes, don't bother hooking the timer.

;;-+, 980929, Hu-Tiong
;;	mov	ax,351Ch		;get the old interrupt into es:bx
	mov	ax,3508h		;get the old interrupt into es:bx
;;-+
	int	21h

	mov	their_timer.offs,bx	;remember the old seg:off.
	mov	their_timer.segm,es

;;-+, 980929, Hu-Tiong
;;	mov	ax,251Ch		  ;now set our recv interrupt.
	mov	ax,2508h		  ;now set our recv interrupt.
;;-+
	mov	dx,offset timer_isr
	int	21h

set_isr_no_timer:
	mov	al,int_no		; Now enable interrupts
	call	unmaskint
	mov	original_mask,al

set_isr_no_irq:
	ret



;;	  public  count_in_err
;;
;;count_in_err:
;;
;;	  assume  ds:nothing
;;
;;	  add	 errors_in,1
;;
;;	  ret
;;
;;
;;
;;	  public  count_out_err
;;
;;count_out_err:
;;
;;	  assume  ds:nothing
;;
;;	  inc	 errors_out
;;
;;	  ret

recv_isr_frame	struc
  recv_isr_ds	  dw	  ?
  recv_isr_dx	  dw	  ?
  recv_isr_ax	  dw	  ?
  recv_isr_ip	  dw	  ?
  recv_isr_cs	  dw	  ?
  recv_isr_f	  dw	  ?
recv_isr_frame	ends

IFDEF OK
;
; I have had a problem with some hardware which under extreme LAN loading
; conditions will re-enter the recv_isr. Since the 8259 interrupts for
; the card are masked off, and the card's interrupt mask register is
; cleared (in 8390.asm at least) disabling the card from interrupting, this
; is clearly a hardware problem. Due to the low frequencey of occurance, and
; extreme conditions under which this happens, it is not lilely to be fixed
; in hardware any time soon, plus retrofitting of hardware in the field will
; not happen. To protect the driver from the adverse effects of this I am
; adding a simple re-entrancy trap here.  - gft - 910617
;

in_recv_isr	db	0	; flag to trap re-entrancy

recv_isr:
	cmp	in_recv_isr, 0
	jne	recv_iret
	mov	in_recv_isr, 1

; I realize this re-entrancy trap is not perfect, you could be re-entered
; anytime before the above instruction. However since the stacks have not
; been swapped re-entrancy here will only appear to be a spurious interrupt.
; - gft - 910617

; In order to achieve back-to-back packet transmissions, we handle the
; latency-critical portion of transmit interrupts first.  The xmit
; interrupt routine should only start the next transmission, but do
; no other work.  It may only touch ax and dx (the only register necessary
; for doing "out" instructions) unless it first pushes any other registers
; itself.
	push	ax
	push	dx
	call	xmit		;; DO nothing

; Now switch stacks, push remaining registers, and do remaining interrupt work.
	push	ds

	mov	ax,cs			;ds = cs.
	mov	ds,ax
	assume	ds:code
	mov	savesp,sp
	mov	savess,ss
	mov	ss,ax			;SS = CS
	mov	sp,offset our_stack	;; 128 words
	cld

	push	bx
	push	cx
	push	si
	push	di
	push	bp
	push	es

; The following comment is wrong in that we now do a specific EOI command,
; and because we don't enable interrupts (even though we should).

; Chips & Technologies 8259 clone chip seems to be very broken.  If you
; send it a Non Specific EOI command, it clears all In Service Register
; bits instead of just the one with the highest priority (as the Intel
; chip does and clones should do).  This bug causes our interrupt
; routine to be reentered if: 1. we reenable processor interrupts;
; 2. we reenable device interrupts; 3. a timer or other higher priority
; device interrupt now comes in; 4. the new interrupting device uses
; a Non Specific EOI; 5. our device interrupts again.  Because of
; this bug, we now completely mask our interrupts around the call
; to "recv", the real device interrupt handler.  This allows us
; to send an EOI instruction to the 8259 early, before we actually
; reenable device interrupts.  Since the interrupt is masked, we
; are still guaranteed not to get another interrupt from our device
; until the interrupt handler returns.	This has another benefit:
; we now no longer prevent other devices from interrupting while our
; interrupt handler is running.  This is especially useful if we have
; other (multiple) packet drivers trying to do low-latency transmits.

	mov	al,int_no	; Disable further device interrupts
	call	maskint 	;; Disable the IRQ on PIC

; The following is from Bill Rust, <wjr@ftp.com>
; this code dismisses the interrupt at the 8259. if the interrupt number
;  is > 8 then it requires fondling two PICs instead of just one.
	mov	al, int_no	; get hardware int #
	cmp	al, 8		; see if its on secondary PIC
	jg	recv_isr_4
;; IRQ < 8
	add	al, 60h 	; make specific EOI dismissal
	out	20h, al
	jmp	recv_isr_3	; all done

;; IRQ > 8
recv_isr_4:
	add	al,60h - 8	; make specific EOI (# between 9 & 15).
	out	0a0h,al 	; Secondary 8259 (PC/AT only)
	mov	al,62h		; Acknowledge on primary 8259.
;; Clear 1st PIC ISR bit
	out	20h,al

recv_isr_3:
;	sti				; Interrupts are now completely safe

	call	recv

	cli				;interrupts *must* be off between
					;here and the stack restore, because
					;if we have one of our interrupts
					;pending, we would trash our stack.

	mov	al,int_no	; Now reenable device interrupts
	call	unmaskint
	pop	es
	pop	bp
	pop	di
	pop	si
	pop	cx
	pop	bx

	mov	ss,savess
	mov	sp,savesp

	pop	ds
	assume	ds:nothing
	pop	dx
	pop	ax
	mov	in_recv_isr, 0	; clear the re-entrancy flag - gft - 901617

recv_iret:

	iret
ENDIF	;;OK


recv_exiting_flag	db	0	;nonzero if recv_exiting will be run.

recv_exiting_addr	dd	?



	public	schedule_exiting

schedule_exiting:

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

	cmp	recv_exiting_flag,0	;is it already scheduled?

	jne	schedule_exiting_1	;yes, don't do it again!

	inc	recv_exiting_flag	;set the flag.

	les	di,savespss		;make es:di -> their stack.

	xchg	ax,es:[di].recv_isr_ip	;stuff our routine's address in,

	mov	recv_exiting_addr.offs,ax	;and save the original address.

	mov	ax,cs

	xchg	ax,es:[di].recv_isr_cs

	mov	recv_exiting_addr.segm,ax

schedule_exiting_1:

	ret



	public	recv_exiting_exit

recv_exiting_exit:

;recv_exiting jumps here to exit, after pushing the flags.

	cli				;protect the following semaphore.

	mov	recv_exiting_flag,0

	push	recv_exiting_addr.segm

	push	recv_exiting_addr.offs

	iret





	public	maskint

maskint:
	or	al,al			;are they using a hardware interrupt?
	je	maskint_1		;no, don't mask off the timer!
	assume	ds:code

	mov	dx,21h			;assume the master 8259.
	cmp	al,8			;using the slave 8259 on an AT?
	jb	mask_not_irq2
	mov	dx,0a1h 		;go disable it on slave 8259
	sub	al,8

mask_not_irq2:
	mov	cl,al
	in	al,dx			;disable them on the correct 8259.
	mov	ah,1			;set the bit.
	shl	ah,cl
	or	al,ah

;
; 500ns Stall required here, per INTEL documentation for eisa machines
; - gft - 910617
;
	push	ax
	in	al,61h	; 1.5 - 3 uS should be plenty
	in	al,61h
	in	al,61h
	pop	ax
	out	dx,al

maskint_1:
	ret

;--------------------------------------------------------------------+
; Name : unmaskint
;
; Description: Enable the interrupt.
;
; Enter : AL = interrupt number.
;
; Return :
;	CL = 0 if the interrupt had been enabled.
;	AL = Interrupt Mask Register.
;
; Destroied registers :
;
;---------------------------------------------------------------------+
	public	unmaskint
unmaskint:
;exit with cl = 0 if the interrupt had been enabled.
	assume	ds:code
	mov	dx,21h			;assume the master 8259.
	mov	cl,al
	cmp	cl,8			;using the slave 8259 on an AT?
	jb	unmask_not_irq2 	;no
	in	al,dx			;get master mask (21h)
	push	ax
	in	al,61h			;wait lots of time.
	in	al,61h
	in	al,61h
	pop	ax
	and	al,not (1 shl 2)	; and clear slave cascade bit in mask
	out	dx,al			;set new master mask (enable slave int)

;
; 500ns Stall required here, per INTEL documentation for eisa machines
; - gft - 910617
;
	push	ax
	in	al,61h	; 1.5 - 3 uS should be plenty
	in	al,61h
	in	al,61h
	pop	ax
	mov	dx,0a1h 		;go enable int on slave 8259
	sub	cl,8

unmask_not_irq2:
	in	al,dx			;enable interrupts on the correct 8259.
	mov	ah,1			;clear the bit.
	shl	ah,cl
	mov	cl,al			;remember the original mask.
	and	cl,ah			;; Return CL=0 if all enable
	cmp	cl, 0
	je	unmask_ret		;; All enable
	not	ah
	and	al,ah
;
; 500ns Stall required here, per INTEL documentation for eisa machines
; - gft - 910617
;
	push	ax
	in	al,61h	; 1.5 - 3 uS should be plenty
	in	al,61h
	in	al,61h
	pop	ax
	out	dx,al

unmask_ret:
	ret


	public	recv_locate

recv_locate:
;called when we want to determine what to do with a received packet.
;enter with es:di -> packet type, dl = packet class.
;exit with cy if the packet is not desired, or nc if we know its type.
	assume	ds:code, es:nothing

; If -n option take IEEE 802.3 encapsulated packets that could be Novell IPX
; and make them Ethernet encapsulated Novell IPX packets (for PDSHELL).
	test	flagword,N_OPTION
	jz	not_n_op

; Make IEEE 802.3-like packets that could be Novell IPX into BlueBook class
; Novell type 8137 packets.
	cmp	dl,IEEE8023		;Is this an IEEE 802.3 packet?
	jne	recv_not_802_3		;no
	cmp	word ptr es:[di],0ffffh ;if this word not ffff
	jne	recv_not_8137		;  then not Novell
	sub	di,2			; back it up to the 8137 word.
	mov	es:[di],3781h		; fake as Novell protocol (8137)
	mov	dl,BLUEBOOK
	jmp	short recv_not_8137

recv_not_802_3:
; Convert incoming Ethernet type 8137 IPX packets to type 8138, as with -n in
; effect we can't send type 8137, and it will only confuse Netware.
	cmp	dl,BLUEBOOK		;Is this a BLUEBOOK packet?
	jne	recv_not_8137		;no, don't change it.
	cmp	word ptr es:[di],3781h	;Is it an 8137 packet?
	jne	recv_not_8137		;no, don't change it.
	mov	es:[di],word ptr 3881h	;yes, mung it slightly.

recv_not_8137:

not_n_op:
	mov	bx,offset handles

recv_find_1:
	cmp	[bx].in_use,0		;is this handle in use?
	je	recv_find_2		;no - don't check the type.
	mov	ax,[bx].receiver.offs	;do they have a receiver?
	or	ax,[bx].receiver.segm
	je	recv_find_2		;no - they're not serious about it.

;per request by the UK people, we match on IEEE 802.3 classes, then types.
;for all others, we match on type, then class.	This lets their software work
;without breaking BLUEBOOK type length=0 clients.
	cmp	[bx].class,IEEE8023	;is this an IEEE 802.3 handle
	jne	recv_find_7		;no.
	cmp	dl,IEEE8023		;is the packet also IEEE 802.3?
	jne	recv_find_2		;no, give up on it now.

;; BlueBook
recv_find_7:
	mov	cx,[bx].packet_type_len ;compare the packets.
	lea	si,[bx].packet_type
	jcxz	recv_find_3		;if cx is zero, they want them all.
	cmp	[bx].class, dl		;is this the right class?
	jne	recv_find_2		;no- don't bother
	push	di
	repe	cmpsb
	pop	di
	je	recv_find_3		;we've got it!

recv_find_2:
;; in_use=0.
	add	bx,(size per_handle)	;go to the next handle.
	cmp	bx,offset end_handles
	jb	recv_find_1
	inc    packets_dropped		;count it as dropped.
;;	  sti
	stc
	ret

recv_find_3:
;; frame_type_len = 0
;; Class match.
	mov	found_handle,bx 	;remember what our handle was.
	clc
	ret


	public	recv_find, recv_found

recv_find:
;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.
;exit with es:di = 0 if the packet is not desired, or es:di -> packet buffer
;  to be filled by the driver.
	assume	ds:code, es:nothing
	push	cx			;preserve packet length.
	push	si			;; preserve descriptor pointer
	call	recv_locate		;search for the packet type.
	pop	si
	pop	cx
	jc	recv_find_5		;we didn't find it -- discard it.

recv_found:
;called to do the first upcall.(Ref. access_type() function )
;exit with es:di = 0 if the packet is not desired, or es:di -> packet buffer
;  to be filled by the driver.
	inc    packets_in
	add    bytes_in, ecx		 ;add up the received bytes.

	mov	bx,found_handle
	les	di,[bx].receiver	;remember the receiver upcall.
	mov	receive_ptr.offs,di
	mov	receive_ptr.segm,es
	test	flagword,W_OPTION	;did they select the Windows option?
	je	recv_find_6		;no, don't check for the upcall.

; does the receiver signature match whats currently in memory?	if not,
; jump to fake return
	push	si
	push	cx
	lea	si,[bx].receiver_sig
	mov	cx,8/2
	repe	cmpsw
	pop	cx
	pop	si
	je	recv_find_6

recv_find_5:
	xor	di,di			;"return" a null pointer.
	mov	es,di
	ret

recv_find_6:
	mov	ax,0			;allocate request.
	stc				;with stc, flags must be an odd number
	push	ax			; save a number that cant be flags
	pushf				;save flags in case iret used.
	call	receive_ptr		;ask the client for a buffer.

	; on return, flags should be at top of stack. if an IRET has been used,
	; then 0 will be at the top of the stack
	pop	bx
	cmp	bx,0
	je	recv_find_4		;0 is at top of stack
	add	sp,2

recv_find_4:
	ret


	public	recv_copy
recv_copy:
;called after we have copied the packet into the buffer.
;enter with ds:si ->the packet, cx = length of the packet.
;preserve bx.
	assume	ds:nothing, es:nothing

	push	bx
	mov	bx,found_handle

	mov	ax,1			;store request.
	clc				;with clc, flags must be an even number
	push	ax			; save a number that can't be flags
	pushf				;save flags incase iret used.

	call	receive_ptr		;ask the client for a buffer.

	pop	bx
	cmp	bx,1			;if this is a 1, IRET was used.
	je	recv_copy_1
	pop	bx
recv_copy_1:
	pop	bx

	ret



	public	send_queue

send_queue:

; Queue an iocb.

; Enter with es:di -> iocb, interrupts disabled.

; Destroys ds:si.

	assume	ds:nothing, es:nothing

	mov	es:[di].next.offs,0	; Zero next offset

	mov	es:[di].next.segm,0	; Zero next segment

	mov	si,send_head.offs	; Queue empty?

	or	si,send_head.segm

	jnz	sq_notempty		; No

	mov	send_head.offs,di	; Set head offset

	mov	send_head.segm,es	; Set head segment

	jmp	sq_settail

sq_notempty:				; Queue is not empty

	lds	si,send_tail		; Get tail segment:offset

	mov	ds:[si].next.offs,di	; Set next offset

	mov	ds:[si].next.segm,es	; Set next segment

sq_settail:

	mov	send_tail.offs,di	; Set tail offset

	mov	send_tail.segm,es	; Set tail segment

	ret





	public	send_dequeue

send_dequeue:

; Dequeue an iocb and possibly call its upcall.

; Enter with device or processor interrupts disabled, ah = return code.

; Exits with es:di -> iocb; destroys ds:si, ax, bx, cx, dx, bp.

	assume	ds:nothing, es:nothing

	les	di,send_head		; Get head segment:offset

	lds	si,es:[di].next 	; Get next segment:offset

	mov	send_head.offs, si	; Set head offset

	mov	send_head.segm, ds	; Set head segment

	or	es:flags[di], DONE	; Mark done

	mov	es:ret_code[di], ah	; Set retcode

	test	es:[di].flags,CALLME	; Does he want an upcall?

	je	send_dequeue_1		; No.

	push	es			; Push iocb segment

	push	di			;  and offset

	clc				; Clear carry.

	mov	ax,1			; Push a number that cant be flags.

	push	ax

	pushf				; Save flags in case iret used.

	call	es:[di].upcall		; Call the client.

	pop	ax			; Pop first word.

	cmp	ax,1			; If this is a 1, IRET was used.

	je	send_dequeue_2		; Far return used.

	add	sp,2			; Pop flags.

send_dequeue_2:

	pop	di			; Pop iocb segment

	pop	es			;  and offset

send_dequeue_1:

	ret

code	ends

	end	start

