; File WINPKT.ASM						8 Dec 1991
; Provides a Packet Driver interface between Windows 3 Enhanced mode 
; applications and a real Packet Driver. This attempts to solve the problem
; of Windows moving applications around in memory willy nilly. Install WINPKT
; after the Packet Driver and before starting Windows. 
; Command line is:
;	WINPKT  WINPKT_interrupt number PD_interrupt_number
; with both in the range of 60h to 7fh.
; Build with the Clarkson Packet Driver Collection subprograms:
; 	masm WINPKT;
; 	link WINPKT;
; 	exe2bin WINPKT.EXE WINPKT.COM
; 	del WINPKT.EXE
;
	include defs.asm

;	Copyright, 1988-9, 1990, Russell Nelson
;	Copyright, 1991, Roger F. James
;	Code revised slightly, formatting cleaned up enormously, added
;	documentation, Joe R. Doupnik, jrd@cc.usu.edu, Utah State Univ,
;	8 Dec 1991.

;   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 word public
	assume  cs:code, ds:code, es:nothing

	org 2ch
phd_environ dw  0
	org 80h
phd_dioa    label   byte
	org 100h
start:	jmp start_1
	even

;Debugging stuff
;total_pkts	dw  0
;gvm_pkts	dw  0
;bvm_pkts	dw  0

npacket_int_no  db  4 dup (0)	; interrupt to communicate

per_handle  struc
in_use		db  0		; non-zero if this handle is in use
their_handle	dw  0		; lower layer handle
recv_handler	dd  0		; receiver upcall
vm_id		dw  0		; VM_ID for this handler
per_handle  ends

handles	per_handle MAX_HANDLE dup(<>)
end_handles label   byte


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

CY	equ 0001h
EI	equ 0200h


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

their_isr	dd  0		; original owner of pkt driver int
old_isr		dd  0		; old pkt driver int
their_2f_isr	dd  0		; original int 2f ISR

; The following are globals that assume that the code that access them
; single threads.
MAX_BUFFER_LEN  equ 1520
our_buffer  db  MAX_BUFFER_LEN dup (0)
buffer_flag db  0
buffer_len  dw  0
their_handler   dd  0		; receiver handler to call
their_bx    dw  0
;
;

vmm_running db  0		; 386 virtual machine manager is running
our_handle  dw  0		; current top level handle

our_isr:
	jmp our_isr_0		; the required signature.
	db  'PKT DRVR',0

our_isr_0:			; check if it one of the calls we
	assume  ds:nothing	; want to intercept (passes addresses)
	cmp	ah,2		; f_access_type?
	je	our_isr_2	; e = yes
	cmp	ah,3		; f_release_type?
	je	our_isr_2
	cmp	ah,5		; f_terminate
	je	our_isr_2
	cmp	ah,8		; f_stop
	je	our_isr_2
our_isr_1:			; nothing needing intercepion
	jmp	old_isr         ; chain to the original Packet Driver

our_isr_2:
	push	ax		; We are interested in this one
	push	bx		; so save some registers
	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		; use bp to access the original regs
	and	_F[bp],not CY   ; start by clearing the carry flag

	cmp	ah,2            ; f_access_type?
	jne	our_isr_3	; ne = no (it's special here)
	jmp	f_access_type	
our_isr_3:
	cmp	ah,3		; f_release_type?
	jne	our_isr_4	; ne = no
	jmp	f_release_type
our_isr_4:
	cmp	ah,5		; f_terminate?
	jne	our_isr_5	; ne = no
	jmp	f_terminate
our_isr_5:
	jmp	f_stop		; must be f_stop

our_isr_error:
	mov	_DH[bp],dh	; error code
	or	_F[bp],CY       ; 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

; Windows (and many many other programs) use the Int 2Fh Multiplexor link,
; and each is supposed to grab it's own calls as seen in AX, or chain them
; to the previous Int 2Fh owner. Windows 3 uses function 16h for most work.
; This is our Int 2Fh routine.
our_2f_isr:
	assume	ds:nothing, es:nothing
	cmp	ax,1608h	; Windows Enhanced "Init completed" broadcast?
	jne	our_2f_1	; ne = not that function, chain it
	mov	vmm_running,1   ; remember that Windows has started
	jmp	their_2f_isr    ; pass it on down the chain
our_2f_1:
	cmp	ax,1609h	; Windows Enhanced "Begin Exit" broadcast?
	jne	our_2f_2	; ne = no
	mov	vmm_running,0   ; remember that Windows has stopped
our_2f_2:
	jmp	their_2f_isr	; propagate broadcasts down the Int 2Fh chain

our_handler:			; Receive upcalls from the Packet Driver
	or	ax,ax		; first (AX=0) call to get buffer?
	jnz	our_handler_2	; nz = no, must be second upcall
	cmp	buffer_flag,1	; must buffer, is it free?
	je	our_handler_1   ; e = no, busy
	inc	cx		; round up pkt size for 16 bit transfers
	cmp	cx,MAX_BUFFER_LEN ; pkt larger than our buffer?
	ja	our_handler_1   ; a = yes, too large
	mov	buffer_flag,1   ; mark buffer as busy now
	mov	buffer_len,cx   ; store pkt length
	mov	ax,cs
	mov	es,ax		; segment of our buffer
	mov	di,offset our_buffer ; tell PD es:di is the buffer address
	retf

our_handler_1:
	xor	ax,ax		; reject the packet by returning es:di = NULL
	mov	es,ax
	xor	di,di
	retf

our_handler_2:			; second upcall, packet transfer complete
	mov	their_bx,bx	; save their registers
	mov	dx,bx
	mov	bx,cs		; set our data segment
	mov	ds,bx
	assume	ds:code

	mov	bx,offset handles	; array of PD handles
our_handler_3:
	cmp	[bx].their_handle,dx	; dx is upcoming handle
	je	our_handler_4		; found their handle
	add	bx,(size per_handle)    ; next handle
	cmp	bx,offset end_handles   ; examined all handles?
	jb	our_handler_3		; b = no, continue.
                			; get here if no matching handle
	mov	buffer_flag,0		; mark the buffer free
	retf

our_handler_4:              		; found the handle
;	inc	total_pkts
	mov	ax,[bx].recv_handler.segm ; appliation's call address
	mov	their_handler.segm,ax
	mov	ax,[bx].recv_handler.offs
	mov	their_handler.offs,ax
	push    bx
	mov	ax,1683h        ; Windows, get current vir mach ident
	int	2fh
	mov	ax,bx		; ident returned in bx
	pop	bx
	cmp	ax,[bx].vm_id	; same as the one the app is using?
	je	our_handler_5	; e = yes, correct VM is running
	jmp	our_handler_7   ; no, another VM is running, switch to wanted
our_handler_5:
;	inc	gvm_pkts
	call	pass_to_app	; copy buffer to application, via double call
	retf

pass_to_app:
	xor	ax,ax		; set up register for first upcall to app
	mov	bx,their_bx	; handle from Packet Driver
	mov	cx,buffer_len	; packet size
	push    ds
	call    their_handler	; do first upcall (request buffer address)
	pop	ds
	mov	ax,es		; check for 0:0 as reject value
	or	ax,ax
	jnz	pass_to_app_1	; nz = have an address
	or	di,di		; check for 0:offset (rather unlikely)
	jnz	pass_to_app_1	; nz = have an offset
	mov	buffer_flag,0   ; packet is being declined, free our buffer
	ret
pass_to_app_1:			; copy from our buffer to app's es:di
	push	di
	mov	cx,buffer_len
	mov	si,offset our_buffer
	cld
	rep	movsb		; copy frame into apps buffer
	mov	ax,1		; set up regs for second upcall
	mov	bx,their_bx	; handle
	mov	cx,buffer_len	; report packet length too
	pop	si
	mov	dx,es
	push	ds
	mov	ds,dx
	assume	ds:nothing

	call	their_handler	; call the application
	pop	ds
	assume	ds:code
	mov	buffer_flag,0	; free buffer
	ret

; Windows Enhanced, request virtual machine in bx, and call back at es:di 
; when it's ready (which, knowing Windows, may take quite a while, hence 
; the requirement to buffer the packet to clear the lan board and ints).
our_handler_7:
;	inc	bvm_pkts
	mov	ax,1685h	; request switch VMs and callback
	mov	bx,[bx].vm_id	; virtual machine of the app
	xor	cx,cx		; flags (bits 0 and 1), zero is don't wait
	mov	dx,40h		; dx:si is priority boost
	xor	si,si
	push	cs
	pop	es		; es:di is address of callback routine
	mov	di,offset our_callback
	int	2fh
	retf

	assume ds:nothing
our_callback:			; get here with correct virtual machine
	push	ax		; call application twice (a la PD) to deliver
	push	bx		; the buffered packet
	push	cx
	push	dx
	push	si
	push	di
	push	ds
	push	es
	push	bp
	mov	ax,cs
	mov	ds,ax		; set up our ds
	assume	ds:code

	call    pass_to_app
	pop	bp
	pop	es
	pop	ds
	assume  ds:nothing
	pop	di
	pop	si
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	iret

	assume  ds:code
f_access_type:				; register for a packet Type
	mov	bx,offset handles	; array of PD handles
access_type_1:
	cmp	[bx].in_use,0		; is this handle in use?
	je	access_type_2		; e = yes, found a free one
	add	bx,(size per_handle)    ; next handle
	cmp	bx,offset end_handles   ; examined all handles?
	jb	access_type_1		; b = no, continue
	jmp	access_type_space	; no handle found, return error
access_type_2:
	mov	our_handle,bx		; save our handle
	mov	[bx].in_use,1		; make handle as in-use
	cmp	vmm_running,0		; is Windows Enhanced mode running?
	je	access_type_3		; e = no, don't bother to redirect
	mov	[bx].recv_handler.segm,es
	mov	[bx].recv_handler.offs,di
	mov	bx,cs
	mov	es,bx
	mov	di,offset our_handler	
access_type_3:
	push	ds
	mov	bx,_DS[bp]
	mov	ds,bx
	assume  ds:nothing

	mov	bx,_BX[bp]		; restore callers registers
	pushf
	call	old_isr			; call Packet Driver
	pop	ds
	assume	ds:code
	mov	bx,our_handle
	jnc	access_type_4		; nc = success
	mov	[bx].in_use,0		; failed, free our handle
	jmp	our_isr_error
access_type_4:
	mov	[bx].their_handle,ax	; handle returned in ax
	mov	_AX[bp],ax		; save return handle
	cmp	vmm_running,0		; Windows Enhanced mode running?
	je	access_type_5		; e = no
	push	bx			; Windows "Get Current Virtual Mach"
	mov	ax,1683h
	int	2fh			; get current VM_ID to bx
	mov	ax,bx
	pop	bx
	mov	[bx].vm_id,ax		; save ident as part of our handle
access_type_5:
	jmp	our_isr_return

access_type_space:
	mov	dh,NO_SPACE
	jmp	our_isr_error

f_release_type:
	mov	bx,_BX[bp]		; restore callers registers
	pushf
	call	old_isr
	jnc	release_type_1		; nc = success
	jmp	our_isr_error
release_type_1:
	mov	ax,_BX[bp]		;just in case
	mov	bx,offset handles
release_type_2:
	cmp	[bx].their_handle,ax	; compare handles
	je	release_type_3		; e = found a match
	add	bx,(size per_handle)    ; next handle
	cmp	bx,offset end_handles   ; examined all handles?
	jb	release_type_2		; b = no, continue
	jmp	err_bad_handle		; no handle found, return error
release_type_3:
	mov	[bx].in_use,0		; say handle is no longer in use
	jmp	our_isr_return

err_bad_handle:
	mov	dh,BAD_HANDLE		; dh is error code
	jmp	our_isr_error

f_terminate:
	mov	bx,_BX[bp]      	; restore callers registers
	pushf
	call	old_isr
	jnc	terminate_1     	; nc = success
	jmp	our_isr_error
terminate_1:
	mov	ax,_BX[bp]      	; just in case
	mov	bx,offset handles
terminate_2:
	cmp	[bx].their_handle,ax    ; compare handles
	je	terminate_3     	; e = found a match
	add	bx,(size per_handle)    ; next handle
	cmp	bx,offset end_handles   ; examined all handles?
	jb	terminate_2     	; b = no, continue
	jmp	err_bad_handle      	; no match, return error
terminate_3:
	mov	[bx].in_use,0		; mark handle as free
	mov	bx,offset handles	; check that all handles are free
terminate_4:
	cmp	[bx].in_use,0		; is this handle free?
	jne	terminate_5		; ne = no, so can't exit completely
	add	bx,(size per_handle)    ; next handle
	cmp	bx,offset end_handles   ; examined all handles?
	jb	terminate_4		; b = no, continue examination
	mov	al,npacket_int_no	; release our_isr
	mov	ah,25h
	push	ds
	lds	dx,their_isr		; ds:dx is routine address
	int	21h
	pop	ds
	mov	ah,35h			; got owner of Int 2Fh
	mov	al,2fh
	int	21h			; to see if it's still us
	mov	ax,es
	mov	cx,cs
	cmp	ax,cx			; our segment?
	jne	terminate_5		; ne = no, can't terminate
	cmp	bx,offset our_2f_isr	; our offset?
	jne	terminate_5		; ne = no, can't terminate
	mov	al,2fh			; restore Int 2f
	mov	ah,25h
	push    ds
	lds	dx,their_2f_isr		; previous owner now is current owner
	int	21h
	pop	ds

	push	cs
	pop	es
	mov	ah,49h			; free our memory
	int	21h
	jmp	our_isr_return
terminate_5:
	mov	dh,CANT_TERMINATE	; error code
	jmp	our_isr_error

; Stop the packet driver doing upcalls. Also a following terminate will
; always succed (no in use handles any longer).
f_stop:
	mov	bx,_BX[bp]		; restore caller's registers
	pushf
	call	old_isr
	mov	bx,offset handles
f_stop_2:
	mov	[bx].in_use,0		; say handle is free
	add	bx,(size per_handle)    ; next handle
	cmp	bx,offset end_handles	; done all?
	jb	f_stop_2		; b = not yet
	clc
	ret

end_resident	label byte
	include	printnum.asm
	include decout.asm
	include digout.asm
	include chrout.asm

usage_msg   label   byte
	db	' Usage: WINPKT <new_packet_int_number>'
	db	' <old_packet_int_number>',CR,LF
	db	' Install WINPKT after the regular Packet Driver, but'
	db	cr,lf,' before starting Windows 3.$'

copyright_msg   label   byte
	db	"Virtual packet driver for Windows 3",CR,LF
	db	"Portions Copyright 1991 Roger F. James",CR,LF,'$'

copyleft_msg    label   byte
	db "Packet driver skeleton copyright 1988-90, Russell Nelson.",CR,LF
	db "This program is free software; see the file COPYING for details."
	db	CR,LF
	db	"NO WARRANTY; see the file COPYING for details.",CR,LF
	db	CR,LF
crlf_msg db	CR,LF,'$'

location_msg    db  'Packet driver is at segment $'
packet_int_no   db  0
opacket_int_no  db  4 dup (0)

already_msg	db  CR,LF,'There is already a packet driver at $'
not_found_msg   db  CR,LF,'There is no packet driver at $'
new_int_msg	db  CR,LF,'Virtual packet driver installed on new interrupt $'
old_int_msg	db  'Using old interrupt $'

not_found_error:
	mov	dx,offset not_found_msg
	mov	di,offset opacket_int_no
	call	print_number
	mov	ax,4c05h		; exit to DOS with errorlevel = 5
	int	21h

already_error:
	mov	dx,offset already_msg
	mov	di,offset npacket_int_no
	call	print_number
	mov	ax,4c05h		; give errorlevel 5
	int	21h

usage_error:
	mov	dx,offset usage_msg
error:	mov	ah,9
	int	21h
	mov	ax,4c0ah        	; give errorlevel 10
	int	21h

start_1:cld
	mov	dx,offset copyright_msg
	mov	ah,9
	int	21h
	mov	dx,offset copyleft_msg
	mov	ah,9
	int	21h
	mov	si,offset phd_dioa+1
	call	skip_blanks     	; end of line?
	cmp	al,CR
	je	usage_error		; e = yes

	mov	dx,offset location_msg	; display load address
	mov	ah,9
	int	21h
	mov	ax,cs			; cs as a word
	call	wordout
	mov	dx,offset crlf_msg
	mov	ah,9
	int	21h

chk_options:
	call	skip_blanks
	cmp	al,'-'          ; any options?
	jne	no_more_opt
usage_error_j_1:
	jmp usage_error
no_more_opt:
	mov	di,offset npacket_int_no
	call	get_number
	call	skip_blanks
	cmp	al,CR
	je	usage_error
	mov	di,offset opacket_int_no
	call	get_number
	call	skip_blanks     	; end of line?
	cmp	al,CR
	jne	usage_error		; ne = no
	mov	al,npacket_int_no
	mov	packet_int_no,al
	call	verify_packet_int
	jnc	packet_int_ok		; nc = success
	jmp	error
packet_int_ok:
	jne	packet_int_unused
	jmp	already_error		; error if no Packet Driver
packet_int_unused:

	mov	al,opacket_int_no
	mov	packet_int_no,al
	call	verify_packet_int
	jnc	packet_int_ok_1
	jmp	error
packet_int_ok_1:
	je	packet_int_used
	jmp	not_found_error
packet_int_used:
	mov	ah,35h          	; get Packet Driver interrupt routine
	mov	al,opacket_int_no
	int	21h
	mov	old_isr.offs,bx		; save here
	mov	old_isr.segm,es
	mov	ah,35h			; get Int 2Fh interrupt routine
	mov	al,2fh
	int	21h
	mov	their_2f_isr.offs,bx	; save here
	mov	their_2f_isr.segm,es
	mov	ah,25h          ;install our 2f interrupt
	mov	dx,offset our_2f_isr
	int	21h

	mov	dx,offset new_int_msg
	mov	di,offset npacket_int_no
	call    print_number
	mov	dx,offset old_int_msg
	mov	di,offset opacket_int_no
	call	print_number
	call	take_packet_int

	mov	ah,49h			; free our environment, because
	mov	es,phd_environ		; we won't need it
	int	21h
	mov	bx,1            	; get the stdout handle
	mov	ah,3eh			; close it in case they redirected it
	int	21h
	mov	dx,offset end_resident
	add	dx,0fh			; round up to next highest paragraph
	mov	cl,4
	shr	dx,cl
	mov	ah,31h			; terminate, stay resident
	xor	al,al
	int	21h

take_packet_int:
	mov	ah,35h			; get new (our) "PD" interrupt
	mov	al,npacket_int_no
	int	21h
	mov	their_isr.offs,bx	; remember previous owner here
	mov	their_isr.segm,es
	mov	ah,25h			; install our packet interrupt
	mov	dx,offset our_isr
	int	21h
	ret

	include verifypi.asm
	include getnum.asm
	include getdig.asm
	include skipblk.asm
	include printea.asm
code    ends
	end	start
