Google
 

Trailing-Edge - PDP-10 Archives - BB-Y393E-SM - monitor-sources/enq.mac
There are 50 other files named enq.mac in the archive. Click here to see a list.
;Edit 3055 to ENQ.MAC by CJOHNSON on Mon 12-Dec-83, for SPR #19771
;		Make ENQC% return EN%QCB and proper lock owner job number
;<4-1-FIELD-IMAGE.MONITOR>ENQ.MAC.2, 25-Feb-82 20:20:15, EDIT BY DONAHUE
;UPDATE COPYRIGHT DATE
;<4.MONITOR>ENQ.MAC.17, 25-Jan-80 15:25:48, Edit by KONEN
;Correct TCO 4.2321
;<4.MONITOR>ENQ.MAC.16,  3-Jan-80 08:08:38, EDIT BY R.ACE
;UPDATE COPYRIGHT DATE
; UPD ID= 47, SNARK:<4.MONITOR>ENQ.MAC.15,  28-Nov-79 17:27:20 by HALL
;REMOVE CODE FOR INTERNAL ENQ AND DEQ OPERATIONS BECAUSE NO ONE CALLS IT
;<4.MONITOR>ENQ.MAC.14, 21-Oct-79 18:43:46, EDIT BY MAGRATH
;TCO 4.2540 - Routine FNDQ, add JFN check to search criteria
;<OSMAN.MON>ENQ.MAC.1, 10-Sep-79 15:28:06, EDIT BY OSMAN
;TCO 4.2412 - Move definition of BUGHLTs, BUGCHKs, and BUGINFs to BUGS.MAC
;<4.MONITOR>ENQ.MAC.12, 29-Aug-79 20:06:49, EDIT BY ZIMA
;USE THE PREFERRED MNEMONICS TO CHECK FOR NUMVAL
;<4.MONITOR>ENQ.MAC.11, 10-Jul-79 12:56:44, EDIT BY ZIMA
;TCO 4.2321 - FIX STRCMP TO RECOGNIZE MISMATCH OF USER CODE AND STRING.
;<4.MONITOR>ENQ.MAC.10,  1-May-79 23:31:04, Edit by MCLEAN
;MAKE JOB NUMBER OF 0,,-1 BE -1 FOR GTOKM .GOENQ..
;<4.MONITOR>ENQ.MAC.9, 18-Apr-79 16:46:41, Edit by MCLEAN
;REMOVE EXTRANEOUS NOINT ON CALL TO SETJSB IN ENQOK
;<4.MONITOR>ENQ.MAC.8, 18-Apr-79 15:10:00, Edit by MCLEAN
;ADD JOB NUMBER TO ENQ QUOTA CHANGE
;<4.MONITOR>ENQ.MAC.7,  5-Apr-79 11:44:12, Edit by MCLEAN
;<4.MONITOR>ENQ.MAC.6,  5-Apr-79 11:25:10, Edit by MCLEAN
;REMOVE 1ST ARG FROM GTOKM
;<4.MONITOR>ENQ.MAC.5,  9-Mar-79 14:05:32, Edit by MCLEAN
;FIX GTOKM TO RETERR NOT ITRAP
;<4.MONITOR>ENQ.MAC.4,  4-Mar-79 15:34:01, EDIT BY KONEN
;UPDATE COPYRIGHT FOR RELEASE 4
;<4.MONITOR>ENQ.MAC.3, 23-Jul-78 16:54:03, Edit by MCLEAN
;ADD GETOK TO SETTING QUOTAS FOR ENQ FOR NON PRIV USERS
;<4.MONITOR>ENQ.MAC.2, 23-Jul-78 16:52:40, Edit by MCLEAN

;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
;  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (C) 1976,1977,1978,1979,1980,1981,1982 BY DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.

	SEARCH PROLOG
	TTITLE ENQ
;THE ENQ AND DEQ JSYS'S

REPEAT 0,<

;Q-BLOCK FORMAT

		+-------------------------+-------------------------+
		!ENQLJQ:                  !ENQNJQ:                  !
		!      BACK POINTER TO    !    FORWARD POINTER TO   !
		!   LAST Q-BLOCK FOR JOB  !   NEXT Q-BLOCK FOR JOB  !
		+-------------------------+-------------------------+
		!ENQLLQ:                  !ENQNLQ:                  !
		!      BACK POINTER TO    !    FORWARD POINTER TO   !
		!        LAST Q-BLOCK     !        NEXT Q-BLOCK     !
		+----------------+--------+-------------------------+
		!ENQFLG:         !ENQCHN: !ENQFRK:                  !
		!      FLAGS     !   PSI  !     FORK TO INTERRUPT   !
		!                ! CHANNEL!  WHEN REQUEST IS LOCKED !
		+----------------+--------+-------------------------+
		!ENQNR:                   !ENQID:                   !
		!      # OF RESOURCES     !     REQUEST ID CODE     !
		!   REQUESTED FROM POOL   !                         !
		+-------------------------+-------------------------+
		!ENQLRQ:                  !ENQFQ:                   !
		!      BACK POINTER TO    !    FORWARD POINTER TO   !
		! LAST Q-BLOCK OF REQUEST ! NEXT Q-BLOCK OF REQUEST !
		+-------------------------+-------------------------+
		!ENQLBP:                  !ENQGRP:                  !
		!        POINTER TO       !    GROUP # FOR          !
		!        LOCK-BLOCK       !    SHARABLE REQUESTS    !
		+-------------------------+-------------------------+
		!ENQNST:		  !ENQJFN:		    !
		!    NEST COUNT		  !	JFN OF REQUEST	    !
		!			  !	OR -1, -2, OR -3    !
		+-------------------------+-------------------------+
		!			  !ENQMSK:		    !
		!			  !     POINTER TO THE	    !
		!			  !      MASK BLOCK	    !
		+-------------------------+-------------------------+

;LOCK-BLOCK FORMAT

		+-------------------------+-------------------------+
		!ENQLHC:                  !ENQNHC:                  !
		!   BACK POINTER TO LAST  !     POINTER TO NEXT     !
		! LOCK-BLOCK ON HASH CHAIN! LOCK-BLOCK ON HASH CHAIN!
		+-------------------------+-------------------------+
		!ENQLLQ:                  !ENQNLQ:                  !
		!      BACK POINTER TO    !    FORWARD POINTER TO   !
		!  LAST Q-BLOCK ON QUEUE  !  FIRST Q-BLOCK ON QUEUE !
		+---------------+---------+-------------------------+
		!ENQFLG:        !         !ENQLVL:                  !
		!      FLAGS    !         !       LEVEL NUMBER      !
		!               !         !       OF THIS LOCK      !
		+---------------+---------+-------------------------+
		!ENQTR:                   !ENQRR:                   !
		!   TOTAL # OF RESOURCES  !    REMAINING NUMBER OF  !
		!        IN THIS POOL     !  RESOURCES IN THIS POOL !
		+-------------------------+-------------------------+
		!ENQTS:                                             !
		!                   TIME STAMP                      !
		!           TIME OF LAST REQUEST LOCKED             !
		+-------------------------+-------------------------+
		!ENQFBP:		  !ENQLT:		    !
		!    FREE BLOCK POINTER	  !  LONG TERM LOCK LIST    !
		!      TO FREE Q-BLOCK	  !      FOR THIS JOB	    !
		+-------------------------+-------------------------+
		!ENQOFN:                  !ENQLEN:                  !
		!    OFN, OR -2, OR -3,   !      LENGTH OF THIS     !
		!  OR 400000 + JOB NUMBER !        LOCK-BLOCK       !
		+-------------------------+-------------------------+
		!ENQNMS:		  !			    !
		!    NUMBER OF WORDS IN   !			    !
		!      THE MASK BLOCK	  !			    !
		+-------------------------+-------------------------+
		!ENQTXT:             ASCIZ STRING                   !
		!                        OR                         !
		!                 500000 + USER CODE                !
		+---------------------------------------------------+

>	;END OF REPEAT 0

;LOCAL STORAGE DECLARED IN STG.MAC

EXTN <HSHTBL,HSHLEN,ENQLOK,ENFKTB,ENFKTL>
;STRUCTURE DEFINITIONS FOR ENQ/DEQ

	SWAPCD

DEFSTR(ENQLJQ,0,17,18)		;BACK POINTER TO LAST Q FOR JOB
DEFSTR(ENQNJQ,0,35,18)		;FORWARD POINTER TO NEXT Q FOR JOB
				;ENQNJQ AND ENQLJQ MUST BE IN WORD 0
DEFSTR(ENQLLQ,1,17,18)		;BACK POINTER TO LAST Q IN LOCK QUEUE
DEFSTR(ENQNLQ,1,35,18)		;FORWARD POINTER TO NEXT Q OF LOCK
DEFSTR(ENQFLG,2,11,12)		;FLAGS OF BLOCK (EITHER LOCK OR Q)
DEFSTR(ENQCHN,2,17,6)		;PSI CHANNEL #, -1 MEANS JOB BLOCKED
DEFSTR(ENQFRK,2,35,18)		;FORK NUMBER OF CREATOR OF Q-BLOCK
DEFSTR(ENQNR,3,17,18)		;# OF RESOURCES REQUESTED
DEFSTR(ENQID,3,35,18)		;ID OF ENQ REQUEST
DEFSTR(ENQLRQ,4,17,18)		;BACK POINTER TO REST OF REQUEST
DEFSTR(ENQFQ,4,35,18)		;FORWARD POINTER TO REST OF REQUEST
DEFSTR(ENQLBP,5,17,18)		;POINTER TO LOCK-BLOCK OF THIS Q
DEFSTR(ENQGRP,5,35,18)		;GROUP NUMBER OF SHARABLE REQUEST
DEFSTR (ENQJFN,6,35,18)		;JFN OF ENQ REQUEST
DEFSTR (ENQNST,6,17,18)		;NEST COUNT
DEFSTR (ENQMSK,7,35,18)		;POINTER TO MASK BLOCK

DEFSTR(ENQLHC,0,17,18)		;BACK POINTER TO LAST LOCK IN HASH LIST
DEFSTR(ENQNHC,0,35,18)		;FORWARD PNTR TO NEXT LOCK ON HASH LIST
				;ENQNHC AND ENQLHC MUST BE IN WORD 0
DEFSTR(ENQLVL,2,35,18)		;LEVEL NUMBER OF LOCK
DEFSTR(ENQTR,3,17,18)		;TOTAL # OF RESOURCES IN POOL
DEFSTR(ENQRR,3,35,18)		;# OF RESOURCES REMAINING IN POOL
DEFSTR(ENQTS,4,35,36)		;TIME STAMP OF LAST REQUEST TO BE LOCKED
DEFSTR(ENQFBP,5,17,18)		;POINTER TO FREE Q-BLOCK
DEFSTR(ENQLT,5,35,18)		;LONG TERM LOCK LIST FOR THIS JOB
	.ENQLT==5		;OFFSET OF LOCK LIST ELEMENT
DEFSTR(ENQOFN,6,17,18)		;OFN, -2, -3, OR 400000+JOB NUMBER
DEFSTR(ENQLEN,6,35,18)		;LENGTH OF LOCK-BLOCK
DEFSTR(ENQNMS,7,17,18)		;NUMBER OF WORDS IN THE MASK BLOCK
DEFSTR(ENQTXT,10,35,36)		;FIRST WORD OF TEXT OR USER CODE
	.ENTXT==10		;INDEX INTO LOCK-BLOCK FOR TEXT BLOCK
DEFSTR(ENQOTA,ENQLST,8,9)	;ENQ/DEQ QUOTA
DEFSTR(ENQCNT,ENQLST,17,9)	;COUNT OF REQUESTS QUEUED UP


	QBLEN==10		;LENGTH OF Q-BLOCK
	LBLEN==10		;LENGTH OF LOCK-BLK NOT COUNTING ENQTXT
	ENQMXW==^D50		;MAXIMUM # OF WORDS IN TEXT STRING
	ENQMFN==3		;MAX ENQ FUNCTION CODE
	DEQMXF==1		;MAX DEQ FUNCTION CODE
	ENQCMF==2		;MAX ENQC FUNCTION CODE
	ENQSTQ==^D50		;STANDARD ENQ/DEQ QUOTA
	.ENQRL==3		;LENGTH OF A LOCK REQUEST
	.ENQHL==2		;# OF HEADER WORDS IN ARGUMENT BLOCK
	.ENWCH==77		;FORK IS WAITING FOR CALL TO WAKFRK
	LVLLEN==11		;LENGTH OF LEVEL NUMBER FIELD

;ENQ/DEQ FLAGS IN "ENQFLG"

	EN.LB==1		;THIS IS THE LOCK-BLOCK
	EN.EXC==2		;REQUEST IS EXCLUSIVE
	EN.TXT==4		;THIS LOCK HAS A TEXT STRING IDENTIFIER
	EN.LOK==10		;THIS Q-BLOCK HAS THE LOCK LOCKED
	EN.INV==20		;THIS Q-BLOCK IS INVISIBLE
	EN.LTL==40		;LONG TERM LOCK
;THE ENQ JSYS

;ACCEPTS IN 1/	FUNCTION CODE
;	    2/	LOCATION OF ARGUMENT BLOCK
;	ENQ
;RETURNS +1:	ERROR - ERROR CODE IN T1
;	 +2:	SUCCESSFUL, SPECIFIED LOCKS ARE LOCKED

;THE ARGUMENT BLOCK HAS THE FOLLOWING FORMAT:
;LOC/		# OF LOCKS ,, LENGTH OF ARG BLOCK
;LOC+1/		PSI CHANNEL # ,, REQUEST ID
;LOC+2/		FLAGS & LEVEL NUMBER ,, JFN, -1, -2, OR -3
;LOC+3/		POINTER TO STRING OR 500000+USER CODE
;LOC+4/		# OF RESOURCES IN POOL ,, # OF RESOURCES REQUESTED
;			.
;			.
;			.
;LOC+N/		FLAGS & LEVEL NUMBER ,, JFN, -1, -2, OR -3
;LOC+N+1/	POINTER TO STRING OR 500000 + USER CODE
;LOC+N+2/	# OF RESOURCES IN POOL ,, # OF RESOURCES REQUESTED

.ENQ::	MCENT			;ENTER JSYS
	NOINT			;DONT ALLOW INTERRUPTS DURING CALL
	LOCK ENQLOK		;LOCK UP THE ENQ DATA BASE
	SETO T1,		;MARK THAT THIS IS A NORMAL TYPE ENQ
	CALL ENQREQ		;GO SERVICE THIS REQUEST
	 JRST ENUNLE		;ERROR RETURN, GO UNLOCK
	UMOVEM T2,1		;RETURN ANSWER FOR USER
	UNLOCK ENQLOK		;FREE UP THE DATA BASE
	OKINT			;ALLOW INTERRUPTS
	SKIPE T1		;DO MDISMS?
	MDISMS			;YES, WAIT FOR LOCK TO FREE UP
	SMRETN			;GIVE SUCCESSFUL RETURN TO USER

ENUNLE:	UNLOCK ENQLOK		;UNLOCK THE DATA BASE
	OKINT			;ALLOW INTERRUPTS AGAIN
	RETERR			;GIVE ERROR RETURN TO USER
REPEAT 0,<
;INTERNAL MONITOR ROUTINE TO ENQ ON A 33-BIT NUMBER

;ACCEPTS IN T1/	33-BIT NUMBER
;ASSUMES A -3 LOCK, LEVEL # OF 0, ONE LOCK, ID = 0, AND ALWAYS WAIT
;	CALL ENQUE
;RETURNS +1:	ALWAYS, LOCK LOCKED

ENQUE:	NOINT			;LOCK UP THE DATA BASE
	LOCK ENQLOK		;...
	SAVEPQ			;SAVE ALL PERMANENT ACS
	CALL MONVAL		;GET ARGUMENTS SET UP
	CALL ENQREQ		;DO THE ENQ
	 BUG(ENQMLF)
	UNLOCK ENQLOK		;FREE UP DATA BASE
	OKINT
	SKIPE T1		;NEED TO MDISMS?
	MDISMS			;YES, WAIT TILL LOCK IS LOCKED
	RET			;AND RETURN

>

;ROUTINE TO SET UP Q1-Q3, P1-P5 FOR INTERNAL ENQ AND DEQ CALLS

;	CALL MONVAL
;RETURNS +1:	ALWAYS

MONVAL:	TLZ T1,700000		;SET UP 500000,,0 + 33-BIT NUMBER
	TLO T1,500000
	MOVE P2,T1		;P2 = USER CODE TO LOCK ON
	MOVEI P1,-3		;P1 = LEVEL 0, -3 LOCK TYPE
	SETZB Q1,Q2		;Q1 = 1 LOCK, Q2 = 0 ID VALUE
	SETZB Q3,P3		;MONITOR LEVEL = 0, EXCULSIVE LOCK
	SETZB T1,P5		;SHARABLE GROUP 0
	RET


;ROUTINE TO SET UP GLOBAL VARIABLES USED BY CALLERS OF VALARG AND VALREQ

;	JSP T1,SETVAR
;RETURNS +1:	ALWAYS

SETVAR:	TRVAR <EDNMS,EDMSK,EDSTP,EDSTB>
	CALLRET 0(T1)

;EDNMS = NUMBER OF WORDS IN THE MASK BLOCK (MINUS COUNT WORD)
;EDMSK = ADDRESS OF THE FIRST MASK WORD OF MASK BLOCK 
;EDSTP = -1 ,, LENGTH OF LOCK REQUEST BLOCK
;EDSTB = ADDRESS OF STATUS BLOCK FOR ENQC FUNCTION 0
;ROUTINE TO DO THE ENQ JSYS

;THIS ROUTINE MUST BE CALLED NOINT AND WITH ENQLOK LOCKED
;ACCEPTS IN T1/	 0 = INTERNAL MONITOR CALL
;		-1 = NORMAL JSYS CALL
;	CALL ENQREQ
;RETURNS +1:	ERROR - CODE IN T1
;	 +2:	OK
;		T1 = 0, REQUEST IS LOCKED
;		T1 NOT 0, DO MDISMS FIRST, THEN RETURN
;		T2 = VALUE TO BE RETURNED IN USER AC 1

ENQREQ:	JSP T1,SETVAR		;SET UP GLOBAL VARIABLES
	STKVAR <ENQFC,ENQHI,ENQQ,ENQLB,ENQERC,ENQOKR>
	SETZM ENQFC		;INITIALIZE FUNCTION CODE 
	JUMPE T1,ENQ5		;IF 0, THIS IS A MONITOR CALL
	SETZM ENQERC		;INITIALIZE ERROR CODE REGISTER
	SETZM ENQOKR		;INITIALIZE OK RETURN ANSWER
	SETZM ENQQ		;INITIALIZE THE Q-BLOCK ADDDRESS
	SETZM ENQLB		;INITIALIZE LOCK BLOCK ADDRESS
	CALL VALARG		;VALIDATE THE USER'S ARGUMENT BLOCK
	 RET			;SOMETHING ILLEGAL
	XCTU [HRRZ T1,1]	;GET FUNCTION CODE
	CAILE T1,ENQMFN		;MAKE SURE IT IS LEGAL
	RETBAD (ENQX1)		;OUT OF BOUNDS, GIVE ERROR RETURN
	MOVEM T1,ENQFC		;SAVE FUNCTION CODE FOR LATER
	CAIN T1,0		;IS THIS FUNCTION 0?
	HRRI Q2,.ENWCH		;YES, MARK THE WAKFRK IS TO BE CALLED
ENQ0:	CALL VALREQ		;VALIDATE THIS LOCK REQUEST
	 JRST ENQERR		;UNDO WHAT WAS JUST DONE
ENQ5:	MOVE T1,ENQFC		;GET FUNCTION CODE
	CAIN T1,.ENQMA		;IS IT MODIFY ACCESS FUNCTION?
	JRST ENQFN3		;YES, HANDLE THIS SEPERATELY
	CALL HASH		;HASH THIS REQUEST
	 JRST ENQERR		;COULDNT HASH, GO UNDO REQUEST
	MOVEM T1,ENQHI		;REMEMBER THE HASH INDEX VALUE
	CALL FNDLOK		;FIND THE LOCK BLOCK
	 JRST [	MOVE T1,ENQHI	;NO LOCK BLOCK, GET HASH INDEX AGAIN
		CALL CRELOK	;CREATE A LOCK BLOCK FOR THIS LOCK
		 JRST ENQERR	;FAILED! GO UNDO REQUEST
		JRST .+1]	;LOCK BLOCK CREATED, ADR IN T1
	MOVEM T1,ENQLB		;SAVE THE LOCK-BLOCK ADDRESS
	LOAD T2,ENQNMS,(T1)	;GET SIZE OF MASK BLOCK FROM LOCK BLOCK
	JUMPE T2,ENQ8		;IF NONE SPECIFIED YET, THEN OK
	CAME T2,EDNMS		;IF NOT 0, THEN BOTH BLOCKS MUST BE EQUAL
	SKIPN EDNMS		;UNLESS THE CALLER IS NOT GIVING A BLOCK
	SKIPA			;MASK BLOCKS ARE COMPATIBLE
	RETBAD (ENQX23)		;MISMATCHED MASK BLOCK SIZE
ENQ8:	LOAD T2,ENQLVL,(T1)	;GET LEVEL NUMBER OF THIS LOCK
	HRRZ T4,P1		;GET OFN
	HRRE T3,Q3		;GET USER MINIMUM LOCK LEVEL
	CAIN T4,-3		;IS THIS A MONITOR LOCK?
	HLRE T3,Q3		;YES, GET MONITOR MINIMUM LEVEL
	CAMG T2,T3		;IS THIS LEVEL NUMBER LEGAL?
	JRST [	TLNE P1,(EN%BLN)	;USER BYPASSING LEVEL NUMBERS?
		JRST [	MOVEI T3,ENQX2	;YES, SET UP ANSWER FOR USER
			MOVEM T3,ENQOKR
			JRST ENQ4]
		MOVEI T1,ENQX2	;NO, GO UNDO THE REQUEST
		JRST ENQERR]
	;..
	;..
ENQ4:	LDB T3,[POINT 9,P1,17]	;GET LEVEL NUMBER SPECIFIED IN REQ
	CAME T2,T3		;LEVEL NUMBERS MATCH?
	JRST [	TLNE P1,(EN%BLN)	;BYPASSING LEVEL #'S?
		JRST [	MOVEI T3,ENQX3	;YES, SET UP ERROR CODE
			MOVEM T3,ENQOKR	;FOR SKIP RETURN
			JRST ENQ6]
		MOVEI T1,ENQX3	;NO, THIS IS ILLEGAL
		JRST ENQERR]	;GO UNDO EVERYTHING
ENQ6:	LOAD T2,ENQTR,(T1)	;GET TOTAL NUMBER OF RESOURCES IN POOL
	HLRZ T3,P3		;GET USER'S VALUE
	CAME T2,T3		;ARE THEY IDENTICAL?
	JRST [	MOVEI T1,ENQX4	;NO, THIS IS NOT ALLOWED
		JRST ENQERR]	;GO UNDO
	CALL FNDQ		;SEE IF THERE IS ALREADY A REQUEST IN
	 SKIPA T1,ENQLB		;NO, GET LOCK BLOCK BACK
	JRST [	TXNE P1,EN%NST	;NESTED LOCK?
		JRST ENQ7	;YES
		MOVEI T1,ENQX5	;YES, THIS IS NOT ALLOWED
		JRST ENQERR]	;GO UNDO THE REQUEST
	MOVE T2,ENQQ		;GET LAST Q-BLOCK IN THIS REQUEST
	SETZ T3,		;INITIALIZE FLAGS
	JUMPN P3,ENQ1		;IF POOLED REQUEST, DONT SET ANY FLAGS
	TLNN P1,(EN%SHR)	;IS THIS A SHARED REQUEST
	TRO T3,EN.EXC		;NO, SET EXCLUSIVE FLAG
ENQ1:	CALL CREQ		;CREATE THE Q-BLOCK FOR THE REQUEST
	 JRST ENQERR		;FAILED, GO UNDO THE REQUEST
ENQ1A:	MOVEM T1,ENQQ		;SAVE THE Q-BLOCK ADR FOR OTHER ENTRIES
ENQ2:	ADD Q1,EDSTP		;ADVANCE THE POINTER TO THE NEXT REQ
	JUMPG Q1,ENQ0		;LOOP BACK FOR ALL REQUESTS
	SKIPE T1,ENQERC		;ANY ERRORS SEEN?
	RET			;YES, TAKE ERROR RETURN NOW
	SKIPN T1,ENQQ		;GET A Q-BLOCK ADDRESS IF ANY
	JRST ENQ3		;NONE, MUST BE CHANGE ACCESS CALL
	CALL QSKD		;DO A SCHEDULING PASS OVER QUEUE
	 JRST ENQNL		;LOCK NOT LOCKED
ENQ3:	SETZ T1,		;FLAG THAT MDISMS IS NOT NEEDED
	MOVE T2,ENQOKR		;SET UP USER'S AC
	RETSKP			;REQUEST IS FULLY LOCKED


ENQ7:	INCR ENQNST,(T1)	;COUNT UP THE NEST COUNT
	JRST ENQ1A		;AND ALLOW THIS ENQ TO CONTINUE
ENQNL:	MOVE T1,ENQFC		;GET THE FUNCTION CODE
	JRST @ENQNLT(T1)	;GO PERFORM THE PROPER EXITING

ENQNLT:	IFIW!ENQWAT		;0 - WAIT UNTIL LOCKED
	IFIW!ENQDEQ		;1 - DEQ THIS REQUEST
	IFIW!ENQRET		;2 - JUST EXIT AND WAIT FOR INTERRUPT


ENQWAT:	MOVE T1,FORKX		;WAIT UNTIL THE REQUEST IS SATISFIED
	CALL GETMSK		;CLEAR OUT WAITING CONDITION
	ANDCAM T2,ENFKTB(T1)	;...
	HRRI T1,ENQTST		;SET UP FOR MDISMS
	HRL T1,FORKX
	RETSKP			;RETURN AND DO AN MDISMS UNTIL LOCKED

ENQDEQ:	MOVE T1,ENQQ		;GET Q-BLOCK ADDRESS
	CALL REQDEQ		;DEQUEUE THIS REQUSET
ENQRET:	MOVEI T1,ENQX6		;GET "LOCK NOT SET" ERROR CODE
	RET			;GIVE ERROR RETURN

ENQERR:	MOVEM T1,ENQERC		;SAVE ERROR CODE
	SKIPN T1,ENQLB		;WAS A LOCK-BLOCK FOUND?
	JRST ENQER1		;NO, JUST EXIT
	LOAD T2,ENQNLQ,(T1)	;IS IT AN EMPTY LOCK-BLOCK?
	CAIN T2,0(T1)		;...
	CALL LOKREL		;YES, DELETE IT
	SKIPE T1,ENQQ		;WAS A Q-BLOCK CREATED?
	CALL REQDEQ		;YES, DELETE THE REQUEST
ENQER1:	MOVE T1,ENQERC		;GET ERROR CODE BACK
	RET			;AND GIVE NON-SKIP RETURN
;FUNCTION CODE 3 (.ENQMA)

ENQFN3:	CALL HASH		;HASH THIS REQUEST
	 JRST ENQF3F		;ERROR
	CALL FNDLOK		;FIND THE LOCK BLOCK
	 JRST ENQF3F		;NOT ENQ'D ON THIS LOCK
	MOVEM T1,ENQLB		;SAVE LOCK-BLOCK ADDRESS
	CALL FNDQ		;FIND THE Q-BLOCK FOR THIS REQUEST
	 JRST ENQF3F		;NOT ENQ'D ON THIS LOCK
	LOAD T2,ENQTR,(T1)	;GET TOTAL # REQUESTED
	JUMPN T2,ENQF3E		;CANT CHANGE POOLED REQUESTS
	LOAD T2,ENQFLG,(T1)	;GET FLAGS OF REQUEST
	TLNE P1,(EN%SHR)	;WANT IT TO BE SHARABLE
	JRST [	TRZN T2,EN.EXC	;YES, SET IT SHARABLE
		JRST ENQ2	;ALREADY WAS SHARABLE
		STOR T2,ENQFLG,(T1) ;STORE UPDATED FLAGS
		MOVE T1,ENQLB	;GET LOCK BLOCK ADDRESS AGAIN
		CALL LOKSKD	;SCHEDULE THIS LOCK
		JRST ENQ2]	;LOOP BACK FOR ALL REQUESTS
	TROE T2,EN.EXC		;SEE IF ALREADY EXCLUSIVE
	JRST ENQ2		;YES, THIS IS OK SINCE NOT CHANGING IT
	LOAD T3,ENQLLQ,(T1)	;SEE IF THIS IS THE ONLY SHARER
	LOAD T4,ENQNLQ,(T1)	;GET POINTER TO LAST AND NEXT Q-BLOCK
	CAME T3,T4		;IF EQUAL, THEN THIS IS THE ONLY SHARER
	JRST ENQF3E		;NOT EQUAL, GIVE ERROR RETURN
	STOR T2,ENQFLG,(T1)	;MAKE THIS BE AN EXCLUSIVE LOCK
	JRST ENQ2		;AND LOOP BACK FOR THE OTHERS

ENQF3E:	MOVEI T1,ENQX8		;FUNCTION 3 ERROR
ENQF3F:	MOVEM T1,ENQERC		;REMEMBER THAT AN ERROR OCCURED
	JRST ENQ2		;GO DO AS MUCH AS POSSIBLE
;THE DEQ JSYS - DEQUEUE REQUESTS

;ACCEPTS IN 1/	FUNCTION
;	    2/	LOCATION OF ARGUMENT BLOCK  (FUNCTION 0 ONLY)
;	DEQ
;RETURNS +1:	UNSUCCESSFUL - ERROR CODE IN T1 
;				(AS MUCH AS POSSIBLE DEQUEUED)
;	 +2:	SUCCESSFUL - REQUEST DEQUEUED

;THE ARGUMENT BLOCK IS IDENTICAL TO THAT OF ENQ

.DEQ::	MCENT			;ENTER JSYS
	NOINT			;LOCK UP
	LOCK ENQLOK		;...
	CALL DEQREQ		;GO DO THE DEQUEUEING
	 JRST ENUNLE		;ERROR DURING DEQ, RETURN ERROR CODE
DEQOKR:	CALL LGTAD		;GET TIME OF DAY
	CAMLE T1,ENQLTS		;IS IT TIME TO DO A GARBAGE COLLECT?
	JRST [	ADDI T1,^D<10*60*3> ;YES, SAVE TIME OF NEXT GC
		EXCH T1,ENQLTS	;DO A GARBAGE COLLECT EVERY 10 MINUTES
		CALL ENQGC
		JRST .+1]
	UNLOCK ENQLOK		;UNLOCK THE DATA BASE
	OKINT
	SMRETN			;GIVE SUCCESSFUL RETURN TO USER

;ROUTINE TO DO THE ACTUAL DEQUEUEING

DEQREQ:	XCTU [HRRZ T2,1]	;GET THE FUNCTION CODE
	CAIL T2,DQTABL		;IS THIS A LEGAL FUNCTION CODE?
	RETBAD (ENQX1)		;NO, TELL USER
	SETO T1,		;THIS IS A JSYS CALL
	JRST @DEQTAB(T2)	;DISPATCH TO FUNCTION CODE

DEQTAB:	IFIW!DEQFN0		;NORMAL DEQ
	IFIW!DEQFN1		;DEQ ALL RESOURCES
	IFIW!DEQFN2		;DEQ THIS ID
DQTABL==.-DEQTAB

REPEAT 0,<

;INTERNAL MONITOR ROUTINE TO DO A DEQ (FUNCTION 0 ONLY)

;ACCEPTS IN T1/	33-BIT NUMBER
;ASSUMES A -3 LOCK, LEVEL # OF 0, ONE LOCK, ID = 0, AND ALWAYS WAIT
;	CALL DEQUE
;RETURNS +1:	ALWAYS

DEQUE:	NOINT			;LOCK UP THE DATA BASE
	LOCK ENQLOK		;...
	SAVEPQ			;SAVE THE PERMANENT ACS
	CALL MONVAL		;SET UP THE ARGUMENTS IN Q1-Q3, P1-P4
	CALL DEQFN0		;DO THE DEQ
	 BUG(DEQMDF)
	UNLOCK ENQLOK		;UNLOCK THE LOCKS
	OKINT
	RET			;AND RETURN TO CALLER
>
;DEQ FUNCTION 0

;ACCEPTS IN T1/	 0 = INTERNAL MONITOR CALL
;		-1 = JSYS CALL  (READ ARGUMENTS FROM USER SPACE)

DEQFN0:	JSP T1,SETVAR		;SET UP GLOBAL VARIABLES
	STKVAR <DQFN0T,DQFN0Q>
	SETZM DQFN0T		;INITIALIZE ERROR COUNTER
	JUMPE T1,DQFN0D		;IF MONITOR CALL, ARGS ARE SET UP
	CALL VALARG		;VALIDATE THE ARGUMENT BLOCK
	 RET			;ILLEGAL ARGUMENT BLOCK
DQFN0A:	CALL VALREQ		;VALIDATE THIS LOCK SPECIFICATION
	 JRST DQFN0B		;ERROR
DQFN0D:	CALL HASH		;HASH THIS REQUEST
	 JRST DQFN0B		;ERROR DURING HASH
	CALL FNDLOK		;FIND THE LOCK-BLOCK
	 JRST DQFN0B		;NO SUCH LOCK-BLOCK
	LOAD T2,ENQFLG,(T1)	;GET FLAGS OF THE LOCK BLOCK
	TXNE P1,EN%LTL		;IS THIS A LONG TERM LOCK
	TXO T2,EN.LTL		;YES, REMEMBER THIS IN THE LOCK BLOCK
	STOR T2,ENQFLG,(T1)
	CALL FNDQ		;FIND THE Q-BLOCK FOR THIS FORK
	 JRST DQFN0B		;COULD NOT FIND THE Q-BLOCK
	MOVEM T1,DQFN0Q		;SAVE THE Q-BLOCK ADDRESS
	LOAD T2,ENQNST,(T1)	;GET NEST COUNT
	JUMPG T2,[DECR ENQNST,(T1)
		JRST DQFN0C]	;THIS WAS A NESTED ENQ, DONT DEQ IT
	LOAD T2,ENQNR,(T1)	;GET NUMBER LOCKED IN ORIGINAL ENQ
	JUMPE T2,[CALL DEQMSK	;IF 0, SEE IF DEQ'ING A MASK
		 JRST DQFN0E	;NOT COMPLETELY DEQUEUED
		MOVE T1,DQFN0Q	;OK TO DELETE THIS Q-BLOCK
		CALL SQDEQ	;GO DELETE THIS Q-BLOCK
		JRST DQFN0C]	;STEP TO NEXT REQUEST
	SUBI T2,0(P3)		;SEE IF DEQ'ING ALL RESOURCES
	JUMPL T2,[MOVEI T1,ENQX12
		JRST DQFN0B]	;DEQ'ING TOO MANY RESOURCES
	JUMPE T2,[CALL SQDEQ	;DEQ'ING ALL OF THEM, DELETE Q-BLOCK
		JRST DQFN0C]
	STOR T2,ENQNR,(T1)	;PUT BACK NEW # OF RESOURCES LOCKED
	LOAD T1,ENQLBP,(T1)	;GET ADDRESS OF LOCK BLOCK
	LOAD T2,ENQRR,(T1)	;GET # OF REMAINING RESOURCES
	ADDI T2,0(P3)		;UPDATE THE COUNT
	STOR T2,ENQRR,(T1)	;STORE NEW COUNT OF REMAINING RESOURCES
DQFN0E:	MOVE T1,DQFN0Q		;GET Q-BLOCK ADDRESS
	LOAD T1,ENQLBP,(T1)	;GET ADDRESS OF THE LOCK BLOCK
	CALL LOKSKD		;GO SCHEDULE THIS LOCK
	JRST DQFN0C		;DONT COUNT UP ERROR COUNTER
DQFN0B:	MOVEM T1,DQFN0T		;SAVE THIS ERROR CODE
DQFN0C:	ADD Q1,EDSTP		;STEP TO THE NEXT LOCK REQUEST
	JUMPG Q1,DQFN0A		;LOOP BACK FOR ALL LOCKS
	SKIPG T1,DQFN0T		;ANY ERRORS SEEN?
	RETSKP			;NO, DEQUEUING COMPLETED
	RET			;YES, RETURN ERROR CODE IN T1
;ROUTINE TO SEE IF A REQUEST IS COMPLETELY DEQ'ED
;ACCEPTS IN T1/	Q-BLOCK ADDRESS
;	CALL DEQMSK
;RETURNS +1:	NOT COMPLETELY DEQUEUED
;	 +2:	OK TO DELETE THIS Q-BLOCK

DEQMSK:	STKVAR <DEQMSF>
	SETZM DEQMSF		;INITIALIZE THE FLAG WORD
	SKIPN T4,EDMSK		;IS THERE A MASK SPECIFIED?
	RETSKP			;NO, THEN DEQ THE WHOLE THING
	LOAD T3,ENQMSK,(T1)	;GET THE POINTER TO THE MASK BLOCK
	JUMPE T3,RSKP		;IF NONE, THEN DEQ THE WHOLE THING
	LOAD T2,ENQLBP,(T1)	;GET LOCK BLOCK ADDRESS
	LOAD T2,ENQNMS,(T2)	;GET LENGTH OF MASK BLOCK
	CAMLE T2,EDNMS		;DO THE LENGTHS MATCH?
	SETOM DEQMSF		;NO, MARK THIS AS NOT COMPLETELY DEQ'D
	CAMLE T2,EDNMS		;GET THE LESSER OF THE TWO LENGTHS
	MOVE T2,EDNMS		;...
DEQMS1:	MOVE T1,0(T3)		;GET FIRST WORD OF MASK BLOCK
	XCTU [ANDCM T1,0(T4)]	;TURN OFF THE BITS BEING DEQUEUED
	MOVEM T1,0(T3)		;UPDATE THE MASK IN THE Q-BLOCK
	SKIPE T1		;ANY RESOURCES LEFT ON?
	SETOM DEQMSF		;YES, MARK THAT THE LOCK IS STILL LOCKED
	AOS T3			;STEP THE MASK POINTER
	AOS T4			;STEP THE USER MASK POINTER
	SOJG T2,DEQMS1		;LOOP BACK THROUGH THE WHOLE MASK BLOCK
	SKIPE DEQMSF		;ANY PART OF THE LOCK STILL LOCKED?
	RET			;YES
	RETSKP			;NO, GO DELETE IT

;DEQ FUNCTION 1

DEQFN1:	HRRZ T1,FORKX		;GET SYSTEM FORK HANDLE
	SETO T2,		;DEQ ALL
	CALL DEQFRK		;DELETE ALL REQUESTS FOR THIS FORK
	JUMPG T1,RSKP		;WERE ANY RELEASED?
	RETBAD (ENQX7)		;NO, THIS IS AN ERROR


;DEQ FUNCTION 2 - DEQ AN ID

DEQFN2:	HRRZ T1,FORKX		;FOR THIS FORK ONLY
	XCTU [HRRZ T2,2]	;GET THE ID
	CALL DEQFRK		;DEQ THIS ID ONLY
	JUMPG T1,RSKP		;WERE ANY RELEASED?
	RETBAD (ENQX7)		;NO, THIS IS AN ERROR
;THE ENQC JSYS - ENQ CONTROL

;USED TO GET STATUS OF LOCKS AND GET AND SET QUOTA
;ACCEPTS IN T1/	FUNCTION CODE  -  0, 1, OR 2
;	    T2/	LOCATION OF ARGUMENT BLOCK
;	    T3/	ADDRESS OF STATUS BLOCK (FUNCTION 0 ONLY)
;	ENQC
;RETURNS +1:	ERROR - CODE IN T1
;	 +2:	SUCCESSFUL

;ARGUMENT BLOCK FOR FUNCTION 0 IS SAME AS FOR ENQ
;	FUNCTION 1 AND 2 ARGUMENT BLOCK IS:	QUOTA ,, JOB NUMBER

.ENQC::	MCENT			;ENTER JSYS
	XCTU [HRRZ T1,1]	;GET FUNCTION CODE
	CAIN T1,.ENQCD		;DOING A DUMP?
	JRST ENQCD		;YES, GO DUMP
	JUMPN T1,ENQC1		;ALL EXCEPT FUNCTION 0 GO TO ENQC1
	NOINT			;LOCK UP
	LOCK ENQLOK		;...
	CALL ENQCF0		;DO FUNCTION 0
	 JRST ENUNLE		;ERROR, GO UNLOCK AND RETURN THE CODE
	JRST DEQOKR		;SUCCESSFUL
;ENQC FUNCTION 0

ENQCF0:	JSP T1,SETVAR		;SET UP THE GLOBAL VARIABLES
	STKVAR <ENCF0L,ENCF0T,ENCF0S,ENCF0I>
	SETZM ENCF0L		;INITIALIZE LOCK BLOCK ADR
	SETZM ENCF0T		;INITIALIZE ERROR CODE REGISTER
	UMOVE T1,3		;GET ADDRESS OF STATUS BLOCK
	MOVEM T1,EDSTB		;SAVE THE ADR OF THE STATUS BLOCK
	CALL VALARG		;VALIDATE THE ARGUMENT BLOCK
	 RET			;ERROR IN FORMAT
ENCF0A:	SETZM ENCF0S		;INITIALIZE TIME STAMP REGISTER
	SETZM ENCF0I		;..AND REQUEST ID REGISTER
	CALL VALREQ		;GET INFO ABOUT THE NEXT LOCK REQUEST
	 JRST ENCF0E		;ERROR ON THIS SPECIFICATION
	CALL HASH		;HASH IT
	 JRST ENCF0E		;ERROR
	CALL FNDLOK		;FIND THE LOCK
	 JRST ENCF0F		;NOT DEFINED, IT MUST BE AVAILABLE
	MOVEM T1,ENCF0L		;REMEMBER THE LOCK ADDRESS
	CALL FNDQ		;FIND OUR REQUEST IF THERE IS ONE
	 TDZA T2,T2		;NOT IN Q, ZERO FLAG REGISTER
	MOVX T2,EN%QCQ		;WE ARE IN THE QUEUE
	JUMPE T2,ENCF0B		;IN THE QUEUE?
	LOAD T3,ENQID,(T1)	;YES, GET REQUEST ID OF US
	MOVEM T3,ENCF0I		;SAVE IT FOR LATER
ENCF0B:	HRRI T2,-1		;ASSUME NOBODY HAS IT LOCKED
	MOVE T1,ENCF0L		;GET ADDRESS OF LOCK AGAIN
	LOAD T3,ENQTS,(T1)	;GET TIME STAMP OF LOCK
	MOVEM T3,ENCF0S		;SAVE IT
	LOAD T3,ENQLVL,(T1)	;GET LEVEL NUMBER
	DPB T3,[POINT LVLLEN,T2,17]
ENCF0G:	LOAD T1,ENQNLQ,(T1)	;SCAN THE LIST LOOKING FOR US
	LOAD T3,ENQFLG,(T1)	;GET FLAGS
	TRNE T3,EN.LB		;BACK TO THE LOCK BLOCK?
	JRST ENCF0D		;YES, NOT FOUND
	LOAD T4,ENQFRK,(T1)	;GET FORK NUMBER OF OWNER
	CAME T4,FORKX		;US?
	JRST ENCF0G		;NO, LOOP BACK THRU LIST
;**;[3055] Insert 6 lines at ENCF0G:+7L		CRJ	7-Dec-83
	TXNE T2,EN%QCQ		;Are we in the queue?
	 TXNN T3,EN.EXC		;Yes, exclusive access requested?
	  SKIPA			;No
	   TXO T2,EN%QCB	;Yes to both, we're in q for exclusive access
	TXNN T3,EN.LOK		;Have we got it locked?
	 JRST ENCF0G		;No, remember our q status, find locker
	JRST ENCF0H		;YES

ENCF0D:	MOVE T1,ENCF0L		;GET LOCK BLOCK ADR AGAIN
	LOAD T1,ENQNLQ,(T1)	;GET FIRST Q ON LIST
	LOAD T3,ENQFLG,(T1)	;GET FLAGS OF Q ENTRY
	TRNN T3,EN.LB		;IS THIS THE LOCK BLOCK?
	TRNE T3,EN.INV		;NO, IS THIS INVISIBLE?
	JRST ENCF0C		;YES, NOBODY OWNS LOCK
	LOAD T4,ENQFRK,(T1)	;GET FORK NUMBER OF THIS Q-BLOCK
	TRNE T3,EN.LOK		;IS IT LOCKED?
	CAME T4,FORKX		;AND DOES THIS BELONG TO OUR FORK?
	SKIPA			;NO, DONT SET LOCKED BIT
ENCF0H:	TLO T2,(EN%QCO)		;YES, MARK THAT WE OWN IT
	TRNE T3,EN.EXC		;IS THIS AN EXCLUSIVE REQUEST?
	TLO T2,(EN%QCX)		;YES, SET THE EXCLUSIVE BIT
	LOAD T3,ENQFRK,(T1)	;GET THE OWNER OF THIS Q-BLOCK
	HLR T2,FKJOB(T3)	;PUT JOB NUMBER IN RIGHT HALF WORD
	LOAD T3,ENQID,(T1)	;GET REQUEST ID
	SKIPN ENCF0I		;IF IT'S NOT ALREADY SET...
	MOVEM T3,ENCF0I		;..DO SO
ENCF0C:	MOVE T3,EDSTB		;GET ADR OF TEH STATUS BLOCK
	UMOVEM T2,0(T3)		;STORE  ANSWER IN STATUS BLOCK
	MOVE T2,ENCF0S		;GET TIME STAMP OF LOCK
	UMOVEM T2,1(T3)		;GIVE IT TO THE USER
	SKIPE T1,ENCF0L		;GET ADR OF LOCK BLOCK
	CALL CNTQ		;COUNT THE NUMBER OF SHARERS
	MOVE T3,EDSTB		;GET BACK THE ADR OF THE STATUS BLOCK
	MOVE T2,ENCF0I		;GET REQUEST ID
	HRL T2,T1		;PUT THE COUNT OF SHARERS IN LH
	UMOVEM T2,2(T3)		;STORE IN STATUS BLOCK
	HRRI T3,3(T3)		;INCREMENT POINTER TO STATUS BLOCK
	MOVEM T3,EDSTB		;SAVE THE UPDATED ADR OF THE STATUS BLOCK
	ADD Q1,EDSTP		;STEP TO NEXT LOCK REQUEST
	JUMPG Q1,ENCF0A		;LOOP BACK IF ANY MORE LOCKS TO CHECK
	SKIPN T1,ENCF0T		;DID ANY ERRORS OCCUR DURING TESTING?
	RETSKP			;NO, GIVE OK RETURN
	RET			;YES, GIVE ERROR RETURN
ENCF0E:	HRLI T1,(EN%QCE)	;SET ERROR BIT IN LH OF WORD
	MOVE T2,T1		;SET UP TO STORE ERROR CODE IN LIST
	HRRZM T1,ENCF0T		;STORE ERROR CODE FOR ERROR RETURN
	JRST ENCF0C		;GO STORE CODE IN STATUS BLOCK

ENCF0F:	MOVEI T2,-1		;MARK THAT THE LOCK IS FREE
	JRST ENCF0C		;GO STORE ANSWER IN STATUS BLOCK
;ENQC FUNCTIONS 1 AND 2

ENQC1:	UMOVE P1,2		;GET POINTER TO ARG BLOCK
	MOVE P2,T1		;SAVE FUNCTION CODE
	CAILE P2,ENQCMF		;LEGAL FUNCTION CODE?
	RETERR (ENQX1)		;NO, GIVE ERROR RETURN
	CAIN P2,.ENQCC		;SETTING QUOTA?
	JRST [	CALL CHKWHL	;YES, MUST BE A WHEEL
		JRST ENQGOK	;NOT PRIVILEGED ASK PERMISSION
		JRST .+1]
ENQOK:	XCTU [HRRZ T1,0(P1)]	;GET JOB NUMBER
	CAIN T1,-1		;USER WANT SELF?
	HRRZ T1,JOBNO		;YES, GET OUR JOB NUMBER
	CAIGE T1,NJOBS		;WITHIN BOUNDS?
	SKIPGE JOBRT(T1)	;IS JOB LOGGED IN?
	RETERR (ENQX21)		;NO, ILLEGAL JOB#
				;DONT ALLOW INTERRUPTS WITH JSB MAPPED
	CALL SETJSB		;MAP IN THIS JSB
	XCTU [HLRZ T2,0(P1)]	;GET NEW QUOTA
	CAIN P2,.ENQCC		;SETTING QUOTA?
	STOR T2,ENQOTA,(T1)	;YES, SET IT
	LOAD T3,ENQOTA,(T1)	;GET CURRENT QUOTA
	CAIN P2,.ENQCG		;USER WANTS QUOTA?
	UMOVEM T3,1		;YES, GIVE IT TO HIM
	CALL CLRJSB		;UNMAP JSB AND ENABLE INTERRUPTS
	SMRETN			;GIVE OK RETURN

ENQGOK:	XCTU [HLRZ T2,0(P1)]	;GET QUOTA
	XCTU [HRRE T1,0(P1)]	;GET JOB NUMBER
	GTOKM (.GOENQ,<T2,T1>,[RETERR ()])
	JRST ENQOK
;ENQC FUNCTION 3 - DUMP THE LOCKS AND QUEUES

ENQCD:	CALL CHKWHL		;MUST BE A WHEEL TO DO THIS FUNCTION
	 RETERR			;NOT PRIVILEGED!
	NOINT			;LOCK THE LOCKS
	LOCK ENQLOK		;...
	MOVSI Q1,-HSHLEN	;SET UP COUNTER FOR MAIN LOOP
	HRRI Q1,HSHTBL
	UMOVE P1,2		;GET POINTER TO DATA BLOCK
	XCTU [MOVN T1,0(P1)]	;GET NEGATIVE LENGTH
	HRL P1,T1		;SET UP AOBJP WORD
ENQCD1:	HRRZ Q2,0(Q1)		;GET POINTER TO LOCK BLOCK IF ANY
ENQCD2:	CAIN Q2,0(Q1)		;BACK TO HASH TABLE?
	JRST ENQCD3		;YES, GO STEP TO NEXT HASH ENTRY
	MOVE T1,Q2		;GET ADDRESS OF LOCK BLOCK
	CALL ENQDMP		;GO DUMP LOCK AND QUEUE BLOCKS
	 JRST ENQCD4		;ALL DONE
	LOAD Q2,ENQNHC,(Q2)	;STEP TO NEXT LOCK BLOCK ON HASH CHAIN
	JRST ENQCD2		;LOOP BACK FOR ALL LOCKS

ENQCD3:	AOBJN Q1,ENQCD1		;LOOP BACK FOR ALL HASH ENTRIES
	AOBJP P1,ENQCD4		;STEP THE USER'S DATA POINTER
	XCTU [SETOM 0(P1)]	;MARK THE END OF THE DUMP
ENQCD4:	UNLOCK ENQLOK		;UNLOCK THE ENQ DATA BASE
	OKINT
	SMRETN			;AND GIVE OK RETURN
;ROUTINE TO DUMP THE ENQ DATA BASE

;ACCEPTS IN T1/	ADDRESS OF A LOCK BLOCK
;	    P1/	ADDRESS IN USER SPACE OF BLOCK TO RECEIVE DATA
;	CALL ENQDMP
;RETURNS +1:	RAN OUT OF SPACE IN USER BUFFER
;	 +2:	OK RETURN

ENQDMP:	LOAD T2,ENQOFN,(T1)	;GET OFN OR -2, -3, OR 400000+JOB
	LOAD T3,ENQFLG,(T1)	;GET LOCK BLOCK FLAGS
	LOAD T4,ENQLVL,(T1)	;GET LEVEL NUMBER OF THIS LOCK
	HRL T2,T4		;SET UP ANSWER IN T2
	TLO T2,(EN%QCL)		;MARK THAT THIS IS A LOCK BLOCK
	TRNE T3,EN.TXT		;STRING POINTER?
	TLO T2,(EN%QCT)		;YES, MARK THAT IN ANSWER
	AOBJP P1,R		;ENOUGH ROOM LEFT?
	UMOVEM T2,0(P1)		;YES, STORE ANSWER IN DATA BLOCK
	LOAD T2,ENQTR,(T1)	;GET # IN POOL
	LOAD T4,ENQRR,(T1)	;GET # REMAINING
	HRL T4,T2		;SET UP ANSWER
	AOBJP P1,R		;IS THERE MORE ROOM?
	UMOVEM T4,0(P1)		;YES, STORE DATA IN BUFFER
	LOAD T2,ENQTS,(T1)	;GET TIME STAMP OF LOCK
	AOBJP P1,R		;UPDATE POINTER TO USER AREA
	UMOVEM T2,0(P1)		;GIVE TIME STAMP TO USER
	LOAD T2,ENQLEN,(T1)	;GET LENGTH OF BLOCK
	SUBI T2,LBLEN		;GET LENGTH OF DATA OR TEXT AREA
	MOVE T3,T1		;GET COPY OF LOCK BLOCK ADDRESS
ENQDM1:	LOAD T4,ENQTXT,(T3)	;GET NEXT WORD OF STRING
	AOBJP P1,R		;STEP TO NEXT WORD
	UMOVEM T4,0(P1)		;GIVE TEXT WORD TO USER
	AOS T3			;STEP TO NEXT WORD OF TEXT
	SOJG T2,ENQDM1		;ANY MORE TO GET?
	MOVE T2,T1		;NO, NOW GET Q-BLOCK INFO
ENQDM2:	LOAD T2,ENQNLQ,(T2)	;GET ADDRESS OF NEXT Q-BLOCK
	CAMN T2,T1		;BACK TO LOCK BLOCK?
	RETSKP			;YES, ALL FINISHED
	LOAD T3,ENQFRK,(T2)	;GET FORK NUMBER OF OWNER
	HLRZ T3,FKJOB(T3)	;GET JOB NUMBER OF CREATOR OF ENTRY
	LOAD T4,ENQCHN,(T2)	;GET PSI CHANNEL #
	HRL T3,T4		;SET UP ANSWER
	CAIN T4,.ENWCH		;THIS FORK WAITING?
	TLC T3,.ENWCH+<EN%QCB>_-^D18 ;YES, SET BLOCKED BIT
	LOAD T4,ENQFLG,(T2)	;GET FLAGS OF ENTRY
	TRNE T4,EN.LOK		;IS THIS LOCKED?
	TLO T3,(EN%QCO)		;YES, SET THE OWNER BIT
	TRNE T4,EN.LOK		;LOCKED?
	TLZ T3,(EN%QCB)		;YES, MAKE SURE BLOCKED BIT IS OFF
	TRNE T4,EN.EXC		;EXCLUSIVE LOCK
	TLO T3,(EN%QCX)		;YES
	AOBJP P1,R		;ENOUGH ROOM?
	UMOVEM T3,0(P1)		;YES, GIVE IT TO USER
	LOAD T3,ENQID,(T2)	;GET ID NUMBER
	LOAD T4,ENQNR,(T2)	;GET NUMBER REQUESTED IF POOLED
	SKIPN T4		;IS THIS A NON-POOLED LOCK?
	LOAD T4,ENQGRP,(T2)	;YES, GET GROUP NUMBER INSTEAD
	HRL T3,T4		;CONSTRUCT ANSWER
	AOBJP P1,R		;STEP POINTER TO DATA BLOCK
	UMOVEM T3,0(P1)		;STORE IN USER BLOCK
	JRST ENQDM2		;LOOP BACK FOR OTHER Q ENTRIES
;ROUTINE TO VALIDATE THE ARGUMENT BLOCK FROM THE USER

;THIS ROUTINE LOADS Q1 - Q3 WITH THE FOLLOWING VALUES:
;	Q1/	# OF LOCKS-1 ,, ADDRESS OF FIRST LOCK REQUEST
;	Q2/	ID ,, PSI CHANNEL #
;	Q3/	MINIMUM MONITOR LEVEL ,, MINIMUM USER LOCK LEVEL
;	EDSTP/	-1 ,, LENGTH OF LOCK REQUEST BLOCK

VALARG:	UMOVE Q1,2		;GET LOCATION OF ARGUMENT BLOCK
	HRRZS Q1		;MUST CONTAIN USER SECTION WHEN SEC NON ZERO***
	UMOVE T1,0(Q1)		;GET THE HEADER WORD
	HRRZ T4,T1		;GET LENGTH OF ARG BLOCK INTO T4
	LOAD T3,.ENNLK,T1	;GET NUMBER OF LOCKS INTO T3
	JUMPE T3,[RETBAD (ENQX9)] ;0 LOCKS IS NOT ALLOWED
	HRLI Q1,-1(T3)		;SAVE NUMBER OF LOCKS-1
	LOAD T2,.ENHLN,T1	;GET THE LENGTH OF HEADER AREA
	HRRZ T1,Q1		;GET ADDRESS OF ARG BLOCK
	CAIGE T2,.ENQHL		;IS IT AN OLD STYLE HEADER?
	MOVEI T2,.ENQHL		;YES, SET UP THE LENGTH MANUALLY
	ADD Q1,T2		;MAKE Q1 POINT TO START OF LOCK BLOCK
	SUBM T4,T2		;GET LENGTH OF LOCK AREA INTO T2
	IDIV T2,T3		;GET SIZE OF EACH LOCK BLOCK
	SKIPE T3		;MUST BE NO REMAINDER TO BE VALID
	RETBAD (ENQX10)		;ILLEGALLY FORMATTED ARGUMENT BLOCK
	HRROM T2,EDSTP		;SET UP THE STEP VARIABLE
	XCTU [MOVS Q2,1(T1)]	;GET ID ,, CHANNEL #
	HRRZ T1,Q2		;VALIDATE PSI CHANNEL NUMBER
	CAILE T1,^D35		;IS IT A LEGAL CHANNEL #
	RETBAD (ENQX11)		;NO, GIVE ERROR RETURN
	CALL GETLVL		;GET THE MINIMUM ALLOWABLE LEVEL
	MOVE Q3,T1		;SAVE IN Q3
	RETSKP			;GIVE OK RETURN
;ROUTINE TO VALIDATE A LOCK REQUEST

;THIS ROUTINE ASSUMES Q1 - Q3 SET UP BY VALARG
;THIS ROUTINE SETS UP P1 - P3 WITH THE FOLLOWING INFORMATION:
;	P1/	FLAGS & LEVEL NUMBER ,, OFN
;	P2/	STRING POINTER OR 500000,,0 + USER CODE
;	P3/	# OF RESOURCES ,, # REQUESTED
;	P4 GETS SET UP WITH WORD LENGTH OF STRING BY HASH
;	P5/	JFN OF REQUEST ,, GROUP #
;	EDNMS/	NUMBER OF WORDS IN MASK BLOCK MINUS THE COUNT WORD
;	EDMSK/	ADR OF FIRST MASK WORD IN USERS MASK BLOCK

VALREQ:	HRRZ P3,Q1		;REMOVE HIGH BITS
	SETZM EDMSK		;INITIALIZE GLOBAL VARIABLES
	SETZM EDNMS
	HRRZ T1,EDSTP		;GET SIZE OF LOCK REQUEST BLOCK
	CAIGE T1,4		;LONG ENOUGH TO CONTAIN A MASK BLOCK?
	JRST VALRQ2		;NO
	UMOVE T1,3(P3)		;YES, GET ADR OF MASK BLOCK
	JUMPE T1,VALRQ2		;IS THERE ONE?
	UMOVE T2,0(T1)		;YES, GET ITS LENGTH
	SUBI T2,1		;DONT COUNT THE COUNT WORD
	SKIPLE T2		;IS IT A LEGAL LENGTH
	CAILE T2,<^D512+^D35>/^D36
	RETBAD (ENQX22)		;ILLEGAL MASK BLOCK LENGTH
	MOVEM T1,EDMSK		;SAVE THE MASK BLOCK ADDRESS
	MOVEM T2,EDNMS		;NUMBER OF WORDS IN MASK BLOCK
	AOS EDMSK		;STEP MASK BLOCK ADR OVER COUNT WORD
VALRQ2:	UMOVE P3,2(P3)		;GET # OF RESOURCES
	HRR P5,P3		;GET GROUP NUMBER IN P5
	TLNN P3,-1		;IS THIS A POOLED REQUEST
	JRST [	SETZ P3,	;NO, ZERO P3
		JRST VALRQ0]
	HLLZS P5		;YES, MAKE GROUP # BE 0
	TRNN P3,-1		;YES, MUST REQUEST AT LEAST 1
	RETBAD (ENQX12)		;ERROR IF 0
	HLRZ T1,P3		;GET TOTAL # IN POOL
	CAIGE T1,0(P3)		;CAN THIS REQUEST BE MET?
	RETBAD (ENQX12)		;NO, GIVE ERROR RETURN
VALRQ0:	HRRZ P2,Q1		;GET ADDRESS ONLY
	UMOVE P2,1(P2)		;GET STRING POINTER
	TLC P2,-1		;SEE IF -1 IN LH
	TLCN P2,-1		;...
	HRLI P2,(POINT 7,0)	;YES, SET UP ASCII POINTER
	LDB T2,[POINT 3,P2,2]	;GET HIGH ORDER 3 BITS
	CAIN T2,5		;IS THIS A USER CODE?
	JRST VALRQ1		;YES
	TLZE P2,37		;NO, INDIRECT OR INDEXED POINTER?
	RETBAD (ENQX13)		;YES, THIS IS NOT ALLOWED
VALRQ1:	HRRZ P1,Q1		;GET ADDRESS ONLY
	UMOVE P1,0(P1)		;GET FLAGS, LEVEL #, AND JFN
	HRL P5,P1		;SAVE JFN OF REQUEST
	HRRZ T1,P1		;GET JFN AGAIN
	CAIN T1,-1		;JOB WIDE LOCK?
	JRST [	HRR P1,JOBNO	;YES, GET JOB NUMBER
		TRO P1,400000	;MAKE IT 400000 + JOB NUMBER
		RETSKP]		;ALL THROUGH
	CAIN T1,-2		;GLOBAL LOCK?
	JRST [	CALLRET CHKENP]	;YES, CHECK IF PRIVILEGED
	CAIN T1,-3		;SYSTEM LOCK?
	JRST [	CALLRET CHKWHL]	;YES, CHECK WHEEL PRIVILEGE
	CALL CHKENJ		;CHECK IF THIS IS A LEGAL JFN
	 RET			;NO, GIVE ERROR RETURN
	HRR P1,T1		;SAVE OFN
	RETSKP			;GIVE OK RETURN
;ROUTINE TO CHECK IF ENQ PRIVILEGE IS SET

;	CALL CHKENP
;RETURNS +1:	PRIVILEGE NOT SET - ERROR CODE IN T1
;	 +2:	PRIVELEGE SET

CHKENP:	MOVE T1,CAPENB		;GET ENABLED PRIVILEGES
	TRNN T1,SC%WHL!SC%OPR!SC%ENQ
	RETBAD (ENQX15)		;NOT PRIVILEGED
	RETSKP			;HAS ENOUGH PRIVILEGES


;ROUTINE TO CHECK IF THE USER IS A WHEEL OR OPERATOR

CHKWHL:	MOVE T1,CAPENB		;GET CAPABILITIES
	TRNN T1,SC%WHL!SC%OPR	;WHEEL OR OPERATOR
	RETBAD (ENQX16)		;NO
	RETSKP			;YES


;ROUTINE TO CHECK A JFN FOR LEGALITY AND GET THE OFN

;ACCEPTS IN T1/	JFN
;	CALL CHKENJ
;RETURNS +1:	NOT A LEGAL JFN FOR LOCKING ON
;	 +2:	JFN LEGAL - OFN IN T1

CHKENJ:	HRLZS T1		;LH = JFN ,, RH = PAGE # 0
	CALL JFNOFN		;GET OFN,,PAGE # FOR PAGE 0 OF THIS JFN
	 JRST [	SKIPGE T1	;SEE IF THERE IS AN ERROR CODE IN T1
		MOVEI T1,ENQX17	;NO, ILLEGAL JFN
		RET]		;GIVE ERROR RETURN
	HLRZS T1		;GET OFN IN RH
	RETSKP			;GIVE OK RETURN
;ROUTINE TO FIND A LOCK-BLOCK

;ACCEPTS IN T1/	HASH INDEX
;	P1-P4/	AS SET UP BY VALREQ
;	CALL FNDLOK
;RETURNS +1:	NOT FOUND
;	 +2:	LOCK-BLOCK FOUND, ADDRESS IN T1

FNDLOK:	STKVAR <FNDLKI,FNDLKO,FNDLKP,FNDLKL>
	MOVEI T1,HSHTBL(T1)	;GET ADDRESS OF HASH CHAIN
	MOVEM T1,FNDLKI		;SAVE THE HASH CHAIN ADDRESS
FNDLK1:	LOAD T1,ENQNHC,(T1)	;GET NEXT LOCK-BLOCK ON CHAIN
	CAMN T1,FNDLKI		;HAVE WE EXHAUSTED LIST?
	RETBAD (ENQX7)		;YES, LOCK BLOCK WAS NOT FOUND
	MOVEM T1,FNDLKL		;REMEMBER THIS LOCK-BLOCK ADDRESS
	LOAD T2,ENQOFN,(T1)	;GET OFN NUMBER
	CAIE T2,(P1)		;IS THIS A MATCH
	JRST FNDLK2		;NO, TRY NEXT ENTRY
	MOVE T2,P2		;GET STRING POINTER
	CALL STRCMP		;COMPARE STRINGS
	 JRST FNDLK2		;NO MATCH
	MOVE T1,FNDLKL		;GET BACK LOCK-BLOCK ADDRESS
	RETSKP			;LOCK-BLOCK WAS FOUND

FNDLK2:	MOVE T1,FNDLKL		;GET LOCK-BLOCK ADDRESS
	JRST FNDLK1		;LOOP BACK TILL LIST SCANNED

;ROUTINE TO FIND A Q-BLOCK

;ACCEPTS IN T1/	LOCK-BLOCK ADDRESS
;	CALL FNDQ
;RETURNS +1:	NO Q-BLOCK UNDER THIS LOCK
;	 +2:	Q-BLOCK FOUND, Q-BLOCK ADDRESS IN T1

FNDQ:	LOAD T2,ENQNLQ,(T1)	;GET ADDRESS OF FIRST Q-BLOCK
	EXCH T1,T2		;GET Q-BLOCK ADR INTO T1 FOR RETURN
FNDQ1:	CAIN T2,0(T1)		;HAVE WE ARRIVED BACK AT LOCK-BLOCK?
	RETBAD (ENQX7)		;YES, GIVE UNSUCCESSFUL RETURN
	LOAD T3,ENQGRP,(T1)	;GET GROUP # OF BLOCK
	CAIE T3,0(P5)		;MATCH?
	JRST FNDQ2		;NO, KEEP LOOKING
	LOAD T3,ENQFRK,(T1)	;GET OWNER OF BLOCK
	CAME T3,FORKX		;IS THIS OUR Q-BLOCK?
	JRST FNDQ2		;NO, KEEP LOOKING
	LOAD T3,ENQJFN,(T1)	;GET JFN FROM Q-BLOCK
	HLRZ T4,P5		;AND JFN FOR NEW REQUEST
	CAMN T4,T3		;DO THE JFNS MATCH?
	RETSKP			;YES, RETURN WITH ADR IN T1
FNDQ2:	LOAD T1,ENQNLQ,(T1)	;STEP TO NEXT Q-BLOCK
	JRST FNDQ1		;LOOP BACK FOR ALL Q-BLOCKS
;ROUTINE TO GET THE HIGHEST LOCKED LEVEL #

;	CALL GETLVL
;RETURNS +1:	ALWAYS
;		T1/	LH = MONITOR LEVEL #, RH = USER LEVEL #

GETLVL:	STKVAR <GETLVU,GETLVM>
	SETOM GETLVU		;INITIALIZE USER LEVEL TO -1
	SETOM GETLVM		;SAME FOR MONITOR LEVEL #
	HRRZ T1,ENQLST		;GET FIRST Q-BLOCK OF JOB
GETLV1:	JUMPE T1,GETLV3		;IF 0, END OF JOB LIST
	LOAD T2,ENQFRK,(T1)	;GET FORK # OF OWNER
	CAME T2,FORKX		;IS THIS OUR FORK?
	JRST GETLV2		;NO, IGNORE IT
	LOAD T2,ENQLBP,(T1)	;GET ADDRESS OF LOCK BLOCK
	LOAD T3,ENQLVL,(T2)	;GET LEVEL NUMBER OF THIS LOCK
	LOAD T4,ENQOFN,(T2)	;GET OFN OF LOCK
	CAIN T4,-3		;IS THIS A MONITOR LOCK?
	JRST [	CAMLE T3,GETLVM	;YES, IS THIS A NEW HIGH MONITOR LEVEL?
		MOVEM T3,GETLVM	;YES, STORE THIS NEW LEVEL NUMBER
		JRST GETLV2]	;GO CHECK OTHER LEVELS
	CAMLE T3,GETLVU		;NO, IS THIS A NEW HIGH USER LEVEL?
	MOVEM T3,GETLVU		;YES, REMEMBER IT
GETLV2:	LOAD T1,ENQNJQ,(T1)	;STEP TO NEXT ENTRY IN JOB LIST
	JRST GETLV1		;LOOP BACK FOR ALL Q-BLOCKS

GETLV3:	HRL T1,GETLVM		;SET UP ANSWER
	HRR T1,GETLVU		;...
	RET			;AND RETURN


;ROUTINE TO COUNT UP THE NUMBER OF SHARERS OF A LOCK
;ACCEPTS IN T1/	LOCK BLOCK ADDRESS
;	CALL CNTQ
;RETRUNS +1:	ALWAYS - COUNT OF SHARERS IN T1

CNTQ:	SETZ T4,		;INITIALIZE THE COUNT
	MOVE T2,T1		;SAVE ADR OF LOCK BLOCK
CNTQL:	LOAD T2,ENQNLQ,(T2)	;STEP TO THE NEXT QUEUE BLOCK
	CAIN T2,0(T1)		;BACK TO THE LOCK BLOCK YET?
	JRST [	MOVE T1,T4	;YES, GET THE COUNT INTO T1
		RET]		;AND RETURN
	LOAD T3,ENQFLG,(T2)	;GET THE FLAGS OF THIS BLOCK
	TXNE T3,EN.LOK		;IS THE LOCK LOCKED?
	AOS T4			;YES, COUNT UP THE COUNT
	JRST CNTQL		;LOOP BACK TILL BACK TO LOCK BLOCK
;ROUTINE TO COMPARE STRINGS OR USER CODES

;ACCEPTS IN T1/	LOCK BLOCK ADDRESS
;	    T2/	STRING POINTER  OR  USER CODE
;	CALL STRCMP
;RETURNS +1:	NO MATCH
;	 +2:	MATCH

STRCMP:	STKVAR <STRCPP>
	LOAD T3,ENQFLG,(T1)	;GET FLAGS OF LOCK-BLOCK
	TRNN T3,EN.TXT		;TEXT OR USER CODE?
	JRST STRCMC		;USER CODE
	LOAD T3,NMFLG,T2	;GET BITS 0-2 TO SEE IF USER SPECIFIED CODE
	CAIN T3,NUMVAL		; 5B2 CANNOT BE A STRING POINTER
	RET			; SO RETURN NO MATCH ON TYPE MISMATCH

	MOVE T3,T2		;BUILD THE STRING POINTER
	AND T3,[007700,,0]	;GET THE BYTE SIZE OF THE OTHER STRING
	TDO T3,[POINT 0,.ENTXT(T1)] ;SET UP THE REST OF THE BYTE POINTER
	MOVEM T3,STRCPP		;SAVE BYTE POINTER INTO LOCK-BLOCK
STRCM0:	XCTBU [ILDB T3,T2]	;GET USER'S BYTE
	ILDB T4,STRCPP		;GET LOCK'S BYTE
	CAME T3,T4		;A MATCH?
	RET			;NO, RETURN
	JUMPN T3,STRCM0		;IF NOT 0, LOOP BACK
	RETSKP			;STRING MATCHED

STRCMC:	LOAD T3,ENQTXT,(T1)	;GET USER CODE
	CAME T2,T3		;IS THIS A MATCH?
	RET			;NO
	RETSKP			;YES
;ROUTINE TO CREATE A LOCK-BLOCK

;ACCEPTS IN T1/	HASH INDEX
;	P1-P4/	AS SET UP BY VALREQ
;	CALL CRELOK
;RETURNS +1:	COULD NOT CREATE LOCK-BLOCK
;	 +2:	SUCCESSFUL - LOCK-BLOCK ADDRESS IN T1

CRELOK:	STKVAR <CRELKH,CRELKL>
	MOVEM T1,CRELKH		;SAVE THE HASH INDEX
	SKIPN T1,P4		;STRING OR CODE?
	MOVEI T1,1		;USER CODE NEEDS ONLY ONE WORD
	ADDI T1,LBLEN		;ADD IN LOCK-BLOCK LENGTH
	MOVEM T1,CRELKL		;REMEMBER THE LOCK-BLOCK LENGTH
	CALL ASGENQ		;GET FREE BLOCK FOR LOCK
	 RET			;NO SPACE LEFT
	MOVE T2,CRELKH		;PUT BLOCK ON HASH LIST
	MOVEI T2,HSHTBL(T2)	;GET ACTUAL ADR OF ENTRY
	STOR T2,ENQLHC,(T1)	;POINT BACK TO HASH TABLE ENTRY
	HRRZ T3,0(T2)		;GET HASH TABLE FORWARD POINTER
	HRRM T1,0(T2)		;MAKE US BE FIRST ON LIST
	STOR T3,ENQNHC,(T1)	;MAKE NEW LOCK POINT TO NEXT LOCK
	STOR T1,ENQLHC,(T3)	;MAKE SECOND ENTRY POINT BACK TO US
	STOR T1,ENQLLQ,(T1)	;THIS IS AN EMPTY LOCK
	STOR T1,ENQNLQ,(T1)	;IT POINTS TO ITSELF
	LDB T2,[POINT 9,P1,17]	;GET THE LEVEL NUMBER
	STOR T2,ENQLVL,(T1)	;REMEMBER THE LEVEL # OF THIS LOCK
	HLRZ T2,P3		;GET # OF RESOURCES
	STOR T2,ENQRR,(T1)	;SET UP REMAINING RESOURCES
	STOR T2,ENQTR,(T1)	;AND TOTAL RESOURCES
	STOR P1,ENQOFN,(T1)	;SAVE OFN CODE
	MOVE T2,CRELKL		;GET LOCK-BLOCK LENGTH
	STOR T2,ENQLEN,(T1)	;SAVE LENGTH FOR RETURNING LOCK-BLOCK
	MOVEI T3,-1		;MARK THAT THIS LOCK IS NOT ON LTL LIST
	STOR T3,ENQLT,(T1)	;THIS CAUSES IT TO BE PUT ON LIST LATER
	MOVEI T2,EN.LB		;GET FLAGS
	LDB T3,[POINT 3,P2,2]	;GET HIGH ORDER 3 BITS
	CAIE T3,5		;A USER CODE?
	TRO T2,EN.TXT		;NO, SET TEXT FLAG
	TXNE P1,EN%LTL		;LONG TERM LOCK?
	TXO T2,EN.LTL		;YES, REMEMBER THIS IN THE LOCK BLOCK
	STOR T2,ENQFLG,(T1)	;STORE FLAGS OF LOCK-BLOCK
	TRNN T2,EN.TXT		;TEXT?
	JRST [	STOR P2,ENQTXT,(T1) ;NO, STORE USER CODE
		RETSKP]		;ALL DONE
	MOVE T2,P2		;BUILD THE BYTE POINTER
	AND T2,[007700,,0]	;GET BYTE SIZE FROM OTHE BYTE POINTER
	TDO T2,[POINT 0,.ENTXT(T1)]
CRELK1:	XCTBU [ILDB T3,P2]	;COPY STRING INTO BLOCK
	IDPB T3,T2		;...
	JUMPN T3,CRELK1		;IF NOT 0, LOOP BACK FOR ALL CHARS
	RETSKP			;LOCK-BLOCK CREATED
;ROUTINE TO CREATE A Q-BLOCK AND PUT IT ON A LOCK-BLOCK CHAIN

;ACCEPTS IN T1/	LOCK-BLOCK ADR
;	    T2/	Q-BLOCK ADR OF MEMBER OF MULTIPLE REQUEST
;	    T3/	FLAGS FOR NEW Q-BLOCK
;	Q1-Q3, P1-P4 AS SET UP BY VALARG AND VALREQ
;	CALL CREQ
;RETURNS +1:	CANNOT CREATE Q-BLOCK
;	 +2:	Q-BLOCK ADDRESS IN T1

CREQ:	STKVAR <CREQL,CREQQ,CREQF,CREQA>
	MOVEM T1,CREQL		;SAVE LOCK-BLOCK ADDRESS
	MOVEM T2,CREQQ		;SAVE MULTIPLE REQUEST Q-BLOCK
	MOVEM T3,CREQF		;SAVE FLAGS
	LOAD T1,ENQOTA		;GET QUOTA FOR THIS JOB
	LOAD T2,ENQCNT		;GET CURRENT COUNT
	HRRZ T3,P1		;GET OFN
	CAIN T3,-3		;SYSTEM LOCK?
	JRST CREQ1		;YES, DONT COUNT UP QUOTA
	CAMG T1,T2		;ROOM LEFT FOR MORE ENTRIES?
	RETBAD (ENQX18)		;NO, DONT CREATE QUEUE
	AOS T2			;COUNT UP COUNT
	STOR T2,ENQCNT		;STORE UPDATED COUNT
CREQ1:	MOVE T2,CREQL		;GET ADR OF THE LOCK BLOCK
	LOAD T1,ENQFBP,(T2)	;SEE IF THERE IS A FREE Q-BLOCK
	SETZRO ENQFBP,(T2)	;CLEAR OUT POINTER TO THE BLOCK
	JUMPN T1,CREQ2		;IF THERE IS A FREE BLOCK, GO USE IT
	MOVEI T1,QBLEN		;GET LENGTH OF Q-BLOCK
	CALL ASGENQ		;GET FREE BLOCK FOR Q
	 RET			;NO SPACE LEFT
CREQ2:	MOVEM T1,CREQA		;SAVE THE ADDRESS OF THE Q-BLOCK
	MOVEI T2,ENQLST		;GET ADDRESS OF ENTRY
	STOR T2,ENQLJQ,(T1)	;POINT BACK TO JOB TABLE ENTRY
	HRRZ T3,0(T2)		;GET FORWARD POINTER
	HRRM T1,0(T2)		;MAKE JOB LIST POINT TO US
	STOR T3,ENQNJQ,(T1)	;MAKE US POINT FORWARD
	SKIPE T3		;IS THIS THE END OF THE LIST
	STOR T1,ENQLJQ,(T3)	;NO, MAKE SECOND ITEM POINT BACK TO US
	MOVE T2,CREQL		;GET LOCK BLOCK ADDRESS AGAIN
	STOR T2,ENQNLQ,(T1)	;MAKE US POINT FORWARD TO LOCK-BLOCK
	LOAD T3,ENQLLQ,(T2)	;GET BACK OF QUEUE FROM LOCK-BLOCK
	STOR T3,ENQLLQ,(T1)	;MAKE US POINT BACK TO IT
	STOR T1,ENQNLQ,(T3)	;MAKE IT POINT TO US
	STOR T1,ENQLLQ,(T2)	;MAKE LOCK-BLOCK POINT BACK TO US
	STOR T2,ENQLBP,(T1)	;SET UP A POINTER TO THE LOCK-BLOCK
	STOR Q2,ENQCHN,(T1)	;SAVE PSI CHANNEL NUMBER
	HLRZ T2,Q2		;GET ID
	STOR T2,ENQID,(T1)	;SAVE THE ID NUMBER
	MOVE T2,CREQF		;GET FLAGS
	STOR T2,ENQFLG,(T1)	;SAVE THEM
	MOVE T2,FORKX		;GET FORK NUMBER OF THIS FORK
	STOR T2,ENQFRK,(T1)	;SAVE IT FOR INTERRUPTING
	STOR P3,ENQNR,(T1)	;STORE # OF RESOURCES REQUESTED
	HLRZ T2,P5		;GET JFN OF REQUEST
	STOR T2,ENQJFN,(T1)	;STORE JFN
	HRRZ T2,P5		;GET GROUP #
	STOR T2,ENQGRP,(T1)	;STORE GROUP NUMBER
	LOAD T1,ENQMSK,(T1)	;DO WE HAVE A MASK BLOCK YET?
	JUMPE T1,CREQ3		;...
	MOVE T2,CREQL		;YES, GET ITS SIZE
	LOAD T2,ENQNMS,(T2)	;  FROM THE LOCK BLOCK
	CAMN T2,EDNMS		;IS THIS THE SAME SIZE AS REQUESTED
	JRST CREQ3A		;YES, GO USE IT
	CALL RELENQ		;NO, RELEASE IT
	MOVE T1,CREQA		;GET THE ADR OF THE Q-BLOCK AGAIN
	SETZRO ENQMSK,(T1)	;CLEAR THE MASK BLOCK ADDRESS
CREQ3:	SKIPN T1,EDNMS		;IS THERE A MASK BLOCK SPECIFIED?
	JRST CREQ5		;NO
	CALL ASGENQ		;YES, GET SPACE FOR IT
	 RETBAD (,<MOVE T1,CREQA ;FAILED, RETURN Q-BLOCK
		MOVEI T2,QBLEN
		CALL RELENQ
		MOVE T1,LSTERR>) ;GET BACK ERROR CODE
CREQ3A:	MOVE T2,CREQA		;GET ADR OF Q-BLOCK
	STOR T1,ENQMSK,(T2)	;SAVE ADR OF MASK BLOCK IN Q-BLOCK
	MOVE T4,EDNMS		;GET THE LENGTH OF THE BLOCK
	MOVE T3,CREQL		;GET ADR OF LOCK BLOCK
	STOR T4,ENQNMS,(T3)	;STORE THE LENGTH OF THE MASK BLOCK
	MOVE T3,EDMSK		;GET THE ADR OF THE USER'S MASK BLOCK
CREQ4:	UMOVE T2,0(T3)		;GET THE NEXT USER MASK WORD
	MOVEM T2,0(T1)		;COPY IT TO THE MASK BLOCK
	AOS T1			;STEP THE USER'S ADR
	AOS T3			;STEP THE MONITOR'S ADR
	SOJG T4,CREQ4		;LOOP BACK TILL THE BLOCK IS COPIED
CREQ5:	MOVE T1,CREQA		;GET BACK THE ADR OF THE Q-BLOCK
	;..
	;..
	SKIPN T2,CREQQ		;IS THIS A MULTIPLE REQUEST?
	JRST [	STOR T1,ENQFQ,(T1) ;NO, MAKE AN EMPTY LIST
		STOR T1,ENQLRQ,(T1)
		RETSKP]		;ALL DONE
	LOAD T3,ENQFQ,(T2)	;ADD THIS Q-BLOCK ONTO LIST OF REQUESTS
	STOR T1,ENQFQ,(T2)	;  POINT FORWARD TO US
	STOR T1,ENQLRQ,(T3)	;  POINT BACK TO US
	STOR T3,ENQFQ,(T1)	;  WE POINT FORWARD
	STOR T2,ENQLRQ,(T1)	;  AND WE POINT BACKWARD
	RETSKP			;ALL THROUGH
;ROUTINE TO DEQ AN ENTIRE REQUEST

;ACCEPTS IN T1/	Q-BLOCK ADDRESS
;	CALL REQDEQ
;RETURNS +1:	ALWAYS, REQUEST DEQUEUED

REQDEQ:	CALL QDEQ		;DEQUEUE THIS Q-BLOCK
	JUMPN T1,REQDEQ		;IF ANY MORE Q-BLOCKS, DEQ THEM TOO
	RET			;EXIT SUCCESSFULLY


;ROUTINE TO DEQ A SINGLE Q-BLOCK

;ACCEPTS IN T1/	Q-BLOCK ADDRESS TO BE DEQUEUED
;	CALL SQDEQ
;RETURNS +1:	ALWAYS - Q-BLOCK DEQUEUED AND SCHEDULING PERFORMED

SQDEQ:	STKVAR <SQDEQQ,SQDEQF>
	LOAD T2,ENQFLG,(T1)	;GET FLAGS OF THIS Q-BLOCK
	MOVEM T2,SQDEQF		;SAVE FOR LATER USE
	CALL QDEQ		;DEQ THIS Q-BLOCK
	JUMPE T1,R		;IF NOT A MULTIPLE REQUEST, RETURN
	MOVEM T1,SQDEQQ		;SAVE Q-BLOCK ADDRESS
	CALL QSKD		;SCHEDULE THIS Q ENTRY
	 RET			;LOCK NOT LOCKED YET
	JUMPN T1,SQDEQ1		;LOCKING WAS COMPLETED ON THIS CALL?
	MOVE T1,SQDEQF		;NO, SEE IF USER NEEDS WAKING UP
	TRNE T1,EN.LOK		;WAS THE ORGINAL Q ALREADY LOCKED?
	RET			;YES, THEN USER HAD BEEN INFORMED
SQDEQ1:	MOVE T1,SQDEQQ		;USER NEEDS AWAKENING, GET Q ADDRESS
	CALLRET INTQ		;GO WAKE UP THE FORK
;ROUTINE TO DEQ A Q-BLOCK

;ACCEPTS IN T1/	Q-BLOCK ADDRESS
;	CALL QDEQ
;RETURNS +1:	ALWAYS - T1 CONTAINS 0 OR Q-BLOCK ADR OF NEXT Q IN THE
;				MULTIPLE REQUEST CHAIN

QDEQ:	STKVAR <QDEQRQ,QDEQLB,QDEQQA>
	MOVEM T1,QDEQQA		;SAVE THE ADR OF THE Q-BLOCK
	LOAD T2,ENQLJQ,(T1)	;REMOVE Q-BLOCK FROM JOB CHAIN
	LOAD T3,ENQNJQ,(T1)	;GET POINTERS BACKWARD AND FORWARD
	SKIPE T3		;IF END OF LIST, DONT STORE BACK POINTER
	STOR T2,ENQLJQ,(T3)	;FIX UP BACK POINTER
	STOR T3,ENQNJQ,(T2)	;AND FORWARD POINTER
	LOAD T2,ENQLLQ,(T1)	;NOW REMOVE IT FROM LOCK CHAIN
	LOAD T3,ENQNLQ,(T1)
	STOR T2,ENQLLQ,(T3)
	STOR T3,ENQNLQ,(T2)
	LOAD T2,ENQLRQ,(T1)	;NOW REMOVE IT FROM THE MULTIPLE LIST
	LOAD T3,ENQFQ,(T1)
	STOR T2,ENQLRQ,(T3)
	STOR T3,ENQFQ,(T2)
	MOVEM T2,QDEQRQ		;SAVE ADDRESS OF NEXT Q-BLOCK
	CAIN T2,0(T1)		;IS THE MULTIPLE LIST EMPTY?
	SETZM QDEQRQ		;YES, RETURN 0 IN T1 ON RETURN
	LOAD T2,ENQLBP,(T1)	;GET ADDRESS OF LOCK BLOCK
	LOAD T3,ENQNR,(T1)	;GET NUMBER OF RESOURCES REQUESTED
	LOAD T4,ENQRR,(T2)	;GET REMAINING RESOURCES COUNT
	ADD T4,T3		;GIVE BACK OUR RESOURCES
	LOAD T3,ENQFLG,(T1)	;GET FLAGS OF REQUEST
	TRNE T3,EN.LOK		;WAS LOCK LOCKED?
	STOR T4,ENQRR,(T2)	;YES, GIVE BACK RESOURCES
	MOVEM T2,QDEQLB		;REMEMBER THE LOCK-BLOCK FOR LATER
	LOAD T3,ENQCNT		;DECREMENT THE QUOTA COUNT
	SOS T3
	LOAD T4,ENQOFN,(T2)	;GET THE OFN VALUE
	CAIE T4,-3		;IF -3 LOCK, DONT DECREMENT QUOTA
	STOR T3,ENQCNT		;STORE UPDATED COUNT
	MOVE T3,QDEQLB		;GET ADR OF LOCK BLOCK
	LOAD T4,ENQFBP,(T3)	;GET POINTER TO FREE Q-BLOCK IF ANY
	JUMPE T4,[STOR T1,ENQFBP,(T3)
		JRST QDEQ0]	;IF NO FREE Q-BLOCK, SAVE THIS ONE
	LOAD T2,ENQNMS,(T3)	;GET THE SIZE OF THE MASK BLOCK
	LOAD T1,ENQMSK,(T1)	;GET THE ADDRESS OF THE MASK BLOCK
	SKIPE T1		;IS THERE A MASK BLOCK?
	CALL RELENQ		;YES, RELEASE ITS SPACE
	MOVE T1,QDEQQA		;GET BACK THE ADR OF THE Q-BLOCK
	MOVEI T2,QBLEN		;NOW GIVE BACK THE Q-BLOCK SPACE
	CALL RELENQ		;...
QDEQ0:	MOVE T1,QDEQLB		;GET ADR OF LOCK-BLOCK
	LOAD T2,ENQNLQ,(T1)	;SEE IF LOCK IS EMPTY
	CAIE T2,0(T1)		;...
	JRST [	CALL LOKSKD	;NO, DO A SCHEDULING PASS ON IT
		JRST QDEQ1]	;AND RETURN
	CALL LOKREL		;RELEASE THE LOCK-BLOCK
QDEQ1:	MOVE T1,QDEQRQ		;GET Q-BLOCK ADR OF MULTIPLE REQUEST
	RET			;AND RETURN
;ROUTINE TO RELEASE A LOCK-BLOCK

;ACCETPS IN T1/	LOCK BLOCK ADDRESS
;	CALL LOKREL
;RETURNS +1:	ALWAYS - LOCK BLOCK GIVEN BACK TO FREE POOL

LOKREL:	STKVAR <LOKRLQ,LOKRLM,LOKRLN>
	LOAD T2,ENQFBP,(T1)	;GET POINTER TO FREE Q-BLOCK IF ANY
	MOVEM T2,LOKRLQ		;REMEMBER IT IF IT NEEDS RELEASING
	SKIPE T3,T2		;ANY FREE Q-BLOCK?
	LOAD T3,ENQMSK,(T2)	;YES, GET THE POINTER TO THE MASK BLOCK
	MOVEM T3,LOKRLM		;SAVE THE ADR TO THE MASK BLOCK
	LOAD T3,ENQNMS,(T1)	;GET THE SIZE OF THE MASK BLOCK
	MOVEM T3,LOKRLN		;SAVE IT FOR LATER
	LOAD T2,ENQFLG,(T1)	;GET THE FLAGS
	TXNE T2,EN.LTL		;IS THIS A LONG TERM LOCK?
	JRST LOKRL1		;YES, DO NOT RELEASE IT YET
	LOAD T2,ENQLHC,(T1)	;REMOVE LOCK-BLOCK FROM HASH CHAIN
	LOAD T3,ENQNHC,(T1)
	STOR T2,ENQLHC,(T3)
	STOR T3,ENQNHC,(T2)
	LOAD T2,ENQLEN,(T1)	;GET THE LENGTH OF THE LOCK-BLOCK
	CALL RELENQ		;RELEASE THE SPACE
	MOVEI T2,QBLEN		;NOW RELEASE THE Q-BLOCK
	SKIPE T1,LOKRLQ		;IF THERE WAS ONE
	CALL RELENQ		;RELEASE IT
	MOVE T2,LOKRLN		;GET THE LENGTH OF THE MASK BLOCK
	SKIPE T1,LOKRLM		;IS THERE A MASK BLOCK TO RELEASE
	CALL RELENQ		;YES, RELEASE IT
	RET			;ALL DONE

LOKRL1:	LOAD T2,ENQLT,(T1)	;GET POINTER TO LONG TERM LOCK LIST
	CAIE T2,-1		;IS THIS ALREADY ON A LIST?
	RET			;YES, NO NEED TO PUT IT ON AGAIN
	HRRZ T2,ENQLTL		;GET POINTER TO JOB LONG TERM LOCK LIST
	STOR T2,ENQLT,(T1)	;LINK THIS LOCK ONTO HEAD OF THAT LIST
	HRRM T1,ENQLTL		;THIS LOCK IS NOW FIRST ON THE LIST
	RET			;AND EXIT, LOCK GETS RELEASED LATER
;ROUTINE TO DO A SCHEDULING PASS GIVEN A Q-BLOCK ADDRESS

;ACCEPTS IN T1/	Q-BLOCK ADDRESS
;	CALL QSKD
;RETURNS +1:	THIS REQUEST IS NOT FULLY LOCKED YET
;	 +2:	THIS REQUEST IS FULLY LOCKED
;		T1 = 0  IF ALREADY LOCKED BEFORE THIS CALL
;		T1 = -1 IF THE LOCKING WAS COMPLETED ON THIS CALL

QSKD:	SAVEQ
	STKVAR <QSKDQ,QSKDT,CKLOKQ,QSKDF,QSKDG,QSKDM,QSKDN>
	SETZM QSKDF		;ASSUME LOCK ALREADY LOCKED ON CALL
	MOVEM T1,QSKDQ		;SAVE THE ADDRESS OF THIS Q-BLOCK
	CALL SETINV		;MAKE SURE INVISIBLE BITS SET OK
	 JRST QSKD0		;GO MAKE SCHEDULING PASS
	JRST QSKD3		;LOCK IS ALREADY LOCKED

QSKD0:	MOVE T1,QSKDQ		;GET ADDRESS OF THIS Q-BLOCK AGAIN
QSKD1:	MOVEM T1,QSKDT		;SAVE IT IN A TEMPORARY REGISTER
	LOAD T2,ENQMSK,(T1)	;GET THE ADR TO THE MASK BLOCK
	MOVEM T2,QSKDM		;SAVE THE ADR OF THE MASK BLOCK
	LOAD T2,ENQLBP,(T1)	;GET THE POINTER TO THE LOCK BLOCK
	LOAD T2,ENQNMS,(T2)	;GET THE LENGTH OF THE MASK BLOCK
	MOVEM T2,QSKDN		;SAVE IT FOR LATER
	LOAD T2,ENQGRP,(T1)	;GET GROUP # OF THIS Q ENTRY
	MOVEM T2,QSKDG		;SAVE FOR COMPARISON LATER
	LOAD T2,ENQFLG,(T1)	;GET THE FLAGS OF THIS Q-BLOCK
	TRNE T2,EN.LOK!EN.INV	;IS THE LOCK LOCKED BY THIS Q-BLOCK
				; OR AN INVISIBLE Q-BLOCK?
	JRST QSKD2		;YES, GO CONTINUE SCANNING SIDEWAYS
CHKLK1:	LOAD T1,ENQLLQ,(T1)	;GET PREVIOUS BLOCK ON QUEUE
	MOVE T2,QSKDT		;GET ORIGINAL Q-BLOCK ADR
	LOAD T3,ENQFLG,(T2)	;RESTORE ITS FLAGS INTO T2
	LOAD T4,ENQFLG,(T1)	;GET THE FLAGS OF THIS BLOCK
	TRNE T4,EN.LB		;IS THIS THE LOCK-BLOCK?
	JRST CHKLK2		;YES, WE ARE AT TOP OF QUEUE
	TRNE T4,EN.INV		;NO, IS THIS Q-BLOCK INVISIBLE?
	JRST CHKLK1		;YES, IGNORE THIS BLOCK ENTIRELY
	TRNN T4,EN.EXC		;IS THIS AN EXCLUSIVE REQUEST?
	TRNE T3,EN.EXC		;OR IS ORIGINAL REQUEST EXCLUSIVE?
	JRST CHKLK3		;YES, GO CHECK MASK BLOCKS
	LOAD T2,ENQGRP,(T1)	;GET GROUP NUMBER OF THIS Q
	CAME T2,QSKDG		;IS THIS THE SAME GROUP
	RET			;NO, CANNOT SCHEDULE THIS REQUEST
	JRST CHKLK1		;LOOP BACK UNTIL AT LOCK-BLOCK

CHKLK2:	MOVE T3,QSKDT		;GET THE NUMBER OF RESOURCES NEEDED
	LOAD T3,ENQNR,(T3)	;...
	LOAD T4,ENQRR,(T1)	;GET THE # OF REMAINING RESOURCES
	SUB T4,T3		;SEE IF ENOUGH LEFT FOR THIS REQUEST
	JUMPL T4,R		;IF NOT, LOCK CANNOT BE LOCKED
	STOR T4,ENQRR,(T1)	;THERE ARE ENOUGH, LOCK THE LOCK
	MOVE T3,QSKDT		;GET BACK ADR OF Q-BLOCK
	LOAD T2,ENQFLG,(T3)	;GET FLAGS AGAIN
	TRO T2,EN.LOK		;LOCK THE LOCK BIT
	STOR T2,ENQFLG,(T3)	;STORE THE UPDATED FLAGS
	LOAD T2,ENQNLQ,(T3)	;NOW MOVE THIS Q-BLOCK TO THE HEAD
	LOAD T4,ENQLLQ,(T3)	;   OF THE QUEUE FOR THIS LOCK
	STOR T2,ENQNLQ,(T4)	;FIRST REMOVE IT FROM THE QUEUE
	STOR T4,ENQLLQ,(T2)	;...
	LOAD T2,ENQNLQ,(T1)	;NOW ADD IT TO THE START OF THE QUEUE
	STOR T3,ENQLLQ,(T2)	;  POINTER BACK TO Q-BLOCK FROM Q-2
	STOR T3,ENQNLQ,(T1)	;  POINTER TO Q-BLOCK FROM LOCK-BLOCK
	STOR T2,ENQNLQ,(T3)	;  POINTER TO SECOND Q-BLOCK
	STOR T1,ENQLLQ,(T3)	;  POINTER BACK TO LOCK-BLOCK
	CALL LGTAD		;GET TIME AND DATE
	MOVE T3,QSKDT		;GET Q-BLOCK ADDRESS
	LOAD T3,ENQLBP,(T3)	;GET LOCK BLOCK ADDRESS
	STOR T1,ENQTS,(T3)	;STORE NEW TIME STAMP IN LOCK BLOCK
	MOVE T1,QSKDQ		;GET ORIGINAL Q-BLOCK ADDRESS
	SETOM QSKDF		;MARK THAT A LOCK WAS LOCKED
	CALL SETINV		;GO CLEAR ANY INVISIBLE BITS
	 JRST QSKD0		;LOCK NOT FULLY LOCKED YET, TRY AGAIN
	JRST QSKD3		;LOCK IS LOCKED, GO GIVE OK RETURN

QSKD2:	MOVE T2,QSKDT		;GET Q-BLOCK ADR OF PRESENT Q-BLOCK
	LOAD T1,ENQFQ,(T2)	;STEP TO NEXT Q-BLOCK IN THIS REQUEST
	CAME T1,QSKDQ		;ARE WE BACK TO THE Q-BLOCK STARTED AT?
	JRST QSKD1		;NO, GO CHECK IF THIS Q LOCKED
QSKD3:	MOVE T1,QSKDF		;GET BACK ANSWER TO BE RETURNED
	RETSKP			;AND SKIP RETURN


CHKLK3:	LOAD T3,ENQMSK,(T1)	;YES, CHECK FOR MASKS
	SKIPE T4,QSKDM		;IS THERE A MASK FOR THIS Q-BLOCK
	JUMPN T3,CHKLK4		;YES, DO BOTH BLOCKS HAVE A MASK?
	JUMPN T4,CHKLK5		;NO, ONLY ONE HAVE A MASK?
	JUMPE T3,R		;IF NEITHER HAVE A MASK, THEN CANNOT LOCK
	MOVE T4,T3		;GET ADR OF MASK BLOCK TO USE
CHKLK5:	MOVE T3,QSKDN		;GET LENGTH OF MASK BLOCK
CHKLK6:	SKIPE 0(T4)		;ARE THERE ANY BITS ON IN MASK?
	RET			;YES, THEN CANNOT LOCK IT NOW
	AOS T4			;STEP TO NEXT WORD IN MASK
	SOJG T3,CHKLK6		;LOOP BACK TIL SCANNED WHOLE MASK
	JRST CHKLK1		;THESE Q-BLOCKS CAN CO-EXIST

CHKLK4:	MOVE Q1,QSKDN		;GET LENGTH OF MASK BLOCK
CHKLK7:	MOVE Q2,0(T3)		;GET NEXT WORD IN MASK BLOCK
	AND Q2,0(T4)		;AND IN THE NEXT MASK BLOCK IN OTHER BLOCK
	JUMPN Q2,R		;IF OVERLAPPED, THEN CANNOT LOCK YET
	AOS T3			;STEP TO NEXT MASK WORD
	AOS T4			;IN BOTH BLOCKS
	SOJG Q1,CHKLK7		;LOOP BACK
	JRST CHKLK1		;THESE Q-BLOCKS CAN CO-EXIST
;ROUTINE TO CLEAR INVISIBLE BITS AS Q-BLOCKS GET LOCKED

;ACCEPTS IN T1/	Q-BLOCK ADDRESS OF ONE ELEMENT OF A REQUEST
;	CALL SETINV
;RETURNS +1:	REQUEST NOT FULLY LOCKED YET
;	 +2:	REQUEST IS NOW FULLY LOCKED

SETINV:	STKVAR <SETINQ,SETINU>
	MOVSI T2,1		;INITIALIZE LEVEL NUMBER SCANNER
	MOVEM T2,SETINU		;...
	MOVEM T1,SETINQ		;REMEMBER Q-BLOCK ADDRESS
SETIN1:	LOAD T2,ENQLBP,(T1)	;GET ADDRESS OF LOCK-BLOCK FOR Q
	LOAD T4,ENQLVL,(T2)	;GET LEVEL NUMBER OF THIS LOCK
	LOAD T3,ENQFLG,(T1)	;GET Q FLAGS
	TRNE T3,EN.LOK		;IS THIS Q ALREADY LOCKED?
	JRST SETIN2		;YES
	CAMG T4,SETINU		;NO, IS THIS A NEW LOW VALUE?
	MOVEM T4,SETINU		;YES, REMEMBER LOWEST NON-LOCKED LEVEL
SETIN2:	LOAD T1,ENQFQ,(T1)	;GET NEXT Q-BLOCK IN THIS REQUEST
	CAME T1,SETINQ		;ARE WE BACK TO STARTING POINT?
	JRST SETIN1		;NO, LOOP BACK FOR OTHER Q-BLOCKS
	MOVE T2,SETINU		;GET LEVEL NUMBER
	TLNE T2,-1		;SEE IF AN UNLOCKED LOCK WAS SEEN
	RETSKP			;NONE SEEN, THE LOCK IS FULLY LOCKED
SETIN3:	LOAD T2,ENQLBP,(T1)	;GET ADR OF LOCK-BLOCK FOR THIS Q
	LOAD T4,ENQLVL,(T2)	;GET LEVEL NUMBER OF LOCK
	LOAD T3,ENQFLG,(T1)	;GET FLAGS OF THIS Q-BLOCK
	CAMG T4,SETINU		;IS LEVEL ABOVE LOWEST UNLOCKED LEVEL?
	TRZA T3,EN.INV		;NO, MAKE THIS Q-BLOCK VISIBLE
	TRO T3,EN.INV		;YES, THE Q-BLOCK MUST REMAIN INVISIBLE
	STOR T3,ENQFLG,(T1)	;STORE UPDATED FLAGS
	LOAD T1,ENQFQ,(T1)	;GET ADR OF NEXT Q-BLOCK IN REQUEST
	CAME T1,SETINQ		;HAVE WE SEEN ALL Q-BLOCKS?
	JRST SETIN3		;NO, LOOP BACK UNTIL ALL SEEN
	RET			;YES, ALL THROUGH (REQ NOT TOTALLY 
				;   LOCKED)
;ROUTINE TO MAKE A SCHEDULING PASS GIVEN A LOCK-BLOCK ADDRESS

;ACCEPTS IN T1/	LOCK-BLOCK ADDRESS
;	CALL LOKSKD
;RETURNS +1:	ALWAYS - ALL NEWLY LOCKED REQUESTS INTERRUPTED

LOKSKD:	STKVAR <LOKSKL,LOKSKQ,LOKSKC>
	MOVEM T1,LOKSKL		;SAVE ADDRESS OF LOCK FOR LATER
	MOVEM T1,LOKSKQ		;INITIALIZE Q-BLOCK ADDRESS REGISTER
	LOAD T2,ENQRR,(T1)	;GET REMAINING RESOURCES IN POOL
	MOVEM T2,LOKSKC		;SAVE COUNT OF RESOURCES
LOKSK1:	LOAD T1,ENQNLQ,(T1)	;STEP TO NEXT Q-BLOCK ON QUEUE
	CAMN T1,LOKSKL		;BACK TO THE LOCK-BLOCK YET?
	RET			;YES, SCHEDULING PASS IS DONE
	MOVEM T1,LOKSKQ		;UPDATE POINTER TO CURRENT Q-BLOCK
	LOAD T2,ENQFLG,(T1)	;GET FLAGS OF THE Q-BLOCK
	TRNE T2,EN.INV		;INVISIBLE?
	JRST LOKSK1		;YES, IGNORE IT
	TRNE T2,EN.LOK		;THIS Q ALREADY LOCKED?
	JRST LOKSK3		;YES, DONT DECREMENT RESOURCE COUNT
	LOAD T2,ENQNR,(T1)	;GET NUMBER OF RESOURCES REQUESTED
	MOVE T3,LOKSKC		;GET REMAINING RESOURCES
	SUB T3,T2		;DECREMENT COUNT
	JUMPL T3,R		;IF NOT ENOUGH LEFT, EXIT
	MOVEM T3,LOKSKC		;STORE NEW COUNT
LOKSK3:	CALL QSKD		;GO SEE IF THIS REQUEST IS LOCKED
	 JRST LOKSK2		;NO
	JUMPE T1,LOKSK2		;IF NOT JUST LOCKED, DONT INTERRUPT
	MOVE T1,LOKSKQ		;GET Q-BLOCK ADDRESS
	CALL INTQ		;INTERRUPT THE PROCESS
LOKSK2:	MOVE T1,LOKSKQ		;GET Q-BLOCK ADDRESS AGAIN
	JRST LOKSK1		;LOOP BACK FOR REST OF QUEUE
;ROUTINES TO WAKE UP OR INTERRUPT WAITING FORKS WHEN LOCK IS LOCKED

;ROUTINE TO WAKE UP A FORK GIVEN THE Q-BLOCK JUST LOCKED

;ACCEPTS IN T1/	Q-BLOCK ADDRESS
;	CALL INTQ
;RETURNS +1:	ALWAYS - FORK AWAKENED OR INTERRUPTED AS APPROPRIATE

INTQ:	LOAD T2,ENQFRK,(T1)	;GET FORK TO BE AWAKENED
	LOAD T1,ENQCHN,(T1)	;GET CHANNEL #
	CAIE T1,.ENWCH		;IS FORK BLOCKED OR NOT
	CALLRET PSIRQ		;NOT BLOCKED, GIVE IT AN INTERRUPT
	MOVE T1,T2		;GET FORK # IN T1
	PUSH P,T1		;SAVE FORK #
	CALL GETMSK		;GET THE BIT POSITION AND WORD INDEX
	IORM T2,ENFKTB(T1)	;SET BIT IN ENQ FORK TABLE
	POP P,T1		;GET BACK FORK NUMBER
	CALLRET UNBLKF		;UNBLOCK THE FORK


;ROUTINE TO TEST IF A FORK IS READY TO CONTINUE (CALLED BY SCHEDULER)

;ACCEPTS IN T1/	FORK #
;	JSP T4,ENQTST
;RETURNS +1:	FORK NOT READY TO CONTINUE
;	 +2:	FORK IS READY TO BE CONTINUED, LOCK IS LOCKED

	RESCD

ENQTST:	PUSH P,T4		;SAVE RETURN ADDRESS
	CALL GETMSK		;GET BIT POSITION AND WORD INDEX
	TDNN T2,ENFKTB(T1)	;IS FORK READY TO CONTINUE?
	RET			;NO
	RETSKP			;YES

	SWAPCD
;ROUTINE TO GET SPACE FOR AN ENQ BLOCK

;ACCEPTS IN 1/	LENGTH OF BLOCK DESIRED
;	CALL ASGENQ
;RETURNS +1:	NO ROOM
;	 +2	T1/	ADR OF BLOCK

ASGENQ:	STKVAR <ASGENC,ASGENO>
	MOVEM T1,ASGENC		;SAVE COUNT OF WORDS NEEDED
	MOVE T2,ENQSPC		;GET SPACE LEFT
	SUB T2,ASGENC		;CALCULATE THE NEW AMOUNT
	JUMPL T2,ASGEN2		;NONE LEFT, GO TRY TO FREE UP SOME
	EXCH T2,ENQSPC		;SAVE NEW COUNT
	MOVEM T2,ASGENO		;SAVE OLD COUNT
	CALL ASGSWP		;GET SPACE
	 JRST ASGEN1		;FAILED, GO TRY TO FREE UP SOME
	RETSKP

ASGEN1:	MOVE T1,ASGENO		;RESTORE OLD COUNTER
	MOVEM T1,ENQSPC
ASGEN2:	HRLOI T1,377777		;RELEASE ALL LONG TERM LOCKS
	CALL ENQGC		;BRUTE FORCE TECHNIQUE TO GET SPACE
	MOVE T2,ENQSPC		;GET SPACE LEFT
	SUB T2,ASGENC		;CALCULATE THE NEW AMOUNT
	JUMPL T2,[RETBAD(MONX01)] ;NONE LEFT
	EXCH T2,ENQSPC		;SAVE NEW COUNT
	MOVEM T2,ASGENO		;SAVE OLD COUNT
	MOVE T1,ASGENC		;GET NUMBER OF WORDS NEEDED
	CALL ASGSWP		;GET SPACE
	 RETBAD (,<MOVE T2,ASGENO ;GET THE OLD COUNT AGAIN
		   MOVEM T2,ENQSPC>) ;RESTORE THE OLD COUNT
	RETSKP


;ROUTINE TO RELEASE ENQ SPACE

;ACCEPTS IN T1/	ADR OF BLOCK
;	    T2/	LENGTH OF BLOCK
;	CALL RELENQ
;RETURNS +1:	ALWAYS

RELENQ:	ADDM T2,ENQSPC		;PUT BACK THE SPACE USED
	CALLRET RELSWP		;AND RELEASE THE BLOCK


;ROUTINE TO GARBAGE COLLECT LONG TERM LOCKS

;ACCEPTS IN T1/	TIME STAMP FOR DELETING LOCK BLOCKS
;	CALL ENQGC
;RETURNS +1:	ALWAYS

ENQGC:	HRRZ T2,ENQLTL		;ANY LOCKS ON THE LIST?
	JUMPE T2,R		;NO
	SAVEQ			;GET SOME ACS TO USE
	MOVEI Q1,ENQLTL-.ENQLT	;GET POINTER TO HEAD OF LIST
	MOVE Q3,T1		;SAVE TIME STAMP
ENQGC1:	LOAD T1,ENQLT,(Q1)	;GET NEXT LOCK ON LIST
	JUMPE T1,R		;0 MEANS NO MORE
	LOAD T2,ENQTS,(T1)	;WHEN WAS THE LAST TIME IT WAS USED
	CAMGE T2,Q3		;IS IT TOO OLD?
	JRST ENQGC2		;YES, GO DELETE IT
	MOVE Q1,T1		;GET POINTER TO THIS LOCK INTO Q1
	JRST ENQGC1		;LOOP BACK FOR ALL LOCKS ON LIST

ENQGC2:	LOAD T2,ENQLT,(T1)	;NOW REMOVE THIS LOCK FROM THE LIST
	STOR T2,ENQLT,(Q1)	;MAKE LIST SKIP OVER THIS ONE
	MOVEI T2,-1		;MARK THAT THIS ONE IS NOT ON LIST
	STOR T2,ENQLT,(T1)
	LOAD T2,ENQFLG,(T1)	;TURN OFF THE LONG TERM LOCK FLAG
	TXZ T2,EN.LTL		;SO IT WILL BE RELEASED
	STOR T2,ENQFLG,(T1)
	LOAD T2,ENQNLQ,(T1)	;NOW SEE IF THE LOCK IS FREE
	CAIN T2,0(T1)		;IT IS FREE IF IT POINTS TO ITSELF
	CALL LOKREL		;IT IS FREE, GO RELEASE IT
	JRST ENQGC1		;LOOP BACK FOR REST OF LIST
;ROUTINE TO CALCULATE AN INDEX INTO THE HASH TABLE

;REQUIRES THAT P1 - P4 BE SET UP BY VALREQ
;	CALL HASH
;RETURNS +1:	ILLEGAL ARGUMENT - ERROR CODE IN T1
;	 +2:	HASH INDEX IN T1
;		P4 CONTAINS WORD LENGTH OF STRING

HASH:	SETZ P4,		;INITIALIZE CHARACTER COUNT
	MOVE T1,P2		;GET STRING POINTER
	LDB T2,[POINT 3,T1,2]	;SEE IF A CODE OR STRING POINTER
	CAIN T2,5		;...
	JRST HASH1		;USER CODE (500000,,0 + 33-BIT NUMBER)
	CALL STHASH		;HASH THE STRING
	 RET			;ILLEGAL STRING POINTER
	MOVEM T2,P4		;SAVE CHARACTER COUNT
HASH1:	HRRZ T2,P1		;GET OFN CODE
	CALL MHASH		;HASH THE TWO NUMBERS TOGETHER
	MOVMS T1		;MAKE SURE IT IS POSITIVE
	IDIVI T1,HSHLEN		;GET FINAL HASH INDEX
	MOVE T1,T2		;USE REMAINDER
	RETSKP			;AND GIVE OK RETURN
;ROUTINE TO HASH TWO NUMBERS TOGETHER

;ACCEPTS IN T1/	NUMBER
;	    T2/	NUMBER
;	CALL MHASH
;RETURNS +1:	ALWAYS - T1/	HASH

MHASH:	XOR T1,RANDOM		;GUARD AGAINST A ZERO IN T1
	XOR T2,RANDOM		;OR IN T2
	MUL T2,RANDOM		;GET A RANDOM NUMBER
	MUL T1,T2		;...
	RET

RANDOM:	5*5*5*5*5*5*5*5*5*5*5*5*5*5*5


;ROUTINE TO HASH A STRING

;ACCEPTS IN T1/	STRING POINTER IN USER SPACE
;	CALL STHASH
;RETURNS +1:	ILLEGAL STRING POINTER - ERROR CODE IN T1
;	 +2:	HASH IN T1
;		# OF WORDS IN STRING IN T2

STHASH:	STKVAR <STHSHP,STHSHX,STHSHC,STHSHB,STHSHN,STHSHM>
	MOVEM T1,STHSHP		;SAVE POINTER
	LDB T2,[POINT 6,T1,11]	;GET THE BYTE SIZE
	MOVEI T3,^D36		;GET NUMBER OF BYTES PER WORD
	IDIVI T3,(T2)		;...
	MOVEM T3,STHSHN		;SAVE NUMBER OF BYTES PER WORD
	IMULI T3,ENQMXW		;GET MAX NUMBER OF CHARACTERS ALLOWED
	MOVEM T3,STHSHM		;SAVE FOR LATER
	AND T1,[007700,,0]	;BUILD A BYTE POINTER
	TDO T1,[POINT 0,T2]	;POINTING TO AC T2
	MOVEM T1,STHSHB		;SAVE IT FOR LATER
	SETZM STHSHC		;INITIALIZE THE COUNT OF CHARACTERS
	SETZM STHSHX		;INITIALIZE ANSWER REGISTER
STHSH1:	MOVE T4,STHSHN		;INITIALIZE COUNTER
	MOVE T3,STHSHB		;AND BYTE POINTER
	SETZ T2,		;INITIALIZE RECEIVER AC
STHSH2:	AOS T1,STHSHC		;COUNT UP CHARACTER COUNTER
	CAMLE T1,STHSHM		;STRING TOO LONG?
	RETBAD (ENQX19)		;YES, GIVE ERROR RETURN
	XCTBU [ILDB T1,STHSHP]	;GET A BYTE FROM THE USER'S STRING
	JUMPE T1,STHSH3		;END OF STRING?
	IDPB T1,T3		;NO, STORE CHARACTER IN T2
	SOJG T4,STHSH2		;LOOP BACK FOR 5 CHARACTERS
	XORM T2,STHSHX		;XOR THIS INTO ANSWER WORD
	JRST STHSH1		;LOOP BACK UNTIL END OF STRING
STHSH3:	XORM T2,STHSHX		;STORE PARTIAL WORD TOO
	MOVE T1,STHSHX		;GET ANSWER
	MOVE T2,STHSHC		;GET CHARACTER COUNT
	IDIV T2,STHSHN		;GET NUMBER OF WORDS
	SKIPE T3		;AND PARTIAL WORDS IN THE STRING
	AOS T2
	RETSKP			;AND RETURN
;ROUTINE TO DEQ ALL REQUESTS FOR A FORK

;ACCEPTS IN T1/	FORK NUMBER
;	CALL ENQFKR  OR  CALL DEQFRK (IF DATA BASE ALREADY LOCKED)
;RETURNS +1:	ALWAYS

ENQFKR::HRRZ T2,ENQLST		;ANY LOCKS LOCKED FOR THIS JOB?
	JUMPE T2,R		;IF NOT, THEN EXIT NOW
	NOINT			;LOCK UP THE DATA BASE FIRST
	LOCK ENQLOK
	SETO T2,		;DEQ ALL
	CALL DEQFRK		;DEQUEUE ALL ENTRIES FOR THIS FORK
	UNLOCK ENQLOK		;UNLOCK THE DATA BASE
	OKINT
	RET			;AND RETURN

;ROUTINE TO DEQ FOR A FORK
;ACCEPTS IN T1/	FORK #
;	    T2/	-1 FOR ALL, OR AN ID TO BE MATCHED
;	CALL DEQFRK
;RETURNS +1 ALWAYS - T1/	COUNT OF LOCKS DEQUEUED

DEQFRK:	STKVAR <DEQFKF,DEQFKQ,DEQFKI,DEQFCT>
	SETZM DEQFCT		;INIT COUNT OF DEQUEUED LOCKS TO 0
	MOVEM T1,DEQFKF		;SAVE FORK NUMBER ON STACK
	MOVEM T2,DEQFKI		;SAVE ID
DEQFK0:	HRRZ T1,ENQLST		;GET FIRST Q ON JOB LIST
DEQFK1:	JUMPE T1,DEQFK2		;IF NO MORE, GO EXIT
	LOAD T2,ENQFRK,(T1)	;GET FORK NUMBER OF CREATOR OF Q
	LOAD T3,ENQID,(T1)	;GET ID OF Q-ENTRY
	SKIPL DEQFKI		;IS THIS A DELETE ALL REQUEST?
	CAMN T3,DEQFKI		;NO, IS THIS A MATCH?
	CAME T2,DEQFKF		;YES, IS THIS TO BE DELETED?
	JRST [	LOAD T1,ENQNJQ,(T1) ;NO, STEP TO NEXT Q-BLOCK IN CHAIN
		JRST DEQFK1]	;LOOP BACK TILL END OF LIST
	CALL REQDEQ		;YES, DELETE IT
	AOS DEQFCT		;COUNT UP NUMBER OF LOCKS DEQUEUED
	JRST DEQFK0		;START OVER AGAIN AT START OF LIST

DEQFK2:	MOVE T1,DEQFCT		;GET THE COUNT OF LOCKS RELEASED
	RET			;AND RETURN


;ROUTINE TO INITIALIZE THE JSB ON JOB CREATION

ENQJBI::SETZB T1,ENQLST		;INITIALIZE POINTER TO Q-BLOCKS
	STOR T1,ENQCNT		;ZERO COUNT OF REQUESTS
	MOVEI T1,ENQSTQ		;GET STANDARD ENQ/DEQ QUOTA
	STOR T1,ENQOTA		;STORE IN JSB
	RET			;AND RETURN
;ROUTINE TO CHECK IF AN OFN HAS A LOCK SET ON IT

;ACCEPTS IN T1/	OFN
;	    T2/ JFN
;	CALL ENQCLS
;RETURNS +1:	OFN IS LOCKED, FILE CANNOT BE CLOSED OR MADE LONG
;	 +2:	OFN IS NOT LOCKED BY THIS JOB

ENQCLS::MOVEM T2,T4		;SAVE JFN BEING CLOSED
	NOINT			;LOCK UP THE DATA BASE DURING SCAN
	LOCK ENQLOK		;...
	HRRZ T2,ENQLST		;GET POINTER TO LOCKS FOR THIS JOB
ENQCL1:	JUMPE T2,ENQCL2		;IF 0, END OF LIST AND OFN IS OK
	LOAD T3,ENQLBP,(T2)	;GET ADDRESS OF LOCK BLOCK
	LOAD T3,ENQOFN,(T3)	;GET OFN OF LOCK
	CAMN T1,T3		;THE SAME OFN?
	JRST [	LOAD T3,ENQJFN,(T2) ;YES, GET JFN OF REQUEST
		CAME T3,T4	;IS JFN OF REQUEST BEING CLOSED ?
		JRST .+1	;NO, GO SCAN REST OF THE LIST
		UNLOCK ENQLOK	;YES, FILE CANNOT BE CLOSED
		OKINT
		MOVEI T1,ENQX20	;GET ERROR CODE
		RET]		;GIVE ERROR RETURN
	LOAD T2,ENQNJQ,(T2)	;GET ADDRESS OF NEXT Q IN LIST
	JRST ENQCL1		;LOOP TIL END OF LIST

ENQCL2:	UNLOCK ENQLOK		;UNLOCK THE LOCKS
	OKINT
	RETSKP			;GIVE OK RETURN


;ROUTINE TO INITIALIZE THE DATA BASE ON SYSTEM START UP

;	CALL ENQINI
;RETURNS +1:	ALWAYS

ENQINI::SETZM ENFKTB		;ZERO THE WAKEUP TABLE
	MOVE T1,[ENFKTB,,ENFKTB+1]
	MOVEI T2,ENFKTL
	CAILE T2,1		;DO BLT ONLY IF TABLE MORE THAN 1 WORD
	BLT T1,ENFKTB-1+ENFKTL
	MOVEI T1,HSHTBL		;INITIALIZE HASH TABLE
	HRLS T1			;  WITH EACH ENTRY POINTING TO ITSELF
	MOVEI T2,HSHLEN		;GET NUMBER OF ELEMENTS IN TABLE
ENQIN1:	HRRZ T3,T1		;SET UP ADDRESS ONLY
	MOVEM T1,0(T3)		;STORE POINTERS TO SELF
	ADD T1,BHC+1		;INCREMENT BOTH HALVES BY 1
	SOJG T2,ENQIN1		;LOOP BACK FOR ALL ENTRIES
	MOVEI T1,ENQMXF		;GET MAX ALLOWABLE USE OF FREE POOL
	MOVEM T1,ENQSPC		;INITIALIZE THE ENQ SPACE POOL
	SETZM ENQLTL		;INIT LONG TERM LOCK LIST
	SETZM ENQLTS		;AND TIME STAMP
	SETOM ENQLOK		;INITIALIZE DATA BASE LOCK
	RET			;RETURN

	TNXEND
	END