	.title	ZTSERVER subroutines
;
;	w.j.m. jun 1989 (0.2)
;	documented 3-jul-1989 wjm
;	mod 18-aug-1989 wjm (0.9): support VMS V5
;
	.ident	/0.9/	;this version needs ZTDRIVER 0.9
;
;*****
; entries:
;	status=ZT_INIT()	- initialize, 
;			set up communication with ZTDRIVER.
;			NOTE: an exit handler is set up,
;				which MUST be executed eventually!
;
;	status=ZT_WAIT()	- wait for message (I/O request) from ZTDRIVER.
;			  On return, the 'message buffer' has been filled in.
;
;	status=ZT_REQCOM()	- send (part of) 'message buffer'
;			to ZTDRIVER and complete I/O.
;
;	status=ZT_TOUSER()	- simulates DMA transfer
;			from 'data buffer' to ZTDRIVER's user buffer.
;			Number of bytes tranferred:
;				 MIN(UCB$W_BCNT,ZT_BUFDSC.dsc$w_length)
;			Can be called at most once per I/O request!
;
;	status=ZT_FRUSER()	- simulates DMA transfer
;			from ZTDRIVER's user buffer to 'data buffer'.
;			Number of bytes transferred:
;				UCB$W_BCNT
;			On return, ZT_BUFDSC.dsc$w_length is set to this number.
;			Can be called at most once per I/O request!
;					
;	ZT_MSGDSC (globaldef): 
;			Descriptor of 'message buffer' (see ZTDEF.MAR)
;				used for communication with ZTDRIVER.
;			Both buffer address and length are FIXED.
;
;	ZT_BUFDSC (globaldef):
;			Descriptor of 'data buffer' (see above).
;			Buffer address is FIXED, actual size is ^xFFFF .
;			ZT_BUFDSC.dsc$w_length is set by ZT_FRUSER(),
;				and by caller of ZT_TOUSER().
;
;
;*****
;
	.link		"SYS$SYSTEM:SYS.STB"/selective_search
	.library	"SYS$LIBRARY:LIB"
	.library	"ZT"
;
	$dvidef
	$iodef
	$prdef
	$prvdef
	$ucbdef
;
smp_code=0				;VMS V4 or earlier
.iif df UCB$L_DLCK, smp_code=1		;VMS V5 
;
	ztdef		; ZT definitions - need $ucbdef
;
;
;*****	macros
;
.macro	chkr0	?lab
	blbs	r0,lab
	pushl	r0
	calls	#1,g^lib$stop
lab:
.endm	chkr0
;
;
;*****	PSECTs
;
	.psect	_data,quad
	.psect	_code,nowrt
	.psect	_nonpagedcode,page,nowrt
nonpagedcode:			;start of psect
	.psect	_nonpageddata,page
nonpageddata:			;start of psect
;
	.page
;*****	nonpaged data
;
	.psect	_nonpageddata
;
zt_ucb:	.long	0
mbx_ucb:.long	0
;
	.align	quad
zt_namedsc:
	.ascid	"_ZTA0:"
;
mbx_name_size=32
mbx_name:
	.blkb	mbx_name_size
	.align	quad
mbx_namedsc:
	.long	0-0
	.long	mbx_name
;
	.align	quad
zt_msgbuf:
	.byte	0[zt_msglen]
;
;
;*****	get ucb from device name
;		called in kernel mode, IPL=0
;		p1 = addr of descriptor		! must be nonpaged
;		p2 = addr of ucb-address	! must be nonpaged
;
	.psect	_nonpagedcode
dev_to_ucb:	.word	^m<r2,r3,r4,r5>
;
	movl	g^CTL$GL_PCB,r4
;
	jsb	g^SCH$IOLOCKR		;;***** start IPL2 *********************
	movaq	@1*4(ap),r1		;;
	jsb	g^IOC$SEARCHDEV		;;search for device
	blbc	r0,90$			;;
	movl	r1,@2*4(ap)		;;r1=UCB
90$:					;;
	pushl	r0			;;
	jsb	g^SCH$IOUNLOCK		;; Unlock the I/O database
	setipl	#0			;;***** end IPL2 ***********************
	popl	r0
;
	ret
;
;
;*****	fake ZT interrupt
;		called in kernel mode, IPL=0
;
	.psect	_nonpagedcode
zt_int:	.word	^m<r2,r3,r4,r5>
;
	movl	zt_ucb,r5
	beql	90$
;
.if eq smp_code
	dsbint	ucb$b_dipl(r5)			;;
.iff						;;
	devicelock	-			;;
		lockaddr=ucb$l_dlck(r5),-	;;
		lockipl=ucb$b_dipl(r5),-	;;
		savipl=-(sp),-			;;
		preserve=NO			;;
.endc						;;
						;;
	jsb	@ucb_l_inter(r5)		;;
						;;
.if eq smp_code					;;
	enbint					;;
.iff						;;
	deviceunlock	-			;;
		lockaddr=ucb$l_dlck(r5),-	;;
		newipl=(sp)+,-			;;
		preserve=NO			;;
.endc
;
90$:
	movl	#1,r0
	ret
;
;
;*****	kernel mode setup
;		called via $CMKRNL
;
	.psect	_nonpagedcode
k_setup:	.word	^m<r2,r3,r4,r5>
;
	movab	g^lib$sig_to_ret,(fp)
;
	pushal	mbx_ucb
	pushaq	mbx_namedsc
	calls	#2,dev_to_ucb
	blbc	r0,90$
;
	pushal	zt_ucb
	pushaq	zt_namedsc
	calls	#2,dev_to_ucb
	blbc	r0,90$
;
	movl	zt_ucb,r5
	movl	mbx_ucb,ucb_l_ztmbx(r5)
;
	bicw	#ucb$m_valid,ucb$w_sts(r5)
	bisw	#ucb$m_online,ucb$w_sts(r5)
;
	calls	#0,zt_int		; interrupt unconditionally,
					; to clear potential hang
;
	movl	#1,r0
90$:
	ret
;
;
;*****	kernel mode shutdown
;		called via $CMKRNL
;
	.psect	_nonpagedcode
k_shut:	.word	^m<r2,r3,r4,r5>
;
	movab	g^lib$sig_to_ret,(fp)
;
	movl	zt_ucb,r5
	beql	90$
;
	clrl	ucb_l_ztmbx(r5)
	bicw	#<ucb$m_online!ucb$m_valid>,ucb$w_sts(r5)
;
; ggf. terminate i/o
;
	bicw	#<ucb$m_online!ucb$m_valid>,-
		zt_msgbuf+zt_w_ucbsts		;just in case ...
;
	calls	#0,zt_int			;interrupt unconditionally
;
90$:
	movl	#1,r0
	ret
;
;
;*****	kernel mode data transfer server -> driver's user
;		called via $CMKRNL
;		implicit inputs: 
;			- 'zt_ucb'
;			- data in 'buffer'
;			- byte count in 'bufbct'
;		implicit outputs:
;			- ucb$w_bcnt cleared
;
	.psect	_nonpagedcode
k_touser:	.word	^m<r2,r3,r4,r5>
;
	movab	g^lib$sig_to_ret,(fp)
;
	clrl	r0
	movab	buffer,r1		; 'from' address
	movzwl	bufbct,r2		; byte count
	movl	zt_ucb,r5
	beql	90$
	cmpw	r2,ucb$w_bcnt(r5)	;sanity check
	bgtru	90$			;br if it failed
;
.if eq smp_code
	dsbint	ucb$b_fipl(r5)			;;
.iff						;;
	forklock	-			;;
		lock=ucb$b_flck(r5),-		;;
		savipl=-(sp),-			;;
		preserve=NO,-			;;
		fipl=YES			;; 
.endc						;;
						;;
	tstl	r2				;;
	bleq	28$				;; br if nothing to be moved!
	jsb	ioc$movtouser			;;
28$:						;;
						;;
.if eq smp_code					;;
	enbint					;;
.iff						;;
	forkunlock	-			;;
		lock=ucb$b_flck(r5),-		;;
		newipl=(sp)+,-			;;
		preserve=NO			;;
.endc
;
	clrw	ucb$w_bcnt(r5)		;make sure we don't copy twice
;
	movl	#1,r0
90$:
	ret
;
;
;*****	kernel mode data transfer, driver's user -> server
;		called via $CMKRNL
;		implicit inputs: 
;			- 'zt_ucb'
;			- byte count in 'zt_msgbuf+zt_w_bnct'
;		implicit outputs:
;			- data in 'buffer'
;			- byte count in 'bufbct'
;			- ucb$w_bcnt cleared
;
	.psect	_nonpagedcode
k_fruser:	.word	^m<r2,r3,r4,r5>
;
	movab	g^lib$sig_to_ret,(fp)
;
	clrl	r0
	clrl	bufbct
	movl	zt_ucb,r5
	beql	90$
;
	movab	buffer,r1		;'to' address
	movzwl	zt_msgbuf+zt_w_bcnt,r2	;byte count
	cmpw	r2,ucb$w_bcnt(r5)	;sanity check
	bgtru	90$			;br if it failed
	movl	r2,bufbct
;
.if eq smp_code
	dsbint	ucb$b_fipl(r5)			;;
.iff						;;
	forklock	-			;;
		lock=ucb$b_flck(r5),-		;;
		savipl=-(sp),-			;;
		preserve=NO,-			;;
		fipl=YES			;; 
.endc						;;
						;;
	tstl	r2				;;
	bleq	28$				;; br if nothing to be moved!
	jsb	ioc$movfruser			;;
28$:						;;
						;;
.if eq smp_code					;;
	enbint					;;
.iff						;;
	forkunlock	-			;;
		lock=ucb$b_flck(r5),-		;;
		newipl=(sp)+,-			;;
		preserve=NO			;;
.endc
;
	clrw	ucb$w_bcnt(r5)		;make sure we don't copy twice
;
	movl	#1,r0
90$:
	ret
;
;
;*****	kernel mode copy of 'result' to driver & activate driver
;		called via $CMKRNL
;		implicit inputs: 
;			- 'zt_ucb'
;			- 'result'
;
	.psect	_nonpagedcode
k_reqcom:	.word	^m<r2,r3,r4,r5>
;
	movab	g^lib$sig_to_ret,(fp)
;
	clrl	r0
	movl	zt_ucb,r5
	beql	90$
;
	movc3	#zt_msglen,zt_msgbuf,ucb_a_ztmsg(r5)	;copy back
;
	calls	#0,zt_int				;'interrupt' driver
;
	movl	#1,r0
90$:
	ret
;
;
;*****	get mailbox & its name
;
	.psect	_data
;
mbx_dvi_itm:
	.word	mbx_name_size
	.word	dvi$_fulldevnam
	.long	mbx_name
	.long	mbx_namedsc
	.long	0
;
	.align 	quad
mbxiosb:
	.blkw	4
;
mbxchan:
	.blkw
;
;
	.psect	_code
get_mbx:	.word	^m<>
;
	$crembx_s	prmflg=#0,-
			chan=mbxchan,-
			maxmsg=#zt_msglen,-
			-;	???		bufquo=#zt_msglen,-
			promsk=#0
	chkr0
;
	$getdvi_s	chan=mbxchan,-
			itmlst=mbx_dvi_itm
	chkr0
;
	ret
;
;
;*****	start mbx read
;
	.psect	_code
read_mbx:	.word	^m<>
;
	$qio_s	efn=#1,-
		chan=mbxchan,-
		func=#io$_readvblk,-
		iosb=mbxiosb,-
		p1=zt_msgbuf,-
		p2=#zt_msglen
	chkr0
;
	ret
;
;
;*****	wait for mbx message
;
	.psect	_code
wait_mbx:	.word	^m<>
;
	$synch_s	efn=#1,-
			iosb=mbxiosb
	chkr0
;
	movzwl	mbxiosb,r0
	chkr0
;
	cmpw	mbxiosb+2,#zt_msglen
	beql	50$
	clrl	r0
	chkr0
50$:
	ret
;
;
;*****	exit handler
;
;
	.psect	_data
curprivs:
	.quad	0
;
;
	.psect	_code
exith:	.word	^m<>
;
	$setprv_s	enbflg=#1,-		;establish initial privileges
			prvadr=curprivs		;(sometimes removed by rundown)
	chkr0
;
	$cmkrnl_s	routin=k_shut,-
			arglst=(ap)
	chkr0
;
	ret
;
;
;*****	setup exit handler
;
	.psect	_data
;
	.align	long
exstat:
	.blkl
exblk:
	.long	0-0,exith,1,exstat
;
;
	.psect	_code
setup_exith:	.word	^m<>
;
	$dclexh_s	desblk=exblk
	chkr0
;
	ret
;
	.page
;*****	externalized routines & data
;
	.psect	_nonpageddata
	.align	quad
ZT_BUFDSC::
bufbct:
	.long	0-0,buffer
;
;
	.psect	_data
	.align	quad
ZT_MSGDSC::
	.long	zt_msglen,zt_msgbuf
;
;
;*****	initialize
;
	.psect	_data
;
	.align	quad
lkwdata_inadr:
	.long	nonpageddata,nonpageddata_end
lkwcode_inadr:
	.long	nonpagedcode,nonpagedcode_end
;
;
	.psect	_code
.entry	ZT_INIT,^m<>
;
	$setprv_s	prvprv=curprivs		; save current privileges
	chkr0
;
	$lkwset_s	inadr=lkwdata_inadr	; lock "nonpaged" data
	chkr0
;
	$lkwset_s	inadr=lkwcode_inadr	; lock "nonpaged" code
	chkr0
;
	calls	#0,get_mbx			; create mbx etc.
	chkr0
;
	calls	#0,read_mbx			; post read on mbx
	chkr0
;
	calls	#0,setup_exith			; set up exit handler
	chkr0
;
	$cmkrnl_s	routin=k_setup,-	; start zt:
			arglst=(ap)
	ret
;
;
;*****	wait for MBX
;
	.psect	_code
.entry	ZT_WAIT,^m<>
;
	calls	#0,wait_mbx
;
	ret
;
;
;*****	move data from 'buffer' to user
;
	.psect	_code
.entry	ZT_TOUSER,^m<>
;
	$cmkrnl_s	routin=k_touser,-
			arglst=(ap)
	ret
;
;
;*****	move data from user to 'buffer'
;
	.psect	_code
.entry	ZT_FRUSER,^m<>
;
	$cmkrnl_s	routin=k_fruser,-
			arglst=(ap)
	ret
;
;
;*****	complete I/O
;
	.psect	_code
.entry	ZT_REQCOM,^m<>
;
	calls	#0,read_mbx			; post read on mbx
	chkr0
;
	$cmkrnl_s	routin=k_reqcom,-
			arglst=(ap)
	ret
;
	.page
;*****	the end
;
	.psect	_nonpageddata
;
buffer:				;the data buffer
	.blkb	^xFFFF		;i.e. 64k-1
;
nonpageddata_end:
	.blkb
;
	.psect	_nonpagedcode
nonpagedcode_end:
	.blkb
;
	.end
