	include	defs.asm

;  Copyright, 1988, 1989, Russell Nelson

;   This program is free software; you can redistribute it and/or modify
;   it under the terms of the GNU General Public License as published by
;   the Free Software Foundation, version 1.
;
;   This program is distributed in the hope that it will be useful,
;   but WITHOUT ANY WARRANTY; without even the implied warranty of
;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;   GNU General Public License for more details.
;
;   You should have received a copy of the GNU General Public License
;   along with this program; if not, write to the Free Software
;   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

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

	public	phd_environ
	org	2ch
phd_environ	dw	?

	public	phd_dioa
	org	80h
phd_dioa	label	byte

	org	100h
start:
	jmp	start_1
	extrn	start_1: near

;we use our dioa for a stack space.  Very hard usage has shown that only
;  27 bytes were being used, so 128 should be sufficient.
our_stack	label	byte


	extrn	int_no: byte
	public	packet_int_no, is_at
packet_int_no	db	?,?,?,?		; interrupt to communicate.
is_at		dw	?		; =1 if we're on an AT.

functions	label	word
	dw	f_driver_info		;function 1
	dw	f_access_type
	dw	f_release_type
	dw	f_send_pkt
	dw	f_terminate
	dw	f_get_address
	dw	f_reset_interface	;function 7
	dw	f_set_rcv_mode		;function 20
	dw	f_get_rcv_mode
	dw	f_set_multicast_list
	dw	f_get_multicast_list
	dw	f_get_statistics
	dw	f_set_address		;function 25


	extrn	driver_class: byte
	extrn	driver_type: byte
	extrn	driver_name: byte

	extrn	send_pkt: near
	extrn	get_address: near
	extrn	set_address: near
	extrn	reset_interface: near
	extrn	recv: near


MAX_P_LEN	equ	8		;leave plenty of room.

per_handle	struc
in_use		db	?		;non-zero if this handle is in use.
packet_type	db	MAX_P_LEN dup(?);associated packet type.
packet_type_len	dw	?		;associated packet type length.
receiver	dd	?		;receiver handler.
		db	?		;padding to get the length to 16.
per_handle	ends

  if (size per_handle) NE 16
	err	The size of the handle must be 16.
  endif

max_handle	equ	8
handles		db	max_handle*(size per_handle) dup(0)
end_handles	label	byte

free_handle	dw	?		;->a handle not in use.
found_handle	dw	?		;->the handle for our packet.
receive_ptr	dd	?		;->the receive routine.

savess		dw	?		;saved during the stack swap.
savesp		dw	?

regs	struc
_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


bytes	struc
	dw	?
	dw	?
	dw	?
	dw	?
	dw	?
_DL	db	?
_DH	db	?
_CL	db	?
_CH	db	?
_BL	db	?
_BH	db	?
_AL	db	?
_AH	db	?
bytes	ends

segmoffs	struc
offs		dw	?
segm		dw	?
segmoffs	ends


	public	our_isr, their_isr
their_isr	dd	?

our_isr:
	jmp	our_isr_0		;the required signature.
	db	'PKT DRVR',0

statistics_list	label	dword
packets_in	dw	?,?
packets_out	dw	?,?
bytes_in	dw	?,?
bytes_out	dw	?,?
errors_in	dw	?,?
errors_out	dw	?,?
packets_dropped	dw	?,?		;dropped due to no type handler.

linc	macro	n
	local	a
	inc	n			;increment the low word
	jne	a			;go if not overflow
	inc	n+2			;increment the high word
a:
	endm

	public	count_in_err
count_in_err:
	assume	ds:nothing
	linc	errors_in
	ret

	public	count_out_err
count_out_err:
	assume	ds:nothing
	linc	errors_out
	ret

our_isr_0:
	assume	ds:nothing
	push	ax
	push	bx
	push	cx
	push	dx
	push	si
	push	di
	push	bp
	push	ds
	push	es
	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 1		;start by clearing the carry flag.
	mov	bl,ah			;jump to the correct function.
	mov	bh,0
	cmp	bx,7			;highest function is 7.
	jbe	our_isr_3
	cmp	bx,20
	jb	our_isr_2
	cmp	bx,25
	ja	our_isr_2
	sub	bx,20-7-1		;map 20 right after 7.
our_isr_3:
	add	bx,bx			;*2
	jmp	functions-2[bx]		;table starts at 1.

f_set_rcv_mode:
f_get_rcv_mode:
f_set_multicast_list:
f_get_multicast_list:
our_isr_2:
	mov	dh,BAD_COMMAND
our_isr_error:
	mov	_DH[bp],dh
	or	_F[bp],1		;return their carry flag.
our_isr_return:
	pop	es
	pop	ds
	pop	bp
	pop	di
	pop	si
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	iret


f_driver_info:
;	Under 1.08, the handle is optional, so we no longer verify it.
;	call	verify_handle
	mov	_BX[bp],majver		;version
	mov	al,driver_class
	mov	_CH[bp],al
	mov	al,driver_type
	cbw
	mov	_DX[bp],ax
	mov	_CL[bp],0		;number zero.
	mov	_DS[bp],ds
	mov	_SI[bp],offset driver_name
	mov	_AL[bp],2		;extended driver
	jmp	our_isr_return


f_get_statistics:
	call	verify_handle		;just in case.
	mov	_DS[bp],ds
	mov	_SI[bp],offset statistics_list
	jmp	our_isr_return


access_type_class:
	mov	dh,NO_CLASS
	jmp	our_isr_error

access_type_type:
	mov	dh,NO_TYPE
	jmp	our_isr_error

access_type_number:
	mov	dh,NO_NUMBER
	jmp	our_isr_error

f_access_type:
	mov	al,driver_class
	cmp	_AL[bp],al		;our class?
	jne	access_type_class	;no.
	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?
	ja	access_type_inuse	;yes - can't be ours.
access_type_7:

; 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:
	cmp	[bx].in_use,0		;is this handle in use?
	je	access_type_5		;no - don't check the type.
	mov	es,_DS[bp]		;get a pointer to their type.
	mov	di,_SI[bp]
	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			;in case cx is zero.
	repe	cmpsb
	jne	short access_type_6	;go look at the next one.
access_type_inuse:
	mov	dh,TYPE_INUSE
	jmp	our_isr_error
access_type_5:
	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
	jb	access_type_4

	mov	bx,free_handle		;did we find a free handle?
	or	bx,bx
	je	access_type_handle	;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
	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 length.
	lea	di,[bx].packet_type
	rep	movsb
	pop	ds

	sub	bx,offset handles	;compute the handle from the offset.
	mov	cl,4
	shr	bx,cl
	mov	ax,cs			;add our phd in.
	add	bx,ax
	mov	_AX[bp],bx		;return the handle to them.

	jmp	our_isr_return


access_type_handle:
	mov	dh,BAD_HANDLE
	jmp	our_isr_error


f_release_type:
	call	verify_handle		;mark this handle as being unused.
	mov	[bx].in_use,0
	jmp	our_isr_return


f_send_pkt:
	linc	packets_out
	add	bytes_out.offs,cx	;add up the received bytes.
	adc	bytes_out.segm,0

	push	ds		; set up proper ds for the buffer
	mov	ds,_DS[bp]	; address for buffer
;following two instructions not needed because si and cx haven't been changed.
;	mov	si,_SI[bp]
;	mov	cx,_CX[bp]	; count of bytes

	call	send_pkt
	pop	ds
	jc	send_pkt_1
	jmp	our_isr_return
send_pkt_1:
	jmp	our_isr_error


f_terminate:
;
; Now disable interrupts
;
	mov	al,int_no
	call	maskint

;
; 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	terminate_1		;no.
	add	al,70h - (8+8)		;map it to the real interrupt.
terminate_1:
	push	ds
	lds	dx,their_recv_isr
	int	21h
	pop	ds

	mov	al,packet_int_no	;release our_isr.
	mov	ah,25h
	push	ds
	lds	dx,their_isr
	int	21h
	pop	ds

;
; Now free our memory
;
	push	cs
	pop	es
	mov	ah,49h
	int	21h

	jmp	our_isr_return


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.
	call	get_address
	jc	get_address_space	;no.
	mov	_CX[bp],cx		;Tell them how long our address is.
	jmp	our_isr_return

get_address_space:
	mov	dh,NO_SPACE
	jmp	our_isr_error


f_set_address:
	mov	bx,offset handles
	mov	dh,CANT_SET		;if a handle in use, we can't set it.
f_set_address_1:
	cmp	[bx].in_use,0		;is this handle in use?
	je	f_set_address_error	;yes - we can't set the address
	add	bx,(size per_handle)	;go to the next handle.
	cmp	bx,offset end_handles
	jb	f_set_address_1

	mov	ds,_ES[bp]		; set new one
	mov	si,_DI[bp]		; set pointer, ds:si is ready
;	mov	cx,_CX[bp]		;Tell them how much room they have.
	call	set_address
;set_address restores ds.
	jc	f_set_address_error	;no.
	mov	_CX[bp],cx		;Tell them how long our address is.
	jmp	our_isr_return

f_set_address_error:
	jmp	our_isr_error


f_reset_interface:
	call	verify_handle
	call	reset_interface
	jmp	our_isr_return


compute_handle:
;enter with the external representation of our handle in _BX[bp],
;exit with the offset of the handle in bx.
	mov	bx,_BX[bp]		;get the handle they gave us
	mov	cx,cs
	sub	bx,cx			;subtract off our phd
	mov	cl,4
	shl	bx,cl			;multiply by the size of the struct,
	add	bx,offset handles	;  and add the offset of the table.
	ret


verify_handle:
;Ensure that their handle is real.  Jump to our_isr_error if it
;isn't real.
	call	compute_handle
	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,0		;if it's not in use, it's bad.
	je	verify_handle_bad
	ret
verify_handle_bad:
	mov	dh,BAD_HANDLE
	add	sp,2			;pop off our return address.
	jmp	our_isr_error


	public	set_recv_isr
set_recv_isr:
	mov	ah,35h			;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	set_recv_isr_1		;no.
	add	al,70h - 8 - 8		;map it to the real interrupt.
set_recv_isr_1:
	int	21h
	mov	their_recv_isr.offs,bx
	mov	their_recv_isr.segm,es

	mov	ah,25h			;now set our recv interrupt.
	mov	dx,offset recv_isr
	int	21h
;
; Now enable interrupts
;
	mov	al,int_no
	call	unmaskint

	ret

their_recv_isr	dd	?

recv_isr:
	push	ax
	push	ds
	mov	ax,cs			;ds = cs.
	mov	ds,ax

	mov	savesp,sp
	mov	savess,ss

	mov	ss,ax
	mov	sp,offset our_stack
	cld

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

	call	recv

;
; The following code is ruthlessly stolen from Phil Karn's NET package.
;
	cmp	is_at,1
	jnz	recv_isr_3	; Only one 8259, so skip this stuff
	mov	al,0bh		; read in-service register from
	out	0a0h,al		; secondary 8259
	nop			; settling delay
	nop
	nop
	in	al,0a0h		; get it
	or	al,al		; Any bits set?
	jz	recv_isr_3	; nope, not a secondary interrupt
	mov	al,20h		; Get EOI instruction
	out	0a0h,al		; Secondary 8259 (PC/AT only)
recv_isr_3:
	mov	al,20h			;acknowledge the interrupt.
	out	20h,al

	pop	es
	pop	bp
	pop	di
	pop	si
	pop	dx
	pop	cx
	pop	bx

	mov	ss,savess
	mov	sp,savesp

	pop	ds
	pop	ax
	iret


maskint:
	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 enable it on slave 8259
	sub	al,8
mask_not_irq2:
	mov	cl,al

	in	al,dx			;enable interrupts on the correct 8259.
	mov	ah,1			;set the bit.
	shl	ah,cl
	or	al,ah
	out	dx,al

	ret


unmaskint:
	mov	dx,21h			;assume the master 8259.
	cmp	al,8			;using the slave 8259 on an AT?
	jb	unmask_not_irq2
	mov	dx,0a1h			;go enable it on slave 8259
	sub	al,8
unmask_not_irq2:
	mov	cl,al

	in	al,dx			;enable interrupts on the correct 8259.
	mov	ah,1			;clear the bit.
	shl	ah,cl
	not	ah
	and	al,ah
	out	dx,al

	ret


	public	recv_find
recv_find:
;called when we want to determine what to do with a received packet.
;enter with cx = packet length, es:di -> packet type.
	assume	ds:code, es:nothing
	push	cx

	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.
	mov	cx,[bx].packet_type_len	;compare the packets.
	lea	si,[bx].packet_type
	or	cx,cx			;in case cx is zero.
	push	di
	repe	cmpsb
	pop	di
	je	recv_find_3		;we've got it!
recv_find_2:
	add	bx,(size per_handle)	;go to the next handle.
	cmp	bx,offset end_handles
	jb	recv_find_1

	linc	packets_dropped

	pop	cx			;we didn't find it -- discard it.
	xor	di,di			;"return" a null pointer.
	mov	es,di
	jmp	short recv_find_4
recv_find_3:
	pop	cx			; the packet_length

	linc	packets_in
	add	bytes_in.offs,cx	;add up the received bytes.
	adc	bytes_in.segm,0

	mov	ax,[bx].receiver.offs
	mov	receive_ptr.offs,ax
	mov	ax,[bx].receiver.segm
	mov	receive_ptr.segm,ax

	sub	bx,offset handles	;compute the handle number from
	shr	bx,1			;  the handle offset.
	shr	bx,1
	shr	bx,1
	shr	bx,1
	mov	ax,cs			;add our phd in.
	add	bx,ax

	mov	found_handle,bx		;remember what our handle was.
	mov	ax,0			;allocate request.
	call	receive_ptr		;ask the client for a buffer.
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.
	assume	ds:nothing, es:nothing
	push	bx
	mov	bx,found_handle
	mov	ax,1			;store request.
	call	receive_ptr		;ask the client for a buffer.
	pop	bx
	ret

code	ends

	end	start
