	title PGLUE - Packet driver real-mode glue routines

	.286p

	include	data.inc

STACK	SEGMENT	para STACK 'STACK'
STACK	ENDS

_TEXT	SEGMENT  para PUBLIC 'CODE'

	IFDEF	EXE
	PUBLIC	_shared
_shared		real_data	<>	; must be first
	ENDIF

_TEXT	ENDS

_DATA	SEGMENT  WORD PUBLIC 'DATA'

	IFNDEF	EXE
	extrn	_shared:word
	ENDIF

_kbd_ack	db	?
		align	2

oldint08off	dw	0
oldint08seg	dw	0

oldint28off	dw	0
oldint28seg	dw	0

oldkbdoff	dw	0
oldkbdseg	dw	0

busyptr		dd	0

	IFDEF	EXE
rpsgen_off	dw	?
rpsgen_seg	dw	?
	ENDIF

	align	2
msb	dw	?	; ax
	dw	?	; bx
	dw	?	; cx
	dw	?	; dx
	dw	?	; flags
	dw	?	; si
	dw	?	; di
	dw	?	; ds
	dw	?	; es

	db	512 dup (?)
mystack	label	word
savess	dw	?
savesp	dw	?
cl_lock	db	0

_DATA	ENDS

	IFDEF	EXE
DGROUP	GROUP	_TEXT, _DATA
	ELSE
DGROUP	GROUP	_DATA
	ENDIF

	ASSUME cs:_TEXT, ds:nothing, es:nothing, ss:nothing

	extrn	_pkopen:near
	extrn	_pkclose:near
	extrn	@pkxmit:near
	IFNDEF	EXE
	EXTRN	OS386_Generate_Signal:FAR
	ENDIF

_TEXT	SEGMENT  para PUBLIC 'CODE'
	packet_funcs	dw	offset init		; 1
			dw	offset shutdown		; 2
			dw	offset kbd_init		; 3
			dw	offset _kbd_shutdown	; 4
			dw	offset kbd_command	; 5
			dw	offset mouse_init	; 6
			dw	offset _mouse_shutdown	; 7
	nfuncs	EQU	($ - offset packet_funcs) / 2

	IFDEF	EXE
init_rpc	PROC	FAR
	mov	dx,offset p_rpc
	mov	ax,DGROUP
	mov	ds,ax
	ASSUME	ds:DGROUP
	mov	rpsgen_off,bx
	mov	rpsgen_seg,es
	ret
init_rpc	ENDP
	ELSE
	EXTRN	main:near
init_rpc	EQU	main
	ENDIF

	ASSUME	ds:nothing

	; real procedure entry point
	; called by kernel with:
	; CX	= # bytes in transaction buffer
	; DS:DX = transaction buffer
	; return AX = # bytes returned
	;
; My procedure will use the buffer count to determine what to do.
; If CX =
;	0:	Send packet in xmit buffer in shared memory segment.  Replace
;			xmit length with return code.  As this is called
;			most often, make it efficient.
;	other:	Other function, determined by first byte in transaction
;			buffer.  1 = init, 2 = shutdown.
;	other calling convention: si = second word in buffer (after function
;	code) ds:dx = buffer.
	PUBLIC	p_rpc
p_rpc		PROC	FAR
	IFNDEF	EXE
	push	bp
	mov	bp,sp
	push	ds
	push	es
	mov	cx,10[bp]
	lds	dx,6[bp]
	ENDIF

	jcxz	short xmit
	push	si
	push	di

	mov	si,dx		; put buffer in si
	lodsw			; load al (function code) and ah
	dec	al
	cmp	al,nfuncs
	jae	err
	mov	bl,al
	xor	bh,bh
	shl	bx,1
	mov	ax,cs:packet_funcs[bx]
	call	ax
	jmp	short exit
err:
	xor	ax,ax
exit:
	pop	di
	pop	si
	IFNDEF	EXE
	pop	es
	pop	ds
	pop	bp
	ENDIF
	ret
xmit:
	mov	ax,DGROUP
	mov	ds,ax
	ASSUME	ds:DGROUP
	mov	ax,_shared.xmitlen
	cmp	_shared.arpqueued,2
	mov	bx,offset _shared.xmitbuf
	jnz	sendit
	mov	bx,offset _shared.xmitarp
sendit:
	call	@pkxmit
	mov	_shared.xmitlen,ax
	xor	ax,ax		; return length 0

	IFNDEF	EXE
	pop	es
	pop	ds
	pop	bp
	ENDIF

	ret
	ASSUME	ds:nothing
p_rpc		ENDP

init	PROC	NEAR
	push	ds		; save transaction buffer seg

	mov	ax,DGROUP
	mov	es,ax
	ASSUME	es:DGROUP
	lodsw			; packet class
	push	ax
	lodsw			; packet interrupt number
	push	ax
	add	si,2		; pad
	mov	di,offset _shared.myip
	movsw			; save my ip address in myip
	movsw			; IP address is 4 bytes (2 words)
	mov	si,dx		; save transaction buffer for output

	mov	ax,es
	mov	ds,ax
	ASSUME	ds:DGROUP

	call	_pkopen		; initialize the packet driver
	add	sp,4		; remove 2 arguments
	pop	es		; retrieve trans buf segment
	ASSUME	es:nothing
	mov	es:[si],ax	; return status from pkopen
	or	ax,ax
	jz	init_ok
	mov	ax,2		; return only 2 bytes (status)
	ret

init_ok:
	push	es

	mov	ah,34h		; get DOS busy flag
	int	21h
	mov	word ptr busyptr+2,es
	mov	word ptr busyptr,bx

	mov	ax,03508h
	int	21h
	mov	oldint08off,bx
	mov	oldint08seg,es
	mov	cx,ds		; save ds
	mov	ax,cs
	mov	ds,ax
	ASSUME	ds:_TEXT
	mov	dx,offset clock_int
	mov	ax,02508h
	int	21h

	mov	ds,cx		; restore ds
	ASSUME	ds:DGROUP

	mov	ax,03528h
	int	21h
	mov	oldint28off,bx
	mov	oldint28seg,es
	mov	ax,cs
	mov	ds,ax
	ASSUME	ds:_TEXT
	mov	dx,offset kbd_loop
	mov	ax,02528h
	int	21h

	mov	ds,cx		; restore ds
	ASSUME	ds:DGROUP

	pop	es
	ASSUME	es:nothing
	mov	word ptr es:[si+2],0
	mov	word ptr es:[si+4],offset _shared
	mov	word ptr es:[si+6],ds
	mov	dx,si		; restore buffer
	mov	ax,8		; 8 bytes returned

	ret
init	ENDP
	ASSUME	ds:nothing

shutdown	PROC NEAR
	push	ds
	mov	ax,DGROUP
	mov	ds,ax
	ASSUME	ds:DGROUP
	call	_pkclose

	mov	cx,ds		; save ds
	mov	dx,oldint08off
	or	dx,dx
	jz	shut_1
	mov	ax,02508h
	mov	ds,oldint08seg
	ASSUME	ds:nothing
	int	21h
shut_1:
	mov	ds,cx
	assume	ds:DGROUP
	mov	dx,oldint28off
	or	dx,dx
	jz	shut_2
	mov	ax,02528h
	mov	ds,oldint28seg
	ASSUME	ds:nothing
	int	21h
shut_2:

	pop	ds
	ASSUME	ds:nothing
	xor	ax,ax		; returned bytes = 0
	ret
shutdown	ENDP

	PUBLIC  @n_clicks
; u_long _near _fastcall n_clicks(void)
;
;	get the number of timer clicks from BIOS
;
@n_clicks  PROC	NEAR
	mov	ah,0
	int	1ah
	mov	ax,dx		; MSC uses AX-lo, DX-hi
	mov	dx,cx		; return values from interrupt 1A
	ret
@n_clicks ENDP

	ASSUME	ds:nothing
kbd_int	PROC far
	push	ax
	push	bx
	push	dx
	push	ds

	mov	ax,DGROUP
	mov	ds,ax
	ASSUME	ds:DGROUP

	in	al,60h
	; first, check for an ack
	mov	ah,al
	and	ah,0f0h
	cmp	ah,0f0h
	je	ack
	mov	dx,_shared.kbd_end
	mov	bx,dx
	inc	dx
	and	dx,31
	cmp	dx,_shared.kbd_start
	je	buf_full
	mov	_shared.kbd_buff[bx],al
	mov	_shared.kbd_end,dx
	or	_shared.input,1
	jmp	short buf_full
ack:
	mov	_kbd_ack,al
buf_full:
	mov	al,20h
	out	20h,al

	pop	ds
	pop	dx
	pop	bx
	pop	ax
	ASSUME	ds: nothing
	iret
kbd_int	ENDP

kbd_init	PROC near
	push	ds
	push	dx

	mov	ax,DGROUP
	mov	ds,ax
	ASSUME	ds:DGROUP

	mov	_shared.kbdopen,1
	sub	ax,ax
	mov	_shared.kbd_start,ax
	mov	_shared.kbd_end,ax

	mov	cx,ds		; save ds
	mov	ax,03509h
	int	21h
	mov	oldkbdoff,bx
	mov	oldkbdseg,es
	mov	ax,cs
	mov	ds,ax
	ASSUME	ds:_TEXT
	mov	dx,offset kbd_int
	mov	ax,02509h
	int	21h
	mov	ds,cx
	ASSUME	ds:DGROUP

	pop	dx
	pop	ds
	ASSUME	ds: nothing
	xor	ax,ax
	ret
kbd_init	ENDP

	PUBLIC	_kbd_shutdown
_kbd_shutdown	PROC near
	push	ds
	push	dx
	mov	ax,DGROUP
	mov	ds,ax
	ASSUME	ds:DGROUP
	xor	ax,ax
	mov	_shared.kbdopen,al
	mov	dx,oldkbdoff
	mov	ds,oldkbdseg
	ASSUME	ds:nothing
	mov	ax,02509h
	int	21h
	pop	dx
	pop	ds
	xor	ax,ax		; returned bytes = 0
	ret
_kbd_shutdown	ENDP

;; called with ds:dx pointing to buffer
kbd_command	PROC near
	mov	ax,ds
	mov	es,ax
	mov	ax,DGROUP
	mov	ds,ax
	ASSUME	ds:DGROUP
	mov	si,dx
	mov	bx,es:[si]	; get command in bh
rexmit:
	mov	es:[si],2000	; time out count
	cli
com_1:
	dec	word ptr es:[si]
	jz	com_2		; did we time out?
	in	al,64h
	or	al,2		; is ready bit set?
	jz	com_1
com_2:
	sti
	mov	_kbd_ack,0
	mov	al,bh
	out	60h,al

	mov	es:[si],5000	; wait for ack
com_3:
	dec	word ptr es:[si]
	jz	com_4		; did we time out?
	mov	al,_kbd_ack
	or	al,al
	jz	com_3
com_4:
	cmp	al,0feh
	je	rexmit
 	xor	ax,ax		; returned bytes = 0
	ret
kbd_command	ENDP

;	old_handle_off	dw	?
;	old_handle_seg	dw	?
;	old_handle_mask	dw	?

;;	struture on input
;;	0: code for routine
;;	2: mouse flags

mouse_init	PROC near
	push	dx
	mov	cx,ds:[si]	; load mouse flags
	mov	ax,DGROUP
	mov	ds,ax
	ASSUME	ds:DGROUP
	mov	ax,cs
	mov	es,ax
	ASSUME	es:_TEXT
	mov	_shared.mouseopen,1
	mov	dx,offset mouse_handle
;	mov	ax,20
	mov	ax,12
	int	33h
;	assume	es:nothing
;	mov	old_handle_mask,cx
;	mov	old_handle_off,dx
;	mov	old_handle_seg,es
	sub	ax,ax
	mov	_shared.mouse_start,ax
	mov	_shared.mouse_end,ax
	pop	dx
	ret
mouse_init	ENDP

	PUBLIC	_mouse_shutdown
_mouse_shutdown	PROC near
	ASSUME	ds:nothing
	ASSUME	es:nothing
	push	dx
	mov	ax,DGROUP
	mov	ds,ax
	ASSUME	ds:DGROUP
	xor	al,al
	mov	_shared.mouseopen,al
;	mov	cx,old_handle_mask
;	mov	dx,old_handle_off
;	mov	es,old_handle_seg

	mov	ax,12
	xor	cx,cx
	mov	dx,cx
	mov	es,dx
	int	33h
	xor	ax,ax
	pop	dx
	ret
_mouse_shutdown	ENDP

mouse_handle	PROC FAR
	ASSUME	ds:nothing
	push	ds
	mov	cx,DGROUP
	mov	ds,cx
	ASSUME	ds:DGROUP

	mov	cx,si
	mov	dx,di
	mov	si,_shared.mouse_end
	mov	di,si
	inc	si
	and	si,31
	mov	_shared.mouse_end,si
	cmp	si,_shared.mouse_start
	jne	mouse_ok
	inc	si
	and	si,31
	mov	_shared.mouse_start,si
mouse_ok:
	shl	di,3
	mov	word ptr _shared.mouse_buff[di],ax
	mov	word ptr _shared.mouse_buff[di+2],bx
	mov	word ptr _shared.mouse_buff[di+4],cx
	mov	word ptr _shared.mouse_buff[di+6],dx
	or	_shared.input,2
	pop	ds
	ASSUME	ds:nothing
	ret
mouse_handle	ENDP

	PUBLIC	@dfputs
; void _fastcall dfputs(cp)
; char *cp;
@dfputs	PROC NEAR
	ASSUME	ds:DGROUP
	ASSUME	es:nothing
	push	si
	mov	si,bx
put_loop:
	lodsb
	or	al,al
	jz	put_exit
	mov	dl,al
	mov	ah,2
	int	21h
	jmp	short put_loop
put_exit:
	pop	si
	ret
@dfputs	ENDP

kbd_loop	PROC FAR
	ASSUME	ds:nothing
	push	ds
	push	ax
	mov	ax,DGROUP
	mov	ds,ax
	ASSUME	ds:DGROUP
	pushf
	call	dword ptr [oldint28off]

	jmp	short clock_1

clock_int:
	push	ds
	push	ax
	mov	ax,DGROUP
	mov	ds,ax
	ASSUME	ds:DGROUP
	pushf
	call	dword ptr [oldint08off]

	push	si
	push	ds
	lds	si,busyptr
	lodsb
	pop	ds
	pop	si

	or	al,al
	jnz	clock_exit

clock_1:

	cli
	mov	al,cl_lock
	or	al,al
	jnz	clock_exit
	inc	al
	mov	cl_lock,al

	mov	al,_shared.rps_handle
	or	al,al
	jz	clock_3

	sti

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

	mov	savesp,sp
	mov	savess,ss
	mov	bx,ds
	mov	ss,bx
	mov	sp,offset DGROUP:mystack

	mov	bx,_shared.prot_ds

	IFDEF	EXE
	call	dword ptr [rpsgen_off]
	ELSE

	mov	msb+2,bx	; Is this necessary?
	push	ds
	push	offset msb
	push	ax
	call	OS386_Generate_Signal
	add	sp,6
	ENDIF

;	mov	cx,_shared.xmitlen
;	jcxz	clock_2
;	push	es
;	push	ds
;	push	cx		; xmitlen
;	push	offset _shared.xmitbuf
;	call	_pkxmit
;	add	sp,4
;	xor	ax,ax
;	mov	_shared.xmitlen,ax
;	pop	ds
;	pop	es
clock_2:
	mov	ss,savess
	mov	sp,savesp

	pop	di
	pop	si
	pop	dx
	pop	cx
	pop	bx
	pop	es
clock_3:
	cli
	xor	al,al
	mov	cl_lock,al
clock_exit:
	pop	ax
	pop	ds
	iret
kbd_loop	ENDP

_TEXT	ENDS
	END	init_rpc
