Google
 

Trailing-Edge - PDP-10 Archives - AP-D543V_SB - filio.mac
There are 17 other files named filio.mac in the archive. Click here to see a list.
TITLE FILIO LEVEL-D DISK SERVICE ROUTINE  V17617
SUBTTL DESIGNED BY T.HASTINGS,T.WACHS,C.WHITE CODED BY T.WACHS/TW 24 OCT 78
	SEARCH	F,S
	$RELOC
	$HIGH



;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
;  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (C) 1973,1974,1975,1976,1977,1978 BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS.
XP VFILIO,17617

;ASSEMBLY INSTRUCTIONS: FILIO,FILIO/C_S,FT50S,FILIO
	ENTRY	FILIO
FILIO::

;ASSEMBLY PARAMETER FOR FNDFIL INTERLOCK

;BITS IN THE ACCESS TABLE STATUS WORD
ACPCRE==:40
ACPSUP==:20
ACPUPD==:10
ACPREN==:200
ACRSUP==:2
ACPNIU==:400000
ACMCNT==:377400
ACPSMU==:4

IOSMON==400000	;THIS FILE IS CURRENTLY DOING MONITOR IO
IOSAU==200000	;THIS FILE HAS THE ALTER-UFD RESOURCE
IOSUPR==100000	;SUPER USETI/USETO DONE ON THIS CHAN
IOSDA==40000	;THIS FIL HAS DISK ALLOCATION QUEUE
IOSRIB==20000	;RIB IS IN MONITOR BUFFER
IOSRDC==10000	;THIS USER CHANNEL HAS READ COUNT UP FOR FILE
IOSWLK==4000	;FILE (WHOLE STR) IS SOFTWARE WRITE-=LOCKED
		; EITHER FOR ALL JOBS OR FOR THIS JOB ONLY
IOSALC==2000	;DONT CHANGE ACCALC WHEN GIVING UP BLOCKS OF A FILE
IOSFIR==1000	;COMPUTE AND STORE OR CHECK THE CHECKSUM
IOSHMS==IOBEG	;HUNG-DEVICE MESSAGE ALREADY TYPED
IOSRST==IOFST	;RESET (RELEASE) WAS DONE ON A SPOOLED DEVICE

;THE FOLLOWING S BITS ARE DEFINED IN COMMON.MOD
;BECAUSE THEY WANT TO BE IN THE SAME POSITION IN S AS IN RIB STATUS WORD
;IOSHRE=100	;HARD READ ERROR ENCOUNTERED
;IOSHWE=200	;HARD WRITE ERROR ENCOUNTERED
;IOSSCE=400	;SOFTWARE CHECKSUM ERROR ENCOUNTERED OR HARD POSITIONING ERROR

;IOSERR=IOSHRE+IOSHWE+IOSSCE
;IOSMER=-IOSERR
DEFINE	NOSCHEDULE <>
DEFINE SCHEDULE <>
IFE	FTCBDB,<
DEFINE	CBDBUG<>
>

IFN	FTCBDB,<
DEFINE	CBDBUG(A,B)<
	AOSA	.+1
	0
IFIDN	<A>,<Y><
	PUSH P,T1
	MOVE T1,JOB##
	CAME T1,CBUSER##
	HALT	.
	POP	P,T1
>
IFIDN	<B>,<Y><
PUSHJ	P,CKBAS##
>
>
>
REPEAT	0,<
NOTE ABOUT STATES OF CHANNELS, KONTROLLERS, UNITS, FILES:

			C	K	U	F
IDLE		I	I	I	I	I
SEEK WAIT	SW			SW
SEEK		S			S
POSITION WAIT	PW			PW	PW
POSITION	P			P	P
TRANSFER WAIT	TW			TW	TW
TRANSFER(BUSY)	T OR B	B	B	T	T

NOTE ABOUT QUEUES:
THERE ARE 2 QUEUES OF FILES
	SW/PW QUEUE FOR EACH UNIT
	TW	QUEUE FOR CHANNEL
A FILE IS ONE AND ONLY ONE OF THE FOLLOWING CONDITIONS:WITH RESPECT TO QUEUES
	A.THE ONLY FILE IN SW/PW QUEUE FOR A UNIT (UNIT IN SW STATE)
	B.ONE OF PERHAPS MANY FILES IN PW QUEUE FOR A UNIT(UNIT IN PW,P,TW,OR T STATE)
	C.ONE OF PERHAPS MANY IN TW QUEUE FOR CHANNEL(CHAN AND KONTROL IN B STATE)
	D.NONE OF THE ABOVE (FILE IN I,P, OR T STATE)

NOTE:	#MEANS INSTRUCTION IS EXECUTED WITH ALL DISK PI CHANNELS OFF
	%MEANS INSTRUCTION IS EXECUTED WHILE JOB## HAS CB RESOURCE
	*MEANS INSTRUCTION MAY BE EXECUTED AT INTERRUPT LEVEL
	(TO SAVE TYPING USED ONLY FOR INSTRUCTIONS NOT IN INTERRUPT MODULE ITSELF)

THE FOLLOWING TECO MACRO WILL PRODUCE A LISTING OF ONLY THE SUBROUTINE
NAMES AND COMMENTS PRIOR TO THEM:
ERDEV1:FILSER.MAC$EWDEV2:FILSER.SUB$
<_;SUBROUT$;0L.U1  !NTST! :S:$"GA ONTST$'.U20L1A-59"E LONTST$' Q2JI

$Q1,.PW 0,.K>EF



>
	SUBTTL	ALLOCATION/DEALLOCATION
CLASIZ=:^D23
TALSIZ=:^D36-CLASIZ
CLAPOS=:^D35
TALPOS=:^D35-CLASIZ
CLAMAX=1B<TALPOS>-1	;MAX CLUSTER ADDR. (BYTE OF ALL 1'S)
DSKSCN=100000	;SCANNING SATS FROM DISK
STRTAD=200000	;ALLOCATE STARTING AT A SPECIFIED LOCATION
SATCHG=400000	;SAT TABLE IN CORE DIFFERS FROM SAT TABLE ON DISK (SIGN BIT)



;SUBROUTINE TO GET A CHUNK OF BLOCKS (ANYWHERE IN STR)
;ENTER WITH T2=HOW MANY TO GET, U=PREFERRED UNIT
;EXIT CPOPJ1 IF GOT ALL (OR SOME) ON DESIRED UNIT
;EXIT CPOPJ IF GOT ALL (OR SOME) ON A DIFFERENT UNIT, WITH T3=UNIT-CHANGE POINTER
;EXIT CPOPJ WITH T3=0 IF GOT NONE (STR FULL).
;THIS ROUTINE GETS EITHER ALL, OR THE LARGEST AVAILABLE CHUNK IN STR
;RETURNS WITH T2=RETRIEVAL POINTER, T1=NUMBER OF BLOCKS OBTAINED
TAKCHK::PUSHJ	P,SAVE2##		;SAVE P1,P2
	MOVE	P1,U		;FOR END-TEST
	SETZ	P2,		;INDICATE NO BEST UNIT YET
	HRRZS	T2		;WANT EXACTLY C(T2) BLOCKS
;THE ABOVE INSTRUCTION WAS CHANGED FROM HRROS SINCE, WHEN THE DISKS GET FULL,
; THIS LOOP CONSUMES LOTS OF TIME. EVENTUALLY, WE WILL CHANGE IT SO THAT
; THE LARGEST HOLE IN A SAT GETS STORED IN CORE, AT WHICH POINT THIS
; WILL GO BACK THE WAY IT WAS
TAKCH1:	SETZ	T1,		;WANT THEM ANYWHERE ON UNIT
	PUSHJ	P,TAKBLK	;TRY TO GET THE BLOCKS
	  JRST	TAKCH2		;CANT GET THAT MANY ON THIS UNIT
	CAIN	U,(P1)		;GOT ALL WE ASKED FOR. SAME UNIT?
	PJRST	CPOPJ1##	;YES - SKIP RETURN
	LDB	T3,UNYLUN##	;NO. GET LOG. UNIT NO.
	TRO	T3,RIPNUB##	;MAKE A UNIT-CHANGE POINTER
	POPJ	P,		;AND NON-SKIP RETURN

;HERE ON NOT-AVAILABLE RETURN FROM TAKBLK
TAKCH2:	CAIG	T2,(P2)		;THIS UNIT BEST SO FAR?
	JRST	TAKCH3		;NO
	MOVE	P2,T2		;YES. SAVE SIZE OF LARGEST HOLE
	HRL	P2,U		;SAVE UNIT OF LARGEST HOLE
TAKCH3:	HLRZ	U,UNISTR##(U)	;STEP TO NEXT UNIT IN STR
	JUMPN	U,TAKCH4	;END OF STR CHAIN?
	HRRZ	U,UNISTR##(P1)	;YES, STR DATA BLOCK LOC
	HLRZ	U,STRUNI##(U)	;1ST UNIT IN STR
TAKCH4:	HRRZ	T2,T1		;RESTORE NUMBER OF BLOCKS TO GET
	CAIE	U,(P1)		;BACK WHERE WE STARTED?
	JRST	TAKCH1		;NO, TRY THIS UNIT
	JUMPE	P2,CPOPJ##	;RETURN IF STR IS FULL
	HLRZ	U,P2		;NOT FULL - SET BEST UNIT
	HRRZ	T2,P2		;LARGEST CONTIGUOUS CHUNK AVAILABLE
	JRST	TAKCH1		;GO SETTLE FOR LARGEST HOLE
;ROUTINE TO ALLOCATE BLOCKS FROM DISK
;ENTER WITH T1= WHERE TO START (OR 0 IF DONT CARE)
;T2= HOW MANY TO ALLOCATE 
;LH(T2)=0 IF TAKE N OR LESS
;LH(T2)=-1 IF TAKE EXACTLY N
;RETURNS CPOPJ IF UNSUCCESSFUL WITH T2=LARGEST HOLE FOUND, T1= ORIGINAL T2, T3=0
;RETURNS CPOPJ1 IF OK, WITH T1= NUMBER OF BLOCKS TAKEN T2 = CLUSTER POINTER FOR GROUP
;T3 POINTS TO FILE STRUCTURE DATA BLOCK
TAKBLK::
	PUSHJ	P,SAVE4##	;SAVE P1-P4
	PUSH	P,W		;SAVE W
	HLL	W,T2		;SAVE LH(T2) (=-1 IF EXACTLY N BLOCKS)
	HRRZS	T2		;SET T2=POSITIVE NUMBER
	SKIPN	T2		;MORE THAN 18 BITS WORTH?
	MOVEI	T2,-1		;YES, ASK FOR MAX
	SKIPN	DINITF##	;IN ONCE-ONLY (REFRESHER)?
	CAMG	T2,UNITAL##(U)	;NO, REQUESTING MORE THAN ARE AVAILABLE?
	JRST	TAKBL		;NO. GET SOME BLOCKS
	MOVE	T3,T2		;YES. AMONT TO GET INTO T3
	SKIPLE	T2,UNITAL##(U)	;ANY BLOCKS AT ALL?
	JUMPGE	W,TAKBL		;YES. REQUEST  MAXIMUM OF UNITAL BLOCKS
	MOVEI	T2,0
	TLNE	F,OCLOSB	;NO. IS CLOSE HAPENING?
	AOJA	T2,TAKBL	;YES. TRY TO GET 1 BLOCK ANYWAY
;(THERE ARE BLOCKS IN SAT TABLES WHICH ARE NOT IN UNITAL, AND ARE ONLY
;GIVEN UP DURING A CLOSE UUO)
	MOVE	T1,T3		;NOT CLOSE. INDICATE 0 SPACE FOUND
	JRST	TAKBLT		;AND TAKE ERROR RETURN

TAKBL:
	LDB	T4,UNYBPC##	;NUMBER OF BLOCKS PER CLUSTER
	HRRZ	P3,T2		;DESIRED NUMBER OF BLOCKS
	ADDI	P3,-1(T4)	;CONVERT TO NUMBER OF CLUSTERS
	IDIV	P3,T4
	MOVE	T3,UNISTR##(U)
	SETO	T2,		;COMPUTE LARGEST ALLOWED GROUP SIZE
	LDB	T2,STYCNP##(T3)	;LARGEST FIELD
	CAIGE	T2,(P3)		;ASKING FOR TOO MUCH?
	HRR	P3,T2		;YES, REDUCE REQUEST
	PUSHJ	P,SUPDA		;QUEUE FOR DISK ALLOCATION IF DONT ALREADY HAVE (ENTER)
	SKIPN	DEVUNI##(F)
	JRST	TAKBLU
	HLRZ	P1,UNISAB##(U)	;LOC OF FIRST SAT BUFFER
	JUMPE	T1,TAKBLA	;GO IF NO START ADDRESS SPECIFIED
;HERE WHEN A START ADDRESS SPECIFIED
	SETZ	T2,
	JUMPL	T1,TAKBLM	;NEGATIVE BLOCK NOS ARE ILLEGAL
	CAML	T1,UNIBPU##(U)	;REQUESTED BLOCK ABOVE TOP OF UNIT?
	JRST	TAKBLM		;YES, ERROR RETURN
	IDIV	T1,T4		;NO, CONVERT TO CLUSTER ADDRESS
	PUSH	P,T1		;SAVE CLUSTER ADDRESS
	PUSHJ	P,CHKNEW	;TEST FOR NEWLY-MOUNTED STR
	MOVE	T1,(P)		;RESTORE ADR
	PUSHJ	P,FNSAT		;GET SAT BUFFER FOR THIS BLOCK
	SKIPN	DEVUNI##(F)	;IF UNIT WAS REMOVED,
	PJRST	TAKBL3		; TAKE ERROR RETERN
	HRLM	P1,UNISAB##(U)	;SAVE BUFFER LOC IN UNISAT
	HLRE	T1,SABSCN##(P1)	;-LENGTH OF WHOLE SAT DATA AREA
	ADDI	P1,SABBIT##(T2)	;SET RH(P1)=FIRST SAT WORD FOR ADDRESS
	ADD	T1,T2		;T1=-NUMBER OF DATA WORDS AFTER THIS ONE
	HRLM	T1,P1		;P1=AOBJN WORD TO SCAN TABLE
TAKBL1:	MOVEI	P2,0		;P2 WILL CONTAIN LARGEST HOLE FOUND
	MOVE	T1,(P1)		;FIRST WORD TO LOOK AT
	LSH	T1,(T3)		;POSITION TO RIGHT BIT
	JUMPL	T1,TAKBL3	;HOLE=0 IF 1ST BIT SET
	HRLM	T2,P2		;SAVE POSITION OF HOLE
	DPB	T3,[POINT 6,P3,17]
	MOVNS	T3
	MOVEI	T4,^D36(T3)	;SET NUMBER OF BITS LEFT IN WORD
	JFFO	T1,.+2		;COMPUTE NUMBER OF LEADING 0'S
	MOVEI	T2,^D36(T3)	;REST OF WORD EMPTY
	TLO	P3,STRTAD	;INDICATE START ADDR. SPECIFIED
	PUSH	P,P1		;SAVE LOC OF 1ST DATA WORD
	PUSHJ	P,GETZR##	;TRY TO GET N 0'S
	  JRST	TAKBL2		;CANT GET ENOUGH

	POP	P,T1		;FOUND THEM  REMOVE GARBAGE FROM PD LIST
	HLRZ	P2,UNISAB##(U)	;LOC OF SAT BUFFER
	JRST	TAKBLQ		;MARK BLOCKS, HOUSEKEEP AND EXIT

;HERE WHEN WE COULDNT GET N CONTIGUOUS BLOCKS
TAKBL2:	POP	P,P1		;PICK UP DATA LOC AGAIN
	JUMPL	W,TAKBL3	;GO IF EXACTLY N NEEDED
	HRR	P3,P2		;WE CAN DO WITH LESS GET LARGEST AVAILABLE
	LDB	T3,[POINT 6,P3,17]
	HLRZ	T2,P2		;RESTORE POSITION
	JRST	TAKBL1		;GO GET THEM

;HERE WHEN N NOT AVAILABLE, WE NEEDED EXACTLY N BLOCKS
TAKBL3:	POP	P,T1		;TAKE STUFF OFF PD LIST
	HRRZ	T2,P2		;LARGEST HOLE AVAILABLE (CLUSTER COUNT)
	JRST	TAKBLM		;CONVERT TO BLOCK COUNT, ERROR RETURN


;HERE WHEN A STARTING ADDRESS WAS NOT SPECIFIED
TAKBLA:	PUSHJ	P,CHKNEW	;MAKE SURE SATS HAVE BEEN READ
	TLZ	P3,677777	;LH(P3) WILL HAVE INDEX OF LARGEST HOLE FOUND
	MOVEI	P2,0		;LH(P2) WILL HAVE SIZE OF LARGEST HOLE
				;THE LEFT HALF OF ACS ARE BEING USED
				;BECAUSE THE PD LIST GETS VERY LONG
				;IF SAT BLOCKS MUST BE READ
	MOVE	T1,UNIDES##(U)
	TLNN	T1,UNPMSB##	;DOES THE UNIT HAVE ONLY 1 SAT TABLE?
	JRST	TAKBLC		;YES, SKIP THE FANCY STUFF

;TRY TO FIND A SAT BLOCK IN CORE CONTAINING ENOUGH CONSECUTIVE 0'S
TAKBLB:	HRRZ	T1,SABTAL##(P1)	;FREE BLOCKS LEFT IN THIS SAT
	CAIGE	T1,(P3)		;ENOUGH TO SATISFY USER?
	JRST	TAKBLG		;NO. LOOK AT NEXT SAT BUFFER

;HERE WHEN THE CURRENT SAT TABLE MAY HAVE ENOUGH CONTIGUOUS CLUSTERS
;SCAN FIRST FROM WHERE THE SCAN LEFT OFF THE LAST TIME
TAKBLC:	MOVE	T1,SABHOL##(P1)	;BIGGEST HOLE IN SAT
	CAIGE	T1,(P3)		;TRYING FOR MORE THAN BIGGEST?
	JUMPGE	T1,TAKBLF	;YES, SKIP SCAN IF WE KNOW SIZE OF HOLE
				; (SABHOL=-1 IF WE DONT KNOW THE SIZE)
	MOVSI	T1,SABBIT##(P1)	;SET UP AN AOBJN WORD  FOR SCAN
	HRLZ	T2,SABSCN##(P1)	;COMPUTE DISTANCE FROM START TO C(SABSCN)
	SUB	T2,T1		;=+N
	ADD	T2,SABSCN##(P1)	;LH=DISTANCE FROM C(SABSCN) TO TOP
				;RH=WHERE TO START LOOKING
	PUSH	P,P1		;SAVE THE BUFFER LOC
	MOVE	P1,T2		;AOBJN WORD FOR SCAN
	HRRI	P2,0		;SET BEST SO FAR TO 0
	PUSHJ	P,GETZ##	;AND TRY TO GET N 0'S
TAKBLD:	  SKIPA	T2,(P)		;COULDN'T GET THEM
	JRST	TAKBLP		;FOUND THEM - UPDATE AND EXIT
;HERE WHEN N WERENT AVAILABLE FROM WHERE SCAN LAST LEFT OFF
;RESCAN TABLE FROM THE START
	MOVEI	P1,SABBIT##(T2)	;FIRST DATA LOC IN BUFFER
	HLL	P1,SABSCN##(T2)	;-LENGTH OF ENTIRE DATA AREA
	PUSHJ	P,GETZ##	;SCAN WHOLE SAT TABLE
	  SKIPA	T1,P2		;STILL CANT FIND ENOUGH
	JRST	TAKBLP		;FOUND THEM - WRAP UP

;HERE WHEN THE CURRENT SAT BUFFER DOESN'T HAVE ENOUGH
	POP	P,P1		;RESTORE BUFFER LOC

TAKBLF:	HRR	P2,T1
	HRRZM	P2,SABHOL##(P1)	;SAVE SIZE OF LARGEST HOLE
	HLRZ	T1,P2		;PREVIOUS MAXIMUM
	CAIL	T1,(P2)		;WAS THIS SAT TABLE BETTER?
	JRST	TAKBLG		;NO
	HRLS	P2		;YES. SAVE SIZE IN LH(P2)
	HRR	P2,P1		;SET RH(P2)=BUFFER LOC
	LDB	T1,SAYNDX##	;GET INDEX OF SAT
	DPB	T1,[POINT 11,P3,17] ;SAVE INDEX IN LH(P3)
TAKBLG:	HRR	P2,P1		;RH(P2)=CURRENT BUFFER
	MOVE	T1,UNIDES##(U)	;DOES THIS UNIT HAVE ONLY 1 SAT?
	TLNN	T1,UNPMSB##
	JRST	TAKBLL		;YES. CANT GET ENOUGH
	TLNE	P3,DSKSCN	;NO. SCANNING SATS FROM DISK?
	JRST	TAKBLI		;YES. READ NEXT ONE
	HLRZ	P1,SABRNG##(P1)	;NO. STEP TO NEXT IN-CORE SAT TABLE
	HLRZ	T1,UNISAB##(U)	;BACK WHERE WE STARTED?
	CAME	P1,T1
	JRST	TAKBLB		;NO. TRY RHIS SAT TABLE
;HERE WHEN ALL SAT TABLES IN CORE ARE THROUGH
;NOTICE THAT WHILE WE WERE LOOKING AT ONLY IN-CORE SAT TABLES WE
;SCANNED ONLY THOSE WHICH HAD A CHANCE OF SUCCESS.
;NOW ALL SAT'S WILL BE LOOKED AT SINCE WE WANT TO FIND
;THE MAXIMUM NUMBER OF CONTIGUOUS BITS IF WE CANT GET ENOUGH
	TLO	P3,DSKSCN	;INDICATE READING SATS FROM DISK
	HLR	P2,UNISAB##(U)	;POINT P2 TO CURRENT SAT
	TROA	P4,-1		;START AT SAT TABLE 0
TAKBLI:	LDB	P4,SAYNDX##	;INDEX OF LAST SAT LOOKED AT
	ADD	P4,UNISPT##(U)	;COMPUTE ABSOLUTE ADDR. OF ITS POINTER
TAKBLJ:	SKIPN	1(P4)		;IS TABLE EXHAUSTED?
	JRST	TAKBLL		;YES. CANT GET ENOUGH
	LDB	T1,[POINT TALSIZ,1(P4),TALPOS]	;FREE COUNT OF THIS SAT
	HLRZ	T3,P2		;LARGEST HOLE FOUND SO FAR
	CAIG	T1,(T3)		;THIS SAT HAVE AT LEAST THAT MANY FREE BITS?
	AOJA	P4,TAKBLJ	;NO. DONT BOTHER SCANNING IT
	SUB	P4,UNISPT##(U)	;YES. COMPUTE INDEX OF SATSPT TABLE
	MOVEI	P4,1(P4)
	PUSHJ	P,SDWNDA	;UNQUEUE,REQUEUE FOR DISK ALLOCATION
	PUSHJ	P,SUPDA		;SO ANY WAITING REQUEST WILL BE
				; SATISFIED BEFORE WE DO IO
	SKIPN	DEVUNI##(F)
	JRST	TAKBLU
	PUSHJ	P,SATST		;GET THE CORRESPONDING SAT TABLE
	MOVE	T1,SABHOL##(P2)	;SIZE OF BIGGEST HOLE IN SAT
	HLRZ	T2,P2		;BIGGEST HOLE FOUND SO FAR
	CAMGE	T1,T2		;WORTH WHILE TO SCAN THE SAT?
	JUMPGE	T1,TAKBLI	;NOT IF WE REALLY KNOW (SABHOL POSITIVE)
	PUSH	P,P2		;SAVE BUFFER LOC
	HRRI	P2,0		;PRESET LARGEST HOLE IN SAT
	JRST	TAKBLD		;GO SCAN THE SAT (FROM BEGINNING)
;HERE WHEN ALL SATS SCANNED, NONE HAS ENOUGH
TAKBLL:	TLNE	P2,-1		;FIND ANY AT ALL?
	JUMPGE	W,TAKBLN	;YES, NEED EXACTLY N?
	HLRZ	T2,P2		;YES. T2=LARGEST HOLE FOUND
TAKBLM:	HRRZ	T1,P3		;T1=SIZE REQUESTED
	LDB	T3,UNYBPC##	;CONVERT BOTH CLUSTER COUNTS
	IMUL	T1,T3		;TO BLOCK NUMBERS
	IMUL	T2,T3
	SETZ	T3,		;T3=0 ON ERROR
	POP	P,W		;RESTORE W
	PJRST	SDWNDA		;GIVE UP DA QUEUE AND ERROR RETURN

;HERE WHEN NOT ENOUGH WERE FOUND, BUT A LESSER AMOUNT WILL DO
TAKBLN:	LDB	P4,[POINT 11,P3,17]	;INDEX OF BEST SAT TABLE
	PUSHJ	P,SATST		;GET CORRESPONDING SAT BLOCK IN
	PUSH	P,P2		;SAVE BUFFER LOC
	HLRZ	P3,P2		;SIZE OF LARGEST HOLE
TAKBLO:	MOVE	T1,(P)		;SET UP AN AOBJN WORD
	HLL	P1,SABSCN##(T1)	; FOR THE BUFFER
	HRRI	P1,SABBIT##(T1)
	MOVEI	P2,0		;SET LARGEST HOLE TO 0
	PUSHJ	P,GETZ##	;GET THE BLOCKS
	  JRST	TAKBLR		;SOMEBODY SNUCK IN!

;HERE WHEN A BUNCH OF BLOCKS HAVE BEEN OBTAINED
;THE BUFFER LOC IS ON THE PD LIST
TAKBLP:	POP	P,P2		;GET BUFFER LOC
	HRRZ	T1,P4		;POSITION OF HOLE
	SUBI	T1,SABBIT##(P2)	;CONVERT TO A CLUSTER NUMBER
	IMULI	T1,^D36
	HLRZ	T2,P4
	MOVNS	T2		;-BIT POSITION IN WORD
	ADDI	T1,^D36(T2)	;CLUSTER NO RELATIVE TO START OF SAT
	LDB	T2,[POINT CLASIZ,SABFIR##(P2),CLAPOS]	;FIRST ADDRESS IN SAT
	ADD	T1,T2		;COMPUTE ACTUAL CLUSTER NUMBER (RELATIVE TO UNIT)
	PUSH	P,T1		;SAVE IT ON THE LIST
	HRRM	P4,SABSCN##(P2)	;SET WHERE TO START NEXT TIME
;HERE WITH CLUSTER ADDRESS ON PD LIST
TAKBLQ:	PUSHJ	P,SETOS##	;MARK THE BLOCKS IN THE SAT TABLE
	STOPCD	.+1,DEBUG,BAO,	;++BIT ALREADY ONE
	HRRZS	P3		;P3=PLUS NUMBER OF CLUSTERS GOTTEN
	PUSHJ	P,FIXCNT	;UPDATE COUNTS
	POP	P,T2		;RESTORE CLUSTER ADDRESS
	MOVE	T3,UNISTR##(U)	;LOC OF STRUCTURE DB
	DPB	P3,STYCNP##(T3)	;SAVE CLUSTER COUNT IN T2
	PUSHJ	P,SDWNDA	;GIVE UP DA QUEUE (EXCEPT IF SIM UPDATE)
	JRST	WPOPJ1##	;AND TAKE GOOD RETURN

;HERE WHEN THE BEST-SO-FAR WHICH WE CAREFULLY COMPUTED IS NO LONGER
;THERE - SOMEONE HAS SNUCK IN WHEN WE UNQUEUED AND GRABBED A CHUNK
;OUT OF THE HOLE WE REMEMBERED
TAKBLR:	JUMPE	P2,TAKBLS	;START ALL OVER IF NOTHING LEFT IN SAT
	MOVE	P3,P2		;SOMETHING LEFT - SETTLE FOR IT
	JRST	TAKBLO		;GO TAKE THE BLOCKS


TAKBLS:	POP	P,T1		;REMOVE JUNK FROM PD LIST
	SKIPLE	T2,UNITAL##(U)	;ANY BLOCKS AT ALL IN UNIT?
	JRST	TAKBLA		;YES. TRY OVER FROM BEGINNING
	HRRZ	T1,P3		;NO. RESTORE AMOUNT REQUESTED
TAKBLT:	POP	P,W		;RESTORE W
	PUSH	P,T1		;SAVE ACS
	PUSH	P,T2
	TLNE	S,IOSDA
	PUSHJ	P,SDWNDA	;GIVE UP THE DA RESOURCE
	MOVEI	T4,.ERFUL	;STR - FULL ERROR
	MOVE	T1,UNISTR##(U)	;STR DB LOC
	SKIPG	STRTAL##(T1)	;STR FULL?
	PUSHJ	P,SETINJ	;YES, SET UP FOR INTERCEPT
	  JFCL			;NOT ENABLED OR STR NOT FULL
	SETZ	T3,		;T3=0 ON ERROR
	POP	P,T2		;RESTORE ACS
	PJRST	TPOPJ##		;AND EXIT

;HERE IF THE UNIT WAS YANKED (DEVUNI=0)
TAKBLU:	SETZB	T2,T3		;INDICATE NO ROOM
	PUSHJ	P,DWNDA		;GIVE UP DA
	PJRST	TPOPJ##		;AND REURN

IFN	FTCCIN!FTDAEM!FTOPRERR,<
;ROUTINE TO CALL SETINT
SETINJ::PUSH	P,J		;SAVE J
	PUSH	P,M		;SETINT CLOBBERS M
	LDB	J,PJOBN##	;JOB NUMBER
	MOVE	R,JBTADR##(J)	;SET R
	SKIPE	J		;SKIP IF SWAP I/O
	PUSHJ	P,SETINT##	;TEST INTERCEPT
	  SOS	-2(P)		;SET FOR NON-SKIP RETURN
	POP	P,M		;RESTORE M
	PJRST	JPOPJ1##	;INTERCEPT SET
>				;END FTCCIN!FTDAEM!FTOPRERR
;SUBROUTINE TO RETURN BLOCKS (DEALLOCATE)
;ENTER WITH T1= DISK ADDRESS  T2= HOW MANY TO DEALLOCATE
GIVBLK::PUSHJ	P,SAVE4##		;SAVE P1-P4
	PUSHJ	P,UPDA		;GET DA RESOURCE
	HLRZ	P1,UNISAB##(U)	;LOC OF FIRST SAT TABLE
	LDB	T4,UNYBPC##	;NUMBER OF BLOCKS PER CLUSTER
	ADDI	T2,-1(T4)	;CONVERT BLOCK COUNT TO CLUSTERS
	IDIV	T2,T4
	MOVNM	T2,P3		;P3=-N FOR UPDATING COUNTS
	IDIV	T1,T4		;CONVERT TO CLUSTER ADDRESS
	PUSHJ	P,FNSAT		;FIND THE SAT  FOR THIS ADDRESS
	MOVEI	T4,SABBIT##(P1)
	ADDI	T4,(T2)		;POSITION IN TABLE
	MOVEI	T1,^D36
	SUBI	T1,(T3)		;POSITION
	MOVN	T3,P3		;COUNT
	PUSHJ	P,CLRBTS##	;CLEAR THE BITS
	  STOPCD	DWNDA,DEBUG,BAZ,	;++BIT ALREADY ZERO
	SETOM	SABHOL##(P1)	;INDICATE SIZE OF LARGEST HOLE IN SAT UNKNOWN
	HRRZ	P2,P1		;SET BUFFER LOC
	PUSHJ	P,FIXCNT	;UPDATE SOME COUNTS
	PJRST	DWNDA		;GIVE UP DA RESOURCE AND RETURN
;SUBROUTINE TO UPDATE SOME COUNTS
;ENTER WITH P3 = HOW MANY CLUSTERS  (PLUS-ALLOCATION, NEG - DEALLOCATION)
; P2=LOC OF SAT BUF.
;RETURNS WITH T1=NUMBER OF BLOCKS
FIXCNT:	MOVN	T1,P3		;-NUMBER OF CLUSTERS
	LDB	T4,UNYBPC##
	IMUL	T1,T4		;-NUMBER OF BLOCKS
	SKIPE	DINITF##	;IF IN ONCE-ONLY
	JRST	FIXCN2		;JUST SET SATCHG BIT
	MOVN	T2,P3		;-NUMBER OF CLUSTERS
	ADDM	T1,UNITAL##(U)	;UPDATE UNIT FREE-TALLY
	MOVE	T3,UNISTR##(U)	;UPDATE STR FREE-TALLY
	JUMPE	T3,FIXCN3	;IF SUPER I/O
	ADDM	T1,STRTAL##(T3)
	MOVE	T3,DEVUFB##(F)	;UPDATE USERS QUOTA
	JUMPE	T3,FIXCN1	;CANT UPDATE QUOTA IF NO UFD
	MOVE	T4,UFBTAL##(T3)
	ADDM	T1,UFBTAL##(T3)
	JUMPLE	T1,FIXCN1	;IF INCREASING UFBTAL,
	JUMPL	T4,FIXCN1	; UFBTAL WAS POSITIVE
	SKIPGE	UFBTAL##(T3)	; AND UFBTAL HAS OVERFLOWED
	HRLOS	UFBTAL##(T3)	; MAKE IT HUGELY POSITIVE AGAIN
FIXCN1:	HRRZ	T3,DEVACC##(F)	;UPDATE HIGHEST BLOCK ALLOCATED
	JUMPE	T3,FIXCN3
	MOVNS	T1
	TLNN	S,IOSALC	;LEAVE ACCALC ALONE IF BIT IS ON
	ADDM	T1,ACCALC##(T3)
FIXCN3:	ADD	T2,SABTAL##(P2)
	TRNE	T2,400000	;COUNT GO NEGATIVE?
	MOVEI	T2,0		;YES, SET TO 0
	HRRM	T2,SABTAL##(P2)
	LDB	T3,SAYNDX##	;AND IN SATSPT TABLE
	ADD	T3,UNISPT##(U)
	DPB	T2,[POINT TALSIZ,(T3),TALPOS]
FIXCN2:	MOVSI	T4,SATCHG	;INDICATE THAT THE SAT
	ORM	T4,SABFIR##(P2)	; BLOCK HAS CHANGED
IFE	FTCCIN!FTDAEM!FTOPRERR,<
SETINJ::
>
	MOVMS	T1
	POPJ	P,		;AND RETURN
;SUBROUTINE TO FIND THE SAT BUFFER ASSOCIATED WITH A GIVEN DISK ADDRESS
;IT MAY WRITE OUT A CURRENT SAT AND READ IN A NEW ONE
;ENTER WITH P1=LOC OF 1ST SAT BUFFER IN RING, T1 = DESIRED CLUSTER ADDRESS
;EXIT WITH T2=RELATIVE LOC IN SAT TABLE WITHIN SAT BLOCK
;  T3=BIT POSITION  P1=BUFFER LOC
;P3,T1 UNCHANGED, P1,P2, P4 CHANGED
FNSAT:	HRRZ	T2,UNICPS##(U)	;NUMBER OF CLUSTERS/SAT TABLE
	MOVE	P2,P1		;USE P2 FOR END TEST

;TRY TO FIND A SAT IN CORE FOR THIS CLUSTER ADDRESS
FNSA1:	LDB	T3,[POINT CLASIZ,SABFIR##(P1),CLAPOS]	;FIRST DISK ADDRESS IN SAT BUFFER
	CAMGE	T1,T3		;IN THIS SAT?
	JRST	FNSA2		;NO
	ADD	T3,T2		;MAYBE, CHECK IF OVER TOP
	CAMGE	T1,T3		;THIS THE ONE?
	JRST	FNSA4		;YES, COMPUTE POSITION
FNSA2:	HLRZ	P1,SABRNG##(P1)	;STEP TO NEXT SAT BUFFER
	CAME	P1,P2		;THROUGH?
	JRST	FNSA1		;NO. TEST IT

;HERE WHEN THE DESIRED SAT IS NOT IN CORE. READ IT FROM DISK
	PUSH	P,T1		;SAVE CLUSTER ADDRESS
	IDIV	T1,T2		;COMPUTE INDEX TO SATPFI TABLE
	MOVE	P4,T1		;STUFF IT IN P4
	PUSHJ	P,NEWSAT	;WRITE THE CURRENT SAT, READ IN NEW
	POP	P,T1		;RESTORE CLUSTER ADDRESS


;HERE WHEN DESIRED SAT IS IN CORE
;T1=CLUSTER ADDRESS,  P1 = LOC OF BUFFER
FNSA4:	LDB	T2,[POINT CLASIZ,SABFIR##(P1),CLAPOS]	;1ST ADDRESS OF SAT
	SUBM	T1,T2		;-DESIRED ADDRESS
	IDIVI	T2,^D36		;COMPUTE WORD COUNT, SHIFT NUMBER
	POPJ	P,
;SUBROUTINE TO ENTER REQUEST IN DISK-ALLOCATION QUEUE
;ALL ACS RESPECTED
;CALL SUPDA IF MIGHT ALREADY HAVE DA

SUPDA::	TLNN	S,IOSDA		;DONT GET DA IF ALREADY HAVE IT
UPDA::	PUSHJ	P,DAWAIT##	;HAVE TO WAIT
	TLO	S,IOSDA		;HAVE DA QUEUE,LIGHT BIT
	PJRST	STRIOS		;SAVE S AND RETURN

;SUBROUTINE TO UNQUEUE DA REQUEST
;ALL ACS RESPECTED

SDWNDA::
IFN FTDSIM,<
;SUBROUTINE TO GIVE UP DA EXCEPT FOR A SIM UPDATE FILE
;ALL ACS RESPECTED
	PUSH	P,T1		;SAVE AN AC
	HRRZ	T1,DEVACC##(F)	;A.T. LOC
	JUMPE	T1,SDWND1	;JUMP IF NO A.T. (SUPER I/O OR THE REFRESHER)
	MOVE	T1,ACCSMU##(T1)
	TRNE	T1,ACPSMU	;SIM UPDATE?
	JRST	TPOPJ##		;YES, DONT GIVE UP DA
SDWND1:	POP	P,T1		;NO, GIVE UP DA RESOURCE
>
DWNDA::	TLZN	S,IOSDA		;CLEAR THE BIT
	STOPCD	CPOPJ,DEBUG,DHD,;++ DON'T HAVE DA
	PUSHJ	P,DAFREE##	;DECREMENT REQUEST COUNT
	PJRST	STRIOS		;STORE S IN DDB AND RETURN
;SUBROUTINE TO GET THE AU RESOURCE
;ALL ACS RESPECTED
UPAU::
	PUSHJ	P,AUWAIT##	;WAIT A WHILE
	TLO	S,IOSAU		;HAVE IT NOW
	PJRST	STOIOS##	;RETURN

;SUBROUTINE TO RELEASE AU RESOURCE
;ALL ACS RESPECTED
DWNAU::	TLZN	S,IOSAU		;CLEAR THE BIT
	STOPCD	CPOPJ,DEBUG,DHA,;++DON'T HAVE AU
	PUSHJ	P,AUFREE##	;DECREMENT COUNT
	PJRST	STOIOS##	;SAVE S AND RETURN
;SUBROUTINE TO FIND A PARTICULAR SAT IN THE
;SAT-BUFFER RING.  IF NOT FOUND, READ IT IN
;ENTER WITH P2 = LOC OF SAT BUFFER, P4 = INDEX OF SAT ADDRESS TABLE TO READ
;P2 ON EXIT CONTAINS THE BUFFER LOC. LH(P2) UNCHANGED.
SATST:	HRRZ	T2,P2		;LOC OF BUFFER (FOR END TEST)
SATS2:	LDB	T3,SAYNDX##	;INDEX OF THIS SAT
	CAIN	T3,(P4)		;RIGHT ONE?
	POPJ	P,		;YES. RETURN
	HLR	P2,SABRNG##(P2)	;NO. STEP TO NEXT
	CAIE	T2,(P2)		;THROUGH?
	JRST	SATS2		;NO. TEST THIS BUFFER
				;YES. READ THE SAT

;SUBROUTINE TO (POSSIBLY) WRITE THIS SAT, READ IN NEW ONE
;ENTER WITH P2 = LOC OF SAT BUFFER, P4 = INDEX OF SAT ADDRESS TABLE TO READ
NEWSAT:	SKIPG	SABFIR##(P2)	;CURRENT SAT BEEN CHANGED?
	PUSHJ	P,SATWRT	;YES. WRITE IT OUT
				;READ NEW SAT
;SUBROUTINE TO READ A NEW SAT BLOCK INTO CORE
;THE SAT DISK ADDRESS IS SPECIFIED BY P4 (INDEX INTO ADDRESS TABLE)
;P2 POINTS TO THE IN-CORE BUFFER
SATRED::DPB	P4,SAYNDX##	;SAVE INDEX TO SATPFI IN MON BUFFER
	MOVE	T4,P4		;INDEX TO SATSPT INTO T4
	PUSHJ	P,SATADR	;SET UP PARAMETERS TO READ
	HRRM	T3,SABTAL##(P2)	;SAVE FREE SPACE FOR THIS SAT
	PUSH	P,DEVUNI##(F)	;SAVE DEVUNI
	MOVEM	U,DEVUNI##(F)	;UNIT WE'RE TALKING TO
	PUSHJ	P,MONRED	;READ THE SAT
	POP	P,DEVUNI##(F)	;RESTORE DEVUNI
	LDB	T4,SAYNDX##	;RESET INDEX
	HRRZ	T1,UNICPS##(U)	;NO. OF CLUSTERS PER SAT
	IMUL	T1,T4		;TIMES INDEX = FIRST ADDR OF SAT
	DPB	T1,[POINT CLASIZ,SABFIR##(P2),CLAPOS]	;SAVE IN BUFFER
	MOVEI	T1,SABBIT##(P2)	;START SCAN AT 1ST LOC OF TABLE
	HRRM	T1,SABSCN##(P2)	;SAVE IN SCAN LOC
	SETOM	SABHOL##(P2)	;DONT KNOW SIZE OF LARGEST HOLE
	SKIPE	DINITF##	;IF IN ONCE-ONLY
	PJRST	SATW2		;DONT CHECK FOR CONSISTENCY
	HLL	T1,SABRNG##(P2)	;STEP TO NEXT SAB IN RING
	HLLM	T1,UNISAB##(U)	;THIS WILL BE NEXT TO BE READ INTO
	MOVE	T1,SABSCN##(P2)	;AOBJN WORD FOR SAT BITS
	SKIPN	T2,T3		;CHECK FOR ERRORS DETECTED IN MONRED
	PUSHJ	P,SATCN		;COUNT 0'S IN SAT
	HRRZ	T1,SABTAL##(P2)	;DONE. # OF 0'S WE EXPECT
	CAIN	T1,(T2)		;RIGHT?
	PJRST	SATW2		;YES. ZERO SATCHG AND RETURN
SATBAD:	MOVSI	T2,UNPSER##	;NO. COUNT NO OF SOFTWARE ERRS
	ADDM	T2,UNIMCT##(U)	; UP BY ONE IN UNIT DB
	MOVNS	T1		;DECREASE UNIT AND STR TALLIES
	LDB	T2,UNYBPC##
	IMULI	T1,(T2)		;CONVERT TO # OF BLOCKS
	ADDM	T1,UNITAL##(U)	;BY THE AMOUNT WE EXPECTED
	HRRZ	T3,UNISTR##(U)
	ADDM	T1,STRTAL##(T3)
	SETZ	T1,		;T1=0
	ADD	T4,UNISPT##(U)	;POINT TO RIGHT SPT WORD
	DPB	T1,[POINT TALSIZ,(T4),TALPOS]	;SET COUNT IN THIS SAT TO 0
	HRRM	T1,SABTAL##(P2)	;SET SABTAL FOR THIS SAT TO 0
	MOVE	T1,SABSCN##(P2)	;SET ALL BITS IN THE SAT TO 1
	SETOM	(T1)
	AOBJN	T1,.-1
	PJRST	SATW2		;ZERO SATCHG (SO SAT WONT BE WRITTEN) AND EXIT
;ROUTINE TO COUNT 0 BITS IN A TABLE
;ARGS: T1=AOBJN POINTER TO TABLE
;VALUES: T2=NO. OF 0-BITS IN TABLE
;RESPECTS T4
SATCN::	SETZ	T2,		;T2 WILL COUNT 0'S FOUND
	PUSH	P,T4
SATR1:	MOVE	T3,(T1)		;COUNT 0-BITS IN 0(T1)
	SETCMB	T4,T3		;ITS EASIER TO COUNT 1'S
	LSH	T4,-1
	AND	T4,[333333,,333333]
	SUB	T3,T4
	LSH	T4,-1
	AND	T4,[333333,,333333]
	SUBB	T3,T4		;EACH OCTAL DIGIT REPLACED BY NUMBER OF 1S IN IT
	LSH	T4,-3
	ADD	T3,T4
	AND	T3,[070707,,070707]
	IDIVI	T3,77		;CASTING OUT 63'S
	ADDI	T2,(T4)		;ACCUMULATE ANSWER IN T2
	AOBJN	T1,SATR1	;COUNT BITS IN NEXT WORD
	PJRST	T4POPJ##	;DONE, ANSWER IN T2
;SUBROUTINE TO WRITE OUT A SAT BLOCK
;THE BUFFER IS SPECIFIED BY P2
SATWRT::TLNE	S,IOSDA		;JOB HAVE DA RESOURCE?
	JRST	SATW1		;YES
	PUSHJ	P,UPDA		;NO, GET IT  (PROBABLY COMING FROM WTUSAT)
	PUSHJ	P,SATW1		;WRITE THE SAT
	PJRST	DWNDA		;GIVE UP DA AND RETURN
SATW1:	LDB	T4,SAYNDX##	;INDEX TO SATPFI
	PUSHJ	P,SATADR	;SET PARAMETERS FOR WRITE
	PUSH	P,DEVUNI##(F)
	MOVEM	U,DEVUNI##(F)	;UNIT WE'RE TALKING TO
	PUSHJ	P,MONWRT	;WRITE THE SAT
	POP	P,DEVUNI##(F)
SATW2:	MOVSI	T1,SATCHG	;ZERO SAT-CHANGED BIT
	ANDCAM	T1,SABFIR##(P2)
	POPJ	P,		;AND RETURN


;SUBROUTINE TO SET UP PARAMETERS FOR A SAT-BLOCK READ OR WRITE
;ENTER WITH T4=INDEX TO SATSPT TABLE P2=BUFFER ADDRESS
;EXIT WITH T1=IOWD FOR THE SAT  T2= DISK ADDRESS T3=NO. OF FREE CLUSTERS
SATADR:	ADD	T4,UNISPT##(U)	;COMPUTE ADDRESS OF SATSPT ENTRY
	LDB	T2,[POINT CLASIZ,(T4),CLAPOS]	;GET DISK ADDRESS
	LDB	T3,UNYBPC##	;NO OF BLOCKS PER CLUSTER
	IMUL	T2,T3		;CONVERT CLUSTER ADR TO BLOCK ADR
	LDB	T3,[POINT TALSIZ,(T4),TALPOS]	;GET FREE COUNT
	MOVEI	T1,SABBIT##-1(P2)	;ADDRESS FOR IOWD
	HLL	T1,SABSCN##(P2)	;LENGTH OF DATA
	POPJ	P,		;RETURN


;SUBROUTINE TO CHECK FOR VIRGIN SAB RING (NEWLY MOUNTED F/S)
;   IF NEW RING (CLUST. ADR.=-1), READ SATS FROM DISK INTO ALL SABS
;CALL WITH P1=1ST SAB ADR
CHKNEW:	LDB	T1,[POINT CLASIZ,SABFIR##(P1),CLAPOS]	;CLUSTER ADDR.
	CAME	T1,[EXP CLAMAX]	;-1?
	POPJ	P,		;NO. OK
	PUSHJ	P,SAVE4##	;YES-THIS IS NEW F/S, NEED TO GET SATS
	SETZM	P4		;P4=1ST INDEX (SABNDX)
	MOVE	P2,P1		;P2=1ST SAB ADDR
CHKNE2:	PUSHJ	P,SATRED	;READ THE SAT
	HLRZ	P2,SABRNG##(P2)	;STEP TO NEXT SAB
	CAIE	P2,(P1)		;BACK WHERE WE STARTED?
	AOJA	P4,CHKNE2	;NO - BUMP INDEX AND READ NEXT SAT
	POPJ	P,		;YES - FINISHED
;SUBROUTINE TO CHECK THE AMOUNT OF SPACE A USER WANTS TO ALLOCATE AGAINST HIS QUOTA
;ALSO CHECKS TO SEE THAT THERE IS SPACE ON THE STR
;ENTER WITH T2= AMOUNT TO ALLOCATE, LH(T2)=-1 IF FROM OUTPUT
;EXIT WITH T2 = AMOUNT ALLOWED (MAY BE 0)
;IF 0 IOBKTL HAS BEEN SET IN S, DEVIOS
CHKQTA::PUSH	P,T2		;SAVE AMOUNT TO ALLOCATE
	TLZ	T2,400000	;MAKE SURE ITS POSITIVE
	MOVE	T4,DEVUFB##(F)	;CHECK AGAINST QUOTA
	MOVE	T3,UNISTR##(U)
	SKIPG	STRTAL##(T3)	;ROOM ON STR?
	JRST	CHKQT1		;NO, COMPLAIN TO USER
IFE FTDQTA,<		;IF NO QUOTAS,
	PJRST	TPOPJ##		;OK RETURN IF STR NOT FULL
>
IFN FTDQTA,<
	MOVE	T1,UFBTAL##(T4)
	TLNN	F,OCLOSB	;DOING A CLOSE?
	CAMG	T2,T1		; OR BELOW QUOTA?
	PJRST	TPOPJ##		;YES. OK
	SUB	T1,STROVR##(T3)	;CHECK MAX OVERDRAW ALLOWED IN STR
	CAML	T2,T1		;TAKE LESSER OF KOYGRP, AMOUNT LEFT
	SKIPLE	T2,T1		;IF 0 OVERDRAW USED UP
	JRST	CHKQT2		;CHECK IF QUOTA JUST GOING NEGATIVE
;HERE IF QUOTA IS NOW EXHAUSTED OR STR IS FULL
CHKQT1:	MOVEI	T4,.ERFUL
	PUSHJ	P,SETINJ	;IS USER INTERCEPTING DISK FULL?
	  SKIPA			;NO, STOP JOB
	JRST	CHKQ1B		;YES, LIGHT AN ERROR BIT AND RETURN
>;END FTDQTA
IFE FTDQTA,<
CHKQT1:
>
	MOVE	T1,JOB##
	MOVE	T1,JBTSTS##(T1)	;DOES JOB WANT TO STOP ON FULL?
	TRNE	T1,JS.SFL
	PUSHJ	P,SAVSTS	;YES, SAVE A RECORD OF RESOURCES JOB OWNS
	  JRST	CHKQ1B		;OWNS MON BUF - LOSE
	SKIPGE	-1(P)		;IF CAME FROM OUTPUT,
	SOS	DEVRET##(F)
	PUSH	P,S
	TRO	S,DR
	PUSHJ	P,DSKFUL	;COMPLAIN TO USER, STOP JOB
	POP	P,S
	MOVEM	S,DEVIOS(F)
	POP	P,T3		;CONTINUED - RECORD OF RESOURCES
	PUSHJ	P,RESSTS	;GET BACK RESOURCES
	POP	P,T2		;RESTORE AMOUNT TO GET
	JUMPGE	T2,CHKQTA
	AOS	DEVRET##(F)
	JRST	CHKQTA		;AND TEST AGAIN


;HERE FOR ERROR RETURN
CHKQ1B:	POP	P,(P)		;REMOVE AMOUNT TO GET FROM LIST
	SETZ	T2,		;INDICATE CANT GET ANY BLOCKS
	PJRST	ERRFUL		;LIGHT BIT AND RETURN
IFN FTDQTA,<
;HERE WHEN QUOTA IS NEGATIVE OR ABOUT TO GO NEGATIVE.
;IF QUOTA IS GT 0 TYPE A WARNING MESSAGE TO USER
CHKQT2:	SKIPGE	UFBTAL##(T4)	;QUOTA ALREADY NEG?
	PJRST	TPOPJ##		;YES. MESSAGE ALREADY TYPED

	HRRM	T2,(P)		;SAVE REDUCED AMOUNT
	MOVEI	T4,.ERQEX	;QUOT1 - EXHAUSTED ERROR
	PUSHJ	P,SETINJ	;SET INTERCEPT IF HE WANTS IT
	  SKIPA			;NO ENABLED - TYPE MESSAGE
	JRST	CHKQT4		;INTERCEPTING - SKIP MESSAGE
	PUSH	P,S		;SAVE ACS WHICH SCNSER WILL USE
	PUSH	P,U
	PUSH	P,J
	PUSH	P,F
	MOVEI	U,0
	PUSHJ	P,TTYFNU##	;FIND TTY FOR CURRENT JOB; SET U,J,F
	JUMPE	U,CHKQT3
	PUSHJ	P,INLMES##
	ASCIZ	/
[EXCEEDING QUOTA ON /
IFN FTSTR,<
	MOVE	T2,(P)		;DDB
	MOVE	T2,DEVACC##(T2)	;AT
	LDB	T1,ACYFSN##	;FSN
	MOVE	T1,TABSTR##(T1)	;STR DATA BLOCK LOC
>	;END CONDITIONAL ON FTSTR
IFE FTSTR,<
	MOVE	T1,TABSTR##	;ADDT OF STR DATE BLOCK
>
	MOVE	T2,STRNAM##(T1)	;STR NAME
	PUSHJ	P,PRNAME##	;TYPE IT
	PUSHJ	P,INLMES##
	ASCIZ	/]
/
	PUSHJ	P,TTYSTR##	;START TTY TYPING (LEAVE JOB RUNNING, IN USER MODE)
CHKQT3:	POP	P,F		;RESTORE ACS
	POP	P,J
	POP	P,U
	POP	P,S
CHKQT4:	HRRZ	T2,(P)
	PJRST	TPOPJ##		;RETURN TO CALLER
>	;END CONDITIONAL ON FTDQTA
;SUBROUTINE TO STOP JOB ON DISK FULL OR QUOTA EXHAUSTED
;TYPES "?DISK FULL OR QUOTA EXHAUSTED FOR XXX "
;CONTINUE RETRIES
DSKFUL::MOVEM	S,DEVIOS(F)	;SAVE S FROM SCNSER
	PUSH	P,F		;SAVE F
	PUSHJ	P,TTYFUW##	;FIND USERS TTY
	PUSHJ	P,TSETBI##	;CLEAR TYPE-AHEAD
	PUSHJ	P,PRQM##	;"?"
	PUSHJ	P,INLMES##	;AND THE MESSAGE....
IFN	FTDQTA,<
	ASCIZ	/QUOTA OR STORAGE EXHAUSTED ON /
>
IFE FTDQTA,<
	ASCIZ	/STORAGE EXHAUSTED ON /
>
	MOVE	T1,(P)		;GET F
	MOVE	T1,DEVUNI##(T1) ;UNIT
	MOVE	T1,UNISTR##(T1) ;STR NAME
	MOVE	T2,STRNAM##(T1) ;TELL USER THE NAME
	PUSHJ	P,PRNAME##	;START TYPING
	PUSHJ	P,HOLD0##
	POP	P,F		;RESTORE F
	MOVE	S,DEVIOS(F)	;RESTORE S
	PJRST	WSCHED##	;AND RESCHEDULE
;SUBROUTINE TO SEE IF THE CURRENT POINTER CAN BE UPDATED
;ENTER WITH T2=NUMBER OF BLOCKS DESIRED
;DEVRET(F) POINTING TO CURRENT RETRIEVAL POINTER
;EXIT WITH T2= LESSER OF ORIGINAL VALUE, AMOUNT LEFT IN CURRENT POINTER
;T1= ORIGINAL T2   IF T2=0 THE CURRENT POINTER IS FULL
CHKADD::
IFN FTDSIM,<
	MOVE	T1,DEVACC##(F)	;IF A SIM UPDATE FILE WITH MORE THAN 1 WRITER,
	LDB	T3,ACYWCT##	; IF A PNTR IS ADDED TO AND THE ADDING DDB CLOSES FIRST,
	SOJG	T3,CHKAD0	; THE NEW PNTR WILL BE OVERWRITTEN, SO
				; DONT ALLOW ADDING TO PNTRS IF GTR THAN 1 WRITER
>
	MOVEM	T2,T1		;DESIRED AMOUNT
	MOVE	T2,@DEVRET##(F)	;CURRENT POINTER
	MOVE	T4,UNISTR##(U)	;STR ADR
	LDB	T3,STYCNP##(T4)	;NO OF CLUSTERS IN CURRENT POINTER
	LDB	T2,STYCLP##(T4)	;ADR OF 1ST CLUSTER
	ADD	T2,T3		;HIGHEST ADR(+1) IN PNTR
	HRRZ	T3,UNICPS##(U)	;NO OF CLUSTERS IN A SAT
	IDIV	T2,T3		;LAST CLUSTER IN PNTR=LAST IN SAT?
	JUMPE	T3,CHKAD1	;YES, CANT ADD TO CURRENT PNTR
				;(ELSE PROBLEMS IN DELETING BLOCKS)
	SETO	T2,		;NO
	MOVE	T3,UNISTR##(U)	;STRUCTURE DB LOC
	LDB	T4,STYCNP##(T3)	;LARGEST LLOWED FIELD
	MOVE	T2,@DEVRET##(F)	;CURRENT POINTER
	LDB	T2,STYCNP##(T3)	;CURRENT GROUP SIZE
	SUBM	T4,T2		;MAX ADDITIONAL CLUSTERS
	CAMLE	T2,T1		;SKIP IF REQUESTED TOO MUCH
	MOVE	T2,T1		;CAN HAVE ALL WE REQUESTED
	POPJ	P,		;RETURN

;HERE IF BLOCK IS FIRST IN NEW SAT
IFN FTDSIM,<
CHKAD0:	MOVE	T1,T2		;ORIGINAL T2 INTO T1
>
CHKAD1:	SETZ	T2,		;RETURN CANT ADD
	POPJ	P,
IFN FTDHIA&FTATTACH,<
ATTCOD==0
DETCOD==1
XCHCOD==2
;HERE TO SWITCH TWO UNITS BUT LEAVE THE DATA BASE ALONE -
;EG TO PUT DSKB2 ON DPA5 WHEN DPA2 GOES DOWN, LEAVE SATS, ETC THE SAME
XCHDSK::PUSHJ	P,SAVE3##	;SAVE SOME ACS
	PUSHJ	P,COMUNI	;SET UP U FOR FIRST UNIT
	  POPJ	P,		;NO UNIT OR LOGICAL-UNIT MATCH
	MOVE	P1,U		;SAVE FIRST UNIT
	LDB	P3,UNYPUN##	;GET ITS NUMBER
	POP	P,U		;RESTORE U FOR COMCON
	PUSHJ	P,COMUNI	;GET SECOND UNIT
	  POPJ	P,		;NONE OR LOGICAL MATCH
	LDB	P2,UNYPUN##	;NUMBER OF SECOND UNIT
	HRRZ	T1,UNIKON##(U)	;KONTROLLER
	HRRZ	T2,UNIKON##(P1)
	CAIE	T1,(T2)		;UNITS ON SAME KONTROLLER?
	JRST	UPOPJ##		;NO, CANT EXCHANGE THEM
	MOVE	T1,UNIBPU##(U)	;IF UNIBPU DOESNT MATCH,
	CAME	T1,UNIBPU##(P1)
	JRST	UPOPJ##		; THEN DIFFERENT TYPE UNITS, CANT EXCHANGE THEM
	DSKOFF		;CANT ALLOW DISK INTERRUPTS WHILE FIDDLING
	SKIPE	T1,UNISTS##(U)	;UNIT IDLE
	CAIL	T1,OWCOD##	; OR IN SOME OPR WAIT STATE?
	SKIPA	T1,UNISTS##(P1)	;YES, 1ST UNIT IS OK
	JRST	XCHUN2		;NO, CANT EXCHANGE
	CAIGE	T1,OWCOD##	;2ND UNIT IDLE OR IN OPR WAIT?
	JUMPN	T1,XCHUN2	;CANT EXCHANGE IF NOT
	MOVEI	T1,O2COD##	;IF UNITS ARENT IN OPR WAK
	SKIPN	UNISTS##(U)	; PUT THEM THERE, ELSE A
	MOVEM	T1,UNISTS##(U)	; WRONG PACK COULD BE WRITTEN
	SKIPN	UNISTS##(P1)
	MOVEM	T1,UNISTS##(P1)
	DPB	P3,UNYPUN##	;OK - EXCHANGE THE UNITS
	MOVEM	U,@KONPTR##(T2)	;MAKE INTERRUPTS FOR 1 UNIT POINT
	EXCH	P1,U		; AT THE OTHER UDB,
	DPB	P2,UNYPUN##	;MAKE THE UDB POINT AT DIFFERENT 
	EXCH	P2,P3		; PHYSICAL UNITS,
	MOVEM	U,@KONPTR##(T2)
	MOVE	T1,UNINAM##(U)	;CHANGE PHYSICAL NAMES IN THE UDBS
	EXCH	T1,UNINAM##(P1)
	MOVEM	T1,UNINAM##(U)
IFN FTDUAL,<
	MOVE	T1,UNI2ND##(U)	;GET ALTERNATE UNITS
	MOVE	T2,UNI2ND##(P1)
	MOVEM	T2,UNI2ND##(U)	;EXCHANGE THEM
	MOVEM	T1,UNI2ND##(P1)
	HRRM	U,UNI2ND##(T2)	;EXCHANGE THE BACKWARDS POINTERS
	HRRM	P1,UNI2ND##(T1)
>

;STILL IN FTDHIA&FTATTACH CONDITIONAL
	MOVEI	T1,UNIHCT##(U)	;MAKE THE ERROR STATE
	MOVEI	T2,UNIHCT##(P1)	; STAY WITH THE DRIVE
	HRLI	T1,-3		; EXCHANGE UNIHCT,SCT,MCT
XCHUN1:	MOVE	T3,(T1)
	EXCH	T3,(T2)
	MOVEM	T3,(T1)
	ADDI	T2,1
	AOBJN	T1,XCHUN1
	AOS	-1(P)		;INDICATE WE WON
XCHUN2:	DSKON			;ALLOW DISK INTERRUPTS AGAIN
	MOVE	F,P1		;GET 1ST UDB INTO F
	HRLZI	T1,XCHCOD	;CODE FOR XCH
	PUSHJ	P,DSKCSC	;CALL DAEMON
	JRST	UPOPJ##		;RESTORE U AND RETURN TO COMCON

;HERE TO TAKE A UNIT OFF-LINE
;RETURNS CPOPJ IF NOT A DSK
;RETURNS CPOPJ1 IF CANT DETACH
;RETURNS CPOPJ2 IF OK
DETDSK::PUSHJ	P,COMUNT	;SET UP U (COMCON ALREADY HAS U PUSHED)
	  POPJ	P,		;NO UNIT OR LOGICAL MATCH
IFN FTDUAL,<
	JUMPL	U,DETDS2	;EASY IF AN ALTERNATE UNIT
>
	HRRZ	T1,UNISTR##(U)	;UNIT IN A STR?
	JUMPN	T1,UPOPJ1##	;CANT DETACH IF IT IS
IFN FTDUAL,<
	SKIPG	T1,UNI2ND##(U)	;DOES THIS HAVE AN ALTERNATE PATH?
	JRST	DETDS2		;NO
	HRROS	UNI2ND##(U)
	HRRZS	UNI2ND##(T1)	;YES, SWITCH MAIN AND ALTERNATE UNITS
	MOVE	T2,UNISYS##(U)
	HLLM	T2,UNISYS##(T1)	;UNLINK MAIN UNIT, INSERT ALTERNATE
	HRLM	T1,UNISYS##(T4)
	MOVEI	T2,O2COD##
	MOVEM	T2,UNISTS##(U)	;SET SO THIS UNIT WONT BE USED
DETDS2:>
	MOVEI	T1,UNVDWN##	;OK, INDICATE UNIT IS DOWN
	DPB	T1,UNYUST##
	HRLZI	T1,DETCOD	;CODE TO SAY DETACH
	PUSHJ	P,DSKCSC	;CALL DAEMON
	POP	P,U
	PJRST	CPOPJ2##	;AND RETURN

;STILL IN FTDHIA&FTATTACH CONDITIONAL
;HERE TO ATTACH A UNIT
;RETURNS NON-SKIP IF UNIT IS DOWN
;CPOPJ1 IF WE CANT CALL CPY ROUTINE NOW (TRY LATER)
;CPOPJ2 IF ALL IS OK
ATTDSK::PUSHJ	P,COMUNT	;SET UP U
	  JRST	[TLO U,400000	;NO MATCH
		 POPJ P,]
	LDB	T1,UNYUST##	;GET UNIT STATUS
IFN	FTDUAL,<
	SKIPLE	UNI2ND##(U)	;IF IT HAS A 2ND PORT
	JUMPE	T1,ATTUN0	; 0 IS OK (JUST DETACHED 1ST PORT)
>
	CAIE	T1,UNVDWN##	;DOWN?
	JRST	UPOPJ##		;NO, CANT ATTACH IT
ATTUN0:	PUSH	P,J		;YES, SAVE J FOR COMCON
	MOVE	J,UNIKON##(U)	;KONTROLLER DATA BLOCK
	MOVSI	T1,KOPDWN##	;CLEAR KONTROL-IS-DOWN BIT
	ANDCAM	T1,KONDWN##(J)
	SKIPGE	KONCPY##(J)	;CAN WE CALL CPY ROUTINE IF KONTROL BUSY?
	SKIPL	KONTAB##(J)	;NO, IS KONTROLLER BUSY?
	TLZA	J,-1		;WE CAN TELL UNIT TYPE NOW
	JRST	ATTUN4		;CANT GET KONTROL - TRY LATER
	PUSHJ	P,@KONCPY##(J)	;DETERMINE UNIT TYPE, CAPACITY
	  JRST	ATTUN5		;UNIT DOWN
	MOVEM	T1,UNIBPU##(U)	;SAVE BLOCKS PER UNIT
	MOVEM	T2,UNIBPM##(U)	;SAVE BLKS PER UNIT INCL. MAINT CYLS
IFN FTRP04,<
	MOVEM	T3,UNIBUC##(U)	;SAVE BLOCKS PER UNIT IN 10/11 COMPAT. MODE
>
	DPB	W,UNYBPY##	;SAVE # BLOCKS PER CYLINDER
	HLRZ	T3,W		;NO OF BLOCKS PER TRACK
	DPB	T3,UNYBPT##	;BLOCKS/TRACK
	DPB	T4,UNYUTP##	;UNIT TYPE
IFN FT22BIT,<
	MOVSI	T1,CP.22B##
	HRRZ	T3,UNICHN##(U)
	TLNE	T4,KOP22B##
	JRST	ATTUN1		;22-BIT CHAN?
	MOVSI	T2,1		;NO, IF MORE THAN 256K,
	CAMGE	T2,MEMSIZ##
	JRST	ATTUN5		; CAN'T USE THE UNIT
	ANDCAM	T1,CHB22B##(T3)	;OK - CLEAR THE BIT IN CDB
	JRST	ATTUN2
ATTUN1:	IORM	T1,CHB22B##(T3)	;22-BIT CHAN - SET THE BIT
>

;STILL IN FTDHIA&FTATTACH CONDITIONAL
ATTUN2:	AOS	-2(P)		;CPOPJ2 IS GOODNESS
	MOVEI	T1,UNVNPM##	;INDICATE NO PACK MOUNTED
	DPB	T1,UNYUST##	; (EG, UNIT IS UP)
	MOVSI	T1,UNPOFL##
	ANDCAM	T1,UNIUST##(U)
	SETZM	UNISTS##(U)
IFN FTDUAL,<
	JUMPGE	U,ATTUN3	;GO IF NOT AN ALTERNATE PATH
	SETZM	UNISTS##(U)	;ALTERNATE - MARK IT USABLE
	JRST	ATTUN4		;CALL DAEMON AND EXIT
ATTUN3:	HLRZ	T1,J		;GET SERIAL NUMBER
	HLL	T1,UNISER##(U)	;DRIVE TYPE
	PUSHJ	P,MATUN		;SEARCH FOR A MATCH
	  JRST	ATTUN4		;NO MATCH
	HRROM	T2,UNI2ND##(U)	;MATCH, MAKE THIS AN ALTERNATE
	HRRZM	U,UNI2ND##(T2)	;POINT MAIN UNIT AT THIS ONE
	MOVE	T1,UNISTR##(T2)
	HRRM	T1,UNISTR##(U)
	PUSHJ	P,UNLUN		;UNLINK UNIT FROM SYSUNI CHAIN
>
ATTUN4:	POP	P,J		;RESTORE J
	HRLZI	T1,ATTCOD	;CODE TO SAY ATTACH
	PUSHJ	P,DSKCSC	;CALL DAEMON
	JRST	UPOPJ1##	;RESTORE U AND RETURN

ATTUN5:	MOVEI	T1,UNVDWN##	;UNIT IS STILL DOWN - SET THE BYTE
	DPB	T1,UNYUST##	;IN UNISTS AGAIN
	SOS	-2(P)		;NON-SKIP RETURN
IFN FTDUAL,<
	JRST	ATTUN3
>
IFE FTDUAL,<
	JRST	ATTUN4
>
;ROUTINE TO SET UP U FOR THE COMMAND
;RETURNS WITH ORIGINAL U ON PD LIST
COMUNI:	PUSHJ	P,SETLGL##	;PRIV'D JOB?
	  POPJ	P,		;NO, ERROR RETURN
	PUSHJ	P,CTXDEV##	;YES, GET DEVICE TYPED BY USER
	MOVE	T1,T2		;DEVICE INTO T1 FOR DEVSRC
COMUNT:	EXCH	U,(P)		;SAVE U ON LIST
	PUSH	P,U
	SETO	T2,		;NEED A COMPLETE MATCH ON UNIT NAME
	PUSHJ	P,SRUNI##	;FIND MATCHING UNIT
	  JFCL			;NO SUCH UNIT
	  CAIA			;LOGICAL MATCH
	JRST	CPOPJ1##	;PHYSICAL MATCH - GOOD RETURN
IFN FTDUAL,<
	HLRZ	U,SYSUNI##	;START AT 1ST UNIT
COMUN2:	SKIPLE	T2,UNI2ND##(U)	;IS THERE AN ALTERNATE TO UNIT?
	CAME	T1,UNINAM##(T2);YES, IS IT OUR UNIT?
	JRST	COMUN3		;NO
	HRROI	U,(T2)		;YES, SET LH(U)=-1 AS A FLAG
	JRST	CPOPJ1##	;AND TAKE FOUND-RETURN
COMUN3:	HLRZ	U,UNISYS##(U)	;NO MATCH, TRY NEXT
	JUMPN	U,COMUN2
>
	MOVE	U,(P)		;RESTORE U
	EXCH	U,-1(P)
	JRST	T2POPJ##		;TAKE GARBAGE OFF STACK AND BADNESS-RETURN
;ROUTINE TO PICK UP OPERATOR COMMENTS AND CALL
;DAEMON FOR DISK CONFIGURATION STATUS CHANGE
;CALL WITH CODE FOR ATT, DET, XCH IN T1, F + U SETUP
;CALLED WITH U FOR COMCON ON -1 OF STACK
DSKCSC:	EXCH	U,-1(P)		;GET LDB FOR COMCON
	PUSH	P,T1
	PUSHJ	P,SKIPS1##	;SKIP OVER TO OPR COMMENTS
	  TDZA	T2,T2		;NONE THERE
	PUSHJ	P,CTEXT##	;PICK THEM UP
	POP	P,T1
	EXCH	U,-1(P)		;GET BACK UDB
	ROT	T2,^D12		;GET FIRST TWO CHARS OR RIGHT
	DPB	T2,[POINT 12,T1,11]  ;AND PUT THEM INTO T1
	HRRI	T1,.ERCSC	;GET CODE FOR DAEMON
	PUSH	P,J		;SAVE J
	PUSHJ	P,DAEEIM##	;WAKE DAEMON
	JRST	JPOPJ##		;RETURN
>	;END FTDHIA&FTATTACH


IFN FTDUAL,<
;ROUTINE TO TEST IF MATCHING SERIAL-NUMBERS (EG DUAL-PORTED DRIVES) EXIST
;CALL WITH T1= DRIVE SERIAL NUNBER, U=UNIT
;RETURNS CPOPJ IF NO MATCH
;RETURNS CPOPJ1 IF A MATCH, T2= MATCHING UNIT
;PRESERVES T4
MATUN::	JUMPE	T1,CPOPJ##	;NO MATCH IF NO SERIAL NUMBER
	HLRZ	T2,SYSUNI##	;START AT FIRST UNIT IN SYSTEM
MATUN1:	CAMN	T1,UNISER##(T2) ;MATCH?
	CAIN	T2,(U)		;YES, FOR A DIFFERENT UNIT?
	JRST	MATUN2		;NO MATCH
	MOVE	T3,UNIPUN##(U)	;MAKE SURE DRIVE NUMBERS MATCH
	XOR	T3,UNIPUN##(T2)
	TRNN	T3,7
	JRST	CPOPJ1##	;MATCH-SKIP RETURN
MATUN2:	HLRZ	T2,UNISYS##(T2) ;STEP TO NEXT UNIT
	JUMPN	T2,MATUN1	;AND TEST IT
	POPJ	P,		;NO MATCH, NON-SKIP

;SUBROUTINE TO UNLINK A UNIT FROM UNISYS CHAIN
;ENTER WITH U=UNIT
;PRESERVES T4
UNLUN::	MOVEI	T1,DIFUSY##	;PRESET PREDECESSOR
UNLUN1:	HLRZ	T2,UNISYS##(T1) ;START AT 1ST UNIT
	JUMPE	T2,CPOPJ##	;SYSTEM ERROR?
	CAIN	T2,(U)		;MATCH?
	JRST	UNLUN2		;YES, UNLINK IT
	MOVE	T1,T2		;NO, RESET PREDECESSOR
	JRST	UNLUN1		;AND TRY NEXT UNIT
UNLUN2:	MOVE	T3,UNISYS##(U)	;GET THIS UNIT'S LINK
	HLLM	T3,UNISYS##(T1) ;AND SAVE IT IN PREDECESSOR'S
	POPJ	P,		;AND RETURN
>	;END FTDUAL
	SUBTTL	USETI/USETO
USETI0::
	HRRE	W,M		;BLOCK NUMBER
	CAME	W,[-1]		;BLOCK -1 MEANS END OF FILE, IS IT?
	CAML	W,MUSTMX##	;TRYING TO READ AN EXTENDED RIB?
	CAIA			;YES
	TLZ	W,-1		;NO, CLEAR HIGH BITS FOR COMPATIBILITY.
FUSETI::
IFN FTSPL,<
	SKIPGE	DEVSPL(F)	;IF THIS IS A SPOOLED DDB,
	POPJ	P,		;USETI IS A NO-OP
>
	PUSHJ	P,WAIT1##	;MAKE SURE ALL I/O IS DONE
	TLNN	F,LOOKB		;LOOKUP DONE?
	JRST	SETSUP		;NO. SUPER USETI IF PRIVILEGED
	HRRZ	U,DEVUNI(F)
	JUMPE	U,CPOPJ##	;GO AWAY IF UNIT WAS REMOVED
	PUSHJ	P,CLSNAM##	;SET RIGHT NAME IN DDB FOR RIBCHK
				; (FILE MIGHT BE RENAMED)
	HRRZ	T1,DEVACC##(F)	;YES. LOC OF ACCESS TABLE
	PUSHJ	P,SAVE1##	;SAVE P1
IFN FTDMRB,<			;IF MULTIPLE RIBS
	MOVE	P1,W		;GET USETI ARGUMENT TO P1
	CAMGE	P1,MUSTMX##	;SKIP IF RH(M) POSSIBLE EXTEDNED RIB
	JRST	USETI2		;NOT LOOKING FOR EXTENDED RIBS
	AOJGE	P1,USETI2	;IF -1 OR POSITIVE, NOT EXTENDED
	HRRZ	U,DEVUNI##(F)	;GET CURRENT UNIT
	PUSHJ	P,PTRTST	;READ POINTERS, RE-WRITE IF CHANGED
	  PJRST	GVMNB0		;ERROR READING RIB
	SKIPL	DEVRIB##(F)	;PRIME RIB?
	JRST	USETI1		;YES, GET EXTENDED
	PUSHJ	P,REDRIB	;NO, READ PRIME RIB
	  PJRST	GVMNB0		;ERROR READING RIB
USETI1:	PUSHJ	P,PTRNXT	;GET EXTENDED RIB
	  JRST	USET1B		;EITHER RIB ERROR OR NONE
	AOJN	P1,USETI1	;JUMP BACK IF NOT THIS RIB
	HRRM	T2,DEVUNI(F)	;(NEW) UNIT
	PUSHJ	P,PTRBLT	;GET POINTERS TO DDB
	MOVE	T1,DEVMBF##(F)	;IOWD TO MONITOR BUFFER
	MOVE	T2,RIBFLR##+1(T1)	;GET RELATIVE BLOCK NUMBER OF RIB
	MOVE	T3,T2		;ALSO GET TOT3 FOR SCNPTR
	MOVEI	T1,DEVRB1##(F)	;ADDRESS OF IN CORE POINTERS
	HRLI	T1,MPTRLN##	;MAKE AOBJN WORD
	PUSHJ	P,SCNPTR	;SET UP DEVBLK, DEVREL, DEVLFT
	  PJRST	GVMNB0		;THIS IS SO "OUTTA SIGHT" IT SHOULD HALT
	MOVNS	DEVREL##(F)	;FLAG SO OUTPUT NEXT IS ILLEGAL
	PUSHJ	P,GVMNB0	;GIVE UP MONITOR BUFFER
	TLZ	S,IOSFIR	;RIBS AREN'T CHECKSUMMED
	JRST	USETI4		;AND EXIT.
USET1B:	SKIPN	T3		;ERROR READING RIB?
	TRO	S,IOBKTL	;NO, NON-EXISTANT, SET IOBKTL
	PJRST	GVMNB0		;GIVE UP MONITOR BUFFER AND EXIT
>				;END CONDITIONAL ON FTDMRB
USETI2:	MOVE	P1,ACCWRT##(T1)	;HIGHEST RELATIVE BLOCK WITH DATA IN THE FILE
	JUMPL	W,USETI5	;OK UNLESS USETI -1
	CAML	P1,W		;ASKING FOR A BLOCK PAST EOF?
	TLOA	P1,400000	;NO, SET P1 NEGATIVE AS A SWITCH
USETI5:	MOVE	W,P1		;YES, INITIALLY SCAN FOR LAST BLOCK
	PUSHJ	P,USET00	;FIND THE PNTR TO THE BLOCK
	  POPJ	P,		;RIB ERROR
	SKIPL	DEVBLK##(F)
	JRST	USETI3
	MOVEM	W,DEVREL##(F)
	PUSHJ	P,FNDPTR
	  POPJ	P,
USETI3:	JUMPL	P1,USETI4	;GO IF NOT PAST EOF
	AOS	DEVBLK##(F)	;PAST EOF - UPDATE PNTRS IN DDB
	AOS	DEVREL##(F)	;SO NEXT INPUT/OUTPUT WILL GET LAST BLOCK
	SOS	DEVLFT##(F)	; OF FILE PLUS 1
	TLZ	S,IOSFIR
	TDOA	S,[XWD IOEND,IODEND]	;INDICATE USETI PAST EOF
USETI4:	TDZ	S,[XWD IOEND,IODEND]	;OK - ZERO EOF BITS
IFN FTDMRB,<
	PUSHJ	P,EXTCKS	;LIGHT IOSFIR IF 1ST BLOCK IN EXT. RIB
>
	PJRST	STOIOS##	;STORE S AND TAKE GOOD RETURN
USETO0::
	HRRE	W,M		;BLOCK NUMBER
	CAME	W,[-1]		;IF NOT USETO TO LAST BLOCK OF THE FILE
	TLZ	W,-1		; CLEAR HIGH ORDER BITS
FUSETO::
IFN FTSPL,<
	SKIPGE	DEVSPL(F)	;IF THIS IS A SPOOLED DDB,
	POPJ	P,		; USETO IS A NOOP
>
	PUSHJ	P,WAIT1##	;WAIT FOR I/O TO FINISH
	TLNN	F,ENTRB		;ENTER BEEN DONE?
	JRST	SETSUP		;NO. (FIRST) SUPER USETO IF LEGAL
	MOVE	T1,W		;YES, ARGUMENT
	AOJN	T1,USETO7	;USETO -1 MEANS LAST BLOCK XFERRED
	MOVE	T1,DEVREL##(F)	;IS THERE A LAST BLOCK?
	SOJG	T1,USET0A	;YES, DO A USETO TO IT
	MOVE	T1,DEVACC##(F)	;NO, UPDATE FILE?
	HRLZ	T1,ACCSTS##(T1)
	TLNE	T1,ACPUPD
	HLRZ	T1,DEVLRL##(F)	;YES. GET DEVREL BEFORE ENTER
USET0A:	MOVE	W,T1		;DO USETO TO THAT BLOCK
USETO7:
IFN FTDMRB,<
	MOVE	T1,DEVACC##(F)	;LOC OF A.T
	MOVE	T1,ACCWRT##(T1)	;HIGHEST WRITTEN BLOCK
	CAML	T1,W		;TRY TO SETO PAST HIGHEST.
	JRST	USETO6		;NO, OK
	PUSH	P,W		;YES. FIRST FIND HIGHEST
	MOVE	W,T1		;SO THAT LAST RIB WILL BE
	PUSHJ	P,USET00	;READ AND DEYRBC SET RIGHT
	  PJRST	TPOPJ##		;RIB ERROR
	POP	P,W		;RESTORE W
>
	PUSHJ	P,GETALC	;GET ADJUSTED ACCALC
	CAMG	T1,W		;WANT ONE BELOW HIGHEST?
	JRST	USETO1		;NO. HAVE TO ALLOCATE
USETO6:	PUSHJ	P,USET00	;YES. SET UP CORRECT POINTERS
	  POPJ	P,		;RIB ERROR
IFN FTDMRB,<			;IF MULTIPLE RIBS
	AOSE	DEVBLK(F)	;IF DEVBLK=-1, CAN'T FIND BLOCK
	JRST	USETO8		;USETO TO ALLOCATED BLOCKS
IFN FTDSIM,<
	PUSHJ	P,GETALC	;SINCE ANOTHER JOB MAY HAVE ALLOCATED,
	CAMG	T1,W		; AND ACCWRT ISN'T YET TO ITS FINAL VALUE,
	JRST	USETO1		; WE MUST REPEAT THE TEST (DEVRIB NOW POINTS TO LAST RIB)
>

;HERE IF DOING A USETO TO LAST BLOCK IN RIB
	PUSHJ	P,USETO9	;ZERO ALLOCATED, UNWRITTEN BLOCKS
	SUBI	W,1		;POINT W TO LAST "REAL" BLOCK
	PUSHJ	P,USET00	;GET POINTERS INTO CORE (SHOULD ALSO GET HIGHEST)
	 POPJ	P,		;RIB ERROR
	MOVE	T3,DEVLPC##(F)	;GET LAST POINTER IN CORE FLAG
	TLZN	T3,DEPLPC##	;CLEAR IT TO FOOL SCNPTR
	STOPCD	CPOPJ##,DEBUG,DBZ,	;++DEPLPC BIT ZERO
	MOVEM	T3,DEVLPC##(F)	;RETURN TO DDB
	MOVE	T3,W		;GET LAST BLOCK ALLOCATED
	ADDI	T3,1
	MOVE	T2,DEVFLR##(F)	;INITIAL BLOCK IN DDB
	MOVEI	T1,DEVRB1##(F)	;SCAN POINTERS IN DDB
	HRLI	T1,MPTRLN##	;STARTING AT DEVRB1
	PUSHJ	P,SCNPTR	;FIND THE POINTER
	STOPCD	.+1,DEBUG,HIF,	;++HOLE IN FILE
	HRRZM	T1,DEVRET##(F)	;SET DEVRET TO LAST POINTER
	HRROS	DEVRSU##(F)	;SET DEVRSU TO -1
	HLLZS	DEVLFT##(F)	;CLEAR DEVLFT SO NXTBLK WILL NOT FIND
	HRLZI	T1,DEPLPC##	;LIGHT DEPLPC AGAIN
	IORM	T1,DEVLPC##(F)	;IN DDB
	POPJ	P,		;AND EXIT
USETO8:	SOS	DEVBLK##(F)	;RETURN DEVBLK TO PROPER VALUE
	PUSHJ	P,EXTCKS	;SET IOSFIR IF FIRST BLOCK IN EXTENDED RIB
>	;END FTDMRB CONDITIONAL
	MOVEM	S,DEVIOS(F)	;SAVE S
;CHECK TO SEE IF A USETO IS SETTING OUTOUT PAST THE LAST BLOCK WRITTEN
; IF SO, WRITE 0'S IN THE INTERVENING BLOCKS
USETO9:	SKIPN	T1,W		;SETTING BLOCK 0?
	POPJ	P,		;YES. THIS IS NON-ALLOCATING
	SUBI	T1,1		;LAST BLOCK TO ZERO
	HRRZ	T2,DEVACC##(F)	;LOC OF A.T.
	CAMG	T1,ACCWRT##(T2)	;PAST HIGHEST BLOCK WRITTEN?
	POPJ	P,		;NO, OK
	PUSHJ	P,SAVE2##	;YES, SAVE SOME ACS
	HRRZ	P1,DEVACC##(F)	;LOC OF A.T.
	MOVE	P2,T1		;HIGHEST BLOCK TO ZERO
USETZ1:	MOVE	T1,ACCWRT##(P1)	;BLOCK-1 TO ZERO
	AOS	W,T1		;BLOCK WE WANT TO ZERO
	PUSHJ	P,USET00	;SET DEVBLK FOR THIS BLOCK
	  POPJ	P,		;RIB ERROR
	SKIPGE	DEVBLK##(F)	;SEMI-GOOD RETURN!
	POPJ	P,		;SHOUD NEVER HAPPEN, BUT....
	PUSHJ	P,GTMNBF	;GET MON BUF
	MOVSI	T2,1(T1)	;1ST WORD IN MON BUF
	HRRI	T2,2(T1)	;SET TO ZERO MON BUF
	SETZM	1(T1)
	BLT	T2,BLKSIZ##(T1)	;ZERO THE BUFFER
	MOVE	T2,DEVBLK##(F)	;BLOCK TO WRITE
IFN FTDSIM,<
	CAMG	W,ACCWRT##(P1)	;SOMEBODY JUST WRITE THE BLOCK?
	JRST	[PUSHJ P,GVMNB0	;YES, WE'RE DONE
		 JRST  USETZ2]
	MOVSI	T3,DEPUWZ##	;INDICATE USETO WRITING ZEROES
	IORM	T3,DEVUWZ##(F)	; IN CASE ANOTHER DDB IS CURRENTLY
				; WRITING THIS BLOCK, ACCWRT NOT YET  UPDATED
>
	PUSHJ	P,MONWRT	;WRITE A BLOCK OF 0'S
IFN FTDSIM,<
	MOVSI	T3,DEPUWZ##	;USETO NOT NOW WRITING ZEROES
	ANDCAM	T3,DEVUWZ##(F)
	MOVE	T1,DEVREL##(F)	;IF THIS WRITE DIDN'T HAPPEN (REAL WRITER SNUCK IN)
	CAMLE	T1,ACCWRT##(P1)	; THEN DON'T CHANGE ACCWRT
>
	AOS	ACCWRT##(P1)	;BUMP NUMBER OF BLOCKS WRITTEN
	PUSHJ	P,GVMNB0	;RETURN MON BUF (DONT WANT TO MONOPOLIZE IT)
	LDB	J,PJOBN##	;JOB NUMBER
	MOVE	T1,JBTSTS##(J)	;JBTSTS
	TLNN	T1,CNTRLC	;JOB TYPED ^C?
	JRST	USETZ2		;NO
	PUSH	P,F
	PUSHJ	P,STOP1##	;YES, RETURN TO MONITOR MODE
	POP	P,F		;RESTORE ACS WIPED BY STOP,
	MOVE	S,DEVIOS(F)
	PUSHJ	P,WSCHED##	;STOP JOB
				;CONTINUE TYPED
				;FALL INTO USETZ2
USETZ2:	CAMLE	P2,ACCWRT##(P1)	;HAVE WE FINISHED YET?
	JRST	USETZ1		;NO, WRITE NEXT BLOCK
	AOS	W,P2		;YES, SET M FOR BLOCK WE ORIGINALLY WANTED
	PUSHJ	P,USET00	;SET DDB POINTERS
	  POPJ	P,		;RIB ERROR
	MOVEM	S,DEVIOS(F)	;SAVE S (NEW IOSFIR)
	MOVE	T2,DEVACC(F)	;GET LOC OF A.T.
	MOVEI	T1,BLKSIZ##	;GET SIZE OF BLOCK
	DPB	T1,ACYLBS##	;FORCE AS FINAL BLOCK'S WORD COUNT
	POPJ	P,		;THROUGH - RETURN

;SUBROUTINE TO OBTAIN THE HIGHEST BLOCK ALLOCATED
;RETURNS WITH NUMBER IN T1
GETALC::MOVE	T1,DEVACC##(F)	;LOC OF A.T
	MOVE	T1,ACCALC##(T1)	;ACCALC
IFN FTDMRB,<
	LDB	T2,DEYRBC##	;CURRENT RIB NUMBER
	LSH	T2,1		;2 NON-DATA BLOCKS PER RIB
	SUB	T1,T2		;ADJUST ACCALC
>
	POPJ	P,		;AND RETURN
UDSD==100			;USER WANTS TO WRITE FORMAT
UDSX==200			;FILIO WANTS TO WRITE FORMATS
MNTCYL==100000		;ON IF USER WANTS MAINTENANCE CYLS

;HERE ON SUSET. UUO
USUSET::
IFN FTDSUP,<
	MOVE	W,M		;SAVE AC BYTE
	MOVE	M,T1		;SAVE ARGUMENT
	PUSHJ	P,VALUUO	;DSK INITED ON THIS CHAN?
	  PJRST	IOIERR##	;"IO TO UNASSIGNED CHAN"
	TLO	M,400000	;YES, INDICATE SUSET.
	AOS	(P)		;SET FOR SKIP (GOOD) RETURN
	JRST	SETSU0		;AND DO SUPER USETI/O
>

SETSUP:
IFN FTDSUP,<	;IF SUPER USETI/USETO
	JFCL	ILLINS##	;CHANGE TO JRST TO MAKE SUPER USETI/O ILLEGAL
SETSU0:	HRRZ	U,TABST0##	;LOC OF 1ST FS
	HLRZ	U,STRUNI##(U)	;U=LOC OF 1ST UNIT IN 1ST STR
	MOVE	T1,DEVNAM(F)	;NAME USER INITED
	PUSHJ	P,ALIASD##	;IS NAME AN ALIAS FOR "DSK"?
	  JRST	SETIMP##	;YES, GIVE THE USER IO.IMP
	PUSHJ	P,SRSTR##	;NO. AN STR NAME?
	  SKIPA			;NO
	JRST	SETSU1		;YES.
	PUSHJ	P,SRUNI##	;A UNIT NAME?
	  POPJ	P,		;NO - RETURN WITHOUT DOING ANYTHING
	  JFCL
	PUSHJ	P,PRVJB##	;YES. PRIVILEGED?
	  JRST	SETSU7		;NO. ILLEGAL
SETSUX:	SKIPL	T1,M		;BLOCK NOT IN M IF SUSET.
	PUSHJ	P,GETWDU##	;UNIT NAME - GET BLOCK NUMBER
	TLZ	T1,777740
	HRRM	U,DEVUNI##(F)	;SAVE UNIT IN DDB
	SETOM	DEVREL##(F)	;INDICATE UNIT WAS INITED(NOT STR)
	TRNN	S,UDSD		;WRITE FORMAT?
	TRZA	S,UDSX		;NO, CLEAR THE BIT
	TRO	S,UDSX		;YES, INDICATE WRITING FORMATS
	JRST	SETSU2		;AND CONTINUE
SETSU1:	TRZ	S,UDSX		;INDICATE NOT WRITING FORMATS
	PUSHJ	P,PRVJB##	;PRIV'D
	  JRST	SETS3A		;NO. ILLEGAL
	JUMPG	M,SETS1A	;GO IF SUPER USET
	MOVE	T1,M		;GET BLOCK NO
	TLZ	T1,777740	;CLEAR UNWANTED BITS
	CAMN	T1,[37,,-1]	;SUSET. TO LAST BLOCK XFERRED?
	SETO	T1,		;YES, MAKE IT -1
	JRST	SETS1B
SETS1A:	PUSHJ	P,GETWDU##	;GET BLOCK NUMBER
SETS1B:	CAME	T1,[-1]		;USETO TO LAST BLOCK XFERRED?
	JRST	SETS1C		;NO
	HRRZ	U,DEVUNI##(F)	;YES, GET RIGHT UNIT
	SOS	T1,DEVBLK##(F)	;GET BLOCK NUMBER
	JRST	SETSU4		;AND CONTINUE
SETS1C:	PUSHJ	P,ADR2UN##	;SET U TO RIGHT UNIT IN STR FOR THIS BLOCK
	  JRST	SETSU3		;ILLEGAL BLOCK NUMBER - LIGHT IOBKTL
SETSU2:	CAML	T1,UNIBPU##(U)	;HIGHER THAN HIGHEST BLOCK ON UNIT?
	JRST	SETSU6		;YES. LIGHT IOBKTL
	JUMPL	T1,SETSU3
	TLNE	M,MNTCYL	;WANT MAINT CYL?
	JRST	SETSU3		;YES, ERROR (THIS BLOCK NOT IN MAINT CYLS)
	MOVEM	T1,DEVBLK##(F)	;NO, SAVE BLOCK NO IN DDB
SETSU4:	SUB	T1,UNIBPU##(U)	;-DISTANCE TO END OF UNIT
SETSU5:	MOVNS	T1		;NO OF BLOCKS LEFT TO END OF UNIT
	TLNE	T1,-1		;MORE THAN 256 K BLOCKS?
	MOVEI	T1,-1		;YES, MAX TRANSFER IS 256K
	HRRM	T1,DEVLFT##(F)	;SAVE IN DEVLFT
	TLZ	S,IOSFIR	;MAKE SURE IOSFIR=0
	TLOA	S,IOSUPR	;INDICATE SUPER USETI/USETO
>	;END CONDITIONAL ON FTDSUP
ERRFUL:
SETSU3:	TRO	S,IOBKTL	;INDICATE TOO HIGH A BLOCK NUMBER
	PJRST	STOIOS##	;SAVE S AND RETURN


SETS3A:	JUMPGE	M,SETSU3	;GO IF SUPER USETI/O
SETS3B:	SOS	(P)		;SUSET, - NON-SKIP RETURN
	MOVE	M,W		;RESTORE AC (PUUOAC)
	PJRST	RTM1##
;HERE IF BLOCK ABOVE HIGHEST BLOCK ON UNIT
IFN FTDSUP,<
SETSU6:
IFN FTRP04,<
	MOVSI	T2,DEPCPT##	;COMPATABILITY MODE?
	TDNN	T2,DEVCPT##(F)
	JRST	SETSU8		;NO
	CAMLE	T1,UNIBUC##(U)	;YES, IS IT A LEGAL BLOCK?
	JRST	SETSU3		;NO, LIGHT AN ERROR BIT
	MOVEM	T1,DEVBLK##(F)	;YES, SAVE BLOCK NUMBER
	SUB	T1,UNIBUC##(U)	;DISTANCE TO END OF UNIT
	JRST	SETSU5		; AND FINISH UP
SETSU8:>
	CAMG	T1,UNIBPM##(U)	;MAINT CYL?
	SKIPL	DEVREL##(F)	;YES, UNIT (NOT STR) INITED?
	JRST	SETSU3		;NO - IOBKTL
	TLNN	M,MNTCYL	;WANT MAINT CYL (OR SUPER USET)?
	JRST	SETSU3		;NO, ERROR
	MOVEM	T1,DEVBLK##(F)	;YES, SAVE BLOCK
	SUB	T1,UNIBPM##(U)	;DISTANCE TO END OF MAINT CYL
	JRST	SETSU5		;FINISH UP
;HERE IF UNPRIU'S SUSET/USET TO A UNIT
SETSU7:	JUMPGE	M,SETSU3	;ERROR IF SUPER USET
	MOVE	T1,.C0JOB##	;SUSET.
	MOVE	T1,JBTPPN##(T1)	;PPN OF REGISTER
	CAMN	T1,UMDPPN##	;USER-MODE DIAGNOSTICS? [6,6]
	TLNN	M,MNTCYL	;TO MAINT CYL?
	JRST	SETS3B		;NO, ERROR
	JRST	SETSUX		;YES, OK
>	;END FTDSUP
;HERE IF THE REQUESTED BLOCK IS HIGHER THAN THE HIGHEST ALLOCATED
USETO1:	PUSHJ	P,SAVE2##	
	MOVE	P1,W		;SAVE REQUESTED BLOCK
	MOVE	P2,T1		;SAVE 1ST UNALLOCATED BLOCK NUMBER
	SOS	W,T1		;SET RH(M) TO HIGHEST ALLOCATED
	PUSHJ	P,USET00	;GET POINTERS INTO CORE FOR HIGHEST BLOCK
	  POPJ	P,		;RIB ERROR
IFN FTDMRB,<
	SKIPL	DEVBLK(F)	;FIND THE BLOCK?
	JRST	USET1A		;YES
	MOVSI	T1,DEPLPC	;NO, IS IT REALLY THERE?
	TDNN	T1,DEVLPC(F)
	STOPCD	CPOPJ,DEBUG,PLP,;++ PAST LAST POINTER
	HRROS	DEVRSU(F)	;YES, SET DEVRSU TO EXTEND THE RIB
USET1A:
>

	MOVE	T2,P1		;TOP BLOCK TO ALLOCATE
	MOVE	T1,P2		;FIRST BLOCK TO ALLOCATE
	SUB	T2,T1		;TOTAL NUMBER TO ALLOCATE
	ADDI	T2,1
IFN FTDQTA,<
	PUSH	P,T2		;SAVE NUMBER REQUESTED
	PUSHJ	P,CHKQTA	;CAN WE GET THAT MANY?
	JUMPLE	T2,TPOPJ##	;NO. ERROR RETURN (IOBKTL SET)
	CAMGE	T2,(P)		;DID WE GET ALL WE ASKED FOR?
	TRO	S,IOBKTL	;NO, TELL THE USER HE ONLY GOT SOME
>	;END CONDITIONAL ON FTDQTA
IFE FTDQTA,<
	PUSHJ	P,CHKQTA	;CHECK IF STR FULL
>
	MOVEM	P1,DEVREL##(F)	;SAVE REQUESTED BLOCK IN DDB
IFE FTDSIM,<
	MOVEM	T2,P1		;SAVE NUMBER TO GET
>
IFN FTDSIM,<
	MOVEM	T2,P2		;NUMBER TO GET
>
IFN FTDQTA,<
	POP	P,T1		;NUMBER REQUESTED
	SUBM	T2,T1		;MINUS NUMBER ALLOWED
	ADDM	T1,DEVREL##(F)	;ADJUST REQUESTED BLOCK BY NO. OBTAINED
>	;END CONDITIONAL ON FTDQTA
IFN FTDSIM,<
	MOVE	T1,DEVACC##(F)
	MOVE	T1,ACCSMU##(T1)	;SIM UPDATE FILE?
	TRNN	T1,ACPSMU
	JRST	USET1C		;NO, CONTINUE
	PUSHJ	P,GTMB2		;YES, GET MON BUF NOW TO AVOID DEADLY EMBRACE
	PUSHJ	P,UPDA		; (MUST GET MON BUF BEFORE DA, SIM UPDATE NEEDS BOTH)
	PUSHJ	P,GETALC	;GET CURRENT NO OF BLOCKS ALLOCATED
	AOS	W		;HAS IT CHANGED (ANOTHER USETO ALLOCATING)
	CAMN	T1,W
	SOJA	W,USET1C	;NO, WE'RE OK
	MOVE	W,P1		;YES, EXTRICATE OURSELVES
	POP	P,(P)
	POP	P,P2
	POP	P,P1		;MAKE STACK RIGHT
	PUSHJ	P,DWNDA		;GIVE UP RESOURCES
	PUSHJ	P,GVMNB0
	JRST	USETO7		;AND TRY AGAIN
USET1C:	MOVE	P1,P2		;RESTORE NUMBER TO GET
>	;END FTDSIM
	PUSHJ	P,CHKADD	;CAN WE ADD TO CURRENT POINTER?
	JUMPLE	T2,USETO3	;NO. GET SPACE ANYWHERE
	AOSE	T1,DEVBLK##(F)	;YES SET T1= 1ST BLOCK
	PUSHJ	P,TAKBLK	;GET BLOCKS AT PREVIOUS END
	  JRST	USETO3		;CANT GET ANY THERE
	PUSHJ	P,ADDPTR	;GOT SOME - ADD TO CURRENT POINTER
USETO2:	SUB	P1,T1		;DECREMENT AMOUNT TO GET
	JUMPLE	P1,USETO5	;FINISH UP IF GOT ENOUGH
;HERE TO GET BLOCKS ANYWHERE 
USETO3:	
	MOVSI	T3,1		;DECREMENT TOTAL NO. OF POINTERS
	ADDB	T3,DEVRSU##(F)	;TOO MANY?
	JUMPGE	T3,USET3B	;YES, TRY TO GET AN EXTENDED RIB
USET3C:	SETZ	T3,
	AOS	T1,DEVRET##(F)	;POINT DEVRET TO 1ST EMPTY POINTER LOC
	CAILE	T1,DEVRBN##(F)	;FILLED THE DDB?
	PUSHJ	P,WRTPTR	;YES. WRITE THE POINTERS
	JUMPN	T3,USETO4	;RETURN WITH IOBKTL IF RIB ERR
	MOVE	T2,P1		;NUMBER TO GET
	MOVEI	T1,0		;ANYWHERE
	PUSHJ	P,TAKBLK	;GET SOME BLOCKS
	  SKIPA			;NOT AVAILABLE ON THIS UNIT
	JRST	USET3A		;GOT THEM
	HLRE	T1,DEVRSU##(F)	;IF 1 SLOT LEFT,
	AOJGE	T1,USET5A	;CANT EXTEND RIB,
	PUSHJ	P,NEXTUN	;STEP TO ANOTHER UNIT IN STR
	  JRST	USET5A		;ALL UNITS FULL - SETTLE FOR WHAT WE GOT SO FAR
USET3A:	MOVEM	T2,@DEVRET##(F)	;SAVE POINTER (OR UNIT-CHANGE) IN DDB
	HRRZ	T3,DEVACC##(F)	;LOC OF A.T.
	MOVEI	T4,ACP1PT##	;ENSURE THAT 1PT IS OFF
	ANDCAM	T4,ACCUN1##(T3)	;SINCE WE JUST GENERATED A NEW POINTER
	TLO	S,IOSFIR	;INDICATE CHECKSUM MUST BE COMPUTED
	JRST	USETO2		;GET MORE BLOCKS IF NEEDED
USET3B:
IFN FTDMRB,<			;IF MULTIPLE RIBS
	MOVN	T1,P1		;GET -NUMBER OF BLOCKS LEFT TO GET
	ADDM	T1,DEVREL##(F)	;SET DEVREL TO END OF RIB FOR EXTRIB
	PUSHJ	P,EXTRIB	;CREATE AN EXTENDED RIB
IFE FTDSIM,<
	  JRST	USETO4		;COULDN'T GET THE RIB, ERROR
>
IFN FTDSIM,<
	  JRST	[PUSHJ	P,USETO4
		 PJRST	DOWNIF]
>
	ADDM	P1,DEVREL##(F)	;RESET DEVREL TO BLOCK TO GET
	ADDI	P1,2		;ACCOUNT FOR REDUNDANT AND EXTENDED RIB
	SUB	P1,T1		;DECREMENT AMOUNT TO GET
	ADDI	P1,2		;ACCOUNT FOR 2 RIBS
	PUSHJ	P,CPYEXT##	;SET UP THE DDB
IFE FTDSIM,<
	  POPJ	P,		;RIB ERROR
>
IFN FTDSIM,<
	  JRST	DOWNIF
>
	JUMPLE	P1,USETO5	;FINISH UP IF GOT ENOUGH
	JRST	USET3C		;NOT ENOUGH, GO GET MORE
>				;END OF CONDITIONAL FTDMRB
USETO4:	PUSHJ	P,ERRFUL	;TOO MANY POINTERS,LIGHT IOBKTL
	MOVNS	P1		;AMOUNT WE WERE UNABLE TO GET
	ADDM	P1,DEVREL##(F)	;ADJUST DEVREL
	POPJ	P,		;AND RETURN TO USER

;HERE IF UNIT OR RIB FULL
USET5A:	PUSHJ	P,USETO4	;LIGHT AN ERROR BIT, ADJUST DEV REL
	SOS	DEVRET##(F)	;ADJUST DEVRET
	MOVSI	T1,-1
	ADDM	T1,DEVRSU##(F)	; AND DEVRSU (INCR'D AT USETO3)


;HERE WHEN ALL BLOCKS HAVE BEEN ALLOCATED
USETO5:	MOVE	W,DEVREL##(F)	;RESET W TO REQUESTED BLOCK
	SKIPLE	P1		;IF COULDN'T GET ALL WE REQUESTED,
	SUB	W,P1		;ADJUST BLOCK NUMBER
	PUSHJ	P,WRTPTR	;WRITE OUT RET POINTERS LEFT IN DDB
	JUMPN	T3,USETO4	;RETURN WITH IOBKTL IF RIB ERR
IFN FTDSIM,<
	PUSHJ	P,DOWNIF	;RETURN DA IF WE OWN IT
	SKIPE	T1,DEVMBF##(F)	;HAVE MON BUF (SIM UPD)?
	PUSHJ	P,GVMNBF	;YES, RETURN IT
>
	PUSH	P,DEVRSU##(F)	;SAVE DEVRSU (USETZ1 MAY CHANGE IT)
	PUSHJ	P,USETO6	;ZERO ALLOCATED, UNWRITTEN BLOCKS
	POP	P,DEVRSU##(F)	;RESTORE DEVRSU
	PJRST	STRIOS		;SAVE S AND EXIT THE UUO

IFN FTDSIM,<
;SUBROUTINE TO GIVE UP THE DA IF WE OWN IT
;ALWAYS RETURNS CPOPJ
DOWNIF::
	TLNE	S,IOSDA		;HAVE DA (SIM UPDATE)?
	PJRST	DWNDA		;YES, RETURN IT
	POPJ	P,
>
;SUBROUTINE TO ADD TO CURRENT POINTER
;ENTER WITH ACS SET AS IN GOOD RETURN FROM TAKBLK-
;T2=CLUSTER POINTER FOR NEW GROUP, T3=ADDRESS OF STRUCTURE DB
;EXIT WITH T1= NUMBER OF NEW BLOCKS GOTTEN
;AND UPDATED POINTER IN @DEVRET AND T2
ADDPTR::PUSH	P,T1		;SAV NO. OF BLOCKS GOTTEN
	LDB	T1,STYCNP##(T3)	;NO. OF CLUSTERS GOTTEN (AT END)
	MOVE	T2,@DEVRET##(F)	;CURRENT POINTER
	LDB	T4,STYCNP##(T3)	;CLUSTER COUNT
	ADD	T4,T1		;PLUS NEW AMOUNT
	HRRZ	T1,DEVACC##(F)	;LOC OF A.T.
	CAME	T2,ACCPT1##(T1)	;IS THIS PNTR THE 1ST?
	SETZ	T1,		;NO. INDICATE BY T1=0
	DPB	T4,STYCNP##(T3)	;SAVE NEW CLUSTER COUNT
	MOVEM	T2,@DEVRET##(F)	;SAVE POINTER
	JUMPE	T1,TPOPJ##	;IS THIS 1ST PNTR?
	MOVEM	T2,ACCPT1##(T1)	;YES. SAVE IT IN A.T.
	JRST	TPOPJ##		;RESTORE T1 AND RETURN


;SUBROUTINE TO STEP TO NEXT UNIT IN FILE STRUCTURE WHICH HAS SPACE LEFT
;IF ALL SPACE IS GONE, RETURN CPOPJ WITH IOBKTL SET
;GOOD RETURN WITH U=DEVUNI= LOC OF NEW UNIT, AND T1=0

;AND A CHANGE-UNIT POINTER STORED IN @DEVRET AND LEFT IN T2
NEXTUN::HRRZ	T1,UNISTR##(U)	;LOC OF STR DB
	HLRZ	T1,STRUNI##(T1)	;LOC OF 1ST UNIT IN STR
	SKIPA			;TEST IF IT HAS ANY SPACE
NEXTU1:	HLRZ	T1,UNISTR##(T1)	;STEP TO NEXT UNIT IN STR
	JUMPE	T1,ERRFUL	;STR IS FULL IF AT END OF UNITS
	SKIPG	UNITAL##(T1)	;NO. UNIT HAVE ANY SPACE?
	JRST	NEXTU1		;NO. TRY NEXT UNIT
	MOVE	U,T1		;YES. SET U
	HRRM	U,DEVUNI##(F)	;AND DEVUNI
	LDB	T2,UNYLUN##	;GET LOGICAL UNIT NUMBER
	TRO	T2,RIPNUB##	;MAKE SURE NON-0
	MOVEM	T2,@DEVRET##(F)	;SAVE IN DDB
	SETZ	T1,		;MAKE SURE T1=0
	JRST	CPOPJ1##	;AND TAKE GOOD RETURN

;SUBROUTINE TO DO THE WORK FOR USETO/USETI
;HALTS IF NO POINTERS TO THE BLOCK
;RETURNS CPOPJ IF THERE IS A RIB ERROR
;SKIP - RETURN IF EVERYTHING IS OK
;ENTER WITH RH(M)=DESIRED BLOCK
;EXIT WITH DEVRET, DEVBLK, DEVLFT, DEVREL SET UP
USET00:	HRRZ	U,DEVUNI##(F)
	JUMPE	U,CPOPJ##	;ERROR IF UNIT WAS YANKED
	HRRZ	U,DEVFUN##(F)	;UNIT FOR 1ST POINTER IN DDB
	HRRM	U,DEVUNI##(F)	;SAVE IN DEVUNI (WILL CHANGE IF UNIT-CHANGE IS READ)
	MOVE	T2,DEVFLR##(F)	;LOWEST REL BLOCK OF POINTERS IN DDB
	MOVE	T3,W		;BLOCK NUMBER TO GET
	CAML	T2,T3		;IS DESIRED BLOCK BELOW THIS FLOOR?
	JRST	USTRIB		;YES. READ IN WHOLE RIB
	MOVEI	T1,DEVRB1##(F)	;NO. SCAN THE POINTERS IN CORE
	HRLI	T1,MPTRLN##	; STARTING AT DEVRB1
	PUSHJ	P,SCNPTR	;TRY TO FIND POINTER TO BLOCK
	  JRST	USTRIB		;NOT THERE - READ WHOLE RIB
				;FOUND IT. DEVBLK,DEVREL,DEVLFT ARE SET UP
	HRRZ	T2,DEVRET##(F)	;CURRENT POINTER LOC
	CAIN	T2,DEVRBN##(F)	;POINTING TO LAST PNTR SLOT?
	SKIPE	DEVRB2##(F)	;YES, IS 2ND PTR 0? (YES IF SET DDB FROM
				;A.T., MORE PNTRS LEFT IN RIB)
	HRRM	T1,DEVRET##(F)	;NO, SET DEVRET TO THIS POINTER
	SUB	T1,T2		;DISTANCE BY WHICH WE CHANGED DEVRET
	HRLZS	T1		;IN LH
	ADDM	T1,DEVRSU##(F)	;UPDATE DEVRSU BY THAT AMOUNT
	JRST	CPOPJ1##	;AND TAKE GOOD RETURN


IFN FTDMRB,<
;SUBROUTINE TO TURN ON IOSFIR FOR FIRST BLOCK IN EXTENDED RIB
EXTCKS:	MOVE	T1,DEVUNI##(F)	;NEWUX WIPES RH (DEVUNI)
	LDB	T2,DEYRBU##	;UNIT OF RIB
	PUSHJ	P,NEWUX
	  JFCL
	EXCH	T1,DEVUNI##(F)	;RESET DEVUNI GET RIB UNIT
	MOVE	T2,DEVRIB##(F)	;POINTER TO (EXTENDED) RIB
	PUSHJ	P,GRPAD		;GET BLOCK NUMBER OF RIB
	ADDI	T2,1		;FIRST BLOCK PAST RIB?
	CAME	T2,DEVBLK##(F)
	POPJ	P,		;NO
	CAMN	T1,DEVUNI##(F)	;YES, RIGHT UNIT?
	TLO	S,IOSFIR	;YES, CHECKSUM TIME
	PJRST	STOIOS##
>
;HERE IF THE POINTERS IN THE DDB DON'T ENCOMPASS THE DESIRED BLOCK
;READ IN THE RIB, AND SCAN IT FROM THE BEGINNING
USTRIB:	PUSHJ	P,PTRTST	;READ POINTERS, REWRITE RIB IF POINTERS HAVE CHANGED
	  JRST	GVMNB0		;ERROR READING RIB
	PUSHJ	P,SAVE1
	SETO	P1,
USTRB5:
IFN FTDMRB,<			;IF MULTIPLE RIBS
	LDB	T2,DEYRBU##	;GET UNIT OF CURRENT RIB
	PUSHJ	P,NEWUX		;SET U
	  STOPCD	GVMNB0,DEBUG,NSU,	;++NO SUCH UNIT
	MOVE	T2,DEVMBF##(F)	;IOWD FOR MONITOR BUFFER
	MOVE	T2,RIBFLR##+1(T2)  ;FIRST WORD OF CURRENT RIB
	SKIPL	DEVRIB##(F)	;IF POSITIVE COULD BE OLD TYPE RIB
>
	MOVEI	T2,0		;WHICH HAS NO RIBFLR WORD
	MOVE	T3,W		;BLOCK NUMBER TO GET
IFN FTDMRB,<			;IF MULTIPLE RIBS
	CAML	T2,T3		;BLOCK BELOW FLOOR OF CURRENT RIB?
	JUMPN	T2,USTRB6	;JUMP IF PRIME RIB
>
	PUSHJ	P,SCNPT0	;SCAN THE CURRENT RIB
IFN FTDMRB,<		;IF MULTIPLE RIBS
	  JRST	USTRB7		;NOT HERE, LOOK IN NEXT RIB
>
IFE FTDMRB,<			;IF NO MULTIPLE RIBS
	  PJRST	GVMNB0
>
	MOVEM	T2,DEVFLR##(F)	;SET LOWEST RELATIVE BLOCK IN DDB
	HRRM	U,DEVFUN##(F)	;SET CORRESPONDING UNIT
	PUSHJ	P,PTRBLT	;BLT POINTERS TO DDB
	AOS	(P)		;SET FOR SKIP RETURN
	PJRST	GVMNB0		;RETURN MONITOR BUFFER AND EXIT

IFN FTDMRB,<			;IF MULTIPLE RIBS
;HERE WHEN WE MUST START LOOKING AT THE PRIME RIB
USTRB6:
	AOJN	P1,GVMNB0
	PUSHJ	P,REDRIB	;READ THE PRIME RIB
	PJRST	GVMNB0		;ERROR READING THE RIB
	PUSHJ	P,SPTRW		;SET UP AOBJN WORD FOR THE RIB
	JRST	USTRB8		;SET UP TO SCAN THE PRIME RIB
>				;END CONDITIONAL ON FTDMRB
;HERE TO GET THE NEXT RIB IN THE CHAIN
IFN FTDMRB,<
USTRB7:	PUSHJ	P,PTRNXT	;GET THE NEXT RIB IN THE CHAIN
				;IF MULTIPLE RIBS
	  JRST	USTRB9		;EITHER ERROR OR COULDN'T FIND THE BLOCK
USTRB8:	MOVE	T3,W		;BLOCK NUMBER TO GET
	JRST	USTRB5		;SCAN THE RIB

;HERE ON NON-SKIP RETURN FROM PTRNXT, EITHER RIB ERROR OR NO NEXT RIB
USTRB9:	PJUMPN	T3,GVMNB0	;RETURN CPOPJ IF RIB ERROR
	PUSHJ	P,GVMNB0	;NO NEXT RIB, GIVE UP THE BUFFER
	SETOM	DEVBLK##(F)	;SET DEVBLK TO -1 AS A FLAG
	PUSHJ	P,DDBZRO##	;ZERO DDB PNTR SPACE SINCE DEYRLC IS WRONG
	JRST	CPOPJ1##	;TAKE A SEMI-GOOD RETURN
>				;END CONDITIONAL ON FTDMRB

;SUBROUTINE TO READ THE POINTERS INTO CORE, COMPARE THE OLD POINTERS IN THE
;RIB WITH THE NEW POINTERS IN THE DDB, AND REWRITE THE RIB IF THEY DIFFER
;SUBROUTINE GETS A MONITOR BUFFER AND RETURNS WITH THE RIB IN IT
;RETURNS WITH T1=AOBJN WORD FOR WHOLE GROUP OF PNTRS IN RIB
;RETURNS CPOPJ IF ERROR READING RIB (STILL WITH MON BUF
;RETURNS CPOPJ1 NORMALLY
PTRTST:
IFN FTDSIM,<
	HRRZ	T1,DEVACC##(F)
	JUMPE	T1,PTRTS0
	MOVE	T1,ACCSTS##(T1)	;SIM UPDATE FILE?
	TRNN	T1,ACPSMU
	JRST	PTRTS0
	PUSHJ	P,GTMNBF	;YES, GET MON BUF
	PUSHJ	P,UPDA		;AND DA TO PREVENT RACE IF WE WRITE RIB
PTRTS0:>
	PUSHJ	P,PTRCUR	;READ THE POINTERS INTO CORE
IFE FTDSIM,<
	JUMPN	T3,CPOPJ##	;RETURN IF ERROR READING RIB
>
IFN FTDSIM,<
	JUMPN	T3,DOWNIF
>
	HLRZ	T3,DEVEXT(F)	;EXTENSION
	LDB	T4,PUUOAC##	;CHAN NUMBER
	MOVE	T4,USRJDA##(T4)	;WAS AN ENTER DONE ON THIS CHAN?
	TLNE	T4,ENTRB+OUTPB	; (IF NOT THIS DDB DIDN'T CHANGE THE PNTRS)
	CAIN	T3,(SIXBIT /UFD/) ;"UFD"?
	JRST	USTRB4		;YES, PNTRS IN THE RIB ARE RIGHT

;HERE WHEN THERE ARE PNTRS IN THE DDB WHICH MAY NOT BE IN THE RIB - CHECK THEM
	MOVE	T3,UNISTR##(U)	;GET ADDRESS OF STRUCTURE DATA BLOCK
	SETO	T2,		;PUT ONE'S IN T2
	LDB	T4,STYCLP##(T3)	;CREATE MASK FOR CLUSTER POINTER
				;PART OF RETRIEVAL POINTER
	LDB	T2,DEYRLC##	;POINTER LOC IN THE RIB
	ADD	T1,T2		;POINT TO 1ST RIB PNTR - CORRESPONDING TO DEVRB1
	MOVEI	T2,DEVRB1##(F)	;POINT T2 TO DDB POINTERS
	HRLI	T2,MPTRLN##	;MAKE T2 AN AOBJN WORD
USTRB1:	SKIPN	T3,(T2)		;GET A PNTR FROM DDB
	JRST	USTRB3		;ALL DONE
	CAMN	T3,(T1)		;SAME AS PNTR IN RIB?
	JRST	USTRB2		;YES
	EXCH	T3,(T1)		;NO. SAVE PNTR IN MON BUF
	JUMPE	T3,USTR1A	;IF OLD PNTR=0, OK
	XOR	T3,(T1)		;XOR RIB WITH MON BUF
	TDNE	T3,T4		;IF PNTR PARTS EQUAL, SKIP
	STOPCD	.+1,DEBUG,PNE,	;++POINTERS NOT EQUAL
				;FALL INTO USTR1A
USTR1A:	TLZ	T1,-1		;ZERO LH(T1) - WAS MRIBLN
USTRB2:	AOBJP	T2,USTRB3	;SKIP IF ALL DDB PNTRS LOOKED AT
	AOJA	T1,USTRB1	;LOOK AT NEXT POINTER

;HERE WHEN ALL POINTERS HAVE BEEN COMPARED, CHANGED PNTRS STORED IN MON BUF
USTRB3:	SKIPL	T1		;T1 NEG IF ALL PNTRS COMPARED
	PUSHJ	P,WRTRIB	;WRITE THE MON BUF AS 1ST RIB
USTRB4:
IFN FTDSIM,<
	PUSHJ	P,DOWNIF	;RETURN DA IF WE OWN IT
>
	PUSHJ	P,SPTRW		;SET T1 AS AN AOBJN WD FOR PNTRS AGAIN
	JRST	CPOPJ1##	;AND TAKE GOOD-RETURN
;SUBROUTINE TO SCAN A BLOCK OF RETRIEVAL POINTERS TO FIND THE GROUP POINTER
;FOR A PARTICULAR BLOCK
;ENTER WITH:
;T1=AOBJN WORD FOR THE SET OF POINTERS
;T2=INITIAL RELATIVE BLOCK OF THE SET OF POINTERS
;T3=DESIRED RELATIVE BLOCK
;ENTER AT SCNPT0 TO SCAN WHOLE RIB (IN MON BUF)
;EXIT WITH:
;T1=ADDRESS OF THE POINTER, LH=-NUMBER OF POINTERS LEFT
;T2=RELATIVE BLOCK NUMBER OF POINTER
;DEVLFT,DEVBLK,DEVREL SET IN THE DDB
;EXIT CPOPJ IF THE POINTER WAS NOT FOUND
;SKIP-RETURN IF THE POINTER WAS FOUND

SCNPT0::PUSHJ	P,SPTRW		;SET T1=AOBJN WORD FOR WHOLE RIB
SCNPTR::PUSHJ	P,SAVE2##		;SAVE P1,P2
	LDB	T4,UNYBPC##	;NUMBER OF BLOCKS PER CLUSTER
	PUSH	P,T3		;SAVE DESIRED BLOCK
	SUB	T3,T2		;T3=RELATIVE BLOCK NUMBER IN SET
	IDIV	T3,T4		;T3=DESIRED CLUSTER
	MOVE	T2,UNISTR##(U)	;LOC OF FILE STRUCTURE DB
	HLLZ	P1,STYCNP##(T2)	;SET UP POS, SIZE  OF POINTER COUNT FIELD
	TLO	P1,T1		;POINTER TO CLUSTER COUNT
	SETZ	P2,		;CLEAR REGISTER TO ACCUMULATE BLOCK COUNT

SCNPT1:	LDB	T2,P1		;GET NUMBER OF CLUSTERS IN THIS POINTER
	JUMPN	T2,SCNPT2	;REAL POINTER IF NON-0
	SKIPN	T2,(T1)		;UNIT CHANGE OR END OF POINTERS
	PJRST	TPOPJ##		;END OF POINTERS. ERROR RETURN
	TRZ	T2,RIPNUB##	;REMOVE BIT 18 (REST IS A LOGICAL UNIT NUMBER)
	PUSHJ	P,NEWUNI	;SET UP U, DEVUNI(F)
	  JRST	TPOPJ##		;INVALID UNIT -NOT FOUND RETURN
SCNPT3:	AOBJN	T1,SCNPT1	;GO BACK TO TEST NEXT POINTER
	JRST	TPOPJ##		;RAN OUT OF POINTERS, ERROR RETURN
;HERE WHEN A REAL POINTER HAS BEEN FOUND
SCNPT2:	ADD	P2,T2		;PLUS LENGTH OF GROUP
	CAML	T3,P2		;IS DESIRED CLUSTER IN THIS POINTER?
	JRST	SCNPT3		;NO, STEP TO NEXT
	LDB	P1,UNYBPC##	;YES. NUMBER OF BLOCKS PER CLUSTER
	SUB	P2,T2		;SET P2 BACK TO BEGINNING OF GROUP
	SUB	T3,P2		;T3=CLUSTER IN GROUP
	IMUL	T3,P1		;T3=BLOCK NUMBER IN GROUP
	ADD	T3,T4		;T3= DISTANCE OF BLOCK FROM START OF PNTR
	POP	P,T4		;BLOCK NUMBER TO GET
	SKIPE	T3		;AT 1ST BLOCK OF A GROUP?
	CAIN	T4,1		;IS IT BLOCK 1?
	TLOA	S,IOSFIR	;YES, SET CHECKSUM BIT
	TLZ	S,IOSFIR	;NO, CLEAR CHECHSUM BIT
	IMUL	T2,P1		;T2=RELATIVE BLOCK NUMBER OF START OF PNTR
	SUB	T2,T3		;COMPUTE NUMBER OF BLOCKS LEFT IN GROUP
	HRRM	T2,DEVLFT##(F)	;SAVE IN DDB
IFN FTDMRB,<
	HRRZ	T2,T1		;GET ADDRESS PORTION OF POINTER
	CAIG	T2,DEVRBN##(F)	;SKIP IF NOT POINTING TO DDB
	CAIGE	T2,DEVRB1##(F)	;SKIP IF POINTING TO DDB
	JRST	SCNP2A		;NOT IN DDB, MUST BE IN MONITOR BUFFER
	MOVE	T2,DEVLPC##(F)	;GET WORD CONTAINING LAST POINTER FLAG
	TLNN	T2,DEPLPC##	;IS POINTER IN DDB?
	JRST	SCNP2D		;NO, PROCEED
	SKIPE	1(T1)		;YES, IS NEXT SLOT EMPTY?
	JRST	SCNP2A		;NO, CHECK TO SEE IF THIS IS LAST SLOT
	HRRZ	T2,DEVLFT##(F)	;IS LAST, MAKE LAST BLOCK UNAVAILABLE
	SOJE	T2,CPOPJ##	;JUMP IF NO BLOCKS AVAILABLE
	JRST	SCNP2C		;STORE THE NEW VALUE OF DEVLFT
SCNP2A:	HRRZ	T2,DEVLFT##(F)	;RETURN DEVLFT TO T4
	AOBJN	T1,SCNP2B	;ABOUT TO RUN OUT OF POINTERS?
	SOJE	T2,SCNP2E	;YES, MAKE LAST BLOCK UNAVAILABLE
SCNP2B:	SUB	T1,[XWD 1,1]	;RESTORE AOBJN WORD
SCNP2C:	HRRM	T2,DEVLFT##(F)	;STORE IN DDB
>				;END FTDMRB
SCNP2D:	MOVEM	T4,DEVREL##(F)	;=CURRENT RELATIVE BLOCK
	MOVE	T2,T4		;GET DEVREL INTO T2
	SUB	T2,T3		;SET TO RELATIVE BLOCK OF START OF GROUP
	SKIPN	T4		;USETI/O TO BLOCK 0?
	TLZ	S,IOSFIR	;YES. DONT COMPUTE CHECKSUM (ITS FOR BLOCK 1)
	MOVE	T4,UNISTR##(U)
	MOVE	T4,STYCLP##(T4)	;SET T4=POINTER TO CLUSTER ADDRESS
	HRRI	T4,(T1)
	LDB	T4,T4		;T4=CLUSTER ADDRESS
	IMUL	T4,P1		;1ST LOGICAL BLOCK ADR. IN POINTER
	ADD	T3,T4		;+DISTANCE TO DESIRED BLOCK
	MOVEM	T3,DEVBLK##(F)	;=LOGICAL ADR. OF DESIRED BLOCK
	JRST	CPOPJ1##	;TAKE GOOD RETURN
IFN FTDMRB,<
SCNP2E:	MOVSI	T1,DEPLPC##	;TELL CALLER WHY HE LOST
	IORM	T1,DEVLPC##(F)
	POPJ	P,
>



;SUBROUTINE TO READ A RIB BLOCK, AND STORE THE POINTERS IN THE DDB
;ENTER AT RDPTRA IF THE POINTERS ARE ALREADY IN THE MON BUF
RDPTRS::PUSHJ	P,PTRGET	;READ THE RIB BLOCK INTO A MON BUF
RDPTRA::PUSHJ	P,PTRCPY	;COPY CURRENT POINTERS FROM MON BUF TO DDB
	PJRST	GVMNB0		;GIVE UP MON BUF AND RETURN


;SUBROUTINE TO WRITE POINTERS
WRTPTR:	PUSHJ	P,PTRCUR	;READ THE RIB
	SKIPN	T3		;DONT TRUST ANYTHING IF RIB ERR
	PUSHJ	P,PTRWRT	;BLT POINTERS INTO MON BUF, WRITE THEM
	PJRST	GVMNBX		;GIVE UP MON BUF AND RETURN

;SUBROUTINE TO GET THE CURRENT POINTERS INTO CORE
;RETURNS T3=0 IF OK, NON-0 IF RIB ERROR
PTRCUR::SKIPN	DEVMBF##(F)	;SKIP IF WE ALREADY HAVE BUFFER
	PUSHJ	P,GTMNBF	;GET THE MONITOR BUFFER
	PUSHJ	P,RIBCUR	;READ THE CURRENT RIB
	PJRST	SPTRW		;SET UP A POINTER AND RETURN


;SUBROUTINE TO COPY POINTERS INTO MON BUF AND WRITE IT
;ENTER WITH T1=AOBJN WORD FOR ENTIRE MONITOR BUFFER
;RETURNS WITHOUT GIVING UP THE MON BUFFER
PTRWRT::PUSHJ	P,DD2MN		;COPY DDB POINTERS INTO MONITOR BUF
	STOPCD	.+1,DEBUG,TMP,	;++TOO MANY POINTERS
				;SHOULDN'T HAPPEN SINCE DEVRSU DIDNT GO POSITIVE
	HRRZ	T2,T1		;SAVE CURRENT POINTER LOC
	PUSHJ	P,SPTRW		;MINUS ORIGINAL POINTER LOC
	SUBI	T2,-1(T1)
	DPB	T2,DEYRLC##	;=CURRENT POSITION IN MON BUF
	PJRST	WRTRIB		;WRITE THE RIB AND RETURN
;SUBROUTINE TO FIND WRITING DDBS ASSOCIATED WITH A FILE
;CALL FNDDDB THE FIRST TIME, TO FIND OTHER DDBS CALL FNDDDN WITH
; T2 AS RETURNED FROM THE FIRST CALL
;CALL WITH T1= L(AT)
;RETURNS CPOPJ IF NO MORE (OR NONE) WRITING DDBS
;RETURNS CPOPJ1 NORMALLY, WITH T2=ADR OF NEXT DDB
FNDDDB:	MOVEI	T2,DSKDDB##	;START AT FIRST
FNDDDN:	HLRZ	T2,DEVSER(T2)	;STEP TO NEXT
	HRRZ	T1,DEVACC##(F)	;LOC OF A.T.
	MOVSI	T3,DVDSK
FNDDD1:	TDNN	T3,DEVMOD(T2)	;DSK DDB?
IFN FTSPL,<
	SKIPGE	DEVSPL(T2)
>
IFE FTSPL,<
	CAIA
>
	SKIPA	T4,DEVACC##(T2)	;YES, POINTING AT RIGHT A.T.?
	POPJ	P,		;NO
IFN FTDSIM,<
	CAIE	T2,(F)		;IGNORE OUR OWN DDB
>
	CAIE	T1,(T4)
	JRST	NXTDDB		;NO, TRY NEXT
	MOVE	T4,DEVWRT##(T2) ;YES, IS THIS DDB WRITING?
	TRNE	T4,DEPWRT##
	JRST	CPOPJ1##	;YES, FOUND THE GUY
NXTDDB:	HLRZ	T2,DEVSER(T2)	;NO, TRY NEXT
	JUMPN	T2,FNDDD1
	POPJ	P,		;COULDNT FIND THE DDB - RETURN NON SKIP
;SUBROUTINE TO FIND CURRENT POINTERS FOR A FILE IN SOME DDB
;CALLED WHEN THE ACCESS TABLE INDICATES POINTERS SHOULD BE THERE,
; BUT THE POINTERS ARE NOT IN THE DDB
;SOME DDB HAS ALLOCATED NEW BLOCKS, THE NEW POINTERS AREN'T YET IN THE RIB
;NON-SKIP RETURN IF COULDN'T FIND THE BLOCK
;NORMAL RETURN IS CPOPJ1
FNDPTR:	HRRZ	T1,DEVACC##(F)	;LOC OF A.T.
	MOVE	T2,ACCCNT##(T1) ;STATUS OF FILE
	TRNE	T2,ACPUPD	;UPDATE?
	TRNN	T2,ACMCNM##	;YES, IS THIS THE ONLY READER?
	JRST	FIXDDB		;YES, CANT FIND A WRITING DDB
	PUSHJ	P,FNDDDB	;FIND THE WRITER
	  JRST	FIXDDB		;NONE THERE - REREAD THE RIB

;HERE WHEN THE RIGHT DDB HAS BEEN FOUND
	MOVSI	T1,DEVRB1##(T2) ;COPY THE CURRENT PNTRS INTO DDB
	HRRI	T1,DEVRB1##(F)	; (MAY INCLUDE POINTERS WHICH ARE ALREADY

	BLT	T1,DEVRBN##(F)	; IN THE RIB)
	MOVE	T1,DEVLPC##(F)	;SET UP ALL THE DDB PARAMETERS
	MOVE	T3,DEVLPC##(T2)
	TLNE	T3,DEPLPC##
	TLOA	T1,DEPLPC##
	TLZ	T1,DEPLPC##
	MOVEM	T1,DEVLPC##(F)
	LDB	T1,DEXRLC##
	DPB	T1,DEYRLC##
	HLRE	T1,DEVRSU##(T2)
	HRLM	T1,DEVRSU##(F)
	MOVE	T3,DEVRET##(T2)
	SUBI	T3,DEVRB1##(T2)
	ADDI	T3,DEVRB1##(F)
	HRRM	T3,DEVRET##(F)
	MOVE	T1,DEVFLR##(T2)
	MOVEM	T1,DEVFLR##(F)
	MOVE	T1,DEVRIB##(T2)
	MOVEM	T1,DEVRIB##(F)
	MOVE	T1,DEVFUN##(T2)
	HRRM	T1,DEVFUN##(F)


;DDB IS ALL SET (IF WE FOUND THE WRITER). CALL USETI TO SET FOR THE RIGHT BLOCK
; WILL FIND IT IN THE DDB POINTERS IF THERE, IF THE UPDATER CLOSED THEY SHOULD
; HAVE BEEN WRITTEN BACK INTO THE RIB (COULDN'T FIND THE RIGHT DDB)
FIXDDB:	PUSH	P,W		;SAVE W
	MOVE	W,DEVREL##(F)	;BLOCK WE'RE LOOKING FOR
	PUSHJ	P,USET00	;GO SET UP FOR IT
	  CAIA
	SKIPG	DEVBLK##(F)	;SEMI-GOOD RETURN?
	SOS	-1(P)		;STILL COULDN'T FIND THEM (SYSTEM ERROR?)
	POP	P,W		;RESTORE W
	PJRST	CPOPJ1##	;EVERYTHING WORKED!
;SUBROUTINE TO READ THE CURRENT RIB
;RETURNS CPOPJ, IF T3 NON-ZERO, ERROR READING RIB
;RETURNS UNIT OF RIB IN T2
RIBCUR::PUSH	P,U		;SAVE CURRENT UNIT
	LDB	T2,DEYRBU##	;GET CURRENT RIB LOGICAL UNIT NUMBER
	PUSHJ	P,NEWUNI	;SET UP U,DEVUNI
	STOPCD	UDEERR,DEBUG,UDE, ;++UNIT DOESN'T EXIST
	LDB	T2,DEYRBA##	;GET CURRENT RIB CLUSTER ADDRESS
	LDB	T3,UNYBPC##	;BLOCKS PER CLUSTER FOR THIS UNIT
	IMUL	T2,T3		;BLOCK NUMBER IN T2
	MOVE	T1,DEVMBF##(F)	;GET IOWD FOR MONITOR BUFFER
	PUSHJ	P,MONRED	;READ THE BLOCK
	PUSHJ	P,RIBCHK	;MAKE SURE ITS A VALID RIB
UDEERR:	SKIPA	T3,[-1]		;RIB ERROR, SET T3=-1
	SETZ	T3,		;T3=0 INDICATES RIB OK
	MOVE	T2,U		;RIB-UNIT IN T2
	POP	P,U		;RESTORE CURRENT UNIT
	HRRM	U,DEVUNI##(F)	;AND SAVE IN DDB
	POPJ	P,		;AND RETURN

IFN FTDMRB,<			;IF MULTIPLE RIBS
;SUBROUTINE TO GET THE NEXT RIB IN A CHAIN INTO CORE
;RETURNS CPOPJ1 WITH NEXT RIB IN CORE, CPOPJ IF NONE OR ERROR
;IF CPOPJ RETURN AND T3 NON-0, ERROR,T3=0,NO NEXT RIB
PTRNXT::SETZ	T3,		;T3=0 INDICATES NO RIB ERROR
	MOVE	T2,DEVMBF##(F)	;IOWD FOR MONITOR BUFFER
	SKIPL	DEVRIB##(F)	;IS CURRENT RIB EXTENDED
	SKIPN	RIBFLR##+1(T2)	;NO, IS THIS AN EXTENDABLE FILE
	SKIPN	T2,RIBXRA##+1(T2)	;GET THE NEXT RIB ADDRESS
	POPJ	P,		;NONE, END OF CHAIN
	MOVEM	T2,DEVRIB##(F)	;MAKE NEXT RIB CURRENT RIB
	PUSHJ	P,PTRCUR	;READ THE RIB
	JUMPN	T3,CPOPJ##	;NON-SKIP RETURN IF ERROR
	JRST	CPOPJ1##	;GOOD RETURN
>	;END CONDITIONAL ON FTDMRB
	SUBTTL	MISCELLANEOUS FUNCTIONS
IFN FTRP04,<

;UNLOAD A DRIVE
UNLOAD::PUSHJ	P,GETWDU	;GET USERS ARGUMENT
	MOVNI	T2,1		;WHOLE WORD MUST MATCH
	PUSHJ	P,SRUNI##	;IS IT A UNIT NAME?
	  PJRST	ECOD1##		;NO - ERROR 1
	  JFCL
	SKIPN	UNILOG##(U)	;YES, IS IT IN A FILE STRUCTURE?
	SKIPE	UNISTS##(U)	;NO, IS IT IDLE?
	PJRST	ECOD2##		;NOT IDLE OR IN AN STR - ERROR 2
	MOVE	J,UNIKON##(U)	;KONTROLLER DATA BLOCK
	SKIPGE	KONUNL##(J)	;DOES DEVICE UNLOAD?
	PJRST	ECOD3##		;NO, ERROR 3
	PUSHJ	P,@KONUNL##(J)	;YES, UNLOAD IT
	  JFCL			;IGNORE IF UNIT NOT READY
	MOVEI	T2,O2COD##	;MARK UNIT AS DOWN,
	MOVEM	T2,UNISTS##(U)	; NO ONCE-A-MINUTE TYPOUT
IFN FTDUAL,<
	SKIPE	T1,UNI2ND##(U)
	MOVEM	T2,UNISTS##(T1)
>
	PJRST	CPOPJ1##	;AND TAKE GOOD RETURN

IFN FTDSUP,<
SETCPT::SKIPA	T1,[IORM T1,DEVCPT##(F)]
CLRCPT::MOVE	T1,[ANDCAM T1,DEVCPT##(F)]
	PUSHJ	P,SAVE1##	;SAVE P1
	MOVE	P1,T1		;SET TO CLEAR/SET THE BIT
	PUSHJ	P,GETWDU##	;GET USERS ARG
	PUSHJ	P,DVCNSG##	;FIND THE DDB
	  PJRST	ECOD1##		;NONE SUCH - ERROR 1
	PUSHJ	P,VALUUX	;LEGAL?
	  PJRST	ECOD1##		;NO, ERROR 1
	MOVE	U,DEVUNI##(F)	;YES, GET UNIT (IF SET UP)
	JUMPE	U,SETCP1
	MOVE	J,UNIKON##(U)	;KONTROLLER DATA BLOCK LOC
	SKIPG	KONRDC##(J)	;DOES DEVICE HAVE COMPAT. MODE?
	PJRST	ECOD2##		;NO, ERROR 2
SETCP1:	MOVSI	T1,DEPCPT##	;YES, GET THE BIT
	XCT	P1		;SET/CLEAR BIT IN DDB
	PJRST	CPOPJ1##	;AND TAKE GOOD RETURN
>
IFE FTDSUP,<
SETCPT::
CLRCPT::POPJ	P,0
>>
IFE FTRP04,<
SETCPT::
CLRCPT::
UNLOAD::POPJ	P,0
>
	SUBTTL	DIAG	DIAGNOSTIC UUO
IFN FTDHIA,<
DIAUUO::PUSH	P,T1
	HRR	M,T1		;ADR OF BLOCK
	PUSHJ	P,GETWDU##	;GET ARG
	CAIN	T1,7		;WANT KON/UNIT NUMBER?
	JRST	DIAKUN		;YES, TELL HIM
IFN FTVM,<
	SKIPE	.UPMP+.UPVRT
	JRST	[POP	P,(P)
		 JRST	DIAVRT]
>
	SETZ	T1,		;SAY WE WANT USER-IOT
	PUSHJ	P,STOTAC##	;TRPSET CALLS GETTAC
	PUSHJ	P,TRPSET##	;CHECK PRIVS, SET USRIOT
;IF GOING TO DO IO HE'D BETTER BE ALL IN CORE
	  JRST	[POP P,(P)	;NO PRIVS, LOSE
		JRST DIANPV]
	POP	P,T1		;OK, RESTORE ARGUMENT
	PUSHJ	P,STOTAC##	;TRPSET STORES 0 IN THE AC
	HLRE	T2,T1		;NUMBER OF ARGUMENTS
	JUMPGE	T2,WRONGN	;ILLEGAL N
	PUSHJ	P,SAVE3##
	MOVN	P1,T2		;P1=NUMBER OF ARGS
	CAIGE	P1,2		;LEGAL?
	JRST	WRONGN		;ILLEGAL N
	HRR	M,T1		;LOC OF ARG BLOCK
	PUSHJ	P,GETWDU##	;GET FUNCTION
	CAILE	T1,MXDIAG	;LEGAL?
	JRST	BADFNC		;ILLEGAL FNCN
	MOVE	P2,T1		;P2=FUNCTION CODE
	PUSHJ	P,GETWD1##	;GET KON/UNIT WORD
	LDB	T2,[POINT 7,T1,6] ;CONTROLLER NUMBER
	HLRZ	U,SYSUNI##	;FIND IT
DIADRS:	HRRZ	P3,UNIKON##(U)
	MOVE	T3,KONREG##(P3)
	ADDI	T3,KONEBK##(P3)	;POINT AT FIRST IO WORD IN KDB
	LDB	T3,[POINT 7,(T3),9] ;KON NUM FROM KDB
	CAIN	T3,(T2)		;MATCH?
	JRST	DIAUNI		;YES, WE HAVE KON
	HLRZ	U,UNISYS##(U)	;NO, STEP TO NEXT
	JUMPN	U,DIADRS	;AND TEST IT
	JRST	ILLKON		;ILLEGAL CONTROLLER NUMBER
DIAUNI:	LDB	T1,[POINT 3,T1,29] ;UNIT
DIAUN1:	LDB	T2,UNYPUN##	;UNIT FROM UDB
	CAMN	T1,T2		;RIGHT ONE?
	JRST	DIAUN2		;YES, SEE IF SAME KON
	HLRZ	U,UNISYS##(U)	;NO, TRY NEXT
	JUMPN	U,DIAUN1
	JRST	ILLUNI		;NO MATCH, YOU LOSE
DIAUN2:	HRRZ	T1,UNIKON##(U)	;ON SAME KONTROLLER?
	CAIE	T1,(P3)
	JRST	ILLUNI		;ILLEGAL UNIT

;STILL IN FTDHIA CONDITIONAL
DIADSP:	MOVE	F,.PDDIA##(W)	;SET UP F
	PUSHJ	P,@DIAFNC-1(P2)	;AND DISPATCH
NOUIO:	  SKIPA	T1,[XC.UIO]	;LOST - CLEAR USRIOT
	JRST	CPOPJ1##	;;WON - SKIP RETURN
	ANDCAM	T1,JOBPD1##(R)
	MOVSI	T1,NSWP!NSHF	;JOB IS NOW MOVABLE
	LDB	T2,PJOBN##
	ANDCAM	T1,JBTSTS##(T2)
	POPJ	P,

DIAFNC:	DIAASU			;(1)ASSIGN SINGLE UNIT
	DIAAAU			;(2)ASSIGN ALL UNITS
	DIARCU			;(3)RELEASE CHAN AND ALL UNITS
	DIASCP			;(4)SPECIFY CHANNEL PROGRAM
	DIARCP			;(5)RELEASE CHAN PROGRAM
	DIAGCS			;(6)GET CHAN STATUS
	DIAKUN			;(7)GET KONTROLLER AND UNIT
MXDIAG==.-DIAFNC
;STILL IN FTDHIA CONDITIONAL
;HERE TO ASSIGN SOME UNIT
DIAASU:	JUMPN	F,DIUNAA	;ALREADY HAVE SOME UNITS ASS'D
	HRRZ	T1,UNISTR##(U)	;TEST DIAG-MODE WHEN INPL'D
	JUMPN	T1,UNNDMD	;NOT IN DIAGNOSTIC MODE
	HLRZ	T1,UNIDIA##(U)	;JOB WHICH OWNS THIS UNIT
	CAME	T1,.C0JOB##	;SOME OTHER JOB HAVE IT?
	JUMPN	T1,UNAAJB	;UNIT ASS'D TO ANOTHER JOB
	JUMPN	T1,DIAHVF	;HAVE A DDB SET IF F NON-0
	PUSHJ	P,FAKDDB##	;GET A DDB
	  JRST	DINEFC		;NOT ENOUGH "FREE" CORE
	HRL	F,.C0JOB##	;SET UNIDIA=JOB,,DDB
	MOVEM	F,UNIDIA##(U)
	JRST	DIAHVF		;AND CONTINUE

;HERE TO ASSIGN ALL UNITS ON A CHANNEL
DIAAAU:	JUMPN	F,DIUNAA	;ALREADY HAVE SOME UNITS ASS'D
	HRRZ	T1,U		;SAVE STARTING-POINT
DIAAA1:	HRRZ	T2,UNISTR##(T1)	;TEST DIAG MODE WHEN IMPL'D
	JUMPN	T2,UNNDMD	;NOT IN DIAG MODE
	HLRZ	T2,UNIDIA##(T1)	;UNIT IN DIAG FOR SOME OTHER JOB?
	CAME	T2,.C0JOB##
	JUMPN	T2,UNAAJB	;UNIT ASS'D TO ANOTHER JOB
	SKIPE	T2		;HAVE A DDB SET UP ALREADY?
	HRRZ	F,UNIDIA##(T1)	;YES, SAVE IT
	HLRZ	T1,UNICHN##(T1)	;STEP TO NEXT UNIT ON CHAN
	CAIE	T1,(U)		;BACK WHERE WE STARTED?
	JRST	DIAAA1		;NO, TEST IT
	JUMPN	F,DIAAA2	;GO IF WE HAVE A DDB
	PUSHJ	P,FAKDDB##	;NONE, GET ONE
	  JRST	DINEFC		;NOT ENOUGH CORE
DIAAA2:	HRL	F,.C0JOB##	;SET JOB,,DDB IN EVERY UNIT
	HRRZ	T1,U		; ON THE CHANNEL
DIAAA3:	SKIPN	UNIDIA##(T1)
	MOVEM	F,UNIDIA##(T1)	;(IF NONE THERE ALREADY)
	HLRZ	T1,UNICHN##(T1)
	CAIE	T1,(U)
	JRST	DIAAA3

DIAHVF:	HRRZ	F,UNIDIA##(U)	;MAKE SURE F IS RIGHT
	MOVEM	U,DEVUNI##(F)	;SAVE U IN DDB
	MOVEM	F,.PDDIA##(W)	;SAVE DDB IN PDB
DIAHV1:	SKIPN	T1,DIADSK##	;SOME OTHER JOB IN DIAG ALREADY?
	JRST	DIASCH		;NO
	MOVEI	T1,2		;YES, SLEEP FOR A WHILE
	PUSH	P,F		;SLEEP ZEROS F
	PUSHJ	P,SLEEP##	;AND THEN TRY AGAIN
	POP	P,F
	JRST	DIAHV1
;STILL IN FTDHIA CONDITIONAL
DIASCH:	HRRZM	F,DIADSK##	;INDICATE WE WANT TO STOP IO
	DPB	T1,PDVTIM##	;NO HUNG TIMER TILL WE START
	MOVSI	T1,NSWP!NSHF	;SO SCHEDULER WONT TRY TO
	IORM	T1,JBTSTS##(J)	; SWAP US (STORE JOB IN FORCE)
	PUSHJ	P,SETACT##	;SET DDB IOACT
	HRRZ	T2,UNICHN##(U)	;SAY WHAT CHANNEL WE WANT TO STOP IO ON
	MOVEM	T2,DIACHN##
	PUSHJ	P,WINDWN	;ANNY IO GOING?
	  PUSHJ	P,WAIT1##	;YES, WAIT FOR IT TO STOP

;HERE WHEN ALL IO IS STOPPED ON THE DESIRED CHANNEL.
	HRROS	DIADSK##	;INDICATE WE'RE IN MIDDLE OF TEST
	SETZM	@DIACHN##	;CHAN IS BUSY
	CAIGE	P1,3		;TIME LIMIT GIVEN?
	TDZA	T1,T1		;NO, SET FOR 1 MINUTE
	PUSHJ	P,GETWD1##	;YES, GET IT
	IDIVI	T1,^D1000	;CONVERT TO SECS
	SKIPN	T1		;IF LESS THAN 1 SEC (OR NONE),
	MOVEI	T1,^D60		; SET TIMER FOR 1 MINUTE
	IDIVI	T1,^D120	;CONVERT TO 2-MINUTE CHUNKS
	MOVEM	T1,DEVDIA##(F)	;REMAINDER IN 2-MINUTE UNITS
	JFFO	T2,.+2		;COMPUTE N SUCH THAT 2^N .GT. TIME
	MOVEI	T3,44-1
	MOVNS	T3
	ADDI	T3,44
	DPB	T3,PDVTIM##	;STORE N IN DDB
	PUSHJ	P,SETACT##	;MAKE SURE IOACT IS ON
	PUSHJ	P,GETWDU
	HLLZ	T2,T1		;GET KONTROLLER DEVICE CODE
	LSH	T2,-3
	TLO	T2,(CONO)	;MAKE A CONO DEV,0
	XCT	T2		; AND EXECUTE IT
	PJRST	CPOPJ1##	;AND TAKE GOOD RETURN

;STILL IN FTDHIA CONDITIONAL
;HERE FROM STOP1A ON ^C OR EXIT, HALT,...  ;OR FROM HNGDSK
DIACLR::PUSHJ	P,FNDPDB##	;FIND FDB
	  POPJ	P,		;THERE ISN'T ONE
	SKIPN	F,.PDDIA##(W)	;DIAG GOING?
	POPJ	P,		;NO
	MOVE	U,DEVUNI##(F)	;YES, SET UP U
	PUSHJ	P,SAVE3##
	HRRZ	P3,UNIKON##(U)	;AND P3
	TLOA	F,-1		;INDICATE FROM DIACLR


;HERE TO RELEASE ALL UNITS
DIARCU:	JUMPE	F,CPOPJ1##	;EXIT IF NO DIAG SET UP
	PUSHJ	P,DIARCX	;GIVE UP IOWD BLOCK IF ONE EXISTS
	PUSH	P,U		;SAVE U
DIARC1:	HRRZ	T1,UNIDIA##(U)	;DDB WHICH HAS UNIT
	CAME	T1,.PDDIA##(W)	;OURS?
	JRST	DIARC2		;NO
	SETOM	UNICYL##(U)	;YES, SET SO WE'LL SEEK ON IT
	SETZM	UNIDIA##(U)	;NOW NO DIAG FOR IT
DIARC2:	HLR	U,UNICHN##(U)	;STEP TO NEXT UNIT
	CAME	U,(P)		;BACK WHERE WE STARTED?
	JRST	DIARC1		;NO, TEST IT
	SETZM	.PDDIA##(W)	;DONT HAVE A DIAG GOING
	SETZM	DIADSK##
	SETOM	@DIACHN##	;CHAN IS FREE
	SKIPL	F
	PUSHJ	P,NOUIO		;CLEAR SOME BITS
	PUSHJ	P,CLRDDB##	;RETURN DDB SPACE
	MOVE	T1,[CRNKUP,,1]	;CAUSE CANT START IO ON UUO LEVEL
	IDPB	T1,CLOCK##	; SO COME BACK WITH PD LIST NOT MAPPED
	IDPB	U,CLOCK##	;SAY WHICH UNIT (CHAN)
	JUMPL	F,TPOPJ##	;NON-SKIP IF CALLED FROM DIACLR
	SETZB	T1,F		;MAKE SURE CRNKUP IS CALLED BEFORE JOB CAN
	PUSHJ	P,SLEEP##	; DO ANOTHER DIAG.
	JRST	TPOPJ1##	;AND TAKE SKIP-RETURN
;STILL IN FTDHIA CONDITIONAL
;HERE TO SET UP A SCHANNEL PROGRAM
DIASCP:	JUMPE	F,NOASUN	;NO ASS'D UNITS
	PUSHJ	P,DIARCX	;RETURN ANY IOWD
	PUSHJ	P,GETWD1##	;GET IOWD
	MOVEM	T1,DEVDMP##(F)	;UNRELOCATED IOWD
	HLRE	T2,T1		;LENGTH OF IOWD
	JUMPE	T2,IOWCPB	;TOO BIG IF 0
	MOVEI	T1,1(T1)	;START ADDRESS
	MOVNS	T2		;+LENGTH
	ADDI	T2,-1(T1)	;TOP ADDRESS
	TRC	T2,(T1)		;LEGAL?
	TRNE	T2,777000
	JRST	[SETZM DEVDMP##(F) ;IOWD CROSSES PAGE BOUNDARY
		JRST IOWCPB]
	HRR	M,T1		;DO ADDRESS CHECKING WITH GETWD
	PUSHJ	P,GETWDU##	; TO INSURE THAT THE PAGE IS IN CORE
IFN FTKA10,<
	HRRZ	P2,KONCOM##(P3)	;WHERE IOWD IS TO GO
	MOVE	T2,DEVDMP##(F)	;IOWD
	ADDI	T2,(R)		;RELOCATE IT
	MOVEM	T2,(P2)		;AND SAVE IN LOW CORE
	SETZM	1(P2)		;TERMINATE LIST
>
IFN FTKI10!FTKL10,<
	MOVE	P3,UNICHN##(U)	;CHAN (FOR MAPIO)
	SETZ	P1,		;SAY FIRST CALL
	MOVE	T2,DEVDMP##(F)	;GET IOWD
	ADDI	T2,(R)		;RELOCATE IT
	HLL	T2,DEVDMP##(F)	;LH MIGHT HAVE INCR'D
	PUSHJ	P,MAPIO##	;RELOCATE THE IOWD
	  JRST	[SETZM DEVDMP##(F)
		JRST DINEFC]	;NO LOW-CORE BLOCKS
	SETZM	(P1)		;TERMINATE LIST
>
	MOVE	T1,UNIKON##(U)	;LOC OF KDB
	MOVE	T1,KONIOC##(T1) ;INITIAL CONTROL WD ADDR
	MOVE	T2,CHB22B##(P3)	;RH20?
	TLNE	T2,CP.RH2##
	TLO	P2,RH2JMP##	;YES, MAKE IT A JUMP
	MOVEM	P2,(T1)		;POINT ICWA AT CORE-BLOCK
	PUSHJ	P,STOTAC##	;TELL USER ICWA
IFN FTKL10,<
	PUSHJ	P,CSDMP##	;SWEEP CACHE
>
	JRST	CPOPJ1##	;AND TAKE GOOD RETURN

;STILL IN FTDHIA CONDITIONAL
;HERE TO RETURN A CHAN PROGRAM
DIARCP:	JUMPE	F,CPOPJ1##	;NOTHING TO DO IF NO DDB
	AOS	(P)
DIARCX:	SKIPN	DEVDMP##(F)	;NOTHING TO DO IF NO IOWD
	POPJ	P,
	SETZM	DEVDMP##(F)	;NOW NO IOWD
IFN FTKI10!FTKL10,<
	MOVE	T1,@KONIOC##(P3) ;GET LOC OF CORE-BLOCK
	PJRST	RTNIOW##	;RETURN THE SPACE
>
IFN FTKA10,<
	POPJ	P,
>


;HERE TO TELL USER FINAL CHAN STATS
DIAGCS:	JUMPE	F,CPOPJ1##	;NOTHING TO DO IF NO DDB
	MOVE	P2,KONIOC##(P3)	;GET ICWA
	CAILE	P1,4		;ASKING FOR TOO MUCH?
	MOVEI	P1,4		;YES, MAX HE CAN GET
DIAGC1:	MOVE	T1,(P2)		;GET A WORD FROM LOW CORE
	PUSHJ	P,PUTWD1##	;TELL USER
	SOJLE	P1,CPOPJ1##	;DONE IF 0
	AOJA	P2,DIAGC1	;GET NEXT WORD

;STILL IN FTDHIA CONDITIONAL
;HERE TO TEST IF IO IS GOING ON A CHANNEL
;RETURNS NON-SKIP IF YES, SKIPS IF CHAN IS NOW IDLE
WINDWN:	DSKOFF
	SKIPL	@DIACHN##	;#IO XFER HAPPENING?
	JRST	WINDW2		;#YES, NON-SKIP
	MOVE	T2,U		;#NO, TEST UNITS FOR POSITIONING
WINDW1:	MOVE	T1,UNISTS##(T2)	;#UNIT SEEKING?
	CAIE	T1,PCOD##
	CAIN	T1,SCOD##
	JRST	WINDW2		;#YES, NON-SKIP
	HLR	T2,UNICHN##(T2)	;#NO, TEST NEXT UNIT
	CAME	T2,U		;#BACK WHERE WE STARTED?
	JRST	WINDW1		;#NO
	AOS	(P)		;#YES, ALL IS NOW QUIET
WINDW2:	DSKON
	POPJ	P,

;ROUTINE TO START UP IO ON A CHANNEL AGAIN
;CALLED ON PI7 SINCE CANT START IO FOR DIFFERENT JOB ON UUO LEVEL
CRNKUP:	PUSHJ	P,SAVE1##
	MOVE	U,T1
	MOVE	P1,DIACHN##	;POINT P1 AT CHANNEL
	SETZM	@DIACHN##	;SO ON-CYLINDER UNITS WONT START IO
	PUSH	P,U
CRNKU1:	HLR	U,UNICHN##(U)	;NEXT UNIT
	DSKOFF
	MOVE	T1,UNISTS##(U)	;#DOES IT WANT POSITIONING?
	CAIN	T1,PWCOD##
	PUSHJ	P,CRNPOS	;#YES, START THE SEEK GOING
	CAME	U,(P)		;#BACK WHERE WE STARTED?
	JRST	CRNKU1		;#NO, TEST NEXT UNIT
	POP	P,(P)		;#YES, REMOVE JUNK FROM PDL
	MOVE	J,UNIKON##(U)	;#GO START AN XFER IF CHNQUE NOT EMPTY
	DSKOFF			;#FIGHT RACE
	SKIPL	KONTAB##(J)	;#DID A SEEK FINISH?
	PJRST	PIKTRX		;#NO, START IO IF CHNQUE NOT EMPTY
	DSKON			;#YES, FORGET IT
	POPJ	P,		;# SINCE IO IS ALREADY GOING

;STILL IN FTDHIA CONDITIONAL
;HERE TO GET KONTROLLER/UNIT
DIAKUN:	POP	P,(P)		;REMOVE SAVED T1
	PUSHJ	P,GETWD1##	;GET DEVICE NAME
	PUSHJ	P,DEVSRG##	;FIND IT
IFE FTDUAL,<
	  JRST	ILLUNI		;NO SUCH UNIT
>
IFN FTDUAL,<
	  JRST	TRYALT
>
	MOVE	T2,DEVMOD(F)
	TLNE	T2,DVMTA	;IS IT A MAG TAPE?
	JRST	DIAMTA##	;YES, LET TAPUUO DO IT
	CAIE	F,DSKDDB##	;NO, SOME FLAVOR OF DISK
	MOVE	T1,DEVNAM(F)
	PUSHJ	P,MSKUNI##	;SET UP A MASK
	PUSHJ	P,SRUNI##	;FIND UDB
	  JRST	ILLUNI		;NOT A DISK UNIT
	  JFCL
DIAKU1:	MOVE	T1,UNIKON##(U)	;KONTROLLER
	MOVE	T2,KONREG##(T1)
	ADDI	T2,KONEBK##(T1) ;POINT TO FIRST I/O INST IN KDB
	LDB	T2,[POINT 7,(T2),9] ;GET KONTROLLER CODE
	LDB	T1,UNYPUN##	;UNIT NUMBER
	LSH	T1,3		;POSITION IT
	LSH	T2,2		;POSITION KONTROLLER DEVICE CODE
	HRL	T1,T2
	AOS	(P)		;SET FOR SKIP-RETURN
	PJRST	STOTAC##	;TELL USER KON,,UNIT AND RETURN
IFN FTDUAL,<
;HERE ON NO UNIT, TEST SECOND UNITS
TRYALT:	HLRZ	T2,SYSUNI##	;START AT BEGINNING
TRYAL1:	SKIPLE	T3,UNI2ND##(T2)	;IS THERE A SECOND UNIT?
	CAME	T1,UNINAM##(T3)	;YES, RIGHT ONE?
	CAIA
	JRST	TRYAL2		;WE FOUND IT
	HLRZ	T2,UNISYS##(T2)	;NO MATCH, TRY NEXT
	JUMPN	T2,TRYAL1
	PJRST	ILLUNI		;NO SUCH UNIT, ERROR
TRYAL2:	MOVE	U,T3		;POINT U AT SECOND UNIT
	JRST	DIAKU1		;AND TELL HIM WHAT IT IS
>
ERCODE	DIANPV,1		;NOT ENOUGH PRIVS
ERCODE	WRONGN,2		;ILLEGAL NUMBER OF ARGS
ERCODE	ILLKON,3		;ILLEGAL CONTROLLER NUMBER
ERCODE	ILLUNI,4		;ILLEGAL UNIT NUMBER
ERCODE	DIUNAA,5		;SOME UNITS ALREADY ASSIGNED
ERCODE	UNNDMD,6		;UNIT NOT IN DIAG MODE
ERCODE	UNAAJB,7		;UNIT ASS'D TO ANOTHER JOB
ERCODE	DINEFC,10		;NOT ENOUGH FREE CORE
ERCODE	NOASUN,11		;NO ASSIGNED UNITS
ERCODE	IOWCPB,12		;IOWD CROSSES PAGE BOUNDARY
ERCODE	BADFNC,13
ERCODE	DIAVRT,14
>		;END FTDHIA
	SUBTTL	MONITOR MODE IO ROUTINES
;SUBROUTINE TO COPY POINTERS FROM DDB TO MONITOR BUFFER
;ENTER WITH T1=AOBJN WORD FOR ENTIRE MONITOR BUFFER
;EXIT CPOPJ IF MON BUF IS FULL AND THERE ARE MORE POINTERS IN DDB
;EXIT CPOPJ1 NORMALLY, WITH T1=LOC OF LAST PNTR STORED
;PRESERVES T4
DD2MN::	MOVE	T3,UNISTR##(U)	;GET ADDR OF STR DATA BLOCK
	SETO	T2,		;CREATE MASK FOR
	LDB	T2,STYCLP##(T3)	;CLUSTER POINTER
	MOVEM	T2,MSKLOC	;STORE IT FOR LATER
	MOVSI	T2,DEPLPC##	;LAST POINTER IN CORE BIT
	ANDCAM	T2,DEVLPC##(F)	;CLEAR THE BIT IF IT WAS ON
	LDB	T2,DEYRLC##	;CURRENT POINTER LOC
	HRLS	T2
	ADD	T1,T2		;UPDATE AOBJN WORD
	MOVSI	T2,MPTRLN##	;LENGTH OF A BUNCH OF POINTERS
	HRRI	T2,DEVRB1##(F)	;LOC OF 1ST POINTER
	HRRM	T2,DEVRET(F)	;SET DEVRET=DEVRB1
	SKIPE	(T2)		;FIRST POINTER EMPTY
	JUMPGE	T1,CPOPJ##	;NO, POPJ IF NO SLOTS
DD2MN2:	SKIPN	T3,(T2)		;GET A POINTER FROM DDB
	SOJA	T1,CPOPJ1##	;ALL POINTERS COPIED - RETURN
	EXCH	T3,(T1)		;STUFF IT IN MON BUF
	JUMPE	T3,DD2MN3	;IF OLD PNTR=0, OK
	XOR	T3,(T1)		;XOR RIB & MON BUF
	TDNE	T3,MSKLOC	;IF PNTRS EQUAL, SKIP
	STOPCD	.+1,DEBUG,PDA,	;++POINTERS WITH DIFFERENT ADDRESSES
DD2MN3:	SETZM	(T2)		;ZERO THE WORD IN DDB
	AOBJP	T2,CPOPJ1##	;THROUGH WHEN DDB RUNS OUT
	AOBJN	T1,DD2MN2	;DO ANOTHER
	SKIPN	(T2)		;MON BUF FULL. MORE POINTERS?
	SOJA	T1,CPOPJ1##	;NO, TAKE SKIP-RETURN
TWOLOC:	POPJ	P,2		;MON BUF FULL AND MORE TO GO
	$LOW
MSKLOC:	Z			;WORD FOR MASK FOR CLUSTER POINTER
	$HIGH
;SUBROUTINE TO READ RIB POINTERS INTO CORE
;GETS A MONITOR BUFFER AND READS THE RIB INTO IT
;RETURNS T3=0 IF OK, T3=-1 IF ERROR; AND T1=AOBJN WORD FOR THE POINTERS
PTRGET::PUSHJ	P,BUFRIB	;GET MON BUF, READ RIB INTO IT
	  SKIPA	T3,[-1]		;RIB ERROR - RETURN T3=-1
	SETZ	T3,		;OK - T3=0
	PJRST	SPTRW		;SET T1=AOBJN WORD AND RETURN


;SUBROUTINE TO COPY CURRENT POINTERS FROM MON BUF TO DDB
;ENTER WITH RIB IN MON BUF, T1=AOBJN WORD FOR POINTERS
;EXIT WITH POINTERS COPIED INTO DDB, DEYRLC UPDATED
PTRCPY::LDB	T2,DEYRLC##	;PREVIOUS POINTER RELATIVE LOC
	SKIPN	DEVRB2(F)	;DEVRB2=0?(PNTR CAME FROM A.T. IF YES)
	SKIPA	T2,TWOLOC	;YES. START AT 3RD ENTRY IN PNTRS
	ADDI	T2,PTRLEN##	;+LENGTH OF A BUNCH OF POINTERS
	HRLS	T2
	ADD	T1,T2		;UPDATE AOBJN WORD (WAS FOR WHOLE POINTER AREA)
				;AND FALL INTO PTRBLT
;SUBROUTINE TO BLT POINTERS FROM MONITOR BUFFER TO DDB
;ENTER WITH T1=AOBJN WORD FOR CURRENT POINTERS IN MONITOR BUFFER
PTRBLT::MOVE	T3,T1		;SAVE CURRENT AOBJN WRD
	PUSHJ	P,SPTRW		;GET AOBJN WRD FOR WHOLE MON BUF
	HRRZ	T2,T3		;CURRENT PNTR LOC
	SUBI	T2,(T1)		;-ORIGINAL PNTR LOC=NEW DEYRLC
	MOVE	T1,T3		;RESTORE CURRENT AOBJN WORD

;SUBROUTINE TO COPY POINTERS FROM MON BUF TO DDB
; STORE DEVRLC WITHOUT COMPUTING
;ENTER T1=AOBJN WORD FOR POINTERS
PTRBL1::DPB	T2,DEYRLC##	;SAVE IN DDB
	HLLM	T1,DEVRSU##(F)	;-NO OF PNTRS LEFT
	MOVSI	T2,MPTRLN##
	HRRI	T2,DEVRB1##(F)	;AOBJN WORD FOR DDB
	HRRM	T2,DEVRET##(F)
PTRBL2:	SKIPA	T3,(T1)		;NEXT POINTER
PTRBL3:	SETZ	T3,		;POINTERS DONE-ZERO
	MOVEM	T3,(T2)		;SAVE IN DDB
	AOBJP	T2,PTRBL4	;COUNT DDB WORD
	AOBJN	T1,PTRBL2	;GET NEXT POINTER
	JRST	PTRBL3		;THOUGH WITH MON BUF

PTRBL4:	MOVE	T3,DEVLPC##(F)	;GET LAST POINTER IN CORE WORD
	AOBJN	T1,PTRBL5	;JUMP IF MORE POINTER SLOTS IN RIB
	PUSHJ	P,GTLPT##	;GET LAST RIB POINTER
	SKIPE	T2		; DONT LIGHT DEPLPC IF 0
	TLOA	T3,DEPLPC##	;NO MORE LEFT, LAST POINTER IS IN CORE
PTRBL5:	TLZ	T3,DEPLPC##	;LAST IS NOT IN CORE
	MOVEM	T3,DEVLPC##(F)	;RESTORE THE FLAG
IFE FTDSIM,<
	POPJ	P,		;AND RETURN
>
IFN FTDSIM,<
;IF ACYWCT .GTR. 0 ,ACPSBC=1, THEN IF A WRITER CHANGES A CHECKSUM
; AND THE NEW PNTR ISNT IN SOME OTHER WRITER'S DDB, IF THE 2ND WRITER
; THEN CHANGES THE CHECKSUM OF THAT PNTR AND CLOSES BEFORE THE 1ST WRITER
; (ORIGINAL ALLOCATER), THEN WHEN THE 1ST WRITER CLOSE WE HAVE A 
; CHECKSUM ERROR IN THE FILE.
;HENCE, WE HAVE TO SCAN DDBS FOR A WRITER WITH THIS PNTR, USE THE PNTER
; FROM THE FOUND DDB.

;STILL IN FTDSIM CONDITIONAL
	HRRZ	T1,DEVACC##(F)	;LOC OF A.T.
	JUMPE	T1,CPOPJ##
	LDB	T2,ACYWCT##	;ARE THERE MULTIPLE WRITERS?
	SOJLE	T2,CPOPJ##
	MOVE	T2,ACCSBC##(T1)	;YES, HAS CHECKSUM CHANGED?
	TRNN	T2,ACPSBC##
	JRST	CPOPJ##		;NO
	MOVE	T3,UNISTR##(U)	;YES, SET UP A MASK FOR
	SETO	T2,		; ADDR PORTION OF RETRIEVAL POINTERS
	LDB	T2,STYCLP##(T3)
	MOVEM	T2,MSKLOC	;AND SAVE IT AWAY
	PUSHJ	P,FNDDDB	;FIND A WRITING DDB FOR THE FILE
	  POPJ	P,		;NONE (DDB COULD BE IN EXTRIB)
PTRBL6:	HRRZ	T3,DEVUNI##(T2)	;SAME UNIT?
	CAIE	T3,(U)
	JRST	PTRBLB		;NO, TRY NEXT DDB
	MOVEI	T3,DEVRB1##(F)	;YES, SET AN AOBJN WORD FOR OUR DDB
	HRLI	T3,MPTRLN##
PTRBL7:	MOVEI	T1,DEVRB1##(T2)	;SET AOBJN WORD FOR POINTERS
	HRLI	T1,MPTRLN##	; FOR THE FOUND-DDB
PTRBL8:	MOVE	T4,(T1)		;GET A DDB POINTER
	JUMPE	T4,PTRBL9	;KEEP GOING IF 0 (PNTR MIGHT BE IN DEVRBN)
	XOR	T4,(T3)		;IS IT IN OUR DDB?
	TDNN	T4,MSKLOC
	JRST	PTRBLA		;YES, COPY PNTRS TO OUR DDB
PTRBL9:	AOBJN	T1,PTRBL8	;NO, TRY NEXT PNTR
	SKIPE	1(T3)		;ANY MORE PNTRS IN OUR DDB?
	AOBJN	T3,PTRBL7	;YES, TRY TO MATCH NEXT POINTER
	JRST	PTRBLB		;NO MATCH - TRY NEXT DDB

;HERE WHEN WE FOUND A MATCH BETWEEN THE WRITING DDB AND OUR DDB
PTRBLA:	MOVE	T4,(T1)		;GET PNTR FROM THE FOUND DDB
	MOVEM	T4,(T3)		;AND STUFF IT IN OURS
	AOBJP	T3,CPOPJ##	;THROUGH IF OUR DDB FULL
	SKIPN	(T3)		; OR IF NO MORE PNTRS IN OUR DDB
	JRST	CPOPJ##
	SKIPE	1(T1)		;IS THERE ANOTHER PNTR IN FOUND DDB?
	AOBJN	T1,PTRBLA	;YES, COPY INTO OUR DDB
PTRBLB:	PUSHJ	P,FNDDDN	;NO, FIND ANOTHER WRITING DDB
	  POPJ	P,		;NO - DONE
	JRST	PTRBL6		;FOUND ANOTHER - TEST ITS PNTRS
>


;SUBROUTINE TO SET AN AOBJN WORD FOR POINTERS IN THE MONITOR BUFFER
;ENTER WITH LOC OF MON BUF IN DEVMBF,  EXIT WITH T1=AOBJN WORD
;T2-T4 RESPECTED
SPTRW::	HRRZ	T1,DEVMBF##(F)	;LOC OF MON BUF (-1)
	ADD	T1,RIBFIR##+1(T1)	;AOBJN WORD (-1)
	AOJA	T1,CPOPJ##	;MAKE REAL AOBJN WORD AND RETURN
;SUBROUTINE TO GET A MONITOR BUFFER
;RETURNS WITH DEVMBF=T1=AN IOWD FOR THE BUFFER
IFN FTDSIM,<
;PRESERVES T2
GTMB2:	PUSH	P,T2		;SAVE T2
	PUSHJ	P,GTMNBF	;GET THE MON BUF
	PJRST	T2POPJ##	;RESTORE T2 AND RETURN
>
GTMNB:
;(SAME AS GTMNBF, PRESERVES T3)
GTMNBF::SKIPE	T1,DEVMBF##(F)	;JOB ALREADY HAVE MONITOR BUFFER?
	STOPCD	CPOPJ##,DEBUG,AHB, ;++ALREADY HAVE BUFFER
GTMNB0::SETZ	T4,		;DONT ADJUST MQREQ THE FIRST TIME
GTMNB4:	PUSHJ	P,MQWAIT##	;GET A MON BUF, WAIT IF NONE FREE
	ADDM	T4,MQREQ##	;ADJUST MQREQ IF THIS IS SECOND TRY TO GET THE BUFFER
GTMNB1::HRRZ	T1,BUFLST##	;GOT ONE - SCAN LIST FOR A FREE BUF
	NOSCHEDULE
GTMNB2:	SKIPL	T2,(T1)		;THIS BUFFER FREE?
	JRST	GTMNB3		;YES. MARK IT
	HRRZ	T1,T2		;NO. STEP TO NEXT IN LIST
	JUMPN	T1,GTMNB2	;TRY THIS ONE
	MOVNI	T4,1		;ADJUST MQREQ AFTER NEXT CALL TO MQWAIT
				;(CANT SOS MQREQ NOW, AS THAT COULD MAKE MQREQ=-1)
	JRST	GTMNB4		;COUNTS ARE OFF - REQUEUE THIS REQUEST
				;(CAN HAPPEN IF SCHED GIVES MON BUF TO A USER WHO IS
				;SWAPPED, AND  NEXT MON BUF IS RELEASED- MQFRE1 IS OFF)
GTMNB3:	HRROS	(T1)		;MARK BUFFER AS TAKEN
	SCHEDULE
	HRLI	T1,MBLKSZ##	;SET T1 AS AN IOWD FOR THE BUFFER
	MOVEM	T1,DEVMBF##(F)	;SAVE IOWD IN DDB
	LDB	T2,PJOBN##	;JOB NUMBER
	MOVEM	T2,MBFJOB##(T1)	;SAVE IN BUFFER
	PJRST	MBFCNT		;COUNT CURRENT NO OF MON BUFS AND RETURN
GVMNBX::
IFN FTDSIM,<
	TLNE	S,IOSDA		;HAVE DA?
	POPJ	P,		;YES, KEEP MON BUF
>
;SUBROUTINE TO RTURN MONITOR BUFFER
;ENTER WITH DEVMBF(F)= LOC OF MONITOR BUFFER
GVMNB0::SKIPN	T1,DEVMBF##(F)	;LOC OF MON BUFFER
	STOPCD	CPOPJ##,DEBUG,DHB, ;++DON'T HAVE BUFFER


;SUBROUTINE TO RETURN THE MONITOR BUFFER
;ENTER WITH T1 = ADDRESS OF THE BUFFER
;(PRESERVES T3 ON THE SLY)
GVMNBF::HRRZS	(T1)		;MARK BUFFER FREE
	HRLZS	MBFJOB##(T1)	;SAVE LAST USER
	PUSHJ	P,MQFREE##	;BUMP COUNT OF FREE BUFFERS
	TLZ	S,IOSRIB	;NO LONGER HAVE RIB IN MONITOR BUFFER
	SETZM	DEVMBF##(F)	;NO LONGER HAVE MONITOR BUFFER

;COUNT THE NUMBER OF FREE MONITOR BUFFERS
;NEEDED SINCE IF 2 JOBS GIVE UP 2 DIFFERENT MON BUFS BEFORE THE SCHEDULER HAS LOOKED AT MQREQ
;ONLY 1 JOB WILL BE GIVEN A MON BUF, AND THE OTHER BUF WILL NOT BE USED AGAIN
MBFCNT:	SKIPE	DINITF##	;ONCE-ONLY?
	PJRST	STRIOS		;YES--RETURN
	SETOM	MQFRE1##	;INITIALIZE COUNT
	HRRZ	T2,BUFLST##	;LOC OF 1ST MON BUF
	SKIPL	T2,(T2)		;FREE?
	AOS	MQFRE1##	;YES. INCREMENT NO OF FREE BUFS-1
	HRRZS	T2		;ADR FIELD ALONE
	JUMPN	T2,.-3		;TEST NEXT MON BUF
	PJRST	STRIOS		;RETURN WITH MQFRE1=# OF MON BUFS -1
;SUBROUTINE TO GET THE MONITOR BUFFER, READ RIB INTO IT
;RETURNS CPOPJ IF ERROR, CPOPJ1 NORMALLY, T1=IOWD
BUFRIB::PUSHJ	P,GTMNBF	;GET MONITOR BUFFER
				;AND FALL INTO REDRIB
;SUBROUTINE TO READ THE PRIME RIB, T1=IOWD
;RETURNS CPOPJ IF ERROR, CPOPJ1 NORMALLY
REDRIB::JSP	T4,SAVUN	;PUSH U, SET UP U FOR RIB
	PUSHJ	P,PRMRIB	;SET UP TO READ THE PRIME RIB
	SKIPN	T1,DEVMBF##(F)	;GET IOWD TO MONITOR BUFFER
	STOPCD	RESUNI,DEBUG,MHB, ;++MUST HAVE BUFFER
	PUSHJ	P,MONRED	;READ THE RIB INTO MON BUF
	PUSHJ	P,RIBCHK	;CHECK RIB
	  JRST	RESUNI		;RIB ERR - NON SKIP RETURN
	AOS	-1(P)		;GOOD RIB - SET FOR SKIP RETURN
				;FALL INTO RESUNI AND RETURN

;SUBROUTINE TO RESTORE DEVUNI
;ENTER WITH CURRENT U SAVE ON PD LIST
RESUNI:	POP	P,U		;CURRENT U
	SKIPE	DEVUNI##(F)	;UNLESS UNIT(STR) WAS REMOVED
	HRRM	U,DEVUNI##(F)	;SAVE IN DDB
	POPJ	P,		;AND RETURN


;SUBROUTINE TO WRITE A RIB
WRTRIB::PUSHJ	P,SAVE1##
	PUSH	P,U		;SAVE CURRENT UNIT
	HRRZ	T1,DEVACC##(F)	;LOC OF A.T.
	JUMPE	T1,WRTRB1
	MOVE	P1,ACCSTS##(T1)	;STATUS OF FILE
	TRNN	P1,ACPUPD	;UPDATE?
	JRST	WRTRB1		;NO
	MOVE	T4,DEVMBF##(F)	;MON BUF
	SKIPE	T3,ACCWRT##(T1)	;NO OF BLOCKS IN FILE
	SUBI	T3,1
	LSH	T3,BLKLSH##	;CONVERT TO WORDS
	HRRZ	T2,T1
	LDB	T1,ACYLBS##	;SIZE OF LAST BLOCK
	ADD	T3,T1		;NO OF WORDS IN FILE
	MOVEM	T3,RIBSIZ##+1(T4)  ;SAVE IN RIB
	MOVE	T1,ACCALC##(T2)	;NO OF BLOCKS ALLOCATED
	EXCH	T1,RIBALC##+1(T4)  ;SAVE IN RIB
	CAMN	T1,RIBALC##+1(T4)  ;DID THE SIZE CHANGE?
	JRST	WRTRB1		;NO
	SETZ	P1,		;YES, SET P1=0 AS A FLAG
	PUSHJ	P,RIBSAT##	;YES, WRITE CHANGED SATS (IN CASE OF A CRASH)
;HERE WITH SATS WRITTEN IF NECCESSARY
WRTRB1:	LDB	T2,DEYRBU##	;GET CURRENT RIB LOGICAL UNIT NUMBER
	PUSHJ	P,NEWUNI	;SET UP U,DEVUNI
	STOPCD	RESUNI,DEBUG,NXU, ;++NON X UNIT
	LDB	T2,DEYRBA##	;GET CURRENT RIB CLUSTER ADDRESS
	LDB	T3,UNYBPC##	;BLOCKS PER CLUSTER, THIS UNIT
	IMUL	T2,T3		;BLOCK NUMBER IN T2
	SKIPN	T1,DEVMBF##(F)	;GET IOWD TO MONITOR BUFFER
	STOPCD	RESUNI,DEBUG,NMB, ;++NEED MONITOR BUFFER
	MOVEM	T2,RIBSLF##+1(T1)  ;SAVE BLOCK NUMBER
	MOVEI	T3,CODRIB##
	MOVEM	T3,RIBCOD##+1(T1)  ;INDICATE BLOCK IS A RIB
	PUSHJ	P,MONWRT	;WRITE HE MON BUF
IFN FTDMRB,<
	JUMPN	P1,WRTRB3	;IF AN UPDATE FILE
	SKIPL	P1,DEVRIB##(F)	; AND IN EXTENDED RIB
	JRST	WRTRB3
	PUSH	P,RIBALC##+1(T1)  ;SAVE RIBSIZ, RIBALC
	PUSH	P,RIBSIZ##+1(T1)
	PUSHJ	P,REDRIB	;READ THE PRIME RIB
	  JRST	[POP P,(P)	;RIB ERROR
		 POP P,(P)
		 JRST WRTRB2]
	POP	P,RIBSIZ##+1(T1)
	POP	P,RIBALC##+1(T1)  ;RESTORE RIBSIZ, RIBALC
	PUSHJ	P,RIBAD		; FOR USE IN CASE OF CRASH
	JSP	T4,RIBUN
	PUSHJ	P,MONWRT	;REWRITE PRIME RIB
WRTRB2:	MOVEM	P1,DEVRIB##(F)
	PUSHJ	P,RIBCUR	;REREAD EXTENDED RIB
	  JFCL
WRTRB3:>	;END FTDMRB
	POP	P,U		;RESTORE CURRENT UNIT
	HRRM	U,DEVUNI##(F)	;AND SAVE IN DDB
	POPJ	P,		;AND RETURN
;SUBROUTINE TO SET UP TO READ THE PRIME RIB
;RETURNS CPOPJ WITH DEVRIB SET UP IN DDB
PRMRIB::PUSHJ	P,RIBAD		;COMPUTE ADR OF RIB
	SETZM	DEVRIB##(F)	;CLEAR DEVRIB FOR DPB'S
	LDB	T3,UNYLUN##	;GET RPIME RIB LOGICAL UNIT NUMBER
	DPB	T3,DEYRBU##	;DEPOSIT IN DDB
	PUSH	P,T2		;SAVE T2
	HRRZ	T2,DEVACC##(F)	;GET ADDRESS OF A.T.
	MOVE	T2,ACCPT1##(T2)	;GET FIRST POINTER FOR FILE
	LDB	T3,STYCLP##(T4)	;GET CLUSTER ADDRESS
	DPB	T3,DEYRBA##	;DEPOSIT IN DDB
	PJRST	T2POPJ##	;RESTORE T2 AND RETURN


;SUBROUTINE TO SAVE CURRENT DEVUNI
;U CHANGED, ALL OTHER ACS RESPECTED
;SAVES THE CURRENT U ON THE PUSH DOWN LIST
;ENTER AT SAVUN TO SAVE CURRENT U, AT RIBUN JUST TO SET U FOR RIB UNIT
;CALLED WITH JSP T4
SAVUN::	PUSH	P,U		;SAVE CURRENT U
RIBUN::	HLRZ	U,DEVUNI##(F)	;UNIT OF RIB
	HRRM	U,DEVUNI##(F)	;SAVE AS CURRENT UNIT
	JRST	(T4)		;AND RETURN


;SUBROUTINE TO COMPUTE A RIB ADDRESS FROM THE ACCESS TABLE
;EXIT WITH T2=BLOCK NUMBER, T4=LOC OF STR DATA BLOCK
;T1 RESPECTED
RIBAD::	HRRZ	T2,DEVACC##(F)	;LOC OF ACCESS TABLE
	MOVE	T2,ACCPT1##(T2)	;GET 1ST POINTER FROM A.T.

;SUBROUTINE TO COMPUTE A DISK ADDRESS FROM A GROUP POINTER
;ENTER WITH GROUP POINTER IN T2
;EXIT WITH T2=BLOCK NUMBER, T4=LOC OF STR DATA BLOCK
;T1 RESPECTED
GRPAD::	MOVE	T4,UNISTR##(U)	;GET BYTE POINTER FOR ADDRESS OF CLUSTER
	LDB	T2,STYCLP##(T4)	;CLUSTER ADDRESS
	LDB	T3,UNYBPC##	;COMPUTE BLOCK ADDRESS (NOTE THAT
	IMUL	T2,T3		; RELATIVE BLOCK 0 OF 1ST POINTER IS RIB LOC)
	POPJ	P,		;AND RETURN
;SUBROUTINE TO READ A UFD RIB
;ENTER WITH T2=BLOCK NUMBER
;RETURNS CPOPJ IF ERROR, CPOPJ1 NORMALLY WITH T1=IOWD, T2=BLOCK
UFDRED::MOVE	T1,DEVMBF##(F)	;IOWD FOR MONITOR BUFFER
	PUSHJ	P,MONRED	;READ THE RIB
	MOVE	T4,T2		;BLOCK NUMBER (FOR RIBERR)
	JUMPN	T3,RIBERR	;CHECK FOR ERRORS DETECTED IN MONRED
	CAME	T2,RIBSLF##+1(T1)	;CHECK BLOCK NUMBER
	JRST	RIBERR		;BAD
	MOVEI	T3,CODRIB##	;OK. CHECK RIB CODE WORD
	CAME	T3,RIBCOD##+1(T1)
	JRST	RIBERR		;BAD
	HLRZ	T3,RIBEXT##+1(T1)	;OK. CHECK EXT="UFD"
	CAIN	T3,(SIXBIT .UFD.)
	SKIPA	T3,RIBNAM##+1(T1) ;OK, GET PPN
	JRST	UFDRE2		;NOT "UFD", CHECK IF "SFD"
	CAME	T3,DEVPPN(F)	;UFD, CHECK PPN
	CAMN	T3,DEVFIL(F)
	SKIPA	T3,RIBSTS##+1(T1)	;OK, GET STATUS WD
	JRST	RIBERR		;BAD
	TROE	T3,RIPABC##	;RIPABC ON?
	PJRST	CPOPJ1##	;YES
	MOVEM	T3,RIBSTS##+1(T1)	;NO, LIGHT IT (UFD WRITTEN BY OLDER MON)
	PUSHJ	P,MONWRT	;REWRITE THE UFD RIB

	JRST	CPOPJ1##	;OK, RETURN

;HERE IF EXTENSION ISN'T "UFD"
UFDRE2:
IFN	FTSFD,<
	CAIE	T3,(SIXBIT .SFD.) ;IS IT "SFD"?
	JRST	RIBERR		;NO, BAD RIB
	HRRZ	T3,DEVSFD##(F)	;YES, GET NAME OF SFD
	MOVE	T3,NMBNAM##(T3)	; FROM ITS ACCESS TABLE
	CAMN	T3,RIBNAM##+1(T1) ;CHECK NAME IN RIB
	JRST	CPOPJ1##	;OK, SKIP-RETURN
>
	JRST	RIBERR		;ERROR


;SUBROUTINE TO READ A RIB INTO THE MONITOR BUFFER
;ENTER WITH T2 = DISK ADDRESS OF RIB
;RETURNS CPOPJ IF ERROR, CPOPJ1 NORMALLY
RIBRED::MOVE	T1,DEVMBF(F)	;T1=IOWD FOR THE DATA
	PUSHJ	P,MONRED	;READ THE RIB
				;AND FALL INTO RIBCHK
;SUBROUTINE TO CHECK THE VALIDITY OF A RIB
;ENTER WITH T1 = IOWD, T2 = DISK ADDRESS
;RETURNS CPOPJ IF ERROR, CPOPJ1 NORMALLY, T1=IOWD
RIBCHK:	MOVE	T4,T2		;BLOCK (FOR RIBERR)
	TRNN	T3,IOIMPM+IODTER+IODERR+IOBKTL	;ERROR BIT FORM READ?
	CAME	T2,RIBSLF##+1(T1)	;BLOCK NUMBER IN RIB RIGHT?
	JRST	RIBERR		;NO. ERROR
	MOVEI	T2,CODRIB##	;YES. IS THIS BLOCK A RIB?
	CAME	T2,RIBCOD##+1(T1)
	JRST	RIBERR		;NO. ERROR
	MOVE	T2,DEVFIL(F)	;YES. FILE NAME
	CAME	T2,RIBNAM##+1(T1)	;MATCH NAME IN RIB?
	JRST	RIBERR		;NO. ERROR
	HLLZ	T2,DEVEXT(F)	;YES. FILE EXTENSION
	HLLZ	T3,RIBEXT##+1(T1)	;EXT STORED IN RIB
	CAME	T2,T3		;MATCH?
	JRST	RIBERR		;NO. ERROR
	HRRZ	T3,RIBFIR##+1(T1)	;YES. REL LOC OF 1ST PNTR IN RIB
	CAILE	T3,BLKSIZ##-2	;LEGAL?
	JRST	RIBERR		;NO. ERROR
	MOVE	T2,DEVPPN(F)	;YES. PPN OF FILE
	CAME	T2,RIBPPN##+1(T1)	;MATCH PPN IN RIB?
	JRST	RIBERR		;NO,RIB ERR
	TLNN	F,RENMB		;DON'T CHECK RIBUFD ON RENAME
				;  ONLY ON RENAME FOR EXTENDED RIBS
	PUSHJ	P,GTUFR##	;YES, COMPUTE RIBUFD WORD
	  PJRST	CPOPJ1##		;NOT YET SET UP, ASSUME OK
	CAMN	T2,RIBUFD##+1(T1)	;MATCH?
	PJRST	CPOPJ1##	;YES, OK RETURN
	ADDI	T2,1		;THE OLD REFRESHER PUT THIS NUMBER 1 TOO LOW
	CAMN	T2,RIBUFD##+1(T1)	; SO ALLOW THAT CASE TO WIN
	PJRST	CPOPJ1##
	MOVE	T4,RIBSLF##+1(T1)  ;RESTORE BLOCK NUMBER
;HERE WHEN THE RIB INFORMATION IS NOT CORRECT.
RIBERR::MOVEI	T1,UNPRER##	;SET UP A BIT
	ADDM	T1,UNIMCT##(U)	;COUNT IN SOFTWARE-ERROR WORD FOR UNIT
	TDO	S,[XWD IOSSCE##,IOIMPM]	;LIGHT ERROR BITS
IFN FTDAEM,<
	SKIPGE	UNIECT##(U)	;IF NOT IN ERROR RECOVERY
	MOVEM	T4,UNIHBN##(U)	; SAVE BLOCK FOR DAEMON
	MOVEI	T1,.ERDPE	;SAY DISK ERROR TO DAEMON
	HRL	T1,F		;PUT DDB ADDR IN LH FOR DAEMON
	PUSHJ	P,DAEERR##	;AND CALL DAEMON LOG ERROR
>
IFN FTDBAD,<
	PUSHJ	P,SAVE2##	;SLFND USES P-ACS
	MOVE	T1,UNISTR##(U)	;STR LOC
	MOVE	T1,STRNAM##(T1)	;STR NAME
	MOVE	P1,U		;SRSTR WIPES U
	PUSHJ	P,SRSTR##	;FIND STR NUMBER
	  JRST	STRIOS		;WHAT???
	MOVE	U,P1		;RESTORE U
	HRRZ	T1,T4		;STR NUMBER
	MOVE	P2,SYSSRC##	;THIS STR IN SYS SEARCH-LIST?
	PUSHJ	P,SLFNA##
	  PJRST	STRIOS		;NO
	AOS	T1,RIBTOT##	;YES, COUNT AN ERROR
	CAMG	T1,RIBECT##	;ABOVE THE THRESHOLD?
	PJRST	STRIOS		;NO, DONT TELL OPR
	PUSH	P,U		;YES, INFORM THE OPR
	HRRZ	U,OPRLDB##	;WHERE TO TYPE THE MESSAGE
	PUSHJ	P,INLMES##
	ASCIZ	/
RIB ERROR ON /
	PUSHJ	P,PRTDDB##	;"STR:NAM.EXT[PPN]"
	PUSHJ	P,PCRLF##
	POP	P,U		;RESTORE U
>	;END FTDBAD CONDITIONAL
	PJRST	STRIOS		;STORE S AND RETURN
;SUBROUTINE TO WRITE A BLOCK (OR SERIES OF BLOCKS)
;ENTER WITH T1= IOWD   T2=BLOCK NUMBER
MONWRT::PUSH	P,S		;SAVE S
	TLO	S,IO		;INDICATE WRITING
	PJRST	MONIO		;DO IO


;SUBROUTINE TO READ A BLOCK (OR SERIES OF BLOCKS)
;ENTER WITH T1 = IOWD FOR THE DATA   T2= BLOCK NUMBER
;RETURNS WITH T2 = BLOCK NUMBER READ, T1 = IOWD,
; AND T3=ERROR BITS SET ON THIS OPERATION
;INSTEAD OF ERROR RETURN - S SET WITH ERROR BITS IN RH. U AND F SET UP
MONRED::PUSH	P,S		;SAVE S
	TLZ	S,IO		;INDICATE READING
MONIO:	TRNE	S,IOACT		;IO IN PROGRESS?
	STOPCD	.,STOP,IIP,	;++ IO IN PROGRESS - ERROR
	SKIPGE	T2
	STOPCD	.,STOP,BIN,	;IO TO A NEGATIVE BLOCK #
	MOVEI	T3,IODTER	;SET FOR ERROR
	CAML	T2,UNIBPU##(U)	;REQUESTED BLOCK ABOVE HIGHEST ON UNIT?
	JRST	MONIO2		;YES. ERROR RETURN
	TRZ	S,IOIMPM+IOBKTL+IODTER+IODERR	;ZERO THE TEMP ERR BITS
	TLO	S,IOSMON	;INDICATE MONITOR IO
	MOVEM	S,DEVIOS(F)	;STORE NEW S IN DDB
	PUSH	P,DEVDMP##(F)	;SAVE CURRENT DEVDMP
	MOVEM	T1,DEVDMP##(F)	;IOWD TO READ THE DATA
	PUSH	P,DEVBLK##(F)	;SAVE CURRENT BLOCK NUMBER
	MOVEM	T2,DEVBLK##(F)	;BLOCK WE WANT TO READ
	PUSHJ	P,UNICHK	;MAKE SURE UNIT IS OKAY.
	  JRST	MONIO1		;NO GOOD - WRITING FROM MON-BUF, UNIT OFF LINE
	HRRZ	J,UNIKON##(U)	;LOC OF KONTROLLER DATA BLOCK
	PUSHJ	P,UUOPWQ	;GO PUT IN QUEUE STRUCTURE
	PUSHJ	P,PWAIT1	;WAIT T1LL DATA IS IN
MONIO1:	ANDI	S,IOIMPM+IODERR+IODTER+IOBKTL	;GET ERROR BITS FROM S
	MOVE	T3,S		;AND SAVE IN T3
	MOVE	T2,DEVBLK##(F)	;RESTORE BLOCK NO. TO T2
	MOVE	T1,DEVDMP##(F)	;AND IOWD TO T1
IFN FTDUAL,<
	HRRZ	U,DEVUNI##(F)	;RESET U
>
	POP	P,DEVBLK##(F)	;RESTORE THE CHANGED STUFF TO THE DDB
	POP	P,DEVDMP##(F)
MONIO2:	POP	P,S
	OR	S,T3		;SET ANY ERROR BITS INTO S
STRIOS::MOVEM	S,DEVIOS(F)
	POPJ	P,		;AND RETURN TO CALLER
IFN FTDHNG,<
;HERE IF THE MONITOR CALLS FILSER ON A HUNG DEVICE
HNGDSK::MOVE	U,DEVUNI##(F)	;UNIT DB
	JUMPE	U,CPOPJ##	;JUST IN CASE F/S HAS BEEN JERKED OUT
IFN FTDUAL,<
	HRRZ	U,DEVCUR##(F)
>
IFN FTDHIA,<
	LDB	J,PJOBN##	;SET UP J
	PUSHJ	P,FNDPDB##	;SET UP W
	  JRST	HNGDSD		;MUST BE SWAPPER
	CAME	F,.PDDIA##(W)	;IN DIAG?
	JRST	HNGDSD		;NO
	SOSGE	DEVDIA##(F)	;YES, HUNG TIME GONE?
	JRST	DIACLR		;YES, RESTART THE IO ON THE CHANNEL
	AOS	(P)		;NO, SET FOR OK RETURN
	MOVEI	T1,7		;SET FOR 2-MINUTE INTERVALS
	DPB	T1,PDVTIM##
	PJRST	SETHNG##	;RESET HUNG TIMER AND KEEP ON GOING
HNGDSD:>
IFN FTVM,<
	SKIPL	DEVSWP(F)	;SWAPPER?
	JRST	HNGDSX
	SETZ	J,		;YES, INDICATE "UUO" LEVEL
	PUSHJ	P,SWPSCN##	;START IO AGAIN
	  AOSA	(P)
	PJRST	CPOPJ1##	;OK, NO MSG
	PJRST	SWPHNG##
HNGDSX:>
	HRRZ	J,UNIKON##(U)	;KONTROLLER DB
	SKIPE	T1,UNISTS##(U)	;STATUS OF UNIT
	CAIN	T1,TWCOD##	;TW OR IDLE?
	AOSE	@KONCHN##(J)	;YES. IS CHAN FREE?
	JRST	HNGDS0		;NO
	AOS	UNIHNG##(U)	;COUNT TOTAL NUMBER OF HUNG TIMEOUTS
	AOS	(P)		;SET FOR NO ERROR MESSAGE (WE RECOVERED)
	HRRZ	P1,KONCHN##(J)	;SET P1 TO CHAN DB LOC
	JUMPE	T1,UUOTWQ	;GO START I/O IF UNIT IS IDLE
	PJRST	PIKTRN		;TW- REMOVE FILE FROM CHAN Q AND START IO
;HERE IF UNIT NOT IN IDLE OR TW, OR CHAN NOT FREE
HNGDS0:	CAIGE	T1,OWCOD##	;UNIT IN OPERATOR WAIT?
	JRST	HNGDS7		;NO, TEST FOR T OR P STATE
	SKIPL	KONPOS##(J)	;DOES KONTROLLER POSITION?
	JRST	HNGD0A		;YES
	SETZM	UNISTS##(U)	;NO, HENCE NO FREE INTERRUPT
	CAIN	T1,OWCOD##	; RETRY OPERATION IF OW
	PUSHJ	P,UUOPWQ	; SINCE UNIT MIGHT HAVE BEEN FIXED
	PJRST	CPOPJ1##	;AND RETURN WITH NO MESSAGE
HNGD0A:
	LDB	J,PJOBN##	;YES, GET JOB NUMBER
	JUMPE	J,CPOPJ1##	;SWAP I/O
	PUSH	P,F		;SAVE FILE IN QUESTION
	HRRZ	F,TTYTAB##(J)	;HUNG JOB'S TTY DDB
	PUSHJ	P,CTLJBD##	;FIND THE CONTROLLING JOB IF THERE IS ONE
	POP	P,F		;RESTORE POINTER TO DSK DDB
	HRRZ	U,DEVUNI##(F)	;RESTORE UNIT
	SKIPG	T1		;SKIP IF THE CURRENT JOB IS CONTROLLED
	TDZA	T2,T2		;IF NOT CONTROLLED, THE CONTROLLING JOB ISN'T SWAPPED
	MOVE	T2,JBTSTS##(T1)	;JOB STATUS OF THE CONTROLLING JOB
	MOVE	T1,JBTSTS##(J)	;STATUS OF JOB
	TLNN	T2,SWP		;IS IT SWAPPED OUT?
				; IF YES, HUNG DEVICE MESSAGE (ELSE THIS IS
				;  DEADLY EMBRACES
	TLNE	T1,CNTRLC	;^C TYPED?
	JRST	HNGDS3		;YES, GIVE HUNG DEVICE
	TLNE	S,IOSHMS	;NO, MESSAGE ALREADY TYPED?
	JRST	HNGDS2		;YES, DONT TYPE IT AGAIN
	PUSH	P,F		;NO, SAVE F
	PUSHJ	P,TTYFND##	;FIND USER'S TTY
	PUSHJ	P,INLMES##	;TYPE THE MESSAGE:
	ASCIZ	/
DISK IS OFF-LINE, WAITING FOR OPERATOR ACTION
TYPE ^C TO GET HUNG MESSAGE (IN 15 SECONDS).
DON'T TYPE ANYTHING TO WAIT FOR THE OPERATOR TO FIX THE DRIVE.
/
	PUSHJ	P,FLSDR##	;DOES HE HAVE A RESOURCE?
	  JRST	HNGDS1		;NO
	PUSHJ	P,INLMES##	;YES, TELL HIM HE'S HUNG THE SYSTEM
	ASCIZ	/(THE SYSTEM WILL DO NO USEFUL WORK UNTIL THE
DRIVE IS FIXED OR YOU TYPE ^C)
/
HNGDS1:	POP	P,F		;RESTORE F
HNGDS2:	MOVE	S,DEVIOS(F)	;S BITS
	TLO	S,IOSHMS	;BIT INDICATING MSG WAS TYPED
	PUSHJ	P,STOIOS##	;SAVE S, RESET HUNG TIME
	PJRST	CPOPJ1##	;RETURN WITH NO ERROR MESSAGE


;HERE IF ^C WAS TYPED - FINISH OUT THE OPERATION
HNGDS3:	DSKOFF
	PUSHJ	P,RETRES	;GIVE UP MONITOR RESOURCES
	LDB	T1,DEYCOD##	;STATE OF FILE (FORMER STATE OF UNIT)
	CAIE	T1,TCOD##	;TRANSFER
	CAIN	T1,PCOD##	; OR POSITION?
	JRST	HNGDS6		;YES
	HRRZ	U,DEVUNI##(F)	;NO, IN A QUEUE
	PUSHJ	P,SAVE2##	;SAVE P1,P2
	MOVEI	P2,MINDVQ##(T3)	;P2=1ST POSSIBLE PRED

;HERE TO FIND ACTUAL PREDECESSOR
HNGDS5:	HLRZ	P1,DEVQUE##(P2)	;FILE THIS ONE POINTS TO
	CAIN	P1,(F)		;POINTING TO OUR FILE?
	JRST	HNGD6A		;YES, GO UNQUEUE
	SKIPE	P2,P1		;NO, LOOK AT NEXT IN Q
	JRST	HNGDS5
	MOVEI	T1,O2COD##	;COULDNT FIND PREDECESSOR (SYSTEM ERROR?)
	MOVEM	T1,UNISTS##(U)	;SET UNIT INTO OPR WAIT
	DSKON		;RESTORE PIS
	POPJ	P,		;AND RETURN

HNGD6A:	MOVE	J,UNIKON##(U)
	SKIPA	P1,KONCHN##(J)
;HERE IF FILE WAS IN POSITION OR TRANSFER STATE
HNGDS6:	AOSA	UNISTS##(U)	;CHANGE UNIT STATE TO O (FROM OW)
				;(INDICATING DONT START IO ON FREE INTERRUPT)
	PUSHJ	P,UNQUEX	;UNQUEUE THE REQUEST
	DSKON
	POPJ	P,		;AND GIVE ERROR MESSAGE RETURN
;STILL IN FTDHNG CONDITIONAL
;HERE IF UNIT MAY BE IN TRANSFER OR POSITION STATE
HNGDS7:	PUSHJ	P,SAVE1##	;SAVE P1
	HRRZ	T1,UNICDA##(U)	;DDB WE'RE TALKING TO
	CAIE	T1,(F)		;SAME AS THIS DDB?
	JRST	HNGD7B		;NO, IT MUST BE IN A QUEUE
	AOS	T1,UNIRCV##(U)	;YES, COUNT NUMBER OF ATTEMPTS AT RECOVERY
	CAIG	T1,10		;TRIED ENOUGH?
	JRST	HNGD7A		;NO, TRY AGAIN
HNGDWN:	HRRZ	P1,KONCHN##(J)	;YES, SET UP P1
	MOVEI	T1,KOPOFL##	;TELL BADUNI TO GRUMBLE AT OPR
	PUSH	P,U
	PUSHJ	P,BADUNI	;GIVE UP, SHOUT AT OPR OVER THIS UNIT
	POP	P,U
	MOVEI	T1,OCOD##
	MOVEM	T1,UNISTS##(U)	;SET STATE TO OCOD
	MOVSI	S,IOSMON
	ANDCAB	S,DEVIOS(F)	;MAKE SURE IOSMON=0
	HRLZS	UNICDA##(U)
	SKIPE	DEVRHB##(F)	;IF NOT REREADING HOME BLOCKS,
	POPJ	P,		; LET USER GET HUNG DEVICE MESSAGE
	PJRST	CPOPJ1##	;AND DONT GIVE ERROR MESSAGE
HNGD7A:	SKIPL	UNIECT##(U)	;IN ERROR RECOVERY?
	JRST	HNGD7B		;YES, LEAVE REGS ALONE
	SETZM	UNIECT##(U)	;NO, SET SO DAEMON WILL BE CALLED ON RECOVERY
	SETZM	UNIERR##(U)
IFN FTRP04,<
	PUSHJ	P,@KONRRG##(J)	;GO READ ALL DRIVE REGISTERS NOW
	PUSHJ	P,FSTREG	;COPY REGISTERS TO UDB
>
HNGD7B:	MOVE	T1,UNISTS##(U)	;RESET T1=UNIT STATUS
	CAIE	T1,TCOD##	;IS UNIT IN TRANSFER STATE?
	JRST	HNGDS8		;NO
	MOVSI	P1,UNPHRC##	;ASSUME WE'LL RECOVER
	PUSHJ	P,@KONSTP##(J)	;STOP UNIT AND CAUSE AN INTERRUPT
	  MOVSI	P1,UNPHNR##	;COUNT NOT-RECOVERED INSTEAD
	ADDM	P1,UNIHNG##(U)
	MOVEM	T2,UNISOF##(U)	;STORE (BEFORE) CONI STATUS
	MOVEM	T3,UNISDI##(U)	;STORE (BEFORE) DATAI STATUS
	PUSHJ	P,SETHNG##	;RESTORE HUNG-TIME
	PJRST	CPOPJ1##	;SKIP RETURN - NO ERROR MESSAGE

;STILL IN FTDHNG CONDITIONAL
HNGDS8:	LDB	T2,DEYCOD##	;STATUS OF FILE
	CAIE	T2,PCOD##	;FILE IN TRANSFER?
	PJRST	CPOPJ1##	;NO, RETURN WITH NO ERROR MESSAGE
				;(UNIT PROBABLY HUNG, THIS JOB IN QUEUE)
	HRRZM	T1,UNISTS##(U)	;RESET SIGN BIT IF ON
	JUMPL	T1,HNGDWN	;GIVE UP IF DIDN'T RECOVER
	MOVEI	T2,UNPHPS##	;YES, INCREMENT HUNG POSITION COUNT
	ADDM	T2,UNIHNG##(U)
	DSKOFF
	SKIPG	KONTAB##(J)	;#KONTROLLER FREE?
	JRST	HNGDS9		;#NO, PUT IN QUEUE
	MOVSI	T1,400000	;#INDICATE PHUNG RECOVERY
	IORM	T1,UNISTS##(U)
	PUSHJ	P,SETHNG##
	PUSHJ	P,@KONRCL##(J)	;#TRY A RECAL
	  JRST	[MOVEI	T1,OWCOD##	;UNIT IS DOWN
		 MOVEM	T1,UNISTS##(U)	; SET UNISTS FOR
		 JRST	.+1]		; ONCE A MINUTE TYPEOUT
	DSKON			;#
	PJRST	CPOPJ1##	;AND RETURN WITH NO ERR MESSAGE
HNGDS9:	MOVEI	T1,PWCOD##	;#INDICATE UNIT IN PW
	MOVEM	T1,UNISTS##(U)	;#
	MOVEI	T1,UNIQUE##(U)	;#PUT FILE IN POSITION QUEUE
IFN FTVM,<
	HRRZ	P1,UNICHN##(U)	;POINT P1 AT CHAN BLOCK
>
	PUSHJ	P,PUTQUE	;# FOR THE UNIT
	PJRST	CPOPJ1##	;AND RETURN W/ NO MSG
>	;END CONDITIONAL ON FTDHNG
IFE FTDHNG,<
HNGDSK==:CPOPJ1##
>

;ROUTINE TO RETURN ANY RESOURCES A DDB HAS
;ENTER WITH JOB NUMBER IN J; EXITS WITH IOACT OFF IN DDB
RETRES::SKIPE	T1,DEVMBF##(F)	;HAVE MON BUF?
	PUSHJ	P,GVMNBF	;YES, RETURN IT
	TLNE	S,IOSDA		;HAVE DA?
	PUSHJ	P,DWNDA
	TLNE	S,IOSAU		;HAVE AU?
	PUSHJ	P,DWNAU
	JUMPE	J,CLRACT##
	CAMN	J,CBUSER##	;HAVE CB?
	PUSHJ	P,GVCBJ##
	PJRST	CLRACT##	;RESET IOACT + POPJ

;SUBROUTINE TO CALL WAIT1
;THIS IS NEEDED SINCE WAIT1 CLOBBERS P1-P4

;THIS INSTRUCTION IS USED TO RESTORE PWAIT1 AFTER RESTART (SEE WTSATS)
PWAITZ::PJRST	WAIT1##

PWAIT1:: PJRST	WAIT1##		;GO TO WAIT1
;HERE TO PUT A SWAPPING REQUEST IN THE QUEUE
;S,F,U SET, T2=IOWD FOR DATA, T1=LOGICAL BLOCK NUMBER


SWAPIO::
IFE FTVM,<
	TLO	S,IOSMON	;SET MONITOR I/O BIT
	HRRM	U,DEVUNI##(F)	;SAVE U (RESET IN UUOSET)
	MOVEM	T1,DEVBLK##(F)	;LOGICAL BLOCK NUMBER
	MOVEM	T2,DEVDMP##(F)	;IOWD
	MOVEM	S,DEVIOS(F)	;S
	PJRST	UUOPTR		;PUT REQUEST IN QUEUE AND RETURN
>	;END IFE FTVM
IFN FTVM,<
	SKIPE	SQREQ##		;ANY REQUESTS?
	SKIPL	DEVSWP(F)	;YES, IS THIS THE SWAPPER?
	STOPCD	.+1,DEBUG,SFU,	;++SWAPPER FOULED UP
	MOVE	S,[IOSMON,,IOACT] ;OK, INDICATE ACTIVE MONITOR IO
	MOVEM	S,DEVIOS(F)
	MOVEI	J,0		;INDICATE ON UUO LEVEL
	PJRST	SWPSCN##	;GO CRANK THINGS UP
>	;END FTVM
SUBTTL UUO LEVEL MODULE
IFN FTDSEK,<
;HERE TO START A SEEK
UUOSEK::PUSHJ	P,VALUUO	;DSK CHAN?
	  POPJ	P,		;NO
	HRRZ	T1,DEVACC##(F)	;IF NOT SUPER I/O,
	TLNN	S,IOSUPR
	JUMPE	T1,CPOPJ##	; DO NOTHING IF LOOKUP/ENTER NOT DONE
	PUSHJ	P,UUOSET	;YES, SET UP U, J AND GET NEXT BLOCK
	  POPJ	P,		;EOF - RETURN
	SKIPL	KONPOS##(J)	;DOES CONTROLLER POSITION?
	SKIPE	UNISTS##(U)	;YES. IS UNIT IDLE?
	POPJ	P,		;NO. IGNORE SEEK REQUEST
IFN FTDHIA,<
	SKIPE	DIADSK##
	POPJ	P,
>
	NOSCHEDULE		;YES. THIS CODE MUST BE INTERLOCKED
	DSKOFF			;SINCE WE ARE DIDDLING QUEUES
	SKIPL	KONTAB##(J)	;#IS CONTROL BUSY
	JRST	UUOSK1		;#NO, START POSITION
	MOVEI	T1,SWCOD##	;#YES, PUT UNIT INTO SW
	MOVEM	T1,UNISTS##(U)	;#PUT FILE INTO SW/PW QUEUE
IFN FTVM,<
	PUSHJ	P,SAVE1##	;PUT QUE USES CHNWAT(P1)
	MOVE	P1,KONCHN##(J)	; SO SET P1 TO CHAN DB LOC
>
	JRST	UUOPW3		;#FOR THIS UNIT AND RETURN

;HERE WHEN CONTROLLER IS FREE - START POSITION (UNLESS UNIT ALREADY POSITIONED)
UUOSK1:	PUSHJ	P,CYLCOM	;#COMPUTE DISTANCE TO TARGET CYLINDER
	JUMPE	T1,SEKRET	;#ALREADY THERE IF DISTANCE =0
	MOVEI	T1,SCOD##	;#NOT POSITIONED, PUT UNIT INTO S STATE
	MOVEM	T1,UNISTS##(U)	;#
	PJRST	STRPS1		;#START POSITION AND RETURN

;HERE IF UNIT IS ALREADY POSITIONED - TURN ON PI AND RETURN
SEKRET:	DSKON			;#
	SCHEDULE
	POPJ	P,		;RETURN
>				;END CONDITIONAL ON FTDSEK
;SUBROUTINE TO ENSURE THAT A CALLI IS FOR A DSK CHAN
;RETURNS CPOPJ IF NOT, CPOPJ1 IF SO
VALUUO::LDB	T1,PUUOAC##
	CAMG	T1,USRHCU##	;CHAN NO LEGAL?
	SKIPN	F,USRJDA##(T1)	;YES, DEVICE ASSIGNED?
	JRST	IOIERR##	;NO, "IO TO UNASSIGNED CHAN"
VALUUX:	MOVE	T1,DEVMOD(F)	;IS DEVICE A DSK?
	TLNN	T1,DVDSK
	POPJ	P,		;NO, IMMEDIATE RETURN
	MOVE	S,DEVIOS(F)
	PJRST	CPOPJ1##	;YES, SKIP RETURN
UUOPTR:	PUSHJ	P,UUOSET	;SET U, J  AND GO TO NXTBLK
	  POPJ	P,		;EOF OR QUOTA EXHAUSTED

UUOPWQ::JUMPN	U,UUOPWR	;JUST IN CASE F/S HAS BEEN JERKED OUT
	TRO	S,IOIMPM	;   LIGHT ERROR BIT
	PJRST	STOIOS##
UUOPWR:
IFN FTDPRI,<
	MOVE	T1,DEVPRI##(F)	;DISK - PRIORITY WORD
	TRNE	T1,DEPUUO	;PRIORITY SET BY UUO?
	JRST	UUOPWS		;YES
	LDB	T1,PJOBN##	;NO, GET DISK-PRIORITY OF JOB
	LDB	T1,JBXPRI##
	DPB	T1,DEYPRI##	;AND SAVE IT IN DDB
UUOPWS:>
IFN FTDSUP,<	;SUPER USETI/USETO
	TLNN	S,IOSUPR	;SUPER USETI/USETO?
>
	SKIPE	DEVBLK##(F)	;NO. 0 BLOCK REQUESTED?
	JRST	UUOPWZ		;NO. OK
IFN FTRP04,<
	SKIPN	DINITF##	;RP04 READS BLOCK 0 TO CHECK FORMAT ERROR
>
	SKIPN	DEVUNI##(F)	;STR BEEN REMOVED?
	JRST	UUOPWZ	;YES, DON'T HALT
	MOVE	T1,DEVPPN(F)	;REQUESTING BLOCK 0 -
	CAME	T1,SYSPPN##	;[1,4]?
	JRST	UUOPWY		;NO - ERROR
	MOVE	T2,DEVFIL(F)	;SINCE HOME BLOCKS ARE WRITTEN AT THE FRONT OF
	CAME	T2,[SIXBIT .HOME.]	; THE DISK, CHECK FOR HOME[1,4]
UUOPWY:	STOPCD	.,JOB,IBZ,	;++I/O TO BLOCK ZERO
UUOPWZ:	PUSHJ	P,SAVE1##	;SAVE P1
UUOPW0:	HRRZ	P1,KONCHN##(J)	;SET P1 = CHANNEL DB
	TLZ	S,IOSHMS	;RESET HUNG-DEVICE MESSAGE BIT
	TRO	S,IOACT		;SET FILE ACTIVE(IO ACT=1)
	MOVEM	S,DEVIOS(F)	;CANT USE SETACT-IOW MAY BE ON
	NOSCHEDULE
	DSKOFF			;#TURN OFF ALL DSK CONTROLLER PI CHANS.
	SKIPG	T2,KONPOS##(J)	;#DOES KONTROL POSITION?
	PJRST	UUOTWQ		;#NO, PUT FILE TW OR T
	SKIPN	T1,UNISTS##(U)	;#IS UNIT IDLE?
	JRST	UUOPW1		;#YES,GO SET PW,P,TW, OR T
IFN FTMDA,<
	LDB	T3,PJOBN##	;NO, IS UNIT IN MDA WAIT?
	CAIN	T1,MDACOD##	; (AFTER FREE INTERRUPT, MDA SHOULD REREAD HOME BLOCKS)
	CAME	T3,MDCJOB##	;YES, IS THIS MDA?
	CAIA			;NO
	JRST	UUOTWQ		;YES, LET IT HAPPEN
>
	CAIE	T1,SWCOD##	;#NO,IS UNIT SW?
	JRST	UUOPW2		;#NO, SET FILE PW
	SETZB	T1,UNIQUE##(U)	;#YES. ERASE QUEUE FOR UNIT
				;#CAN ONLY BE 1 SEEK WAITER IF ANY FILE IN SW/PW QUEUE
	MOVEM	T1,UNISTS##(U)	;#AND SET UNIT STATE=I
UUOPW1:
IFN FTDHIA,<
	SKIPE	DIADSK##	;TRYING TO SHUT DOWN IO?
	CAME	P1,DIACHN##	;YES, FOR THIS CHAN?
	CAIA			;NO
	JRST	UUOPW2		;YES, JUST QUEUE THIS REQUEST
>
IFN FTRP04,<
	TLNN	T2,KOPPWX##	;UNIT/KONTROLLER POSITION WHILE TRANFERRING?
>
	SKIPL	KONTAB##(J)	;#IS KONTROL BUSY?
	JRST	UUOPOS		;#NO, PUT FILE P, TW OR T
IFN FTDUAL,<
	SKIPE	T1,UNI2ND##(U)	;IS THERE AN ALTERNATE PATH?
	SKIPE	UNISTS##(T1)	;YES, IS THE UNIT USEABLE?
	JRST	UUOPW2		;NO
	MOVE	T2,UNIKON##(T1) ;YES, T1=UNIT T2=KON
IFN FTDHIA,<
	HRRZ	T3,KONCHN##	;ALTERNATE CHAN
	SKIPE	DIADSK##	;DIAG IN PROGRESS FOR IT?
	CAME	T3,DIACHN##
>
	SKIPGE	KONTAB##(T2)	;KONTROLLER IDLE?
	JRST	UUOPW2		;NO
	HRRM	F,UNICDA##(U)	;YES, 1ST UNIT ALSO IS THIS DDB
	HRRZ	U,T1		;SET UP NEEDED ACS
	HRRZ	J,T2
	HRRZ	P1,KONCHN##(J)
	JRST	UUOPOS		;AND GO START SEEK (OR MAYBE XFER)
>
UUOPW2:	MOVEI	T1,PWCOD##	;#YES, PUT FILE PW
	SKIPN	UNISTS##(U)	;#UNIT IDLE?
	MOVEM	T1,UNISTS##(U)	;#YES SET UNIT PW
	DPB	T1,DEYCOD##	;#SET FILE PW
UUOPW3:	MOVEI	T1,UNIQUE##(U)	;#QUEUE ON WHICH TO INSERT FILE
	PJRST	PUTQUE		;#PUT FILE ON PWQ AND RETURN

UUOPOS:	PUSHJ	P,CYLCOM	;#IS UNIT ALREADY POSITIONED?
	JUMPN	T1,STRPOS	;#NO, START POSITIONING IF 0
	PJRST	UUOTWQ		;#YES, SET FILE+UNIT TO T, KONCHN TO B
				;# OR FILE+UNIT TO TW AND ADD TO TWQ
;SUBROUTINE TO CHECK STATUS OF A UNIT
;RETURNS CPOPJ1 IF OK (MAY HAVE HAD TO CALL HNGSTP)
;RETURNS CPOPJ IF NG - UNIT OFF-LINE, DDB HAS MON-BUF TO WRITE
UNICHK:	SKIPE	T1,UNISTS##(U)	;STATUS OF UNIT
	CAIGE	T1,MDACOD##	;OKAY ?
	PJRST	CPOPJ1##	;YES
IFN FTMDA,<
	MOVE	J,.C0JOB##	;NO, IS UNIT IN MDA WAIT
	CAMN	J,MDCJOB##	;AND THIS JOB=MDA?
	CAIE	T1,MDACOD##	;IF SO, LET MDA DO THE IO
>
	SKIPGE	DEVSWP##(F)	;IS IT THE SWAPPER?
	PJRST	CPOPJ1		;YES, LET IT TRY ANYWAY
	PUSHJ	P,SAVSTS	;SAVE A RECORD OF JOB'S RESOURCES
	  JRST	UNICH1		;HAS MON BUF AND WRITING - LOSE
	PUSHJ	P,HNGSTP##	;TELL OPR AND USER ABOUT THE PROBLEM
	POP	P,T3		;RECORD OF RESOURCES
	PUSHJ	P,RESSTS	;GET THEM AGAIN
	JRST	UNICHK		;AND TRY AGAIN

UNICH1:	TRO	S,IODERR	;LOSE - LIGHT ERROR BIT
	POPJ	P,		;AND RETURN

;SUBROUTINE TO MAKE A RECORD OF RESOURCES JOB OWNS
;RETURNS WITH THE RECORD ON THE PD LIST AND NO RESOURCES
;RETURNS CPOPJ IF HAS MON-BUF, ELSE CPOPJ1
; (CANT CONTINUE IF THE JOB HAS A MON-BUF SINCE IT HAS ALREADY SET UP
; THE IOWD IN THE DDB, MIGHT GET THE OTHER BUFFER BACK ON CONTINUE)
SAVSTS::SKIPE	T1,DEVMBF##(F)	;NO. HAVE MON BUF?
	POPJ	P,		;YES, THIS OPERATION LOSES
	MOVE	J,JOB##		;NO, OK TO ATTEMPT RECOVERY
	HLR	T1,S		;SAVE IOSDA,IOSAU BITS
	CAMN	J,CBUSER##	;JOB HAVE CB?
	TLO	T1,1		;YES, LIGHT A BIT
	PUSH	P,T1		;SAVE RECORD OF RESOURCES
	PUSHJ	P,RETRES	;GIVE UP ALL RESOURCES JOB OWNS
	POP	P,T1		;RECORD
	EXCH	T1,(P)		;PUT BACK ON STACK
	PJRST	1(T1)		;AND TAKE SKIP-RETURN

;SUBROUTINE TO GET BACK THE RESOURCES
;ENTER WITH T3 = THE RECORD AS SET BY SAVSTS
;PRESERVES T2
RESTS::
RESSTS: HRRZ	U,DEVUNI##(F)	;RESET U
	MOVE	S,DEVIOS(F)	;AND S
	TLNE	T3,1		;WANT CB?
	PUSHJ	P,GETCB##
	TLNE	T3,-200		;WANT MON BUF?
	PUSHJ	P,GTMNB		;YES
	TRNE	T3,IOSDA	;GET DA IF NEEDED
	PUSHJ	P,UPDA
	TRNE	T3,IOSAU	;GET AU IF NEEDED
	PUSHJ	P,UPAU
	POPJ	P,		;AND RETURN TO CALLER
;SUBROUTINE TO SET UP U AND J, EXIT TO NXTBLK (WHICH RETURNS CPOPJ 
;OR CPOPJ1 DEPENDING ON WHETHER ANOTHER BLOCK OF THE FILE EXISTS
UUOSET::HRRZ	U,DEVUNI##(F)	;SET U(UNIT DATA BLOCK)
	JUMPE	U,CPOPJ##	;JUST IN CASE F/S HAS BEEN JERKED OUT
	PUSHJ	P,UNICHK	;MAKE SURE UNIT IS OKAY.
	  POPJ	P,		;UNIT IS DOWN
	HRRZ	J,UNIKON##(U)	;SET J(KONTROL DATA BLOCK)
;FALL INTO NXTBLK,  GET THE NEXT BLOCK OF THE FILE AND RETURN

;SUBROUTINE TO OBTAIN THE NEXT BLOCK ADDRESS
;ENTER WITH J,F,U,S SET UP
;INTERRUPT LEVEL CHECKS BEFORE CALLING TO MAKE SURE STILL POINTERS IN CORE SO NO IO
;RETURNS CPOPJ IF EOF OR DISK FULL OR PNTR BLOCK FULL
;RETURNS CPOPJ1 IF OK
NXTBLK:	JUMPL	S,CPOPJ1##	;*RETURN IF A MONITOR READ
	MOVMS	DEVREL##(F)	;NEGATIVE DEVREL IS FLAG FROM USETI -N
	HRRZ	T1,DEVLFT##(F)	;*NO, NUMBER  OF BLOCKS LEFT IN CURRENT GROUP
	HRRZ	T2,DEVACC##(F)	;*YES. LOC OF A.T.
	MOVE	T2,ACCWRT##(T2)	;*ACTUAL NUMBER OF BLOCKS WRITTEN
	TLNE	S,IO+IOSUPR	;*READING?
	JRST	NXTBLA		;*NO
	CAMGE	T2,DEVREL##(F)	;*TRYING TO READ MORE THAN WAS WRITTEN?
	POPJ	P,		;*YES. TAKE EOF RETURN
	JRST	NXTBL0		;NO, CONTINUE
NXTBLA:	LDB	T3,DEYFNC##	;WRITING, FILE APPEND ONLY ?
IFN FTDSUP,<	;SUPER USETI/USETO
	TLNN	S,IOSUPR	;AND NOT SUPER USETO ?
>
	CAIE	T3,FNCAPP##
	JRST	NXTBL0		;NO
	CAML	T2,DEVREL##(F)	;YES.  WRITING IN THE MIDDLE ?
	POPJ	P,0		;YES, RETURN IOBKTL
NXTBL0:	SOJGE	T1,CPOPJ1##	;*NO, IF BLOCKS LEFT, RETURN (THIS PNTR OK)
IFN FTDSUP,<	;SUPER USETI/USETO
	TLNE	S,IOSUPR	;*SUPER USETI/USETO?
	POPJ	P,		;*YES - NO PNTRS TO READ
>


NXTBL1:	MOVSI	T2,1		;*DECREMENT NO. OF POINTERS
	SKIPG	DEVRSU##(F)	;* IF RIB WASNT ALREADY FULL
	ADDB	T2,DEVRSU##(F)	;*RIB NOW FULL?
	JUMPGE	T2,NXTB1B	;*YES, LIGHT IOBKTL
	AOS	T1,DEVRET##(F)	;*STEP TO NEXT POINTER IN CORE
	CAIG	T1,DEVRBN##(F)	;*RUN OUT OF IN-CORE POINTERS?
	JRST	NXTBL5		;*NO, CONTINUE
	MOVE	T1,DEVREL##(F)	;YES, UPDATE RELATIVE BLOCK NUMBER
	MOVEM	T1,DEVFLR##(F)	;OF LOWEST BLOCK IN DDB POINTERS
	HRRM	U,DEVFUN##(F)	;AND UNIT OF 1ST PNTR IN DDB
	TLNN	S,IO		;READING?
	JRST	NXTBL4		;YES
;HERE WHEN WRITING AND THE DDB POINTER SPACE IS FULL
	TLNN	F,OCLOSB	;OUTPUT CALLED BY CLOSE?
	JRST	NXTBL2		;NO. CONTINUE

;HERE IF THE OUTPUT IS FROM CLOSE
	SKIPN	DEVMBF##(F)	;HAVE MONITOR BUFFER?
	PUSHJ	P,GTMNBF	;GET MONITOR BUFFER
	PUSHJ	P,RIBCUR	;READ THE CURRENT RIB
	JUMPN	T3,CPOPJ##	;NON-ZERO T3 MEANS ERROR READING RIB
	PUSHJ	P,SPTRW		;SET AOBJN WORD FOR POINTERS
	MOVE	T4,T1		;SAVE ORIGINAL POINTER WORD
	PUSHJ	P,DD2MN		;COPY POINTERS INTO MON BUF
	  JFCL
	SUBM	T1,T4		;COMPUTE NEW FREE-POINTER COUNT
	AOS	T2,T4
	DPB	T4,DEYRLC##	;AND SAVE IT IN DDB
	AOBJN	T1,.+1		;POINT T1 AT NEXT GROUP OF POINTERS
	SKIPE	(T1)		;MORE POINTERS? (PRE-ALLOCATED SPACE)
	PUSHJ	P,PTRBL1	;YES, COPY THEM TO DDB
	MOVEI	T2,DEVRB1##(F)	;RESET DEVRET
	HRRM	T2,DEVRET##(F)
	HLRE	T2,DEVRSU##(F)	;NUMBER OF PNTRS LEFT
	AOJGE	T2,NXTB1A	;GO IF LAST PNTR
	SKIPL	DEVRIB##(F)	;NOT LAST, PRIME RIB?
	TLOA	S,IOSRIB	;YES, SET FLAG TO INDICATE TO CLOSE CODE
NXTB1A:	TLZ	S,IOSRIB	;RIB NOT PRIME, BETTER WRITE IT OUT
	PUSHJ	P,WRTRIB	;WRITE OUT THE RIB
	JRST	NXTBL3		;AND CONTINUE

;HERE IF THE CURRENT RIB IS FULL
NXTB1B:	TLNE	S,IO		;READING?
	JRST	NXTB1D		;NO
	PUSHJ	P,PTRCUR	;READ THE CURRENT RIB
	JUMPN	T3,CPOPJ##	;ERROR IF T3 NON-ZERO
IFN FTDMRB,<			;IF MULTIPLE RIBS
	PUSHJ	P,PTRNXT	;YES, GET THE NEXT RIB
	  PJRST	GVMNB0		;RIB ERROR
	PUSHJ	P,CPYEXT##	;COPY FIRST POINTERS FROM EXTENDED RIB
	  POPJ	P,		;RIB ERROR
	HRRZ	T1,DEVLFT##(F)	;GET COUNT OF NUMBER OF BLOCKS LEFT IN PNTR
	JRST	NXTBL7		;AND CONTINUE
>
;IF NO MULTIPLE RIBS, FALL INTO POPJ AT NXTBID
;HERE WHEN WRITING AND WE'VE RUN OUT OF ROOM IN THE CURRENT RIB
NXTB1D:
IFE	FTDMRB,<
	POPJ	P,		;RIB FULL - ERROR RETURN
>
IFN FTDMRB,<			;IF MULTIPLE RIBS
	PUSHJ	P,EXTRIB	;YES, ALLOCATE ANOTHER RIB
IFE FTDSIM,<
	  POPJ	P,		;ERROR
>
IFN FTDSIM,<
	  PJRST	DOWNIF		;ERROR- RETURN DA AND EXIT
	PUSHJ	P,DOWNIF	;OK - GIVE UP DA IF WE HAVE IT
>
	PUSHJ	P,CPYEXT##	;SET UP THE DDB
	  POPJ	P,		;RIB ERROR
	JRST	NXTBLK		;USE THE NEW BLOCKS ACQUIRED
>

;HERE IF THE OUTPUT IS NOT FROM CLOSE
NXTBL2:	PUSHJ	P,PTRCUR	;GET THE RIB
	JUMPN	T3,GVMNB0	;ERROR IN RIB KILLS US
	PUSHJ	P,PTRWRT	;SAVE POINTERS AND WRITE (KEEP THE MON BUF)
	PUSHJ	P,SPTRW		;SET T1 TO AN AOBJN WORD FOR ALL THE PNTRS
	LDB	T2,DEYRLC##	;NEW POINTER LOC
	HRLS	T2		;SET TO ADD TO AOBJN WORD
	ADD	T1,T2
	SKIPE	(T1)		;POINTERS (PRE-ALLOCATED SPACE)?
	PUSHJ	P,PTRBLT	;COPY THE NEW POINTERS INTO DDB
	PUSHJ	P,GVMNB0	;GIVE UP MONITOR BUFFER
NXTBL3:	SETZM	DEVBLK##(F)	;INDICATE NO CONTIGUITY
	JRST	NXTBL5		;AND CONTINUE

;HERE WHEN THE POINTERS RAN OUT ON INPUT

NXTBL4:	PUSHJ	P,CLSNAM##	;MAKE SURE NAME IN DDB IS RIGHT
				; (FILE MIGHT BE RENAMED)
	PUSHJ	P,PTRTST	;READ NEW POINTERS, WRITE THESE IF CHANGED (CHECKSUM)
	  JFCL
	PUSHJ	P,RDPTRA	;COPY NEW SET OF POINTERS INTO DDB
	SKIPE	DEVRB1##(F)	;GET NEW POINTERS?
	JRST	NXTBL5		;YES
	PUSHJ	P,FNDPTR	;NO, FIND THEM IN A DDB
	  POPJ	P,		;COULDN'T FIND THEM
;HERE WITH DEVRET POINTING TO NEXT RETRIEVAL POINTER (OR 0)
NXTBL5:	SKIPE	T2,@DEVRET##(F)	;*IS THERE ANOTHER POINTER?
	JRST	NXTBL6		;*YES

	MOVE	T1,DEVACC##(F)	;NO, ARE WE UP TO END OF FILE?
IFN FTDSIM,<
	TLNN	S,IO		;IF READING,
	SKIPA	T1,ACCWRT##(T1)	;USE NUMBER OF BLOCKS WRITTEN
	PUSHJ	P,GETALC	;ELSE USE NUMBER OF BLOCKS ALLOCATED
>
IFE FTDSIM,<
	MOVE	T1,ACCWRT##(T1)	;ELSE USE NUMBER OF BLOCKS WRITTEN
>
	CAMG	T1,DEVREL##(F)
	JRST	NXTB5A		;YES, RETURN EOF OR ALLOCATE BLOCKS
	TLNN	S,IO		;NOT AT EOF. READING?
	JRST	FNDPTR		;YES, FIND POINTERS IN SOME DDB
IFE FTDSIM,<
	JRST	OUTGRP		;NO, ALLOCATE SOME MORE (SYSTEM ERROR?)
>
IFN FTDSIM,<
	JRST	FIXDDB		;NO, FIND NEWLY-ALLOCATED PNTRS IN RIB
>

;HERE IF WE ARE UP TO THE END OF THE FILE
NXTB5A:	TLNN	S,IO		;READING?
	POPJ	P,		;YES - EOF
				;NO, FALL INTO OUTPUT ALLOCATION
;HERE TO ALLOCATE SOME MORE SPACE FOR AN OUTPUT FILE
OUTGRP:	HRRZ	T1,UNISTR##(U)	;LOC OF STR DATA BLOCK
	HLRZ	T2,UNIGRP##(U)	;YES, NUMBER OF CLUSTERS TO ALLOCATE
	TLO	T2,400000	;TELL CHKQTA THAT THE CALL IS FROM OUTPUT
	PUSHJ	P,CHKQTA	;CHECK USERS QUOTA OR DISK FULL
IFN FTDQTA,<
	JUMPLE	T2,OUTG5A	;CAN'T GET ANY MORE RETURN (IOBKTL SET)
>
;HERE WITH T2 = AMOUNT TO ALLOCATE, STR HAS SOME SPACE
IFN FTDSIM,<
	MOVE	T1,DEVACC##(F)	;IF SIMULT UPDATE FILE
	MOVE	T1,ACCSMU##(T1)	; GET MON BUF NOW, BEFORE GETTING DA
	TRNE	T1,ACPSMU	; TO AVOID A DEADLY EMBRACE
	PUSHJ	P,GTMB2
>
	SKIPN	T1,DEVBLK##(F)	;CONTIGUITY ALLOWED?
	JRST	OUTGR3		;NO. GET SPACE ANYWHERE
	SOS	DEVRET##(F)	;YES. POINT TO CURRENT RETRIEVAL PTR
	PUSHJ	P,CHKADD	;ROOM LEFT IN POINTER?
	JUMPLE	T2,OUTGR2	;NO. GET SPACE ANYWHERE
	MOVE	T1,DEVBLK##(F)	;YES. GET SPACE PAST 1ST UNALLOCATED BLOCK
	PUSHJ	P,TAKBLK	;YES. GET SPACE AT END
	  JRST	OUTGR2		;TRY FOR SPACE ANYWHERE ON UNIT
	PUSHJ	P,ADDPTR	;ADD NEW BLOCKS TO CURRENT POINTER
	MOVSI	T2,-1		;DEVRSU WAS INCREMENTED AT NXTBL1,
	ADDM	T2,DEVRSU##(F)	; SO DECREMENT IT BACK DOWN
IFN FTDSIM,<
	TLNE	S,IOSDA		;IF A SIM UPDATE FILE,
	JRST	OUTG4A		; REWRITE RIB WITH NEW POINTERS IN IT
>
	JRST	NXTBL8		;STORE NEW DEVLFT AND CONTINUE

;HERE WHEN CANT GET SPACE CONTIGUOUS WITH THE OLD SPACE
OUTGR2:	AOS	DEVRET##(F)	;POINT TO NEW POINTER LOC
	MOVE	T2,T1		;RESTORE AMOUNT TO GET
OUTGR3:	MOVEI	T4,TAKCHK	;SET TO TAKE N BLOCKS ON ANY UNIT
	HLRE	T1,DEVRSU##(F)	;UNLESS THERE IS ONLY ROOM FOR 1 POINTER
	CAMLE	T1,[EXP -2]	;IN WHICH CASE, SET TO STAY ON THIS UNIT
	MOVEI	T4,TAKBLK
	AOJN	T1,OUTG3A	;JUMP IF NOT LAST POINTER
	MOVSI	T1,DEPLPC##	;IS LAST, SET LAST POINTER IN CORE BIT
	IORM	T1,DEVLPC##(F)	;SO BLOCK WILL BE RESERVED FOR REDUNDANT RIB
OUTG3A:	SETZ	T1,		;GET BLOCKS ANYWHERE
	PUSHJ	P,(T4)		;GET SOME BLOCKS
	  JRST	OUTGR5		;ON NEW UNIT
OUTGR4:	MOVEM	T2,@DEVRET##(F)	;GOT SPACE ON SAME UNIT - SAVE POINTER IN DDB
IFN FTDSIM,<
	TLNN	S,IOSDA		;SIM UPDATE FILE?
	JRST	OUTG4B		;NO, CONTINUE
OUTG4A:	PUSHJ	P,WRTPTR	;YES, REWRITE RIB WITH NEW PNTRS
	PUSHJ	P,DWNDA		;GIVE UP DA NOW THAT RIB WRITTEN
	PUSHJ	P,GVMNB0
	PJRST	FIXDDB		;CALL USETI TO GET PNTRS BACK INTO CORE
				; (DD2MN ZEROES THE DDB)
OUTG4B:>
	HRRZ	T3,DEVACC##(F)	;LOC OF A.T.
	MOVEI	T4,ACP1PT##	;MAKE SURE THAT 1PT
	ANDCAM	T4,ACCUN1##(T3)	; IS OFF IN THE UN1 WORD
	JRST	NXTBL6		;AND CONTINUE
;GOT SOME SPACE ON A NEW UNIT, OR STR FULL
OUTGR5:	JUMPE	T3,OUTGR6	;IF GOT ANY
	MOVSI	T1,1
	ADDB	T1,DEVRSU##(F)	;UPDATE DEVRSU
	JUMPGE	T1,OUTGR6	;ERROR IF ALL SLOTS TAKEN (SHOULD NEVER HAPPEN)
	AOBJN	T1,OUTG5B
	MOVSI	T1,DEPLPC##
	IORM	T1,DEVLPC##(F)
OUTG5B:	MOVEM	T3,@DEVRET##(F)	;SAVE UNIT-CHANGE IN DDB
	HRRM	U,DEVUNI##(F)	;SAVE NEW UNIT IN DDB
	AOS	T1,DEVRET##(F)	;POINT TO NEXT PNTR SLOT
	CAIG	T1,DEVRBN##(F)	;DDB FULL?
	JRST	OUTGR4		;NO, STORE REAL POINTER IN DDB
	PUSH	P,T2		;YES, SAVE PNTR
	PUSHJ	P,WRTPTR	;COPY PNTRS TO RIB, WRITE
	POP	P,T2		;RESTORE NEW RETRIEVAL POINTER
	JUMPE	T3,OUTGR4	;CONTINUE IF NO RIB ERR
	PUSHJ	P,CNVPTR	;RIB ERR- GIVE BACK THE BLOCKS
	  JFCL			;BAD UNIT!
	  STOPCD OUTGR6,DEBUG,LNP,  ;++LAST POINTER NOT A POINTER
	MOVE	T2,T1		;SET TO GIVE BACK THE BLOCKS
	MOVE	T1,DEVBLK##(F)
	PUSHJ	P,GIVBLK	;RETURN THE BLOCKS, UPDATE COUNTS
	JRST	OUTGR6		;UPDATE DEVRSO AND ERROR RETURN

;HERE WHEN STRUCTURE IS FULL, RETURN DEVRSU TO PRE-CALL STATE
OUTG5A:
	MOVEI	T4,.ERFUL	;STR FULL INTERCEPT
	PUSHJ	P,SETINJ	;LET JOB KNOW
	  JFCL			;DON'T CARE IF NOT ENABLED

;HERE WHEN THERE ARE NO FREE BLOCKS LEFT IN THE STR
OUTGR6:	SOS	DEVRET##(F)	;POINT DEVRET BACK TO LAST REAL POINTER
	MOVSI	T1,-1		;DECR DEVSRU
	ADDM	T1,DEVRSU##(F)	;(INCREMENTED AT NXTBL1)
	PJRST	ERRFUL		;LIGHT AN ERROR BIT AND RETURN
;HERE WHEN WE HAVE A POINTER IN T2 (MAY BE UNIT CHANGE)
NXTBL6:	PUSHJ	P,CNVPTR	;*CONVERT POINTER TO BLK, COUNT
	  JRST	OUTGR6		;BAD UNIT-CHANGE PTR-LOSE
	  JRST	NXTBL1		;*WAS A UNIT-CHANGE.  TRY NEXT

;HERE WITH T1=BLOCK COUNT, DEVBLK SET UP
	TLO	S,IOSFIR	;*INDICATE CHECKSUM MUST BE COMPUTED
	MOVEM	S,DEVIOS(F)	;*SAVE S IN DDB
	TLNE	S,IO		;*READING?
	JRST	NXTBL8		;*NO, ANY ALLOCATED BLOCK IS OK
NXTBL7:	HRRZ	T2,DEVACC##(F)	;*YES, MAKE SURE THESE BLOCKS ARE ALL WRITTEN IN
	MOVE	T2,ACCWRT##(T2)	;*HIGHEST BLOCK WRITTEN
	SUB	T2,DEVREL##(F)	;*-1ST RELATIVE BLOCK OF GROUP
	AOJLE	T2,CPOPJ##	;*EOF IF NO BLOCKS LESS THAN HIGHEST WRITTEN
NXTBL8:	MOVE	T2,DEVLPC##(F)	;GET WORD TO TEST FOR LAST POINTER
	TLNN	T2,DEPLPC##	;LAST POINTER IN CORE?
	JRST	NXTBL9		;NO, NO NEED TO WORRY
	HRRZ	T2,DEVRET##(F)	;GET ADDRESS OF CURRENT POINTER IN DDB
	CAIE	T2,DEVRBN##(F)	;POINTING TO LAST SLOT IN DDB?
	SKIPN	1(T2)		;NO, NEXT SLOT EMPTY?
	SOJE	T1,NXTBL1	;YES, JUMP IF CURRENT POINTER EXHAUSTED
NXTBL9:	HRRM	T1,DEVLFT##(F)	;*AND SAVE IN DDB
	HRRZ	J,UNIKON##(U)	;*RIB MIGHT BE ON ANOTHER KONTROLLER
	JRST	CPOPJ1##	;*AND TAKE SKIP RETURN
IFN FTDMRB,<			;IF MULTIPLE RIBS
;SUBROUTINE TO CREATE AN EXTENDED RIB
; RETURNS CPOPJ IF ERROR OR RIB NOT EXTENDABLE
; RETURNS CPOPJ1 WITH NEW RIB IN THE MONITOR BUFFER AND T1=NUMBER OF NEW BLOCKS ADDED
EXTRIB:
IFN FTDSIM,<
	MOVE	T1,DEVACC##(F)
	MOVE	T1,ACCSMU##(T1)	;SIM UPDATE FILE?
	TLNN	S,IOSDA		; AND DDB WITHOUT DA (OR MON BUF)?
	TRNN	T1,ACPSMU
	JRST	EXTRB0
	PUSHJ	P,GTMNBF	;YES, GET DA BEFORE READING RIB
	PUSHJ	P,UPDA		; CAUSE ANOTHER JOB MIGHT TRY TO EXTEND RIB
EXTRB0:>
	PUSHJ	P,PTRCUR	;GET CURRENT RIB INTO CORE
	JUMPN	T3,GVMNB0	;RIB ERROR IF T3 NON-0
	PUSHJ	P,DD2MN		;COPY POINTERS FROM DDB TO RIB
	STOPCD	GVMNB0,DEBUG,NPD,	;++NO POINTERS IN DDB
	MOVE	T1,DEVMBF##(F)	;IOWD TO MONITOR BUFFER
	SKIPG	DEVRIB##(F)	;CURRENT RIB EXTENDED?
	JRST	EXTRB1		;YES, CAN EXTEND AGAIN
	SKIPE	RIBFLR+1(T1)	;PRIME RIB. RIBFLR=0?
	PJRST	EXTRB3		;NO, CANNOT EXTEND THIS RIB
EXTRB1:	SKIPE	T2,RIBXRA##+1(T1) ;RIB ALREADY EXTENDED?
	JRST	EXTRB2		;YES, GO GET THE NEXT RIB
	PUSHJ	P,SAVE1##
	MOVEI	T1,DEPWRT##	;MAKE SURE FNDDDB DOESN'T
	ANDCAM	T1,DEVWRT##(F)	; FIND THIS DDB WHILE NUMBERS ARE CHANGING
	PUSHJ	P,GETALC	;GET "REAL" ACCALC
	MOVE	P1,T1		;RIBFLR=ACCALC - 2 RIB BLOCKS
	SUBI	P1,2
	PUSHJ	P,GTLPT##	;GET LAST RIB POINTER
	PUSHJ	P,CNVPTR	;DECODE THE POINTER
	  JFCL
	  STOPCD GVMNB0,DEBUG,UPI,	;++UNIT POINTER ILL.
;STILL IN FTDMRB CONDITIONAL
	SOJ	T1,		;DECREMENT NUMBER OF BLOCKS LEFT
	ADDM	T1,DEVBLK##(F)	;NOW DEVBLK IS LAST BLOCK IN THE RIB
	MOVE	T1,DEVMBF##(F)	;IOWD FOR MONITOR BUFFER
	MOVE	T2,DEVBLK##(F)	;GET ABSOLUTE BLOCK NUMBER OF REDUNDANT RIB
	MOVEM	T2,RIBSLF##+1(T1) ;STORE IN THE RIB
	MOVEI	T2,CODRIB##	;RIB IDENTIFICATION CODE
	MOVEM	T2,RIBCOD##+1(T1) ;STORE IN THE RIB
	SETZ	T1,		;TELL TAKBLK TO GET BLOCKS ANYWHERE
	HRROI	T2,3		;LOOK FOR 2 BLOCKS (RIBS + 1 DATA)
	PUSHJ	P,TAKBLK	;ALLOCATE SOME BLOCKS 
	  PJRST	EXTRB3		;ERROR, COULDN'T GET THE BLOCKS
	PUSH	P,T1		;SAVE NUMBER OF BLOCKS JUST TAKEN
	MOVEM	T2,DEVRB1##(F)	;STORE NEW POINTER IN DDB
	PUSH	P,DEVRIB##(F)	;SAVE CURRENT RIB POINTER
	LDB	T4,DEYRBC##	;NUMBER OF CURRENT RIB
	MOVSI	T1,400000	;TURN ON BIT 0 IN T1
	MOVEM	T1,DEVRIB##(F)	;NEGATIVE DEVRIB MEANS CURRENT RIB IS EXTENDED
	ADDI	T4,1		;INCREMENT RIB NUMBER
	DPB	T4,DEYRBC##	;AND SAVE IN DDB
	MOVE	T4,UNISTR##(U)	;GET ADDRESS OF SDB FOR CURRENT RIB UNIT
	LDB	T1,STYCLP##(T4)	;EXTRACT CLUSTER ADDRESS FROM POINTER
	DPB	T1,DEYRBA##	;SAVE IN DEVRIB
	LDB	T1,UNYLUN##	;GET CURRENT RIN LOGICAL UNIT NUMBER
	DPB	T1,DEYRBU##	;SAVE IN DEVRIB
;STILL IN FTDMRB CONDITIONAL
	MOVE	T1,DEVMBF##(F)	;IOWD TO MONITOR BUFFER
	MOVE	T2,DEVRIB##(F)	;POINTER TO NEXT RIB ON CHAIN
	MOVEM	T2,RIBXRA##+1(T1) ;SAVE IN CURRENT RIB
	POP	P,DEVRIB##(F)	;RESTORE POINTER TO CURRENT RIB
	MOVE	T2,RIBSLF##+1(T1) ;GET BLOCK NUMBER FOR REDUNDANT RIB WRITE
	PUSHJ	P,MONWRT	;WRITE THE REDUNDANT RIB
	PUSHJ	P,WRTRIB	;WRITE THE WORKING COPY OF THE RIB
	MOVE	T1,DEVMBF##(F)	;GET THAT IOWD AGAIN
	MOVE	T2,RIBXRA##+1(T1) ;POINTER TO EXTENDED RIB
	MOVEM	T2,DEVRIB##(F)	;NEW CURRENT RIB
	PUSHJ	P,SPTRW		;SET UP POINTER TO RIB
	MOVE	T4,T1		;MOVE POINTER TO T4
	SETZM	(T1)		;CLEAR THE POINTER LOCATION
	AOBJN	T1,.-1		;CLEAR ALL POINTERS IN THE RIB
	MOVE	T1,DEVMBF##(F)	;GET IOWD TO MONITOR BUFFER
	MOVEM	P1,RIBFLR##+1(T1) ;SET UP RIBFLR TO FIRST BLOCK NUMBER IN RIB
	SETZM	RIBXRA##+1(T1)	;CLEAR POINTER TO NEXT(NON-EXISTANT) RIB
	MOVE	T2,RIBFIR##+1(T1) ;GET AOBJN WORD
	MOVE	T2,DEVRB1##(F)	;GET FIRST POINTER IN RIB
	MOVEM	T2,(T4)		;SAVE FIRST POINTER IN RIB
	SETZM	DEVRB1##(F)	;CLEAR THE POINTER LOCATION
	PUSHJ	P,GRPAD		;COMPUTE DISK ADDRESS FROM POINTER
	MOVEM	T2,RIBSLF##+1(T1) ;SAVE IN THE RIB
	PUSHJ	P,WRTRIB	;WRITE THE RIB
	MOVEI	T1,DEPWRT##	;ITS OK FOR FNDDDB TO
	IORM	T1,DEVWRT##(F)	; SEE US AGAIN
	JRST	TPOPJ1##	;GOOD RETURN

;HERE WHEN THIS RIB ALREADY IS EXTENDED
EXTRB2:	PUSH	P,T2
	PUSHJ	P,WRTRIB	;WRITE CURRENT RIB (NEW CHECKSUMS
	POP	P,DEVRIB##(F)	;SET UP POINTER TO NEXT RIB
	PUSHJ	P,RIBCUR	;READ THE NEXT RIB
	PJUMPN	T3,CPOPJ##	;ERROR READING RIB IF T3 NON-ZERO
	JRST	CPOPJ1##	;HAPPY RETURN

;HERE WHEN THE RIB CAN'T BE EXTENDED
EXTRB3:	PUSHJ	P,WRTRIB	;WRITE THE RIB (WITH NEW PNTRS)
	MOVEI	T1,DEPWRT##	;ITS OK FOR FNDDDB TO
	IORM	T1,DEVWRT##(F)	; SEE US AGAIN
IFN FTDSIM,<
	PUSHJ	P,DOWNIF	;GIVE UP DA IF WE OWN IT
>
	PJRST	GVMNB0		;GIVE UP MON-BUF AND RETURN
>	;END CONDITIONAL ON FTDMRB
;SUBROUTINE TO CONVERT A RETRIEVAL POINTER
;ENTER WITH T2=POINTER
;EXIT CPOPJ LF BAD UNIT-CHANGE PNTR
;EXIT CPOPJ1 (WITH NEW U SET UP) IF CHANGE-UNIT POINTER
;EXIT CPOPJ2 WITH DEVBLK SET AND T1=COUNT IF A REAL POINTER
CNVPTR::TLNE	T2,-1		;*REAL POINTER?
	JRST	CNVPT2		;*YES
	TRZ	T2,RIPNUB##	;*CHANGE UNIT. REMOVE BIT 18
CNVPT1:	PUSHJ	P,NEWUNI	;*SET U, DEVUNI
	  TDZA	T2,T2		;*INVALID U - SET TO 0
	JRST	CPOPJ1##	;*OK - RETURN
	SOS	(P)
	JRST	CNVPT1		;*SET U TO 1ST UNIT IN STR AND RETURN

CNVPT2:	MOVE	T4,UNISTR##(U)	;*STR DB LOCATION
	LDB	T1,STYCLP##(T4)	;*CLUSTER ADDRESS
	LDB	T3,UNYBPC##	;*BLOCKS PER CLUSTER
	IMUL	T1,T3		;*BLOCK ADDR
	MOVEM	T1,DEVBLK##(F)	;*SAVE IN DDB
	LDB	T1,STYCNP##(T4)	;*GROUP COUNT FIELD
	IMUL	T1,T3		;*BLOCK COUNT
	JRST	CPOPJ2##	;*RETURN

;SUBROUTINE TO RESET U, DEVUNI(F) TO A NEW VALUE
;ENTER WITH LOGICAL UNIT NUMBER IN T2
;EXIT WITH U, DEVUNI SET TO NEW VALUE
;RETURNS CPOPJ IF NO SUCH UNIT, CPOPJ1 NORMALLY
;NEWUX SAME, PRESERVES T1,T3,T4
NEWUX:
NEWUNI::HRRZ	U,UNISTR##(U)	;*LOC OF FILE STRUCTURE DB
	JUMPE	U,CPOPJ##	;*RETURN NON-SKIP IF NOT IN A F/S
	HLRZ	U,STRUNI##(U)	;*LOC OF UNIT 0 IN STRUCTURE

;SUBROUTINE TO RESET U,DEVUNI(F) TO A NEW VALUE
;ENTER WITH POINTING TO 1ST UNIT IN STR
;EXIT WITH U,DEVUNI(F) SET TO NEW VALUE
;T1,T3,T4 RESPECTED
NEWUN::	SOJL	T2,NEWUN2	;*DONE IF T2 COUNTS OUT
	HLRZ	U,UNISTR##(U)	;*STEP TO NEXT UNIT OF STRUCTURE
	JUMPN	U,NEWUN		;*TRY NEXT
	JRST	NEWUN3		;*DESIRED UNIT WAS HIGHER THAN LAST UNIT IN STRUCTURE
NEWUN2:	HRRM	U,DEVUNI##(F)	;*SET DDB
	HRRZ	J,UNIKON##(U)	;*SET UP J
	JRST	CPOPJ1##	;*AND EXIT

NEWUN3:	TRO	S,IOBKTL	;*ILLEGAL UNIT - LIGHT ERROR BIT
	JRST	STRIOS		;*SAVE S AND ERROR RETURN
	SUBTTL	FILINT  - INTERRUPT HANDLING MODULE
FILINT::PUSHJ	P,SAVE4##	;SAVE P1-P4
	PUSH	P,T3		;SAVE DATAI WORD
	PUSH	P,T2		;SAVE CONI WORD
	PUSH	P,T1		;SAVE COMMUNICATION WORD
;SET UP P1 AS A GLOBAL IN FILINT MODULE = ADDRESS OF CHAN DATA BLOCK
	HRRZ	P1,KONCHN##(J)	;SET UP P1=LOC OF CHAN DB
	HLLZ	P2,T1		;GET INTERRUPT BITS
	TLZ	P2,1777		;MASK OUT UNIT
	ANDCAM	P2,(P)		;MASK OUT OF COMMUNICATION WORD
	SETZM	P4		;P4 WILL KEEP THE DRIVE NUMBER
POSTST:	JFFO	P2,.+2		;ANY MORE POSITIONS?
	JRST	POSDON		;NO, CLEAN UP TRANSFER
	LSH	P2,1(P3)	;YES. SET P2 FOR NEXT DRIVE TEST
	ADDB	P4,P3		;COMPUTE THE DRIVE 
	SKIPN	U,@KONPTR##(J)	;SET U=UNIT BLOCK ADR
	JRST	FINPS2		;NO UNIT BLOCK - IGNORE THE INTERRUPT
IFN FTDUAL,<
	MOVE	T1,UNICYL##(U)	;IF THIS IS PART OF DUAL-PORT DRIVE
	SKIPE	T2,UNI2ND##(U)
	MOVEM	T1,UNICYL##(T2) ;THEN BOTH UNITS ARE ON SAME CYL
>
	SKIPE	T1,UNISTS##(U)	;GET STATE OF UNIT
	CAIL	T1,OWCOD##	;OPERATOR WAIT?
	JRST	FREINT		;IDLE OR OPR WAIT
	JUMPL	T1,RECALH	;IF NEGATIVE WE'RE IN PHUNG RECOVERY
	CAIN	T1,TCOD##	;IS UNIT IN TRANSFER STATE?
	JRST	RECALR		;YES - POSITIONING INT. FROM XFERRING DRIVE
	CAIE	T1,SCOD##	;NO, WAS UNIT S?
	JRST	FINPS1		;NO,
	SETZM	UNISTS##(U)	;YES, SET STATUS =0 (IDLE)
	SKIPN	UNIQUE##(U)	;ANY POSITIONS WAITING?
	AOJA	P4,POSTST	;NO. TEST NEXT POSITION BIT
	PUSH	P,KONCUA##(J)	;YES, SAVE KONCUA (STARTING
				; A POSITION WILL CHANGE IT)
	PUSHJ	P,UNIPOS	;START UNIT POSITIONING (FORGET SEEK)
	POP	P,T1		;KONCUA
	MOVE	T2,UNISTS##(U)	;STATE OF UNIT
	CAIE	T2,TCOD##	; TRANSFER?
	MOVEM	T1,KONCUA##(J)	;NO, RESET KONCUA (CHANGED
				; SINCE UNIT WAS POSITIONED, POSDON
				; WILL CHECK STATE OF UNIT
	AOJA	P4,POSTST	;AND GO TEST NEXT POSITION BIT

;CALLED BY KON ROUTINE WHEN A UNIT GOES OFF-LINE
;PRESERVES T1
FILDN::	MOVEI	T2,O2COD##	;IF  IDLE SAY OFF-LINE, NO MESSAGE
	SKIPN	UNISTS##(U)	; (IF BUSY HNGDSK WILL CATCH IT)
	MOVEM	T2,UNISTS##(U)	; AND RETURN
	POPJ	P,
;HERE ON AN UNSOLICITED INTERRUPT, UNIT IDLE OR OPR WAIT
FREINT:	SKIPG	KONPOS##(J)	;DOES UNIT DO POSITIONING?
	STOPCD	FINPS2,DEBUG,FDP,	;++FIXED-HEAD DEVICE POSITION
	MOVSI	T2,UNPUNO##	;CLEAR 'OFF-LINE' AND FILE UNSAFE BITS
	ANDCAM	T2,UNIDES##(U)
OWRSET:	CAIE	T1,OW2COD##
	CAIN	T1,OWCOD##	;WAS THERE A FILE IN T STATE ON THIS UNIT?
IFE FTDUAL,<
	JRST	OWRSE1		;YES, ASSUME POWERED UP IN RESPONSE TOO TELOPR
>
IFN FTDUAL,<
	JRST	OWRSED
>
	SKIPE	DINITF##	;IF STILL IN ONCE-ONLY
	JRST	FREIN2		; DONT DO ANYTHING FANCY
	SKIPE	UNILOG##(U)	;IS THIS PACK KNOWN TO THE SYSTEM?
	JRST	FREIN1		;YES, REREAD HOME BLOCKS
IFN FTMDA,<
	PUSHJ	P,CALMDA	;NO, TELL MOUNTABLE DEVICE ALLOCATOR
	  JRST	FREIN2		;NOT RUNNING, CONTINUE
	PUSHJ	P,SET4MD	;SET SO ONLY MDA CAN READ DRIVE
	AOJA	P4,POSTST	;AND TEST NEXT DRIVE
>
IFE FTMDA,<
	JRST	FREIN2
>

;HERE IF PACK KNOWN TO SYSTEM
FREIN1:	MOVSI	T2,UNPRHB##	;ALREADY GOTTEN AN INTERRUPT FROM DRIVE?
	TDNN	T2,UNIDES##(U)
	AOS	HOMFLG##	;NO, COUNT UP NO OF HOMES TO READ
	IORM	T2,UNIDES##(U)	;INDICATE THIS UNIT NEEDS REREADING
	SETZM	UNISTS##(U)	;MAKE IDLE SO WONT START A SEEK
	AOJA	P4,POSTST	;AND GO TEST NEXT UNIT
FREIN2:	SKIPE	T1,UNIQUE##(U)	;FILES WAITING TO START POSITIONING?
	MOVEI	T1,PWCOD##	;YES
	MOVEM	T1,UNISTS##(U)	;SET UNIT IDLE OR PW
	JUMPE	T1,FINPS2	;TEST NEXT UNIT IF IDLE
	PUSHJ	P,SETID1	;POSITIONERS WAITING - START ONE GOING
	AOJA	P4,POSTST	;AND TEST NEXT UNIT
;SUBROUTINE TO SET THINGS UP SUCH THAT ONLY MDA CAN READ A UNIT
SET4MD:	MOVEI	T1,MDACOD##
	MOVEM	T1,UNISTS##(U)	;SET SO ONLY MDA CAN READ THE DRIVE
	MOVSI	T1,UNPWMD##	;SET THAT UNIT IS WAITING FOR MDA
	IORM	T1,UNIDES##(U)	; SO WILL GO BACK TO MDA WAIT AFTER A READ
	POPJ	P,		;AND RETURN

;HERE FOR POSITION INTERRUPT, DRIVE IN TRANSFER STATE
;THIS HAPPENS ON RECALIBRATE AFTER DATA ERROR
RECALR:	MOVE	F,UNICDA##(U)	;CURRENT DDB
	MOVE	S,DEVIOS(F)	;S WORD (SO STARTE WILL KNOW IF READ OR WRITE)
	SKIPL	T1,CHNECT##(P1)	;RECALIBRATE INTERRUPT?
	JRST	RECAL1		;NO - START DATA AGAIN
	SETZM	CHNECT##(P1)	;YES - REPOSITION DRIVE
IFN FTRP04,<
	AOJN	T1,RECAL1	;IF CHNECT=-1,,0 OFFSET COMPLETED
>
RECALH:	HRRZS	UNISTS##(U)	;CLEAR SIGN BIT (PHUNG RECOVERY)
	PUSHJ	P,@KONPOS##(J)	;START POSITION
	  PUSHJ	P,BADUNI	;DRIVE IS NOW DOWN
	AOJA	P4,POSTST	;GO HANDLE OTHER ATTENTION INTERRUPTS
RECAL1:	PUSHJ	P,STARTE	;START DATA TRANSFER
	AOJA	P4,POSTST	;AND TEST NEXT ATTENTION BIT
IFN FTDUAL,<
OWRSED:	SKIPLE	T2,UNI2ND##(U)	;IF THIS INTERRUPT ON A-PORT
	CAME	T1,UNISTS##(T2)	; AND B-PORT IS BUSY
	CAIA
	JRST	FINPS2		; WAIT FOR INTERRUPT ON B TO START IO
	HRRZ	T1,(P)		;COMMUNICATION OWRD
	TRZE	T1,IODTER+IODERR ;IS AN ERROR UP
	CAIE	T1,OPPOS##	; AND JUST A POSITION COMPLETE?
	JRST	OWRSE2		;NO, "REAL" FREE INTERRUPT
	LDB	T1,[POINT 3,(P),17] ;YES, DID THIS DRIVE HAVE THE ERROR
	CAIN	T1,(P4)
	JRST	FINPS2		;YES, IT ISN'T THE INTERRUPT WE WANT
OWRSE2:	PUSH	P,U		;NO, FREE INTERRUPT
	JRST	OWRSE1		;GO REDO THE OPERATION
>

;HERE FOR POSITION INTERRUPT, DRIVE NOT IN S OR T STATE
FINPS1:	TRNE	T1,1		;IS UNIT CURRENTLY IN A WAIT STATE?
	JRST	FINPS2		;YES, SPURIOUS INTERRUPT
	HRRZ	F,UNICDA##(U)
	JUMPE	F,FINPS2	;FORGET IT IF SPURIOUS
	MOVE	S,DEVIOS(F)
	TLNE	S,IOSMON	;MONITOR OR SWAPPER IO?
	AOSA	UNIMSC##(U)	;YES. COUNT 1 MORE MONITOR OR SWAP SEEK
	AOS	UNIUSC##(U)	;NO. COUNT 1 MORE USER SEEK
	SKIPN	DEVUNI##(F)	;UNIT BEEN YANKED?
	JRST	FINPS3		;YES
IFN FTDUAL,<
	PUSH	P,U
>
	CAIN	T1,PCOD##	;IF UNIT WAS IN POSITION,
OWRSE1:	PUSHJ	P,SETTTW	;SET FILE TO TW, OR T AND START TRANSFER
IFN FTDUAL,<
	POP	P,U		;IF WE STARTED IO ON ALTERNATE UNIT
	HRRZ	J,UNIKON##(U)	;THEN U, J AND P1 WERE CHANGED
	HRRZ	P1,KONCHN##(J)	;SO RESET THEM
>
FINPS2:	AOJA	P4,POSTST	;GO TEST NEXT POSITION INTERRUPT

;HERE IF UNIT WAS YANKED DURING A SEEK
FINPS3:	MOVEI	U,0
	PUSHJ	P,UNIYNK	;FIX UP DDB,UDB
	AOJA	P4,POSTST	; AND TEST NEXT POSITION INT.

;SUBROUTINE TO PUT THE DRIVE POINTED TO BY U IN T OR TW
;P1= CHAN DATA BLOCK ADR.
;IF PUT THE DRIVE IN T1, START TRANSFERRING DATA
;UUOTWQ IS CALLED FROM UUO LEVEL

SETTTW:	DSKOFF			;TURN ALL DISK PI'S OFF
	HRRZ	F,UNICDA##(U)	;*SET F TO FILE
IFN FTVM,<
	MOVE	T1,UNIBLK##(U)	;IN CASE OF SWAPPING ON 2 DF10'S
	SKIPL	DEVSWP##(F)
	JRST	UUOTWQ
	MOVEM	T1,DEVBLK##(F)	; DEVBLK MAY BE TIMESHARED
	PUSHJ	P,SWPCHK##	;CAN WE START IO NOW?
	  JRST	SETTW0		;NO, QUEUE TILL LATER
>
UUOTWQ:
IFN FTDHIA,<
	SKIPE	DIADSK##	;SHUTTING DOWN IO
	CAME	P1,DIACHN##	; FOR THIS CHANNEL?
	JRST	UUOTWR		;NO
	CONSO	PI,PIPROG##	;YES, ON UUO LEVEL?
	JRST	SETTW0		;YES, PUT REQUEST IN TW QUEUE
UUOTWR:>
	AOSG	@KONCHN##(J)	;#CHAN AVAILABLE?
IFN FTDUAL,<
	JRST	UUOTWS		;#YES, START IO
	SKIPE	T1,UNI2ND##(U)	;#NO, IS THIS A DUAL-PORTED DRIVE?
	SKIPE	UNISTS##(T1)	;#YES, IS THE 2ND DRIVE USEABLE?
	JRST	SETTW0		;#NO, QUEUE THE REQUEST ON 1ST CHAN
	LDB	T2,UNZUST##	;STATUS OF OTHER PORT
	CAIN	T2,UNVDWN##	;DOWN? (DETACH COMMAND)
	JRST	SETTW0		;YES, DON'T TRY TO USE IT
	MOVE	T2,UNIKON##(T1)	;#YES, T1=UNIT  T2=KON
	SKIPL	KONTAB##(T2)	;KONTROLLER IDLE?
	AOSE	@KONCHN##(T2)	;#YES, CHAN IDLE?
	JRST	SETTW0		;CANT START IT NOW
	HRRM	F,UNICDA##(U)	;SAVE DDB IN MAIN UDB
	MOVE	T3,UNISTR##(U)	;WILL NEED UNISTR LATER
	MOVEM	T3,UNISTR##(T1)	;(CHKSUM), SO SET IT UP
	MOVE	T3,UNIBPC##(U)	;WILL NEED UNIBPL
	MOVEM	T3,UNIBPC##(T1)	; IF WE GO TO NXTBLK
	HRRZ	J,T2		;WE CAN START THE IO ON THE ALTERNATE UNIT
	HRRZ	U,T1		; SO SET UP NEEDED ACS
	MOVE	P1,KONCHN##(J)
UUOTWS:>
IFE FTDSIM,<
	PJRST	STRTIO		;#YES, START IO
>
IFN FTDSIM,<
	PJRST	SETBS1		;HAVE TO CHECK FOR WRITE/USETO RACE
>
SETTW0:	MOVEI	T1,TWCOD##	;#TRANSFER WAIT STATE CODE
	DPB	T1,DEYCOD##	;MAKE DDB TW
IFN FTVM,<
	SKIPL	DEVSWP##(F)	;#IF THIS IS THE SWAPPER,
	JRST	SETTW1
	SETZM	UNISTS##(U)
	JRST	PUTQU3		;# DONT PUT IN CHAN QUEUE (START IT AT SWPPIK ONLY)
SETTW1:
>
				;#PUT IN TWQ AND RETURN
	CONSO	PI,PI.IPA-PI.IP7  ;IF ON UUO LEVEL,
	SKIPN	UNISTS##(U)	; DON'T CHANGE UNISTS IF NON-0
	MOVEM	T1,UNISTS##(U)	;ELSE MAKE UNIT TW STATE
IFN FTDUAL,<
	PUSHJ	P,SECCOD	;#MAKE OTHER UNIT STATE = THIS ONE
	HRRM	U,DEVCUR##(F)	;#SAVE CURRENT UNIT IN DDB
>
	MOVEI	T1,CHNQUE##(P1)	;#T1= QUEUE ON WHICH TO PUT FILE
IFN FTDUAL,<
REPEAT 0,<	;NOT NEEDED UNTIL WE GET A FIXED-HEAD DUAL-PORTED DRIVE
	SKIPLE	T2,UNI2ND##(U)	;IF A MAIN-UNIT
	SKIPN	UNISTS##(T2)
	JRST	PUTQUX		; WHICH HAS AN ALTERNATE UNIT
	MOVE	T1,UNICHN##(T2)	;IF THE ALTERNATE UNIT IS BUSY THE REQUEST
	MOVEI	T1,CHNQUE##(T1)	; SHOULD BE PUT ON THE ALTERNATE CHAN QUEUE
>	;END REPEAT 0
>	;END FTDUAL
				;AND FALL INTO PUTQUX
;SUBROUTINE TO ENTER A FILE IN A QUEUE
;  PUTQUX FOR CHANNEL (XFER WAIT) QUEUES
;	  P1=ADDR. CHAN. DATA BLOCK
;  PUTQUE FOR UNIT (POSITION WAIT) QUEUES
;	  U=ADDR. UNIT DATA BLOCK
;C(T1)=LOC OF QUEUE POINTER   F POINTS TO FILE
;LH OF QUEUE POINTER CONTAINS FIRST FILE (DDB) ADR., RH=0
;THESE ARE REALLY LISTS NOT QUEUES, SINCE ARE REMOVED IN RANDOM ORDER
PUTQUX:
IFN FTDSTT,<			;IF KEEPING DISK STATISTICS
	AOSA	CHNQUL##(P1)	;  INCREMENT XFER QUEUE LENGTH
>
PUTQUE:
IFN FTDSTT,<
IFE FTVM,<
	AOS	UNIQUL##(U)	;  OR POSITION QUEUE
>
IFN FTVM,<
	AOSA	UNIQUL##(U)
>
>
IFN FTVM,<
	AOS	CHNWAT##(P1)
>
IFE FTDPRI,<
	MOVE	T2,(T1)		;CURRENT FIRST IN "QUEUE"
	HRLM	F,(T1)		;MAKE THIS FIRST
	HLLM	T2,DEVQUE##(F)	;POINT THIS AT NEXT
				; TO BE ADR. FILE(DDB)BEING ADDED 
>
IFN FTDPRI,<	;IF DISK PRIORITY
	MOVEI	T4,MINDVQ##(T1)	;SET TO POINTER-OFFSET (FIRST PREDECESSOR)
	SKIPN	(T1)		;EMPTY?
	JRST	PUTQU2		;YES, GO
	PUSHJ	P,SAVE1##	;NO, SAVE P1
	PUSHJ	P,DFPRI		;COMPUTE PRIORITY
	MOVE	P1,T2		;IN P1
	HLRZ	T3,(T1)		;FIRST ITEM IN QUEUE
PUTQU1:	PUSHJ	P,D3PRI		;PRIORITY OF FILE IN QUEUE
	CAMGE	T2,P1		;IS NEW ENTRY HIGHER?
	JRST	PUTQU2		;YES. INSERT IT HERE
	MOVE	T4,T3		;NO, SAVE AS PREDECESSOR
	HLRZ	T3,DEVQUE##(T3)	;STEP TO NEXT IN QUEUE
	JUMPN	T3,PUTQU1	;TEST THAT ONE
PUTQU2:	HLLZ	T2,DEVQUE##(T4)	;POINTER TO NEXT IN QUEUE
	HRLM	F,DEVQUE##(T4)	;LINK THIS FILE TO IT
	HLLM	T2,DEVQUE##(F)	;INSERT NEW LINK TO NEXT
>
PUTQU3:	DSKON			;#TURN ALL DISK PI'S BACK ON
	SCHEDULE
	SETZ	T1,		;SET HUNG-TIME TO ZERO
	DPB	T1,PDVCNT##	; SO WONT TIME OUT WHILE IN A QUEUE
	POPJ	P,		;AND RETURN


IFN FTDPRI,<
;SUBROUTINE TO COMPUTE PRIORITY OF A DISK FILE
;ENTER AT D3PRI IF DDB ADR IN T3, AT DFPRI IF IN F
;EXIT WITH T2=PRIORITY.  RESPECTS ALL ACS EXCEPT T2
D3PRI:	LDB	T2,DEZPRI##	;PNTR INDEX = T3
	CAIA
DFPRI:	LDB	T2,DEYPRI##	;INDEX=
	TRZE	T2,MINDPR	;PRIORITY NEGATIVE?
	MOVNS	T2		;YES, MAKE T2 NEGATIVE
	POPJ	P,		;AND RETURN
>
;ROUTINE TO SET UP R (POSSIBLY AN INTERRUPT LEVEL)
ADRINT:	LDB	T1,PJOBN##	;JOB NUMBER
	SKIPN	R,JBTADR##(T1)	;GET ADDRESS
	SKIPG	DEVSWP##(F)	;NONE - SWAPPER OR REREADING HOME BLOCKS?
	POPJ	P,		;HAVE R=ADDR, OR SWAPPER
	SKIPE	DINITF##	;NOTHING - ONCE CODE?
	POPJ	P,		;YES, OK RETURN
	STOPCD	CPOPJ##,DEBUG,JNC,	;++JOB NOT IN CORE
;SUBROUTINE TO START IO ON FILE POINTED TO BY F
;P1= CHAN. DATA BLOCK ADR.
;ALSO CALLED AT UUO LEVEL
STRTIO::MOVSI	T1,400000	;#SET KONTROL BUSY	
	ORM	T1,KONTAB##(J)	;#
IFN FTPDBS,<
	IOSMON==IOSMON		;FOR CREF. BIT IS ALREADY IN T1
	SKIPGE	DEVSWP##(F)	;IS THIS THE SWAPPING DDB?
	IORM	T1,DEVIOS(F)	;YES--MAKE SURE IOSMON IS SET IN DDB.
> ;END FTPDBS
	MOVEI	T1,TCOD##	;#SET FILE, UNIT TO T1
	PUSHJ	P,FILCOD	;SETPAR EXPECTS T1 TO STILL CONTAIN TCOD
				; FOR METER POINT
	PUSHJ	P,SETPAR	;SET KONCUA,UNICDA,UNIBLK
IFN FTDUAL,<
	HRRM	U,DEVCUR##(F)	;SAVE CURRENT UNIT IN DDB
	PUSHJ	P,SECCOD	;MAKE OTHER UNIT STATE = THIS ONE
>
	DSKON			;#TURN ALL DISK PI'S BACK ON
	SCHEDULE
	MOVE	S,DEVIOS(F)	;S WORD
	PUSHJ	P,ADRINT	;SET UP R FROM JBTADR
	PUSHJ	P,SETLST	;SET UP IOLST, NUMBER OF BLOCKS
IFN FTKL10,<
	PUSHJ	P,CFDMP		;INVALIDATE CACHE, VALIDATE MEMORY
>
	SETZM	CHNECT##(P1)	;ZERO ERROR COUNT LOC
	SETZM	CHNRCT##(P1)	;ZERO RECALIBRATE-COUNT
	SETZM	UNIRCV##(U)	;ZERO HUNG UNIT RETRY-COUNTER
	MOVSI	T1,UNPFIR##	;SET SIGN BIT IN UNIECT
	ORM	T1,UNIECT##(U)	; TO INDICATE NO ERRORS YET
	JUMPL	S,STARTE	;NO CHECKSUMS IF MONITOR IO
	TLNE	S,IO		;DATA - READING?
	TLZN	S,IOSFIR	;WRITING DATA - CHECKSUM TIME?
	JRST	STARTE		;NO
	MOVEM	S,DEVIOS(F)	;YES, SAVE S IN DDB
	PUSHJ	P,CHKSUM	;COMPUTE CHECKSUM
	SKIPN	T2,@DEVRET##(F)	;GET RETRIEVAL PNTR
	MOVE	T2,DEVRB1##(F)	;JUST 1 PNTR IN DDB, STORED IN DEVRB1
	MOVE	T3,UNISTR##(U)	;LOC OF STR DB
	MOVE	T4,T2		;SAVE OLD POINTER
	DPB	T1,STYCKP##(T3)	;SAVE CHKSUM IN PNTR
	CAMN	T4,T2		;DID CHECKSUM CHANGE?
	JRST	STARTE		;NO, CRANK UP THE IO
	SKIPN	@DEVRET##(F)	;YES, JUST 1 PNTR IN DDB?
	MOVEM	T2,DEVRB1##(F)	;YES. SAVE NEW PNTR BACK OVER OLD
	SKIPE	@DEVRET##(F)
	MOVEM	T2,@DEVRET##(F)	;SAVE PNTR IN DDB
	HRRZ	T1,DEVACC##(F)	;ACCESS TABLE
	MOVE	T3,DEVREL##(F)	;RELATIVE BLOCK OF FILE
	SOJN	T3,STRTI1	;IF 1ST BLOCK,
	MOVEM	T2,ACCPT1##(T1)	; SO ANOTHER JOB CAN READ THIS FILE (UPDATE)
STRTI1:	MOVE	T3,ACCSTS##(T1)	;FILE STATUS
	TRNN	T3,ACPUPD	;FILE BEING UPDATED?
	JRST	STARTE		;NO
	MOVEI	T4,ACPSBC##	; SO DDBS WHICH HAVE POINTER IN CORE ALREADY
	ORM	T4,ACCSBC##(T1)	; WON'T GET SPURIOUS CHECKSUM ERRORS

IFN  FTDSIM,<
;MIGHT GET A WRONG CHECKSUM INTO THE RIB OF A SIM-UPDATE FILE IF WE
; LEAVE A BAD CHECKSUM IN SOME OTHER DDB, SO FIND ALL WRITERS OF THE FILE
; AND CHANGE THE CHKSUMS IN THEIR DDB'S
	LDB	T3,ACYWCT##	;IF WE'RE THE ONLY WRITER,
	SOJLE	T3,STARTE	; FORGET IT
	PUSH	P,T2		;OTHER WRITERS - SAVE THE NEW PNTR
	MOVE	T3,UNISTR##(U)	;SET UP A MASK FOR PNTR ADDRESSES
	SETO	T2,
	LDB	T2,STYCLP##(T3)
	MOVEM	T2,MSKLOC	;AND SAVE IT
	PUSHJ	P,FNDDDB	;FIND A WRITING DDB
	  JRST	STRTI6		;NONE THERE (SYSTEM ERROR?)
	HRRZ	T3,DEVUNI##(T2)	;UNIT
	CAIE	T3,(U)		;SAME UNIT?
	JRST	STRTI5		;NO, FIND ANOTHER DDB

;HERE WHEN WE FOUND A WRITING DDB
STRTI2:	MOVEI	T1,DEVRB1##(T2) ;SET A POINTER FOR THE
	HRLI	T1,MPTRLN##	;RETRIEVAL PNTRS OF FOUND DDB
STRTI3:	SKIPN	T3,(T1)		;GET A RET PNTR FROM THE DDB
	JRST	STRTI4
	XOR	T3,(P)		;DOES IT MATCH NEW PNTR?
	TDNE	T3,MSKLOC
	JRST	STRTI4		;NO, TRY NEXT PNTR
	MOVE	T3,(P)		;YES, SUBSTITUTE NEW PNTR
	MOVEM	T3,(T1)		;IN THE FOUND DDB
	JRST	STRTI5		;AND TEST FOR ANOTHER WRITER
STRTI4:	AOBJN	T1,STRTI3	;TES NEXT PNTR IN FOUND DDB
STRTI5:	PUSHJ	P,FNDDDN	;LOOK FOR ANOTHER WRITER
	  JRST	STRTI6		;NONE THERE, DONE
	JRST	STRTI2		;FOUND ONE, LOOK FOR PNTR IN THIS DDB
STRTI6:	POP	P,(P)		;DONE - REMOVE PNTR FROM STACK
>	;END FTDSIM
STARTE::MOVEI	T1,KONRDS##(J)	;SET TO READ - STOP ON ERROR FOR F.S.
				; ALL BUT LAST TRY SO KNOW BAD BLOCK+WORD
	TLNE	S,IO
	MOVEI	T1,KONWTS##(J)	; WRITE DATA - STOP ON ERROR FOR F.S.
				; ALL BUT LAST TRY SO KNOW BAD BLOCK+WORD
IFN FTDSUP,<
	TLNN	S,IOSUPR	;SUPER I/O MODE?
	JRST	STARTF		;NO, START THE IO
	TRNE	S,UDSX		;YES, READ/WRITE HEADERS AND DATA?
	AOJA	T1,STARTF	;YES, RDF/WTF = RDS/WTS +1
IFN FTRP04,<
	MOVSI	T2,DEPCPT##	;NO, COMPATABILITY MODE?
	TDNE	T2,DEVCPT##(F)
	ADDI	T1,2		;YES, ENTRY POINT = RDS(WTS)+2
>>
STARTF:	MOVEM	S,DEVIOS(F)	;SAVE S
	DSKOFF			;MAKE SURE NO INTERRUPTS HAPPEN
	PUSHJ	P,@(T1)		;#START READ OR WRITE
	  JRST	BADUNI		;#UNIT NOT OK
	DSKON			;#
	POPJ	P,

;SUBROUTINE TO SET STATE OF FILE,UNIT TO C(T1)
;THIS ROUTINE RESPECTS AC T1
FILCOD:	DPB	T1,DEYCOD##	;#SET STATE OF FILE
	MOVEM	T1,UNISTS##(U)	;#SET STATE OF UNIT
	POPJ	P,		;#AND RETURN

IFN FTDUAL,<
;ROUTINE TO SET 2ND UNIT'S STATE CODE = THIS UNIT'S
SECCOD:	SKIPL	T1,UNI2ND##(U)	;IS THIS AN ALTERNATE UNIT?
	POPJ	P,		;NO
	MOVE	T2,UNISTS##(U)	;YES, GET STATE WORD OF UNIT
	MOVEM	T2,UNISTS##(T1)	;AND SAVE IN UDB FOR PRIME UNIT
	POPJ	P,		;AND RETURN
>

;SUBROUTINE TO SET UP AN IOLIST BLOCK
;ENTER WITH J,U,F AND S SET UP
;P1=CHAN.DATA BLOCK ADR.
;ALSO CALLED AT UUO LEVEL
;RETURNS WITH THE LIST IN CORE, POINTED TO BY @KONIOC(J)
SETLST:	PUSHJ	P,NXTBLK	;GET NEXT BLOCK OF FILE
	  STOPCD .,JOB,BWA,	;++BLOCK WENT AWAY
IFN FTKA10,<
	MOVE	T4,KONCOM##(J)	;AOBJN POINTER FOR IO LIST BLOCK
>
IFE FTVM,<
	HRRZM	T4,@KONIOC##(J)	;SET KONIOC TO LOC OF IOLIST BLOCK
>
IFN FTKI10!FTKL10,<
	PUSHJ	P,SAVE4##	;YES, SAVE P1-P4
	HRLZ	P3,J		;SAVE J
	HRR	P3,P1		;SAVE CHAN DB ADR
	TLO	P3,400000	;TELL MAPIO TO STORE EXPECTED TERM WD
	LDB	J,PJOBN##	;JOB NUMBER
IFN FTSWAP,<
IFE FTVM,<
	SKIPGE	DEVSWP##(F)	;SWAPPER?
	MOVM	J,FINISH##	;YES, GET THE SEGMENT NUMBER
>
IFN FTVM,<
	SKIPLE	DEVSWP##(F)	;"THIS" MAKES JOB ADDRESSABLE ITSELF
>
>	;END FTSWAP
	PUSHJ	P,SVEUB##	;SET UP UBR
	SETZB	P1,P2		;P1=FREE CORE LOC
>	;END FTKI10
	PUSH	P,U		;SAVE U
	MOVE	T2,DEVBLK##(F)	;BLOCK TO START AT
	HRRZ	T1,UNIBPY##(U)	;NUMBER OF BLOCKS PER CYLINDER
	IDIVI	T2,(T1)		;T3=RELATIVE BLOCK IN CYL
	SUBI	T1,(T3)		;T1=DISTANCE TO END OF CYLINDER
	CAMLE	T1,MAXTRN##	;GTR THAN THE MAX NUMBER OF BLOCKS ALLOWED?
	MOVE	T1,MAXTRN##	;YES, REDUCE COUNT (SO HIGH PRIORITY REQUESTS
				; WONT GET LOCKED OUT TOO LONG)
	HRRZ	T3,DEVLFT##(F)	;NO OF BLOCKS LEFT IN CURRENT GROUP
	SKIPL	S		;ASSUME ALL BLOCKS TO EOL OK IF MON MODE
	CAMLE	T3,T1		;MORE THAN TO END OF CYLINDER?
	MOVE	T3,T1		;YES, REDUCE COUNT
	TLNE	S,IO+IOSUPR+IOSMON	;READING DATA?
	JRST	SETLS0		;NO. USE ALL OF GROUP
	MOVE	T2,DEVACC##(F)	;YES. GET LOC OF A.T.
	MOVE	T2,ACCWRT##(T2)	;NO OF DATA BLOCKS IN FILE
	SUB	T2,DEVREL##(F)	;NO OF DATA BLOCKS LEFT (FROM CURRENT POSITION)
	CAIGE	T2,-1(T3)	;MORE IN GROUP THAN HAVE DATA?
	MOVEI	T3,1(T2)	;YES, ONLY READ BLOCKS WITH DATA
SETLS0:	JUMPL	S,SETDMP	;SAME AS DUMP IF MONITOR MODE
	LDB	T1,PIOMOD##
	CAIL	T1,SD		;NO, DUMP MODE?
	JRST	SETDMP		;YES
	HRLZ	T1,T3		;BUFFERRED MODE. NUMBER OF BLOCKS LEFT IN GROUP
	MOVNS	T1		;SET FOR AOBJN POINTER
	TLNN	S,IO		;READING?
	SKIPA	T2,DEVIAD(F)	;YES. USE DEVIAD
	MOVE	T2,DEVOAD(F)	;WRITING, USE DEVOAD
	HRLI	T2,MBLKSZ##	;SET LH(T2) FOR AN IOWD
	HLRZ	U,R		;SET U TO USERS PROTECTION
	SUBI	U,BLKSIZ##	;-200 FOR ADRRESS CHECK
	MOVEI	T3,1(T2)	;STARTING BUFFER LOC
	HRLM	T3,DEVUVA##(F)	;SAVE UVA-1 OF CHKSUM WORD
	SUBI	T3,1

SETLS1:	CAMLE	T2,[XWD MBLKSZ##,JOBPFI##]	;IS BUFFER LOC ABOVE JOBPFI
	CAIGE	U,(T2)		;AND BELOW TOP OF USER'S SPACE?
	JRST	SETLS5		;NO  - LET UUOCON GIVE ADR ERROR MESSAGE
IFN FTKA10,<
	ADDI	T2,(R)		;YES, RELOCATE
>
IFN FTVM,<
	EXCTUU	<MAP P4,-1(T2)>	;IS THE PAGE IN CORE?
IFN FTKI10,<
	TRNN	P4,420000
>
IFN FTKL10,<
	PUSHJ	P,SFLTST	;CALL COPY OF FLTST FOR FILSER
>
	EXCTUU	<MAP P4,BLKSIZ+1(T2)>
IFN FTKI10,<
	TRNN	P4,420000
>
IFN FTKL10,<
	PUSHJ	P,SFLTST	;CALL COPY OF FLTST FOR FILSER
>
	  JRST	SETL1B		;NO PAGE FAILURE, CONTINUE
	JRST	SETLS5		;PAGE FAILURE WILL OCCURR, TERMINATE THE IO LIST
SETL1B:>	;END FTVM
	TLNN	S,IO
	JRST	SETLS2
	EXCTUX	<SKIPL (T2)>	;WRITING - IS NEXT BUFFER FULL?
	JRST	SETL5S		;NO, DONE
	JRST	SETLS3		;YES. SET IOWORD FOR BUFFER
SETLS2:	EXCTUX	<SKIPG (T2)>	;READING - IS NEXT BUFFER FREE?
	JRST	SETL5S		;NO. DONE
SETLS3:	ADDI	T2,1		;INCREMENT BY 1 FOR WORDCOUNT WORD
IFN FTKI10!FTKL10,<
	JUMPGE	S,SETL30	;IF MONITOR I/O,
	MOVE	P4,T2		;SAVE IOWD IN P4
IFN FTKL10,<
	MAP	T2,(T2)		;CONVERT TO UNMAPPED FOR MAPIO
	HLL	T2,P4		;GET WORD COUNT BACK
>
SETL30:	ADDI	T2,(R)
	HRLI	T2,MBLKSZ##
	MOVEI	P4,0
	PUSHJ	P,MAPIO##	; STORE THE IOWD IN FREE-CORE
	  JRST	PUNTB		;NOT ENOUGH MONITOR FREE-CORE
	SUBI	T2,(R)
	HRLI	T2,MBLKSZ##
>	;END FTKI10!FTKL10
IFN FTKA10,<
	MOVEM	T2,(T4)		;SAVE THE IOWD
>
	TLNE	S,IO		;READING?
	JRST	SETL3C		;NO
	EXCTXU	<HLREM T2,(T2)>	;YES, SAVE -WDCNT IN BUFFER WORD 0
	EXCTUU	<MOVNS (T2)>	;SET +WDCNT


SETL3C:	EXCTUX	<HRR T2,-1(T2)>	;STEP TO NEXT BUFFER
	CAIE	T3,(T2)		;BACK TO THE BUFFER WE STARTED AT?
	TRNE	S,IOCON		;OR DISCONTINUOUS MODE?
	AOJA	T4,SETLS4	;YES, IOWDS ARE ALL SET
IFN FTKA10,<
	AOBJP	T4,SETLS4	;COUNT THE IOWD, THROUGH IF NO MORE ROOM IN BLOCK
>
	AOBJN	T1,SETLS1	;COUNT THE BLOCK AND TRY FOR ANOTHER IOWD
	JRST	SETLS5		;NO BLOCKS LEFT IN THIS POINTER


IFN FTKL10&FTVM,<
;COPY OF FLTST FOR FILSER. CALL WITH RESULT OF MAP INSTRUCTION
; IN P4. SKIP RETURN IF FAULT WILL OCCUR, NON SKIP IF OK.
; (THIS IS BACKWARDS W.R.T. THE REAL FLTST)

SFLTST:	TLNN	P4,(1B8)	;PAGED REF?
	POPJ	P,		;NO, WILL NOT FAULT
	TLNN	P4,(1B1)	;BAD FAIL OR
	TLNN	P4,(1B2)	;NO ACCESS ALLOWED AND
	JRST	CPOPJ1##	;WE WILL FAULT
	POPJ	P,		;OTHERWISE WE WILL NOT

>;END IFN FTKL10&FTVM
;HERE WHEN THE IOLIST IS COMPLETELY SET UP
SETLS4:	AOJA	T1,SETLS5
SETL5S:
IFN FTKL10&FTMS,<
	EXCH	T1,T2		;WE'VE TOUCHED THE T2-BUFFER,
	PUSHJ	P,OUCHE##	; WON'T GET IT OUT OF CACHE BELOW,
	MOVE	T1,T2		; SO REMOVE IT FROM CACHE NOW
>
SETLS5:
IFN FTKI10!FTKL10,<
SETL5A:	HLRZ	J,P3		;RESTORE KONTROLLER DB LOC
	TRZ	J,400000
IFN FTKL10,<
	MOVEM	T2,KONCNT##(J)	;SAVE ACTUAL WRDCNT IF DUMP-MODE
	MAP	P2,(P2)		;CONVERT VIRTUAL ADDRESS TO PHYSICAL ADDRESS
>
	HRRZS	P2		;JUST ADDRESS (DF10-STYLE JUMP)
IFN FTKL10&FT22BIT,<
	JUMPE	P2,SETL5C	;ALL ZERO IF NO IOWDS
	MOVE	T2,CHB22B##(P3)	;RH20?
	TLNE	T2,CP.RH2##
	TLO	P2,RH2JMP##	;YES, MAKE IT THAT FLAVOR OF JUMP
>
SETL5C:	MOVEM	P2,@KONIOC##(J)	;SAVE LOC OF 1ST IOWD IN KONTROL BLOCK
IFN FTKL10&FT22BIT,<
IFN FTVM,<
	SKIPGE	DEVSWP##(F)	;SWAPPER?
	JRST	SETL5B		;YES, THIS ALREADY FIXED THINGS
>
	TLNE	P2,-1		;RH20?
	PUSHJ	P,RH2ND##	;YES, FINISH THE LIST
>
SETL5B:	MOVE	P1,P3
SETLS6:>	;END FTKI10!FTKL10
IFN FTKA10,<
	SETZM	(T4)		;TERMINATE IO LIST
	HLRE	T2,-1(T4)	;GET NEGATIVE WORD COUNT
	HRRZ	T3,-1(T4)	;ISOLATE ADDRESS
	SUB	T3,T2		;DETERMINE FINAL DATA ADDRESS
	HRL	T3,T4		;GET IOWD LIST TERMINATION ADDRESS
	MOVEM	T3,CHNTCW##(P1)	;SAVE AS EXPECTED TERMINATION CONTROL WD
>
SETLS7:	HRRZM	T1,CHNNUM##(P1)	;NUMBER OF BLOCKS TO TRANSFER
				; CLEAR LH (ERROR FLAG)
IFE FTKL10&FTMS,<
	JRST	UPOPJ##
>
IFN FTKL10&FTMS,<
	JUMPE	P2,UPOPJ##	;DONE IF NO LIST
SETLS8:	SKIPN	T1,(P2)		;GET NEXT IOWD
	JRST	UPOPJ##		;DONE IF 0
	TLNN	T1,577777	;IS IT A GOTO?
	JRST	[HRR P2,T1	;YES, POINT AT WHERE IT GOES
		 JRST SETLS8]	;AND GO TEST THAT IOWD
	TLNN	P2,-1		;POINT T1 AT 1ST DATA LOC -2
	SOSA	T1		; (POINTER TO NEXT BUFFER)
	SUBI	T1,2		;GET THAT LOC OUT OF THE CACHE
	PUSHJ	P,OUCHE##	;SINCE WE WON'T SWEEP WE MUST CLEAR VALI
	AOJA	P2,SETLS8	; FOR THAT CACHE LINE
>
;HERE TO SET UP A DUMP-MODE LIST
;ASSUMES THE CURRENT IOWD (RELOCATED) IS IN DEVDMP(F)
;UPDATES DEVDMP(F) BY NUMBER OF WORDS TRANSFERRED
;IF THE IOWD IS FINISHED, DEVDMP WILL BE POSITIVE
SETDMP:
IFN FTVM,<
	SKIPGE	DEVSWP##(F)	;IS IT THE SWAPPER?
	JRST	SETSWP		;YES, DO IT DIFFERENTLY
>
IFN FTKI10!FTKL10,<
	HLRZ	T4,P3
	TRZ	T4,400000
	MOVEI	T1,KONDMP##(T4)
	PUSH	P,T1
>
	HLLZ	T1,DEVDMP##(F)	;NUMBER OF WORDS IN IOWD
	MOVNS	T1		;+N
	HRLZ	T2,T3		;NUMBER OF BLOCKS LEFT IN GROUP
	LSH	T1,MBKLSH##	;NUMBER OF BLOCKS IN IOWD
	JUMPE	T2,SETDM2	;0 IS OK
IFN FTDSUP,<
	TDC	S,[IOSUPR,,UDSX]
	TDCN	S,[IOSUPR,,UDSX]	;FORMATTING?
	JRST	SETDM2		;YES, ASSUME HE REALLY KNOWS
>
	CAMLE	T1,T2		;MORE REQUESTED THAN IN GROUP?
	MOVE	T1,T2		;YES, TAKE LESSER AMOUNT
SETDM2:	LSH	T1,BLKLSH##	;CONVERT BACK TO WORDS
	MOVNM	T1,T3		;SAVE N
	MOVE	T2,DEVDMP##(F)	;CURRENT IOWD
	SKIPN	DINITF##	;IF NOT IN ONCE-ONLY,
IFN FTKA10,<
	MOVEM	T2,KONDMP##(J)	;SAVE IN KONT DATA BLOCK IN CASE UNIT IS OFF-LINE
>
IFN FTKI10!FTKL10,<
	MOVEM	T2,@0(P)
	POP	P,(P)		;REMOVE THIS WORD FROM PD LIST
>
	HLL	T2,T3		;SET LH OF IOWD
IFN FTKI10!FTKL10,<
	MOVEI	P4,0
	PUSHJ	P,MAPIO##	;SAVE THE IOWD IN FREE-CORE
	  JRST	PUNTD		;NOT ENOUGH FREE CORE
	SKIPA			;AND CONTINUE
>
	MOVEM	T2,(T4)		;STORE IOWD IN BLOCK
SETD2B:	JUMPL	S,SETDM3	;GO IF MONITOR MODE
	ADDM	T1,DEVDMP##(F)	;UPDATE WDCNT
	HLRZ	T2,T1
	ADD	T2,DEVDMP##(F)	;UPDATE ADDRESS
	HRRM	T2,DEVDMP##(F)	; (DON'T PROPOGATE CARRY)
	TLNE	T3,BLKSIZ##-1	;EVEN NUMBER OF WRDS FOR BLOCK?
	TLNN	S,IO		;NO. WRITING?
	JRST	SETDM3		;NO. OK
	SKIPN	DEVMBF##(F)	;HAVE MON BUF?
	JRST	SETDM3		;NO, HARDWARE MUST WRITE 0'S IN REST OF BLOCK
	TLO	T3,MBLKSZ##	;YES. MAKE SURE T3 .GT. -200
	MOVNS	T3		;PARTIAL-BLOCK WORD COUNT
IFN FTKI10!FTKL10,<
	ADD	T3,[XWD MBLKSZ##,0]	;-NO OF WDS LEFT
	HRRZ	T2,DEVMBF##(F)	;MAKE AN IOWD FOR 0'S
	ADD	T2,T3
	TLO	S,IOSMON	;SO MAPIO WON'T UNRELOCATE THE IOWD
	PUSHJ	P,MAPIO##	;SAVE IT
	  JRST	PUNTA
	TLZ	S,IOSMON
	JRST	SETDM3		;AND CONTINUE

IFN FTVM,<
;HERE TO SET UP AN IO LIST FOR THE SWAPPER
SETSWP:	PUSHJ	P,SWPP1		;POINT P1 AT THE SWPLST ENTRY
				;T3=MAXIMUM NUMBER OF BLOCKS TO DO
	PUSHJ	P,THIS##	;GO SET THE IO LIST
				;RETURNS T1= NO OF BLOCKS, P3=L(LIST)
	JRST	SETL5A		;GO FINISH UP THE IO LIST STUFF

;SUBROUTINE TO POINT P1 AT SWPLST ENTRY
;PRESERVES T1
SWPP1:
IFN FTDUAL,<
	SKIPGE	T2,UNI2ND##(U)
	SKIPA	P1,UNISWA##(T2)
>
	MOVE	P1,UNISWA##(U)
	POPJ	P,
>	;END FTVM
>	;END FTKI10
	HLRZ	T2,T3		;SAVE IT
	ADDB	T3,(T4)		;MAKE LAST IOWD EVEN NO OF BLOCKS
	HLRES	T3		;GOOD WDCNT OF LAST IOWD
	MOVNS	T3		;+N
	ADD	T3,(T4)		;ADR-1 OF 1ST WORD IN PARTIAL BLOCK
	HRL	T3,DEVMBF##(F)	;ADR-1 OF MON BUF
	MOVSS	T3		;SET FOR BLT
	AOBJN	T3,.+1
	ADDI	T2,-1(T3)	;LAST DATA WORD
	BLT	T3,(T2)		;COPY PARTIAL BLOCK TO MON BUF
	MOVE	T2,DEVMBF##(F)	;IOWD FOR PARTIAL BLOCK
	SKIPGE	(T4)		;IF LAST IOWD STILL POINTS TO DATA,
	ADDI	T4,1		; POINT TO NEXT IOWD SLOT
	MOVEM	T2,(T4)		;STORE IOWD FOR WRITING TERMINATING 0'S
SETDM3:
IFN FTKL10,<
	HLRZ	T2,T1		;SAVE ORIGINAL WRDCNT (RH20 MAY NEED IT)
>
	ADD	T1,[XWD BLKSIZ##-1,0]	;ACCOUNT FOR PARTIAL BLOCKS
	LSH	T1,MBKLSH##-^D18	;CONVERT T1 TO BLOCKS
	AOJA	T4,SETLS5	;ZERO IOLIST+1, SET CHNNUM AND RETURN
IFN FTKI10!FTKL10,<
;HERE WHEN NO MONITOR FREE-CORE LEFT
PUNTB:
	JUMPN	P1,SETLS5	;AT NEW IOWD, JUST DO FEWER BUFS
PUNTA:	TLZ	S,IOSMON
PUNTC:	JUMPE	P1,PUNTXX	;IF GOT SOME CORE
	SUB	P1,[XWD 1,1]	; POINT TO LAST FULL IOWD
	SETZM	1(P1)		;ZERO LAST (PARTIAL) IOWD
PUNTD:	JUMPE	P1,PUNTXX	;NO FREE-CORE IF 0
IFE FT22BIT,<
	MOVEI	T1,-1		;GOT SOME, COUNT NO OF WORDS
>
IFN FT22BIT,<
	MOVEI	T4,0		;SET INDEX INTO POINTER-TABLE
	SKIPGE	CHB22B##(P3)	;0 IF 18-BIT CHAN,
	MOVEI	T4,1		;1 IF 22-BIT CHAN
	MOVE	T1,MSK22B##(T4) ;GET MASK FOR ADDRESS
>
	MOVE	T2,P2		;INITIAL ADR OF IOWDS
PUNTE:	SKIPN	T3,(T2)		;GET NEXT IOWD
	JRST	PUNTG		;DONE-DO PARTIAL LIST
	TLNN	T3,-1
	JRST	PUNTF		;GO TO WORD IF POSITIVE
	SUB	T1,T3		;COUNT THE WORDS
IFE FT22BIT,<
	TROA	T1,-1		;IGNORE THE ADDRESS
>
IFN FT22BIT,<
	TDOA	T4,MSK22B##(T4)
>
PUNTF:	SOS	T2,T3		;POINT TO NEW IOWD
	AOJA	T2,PUNTE	;GO DO NEXT IOWD
PUNTG:
IFN FT22BIT,<
	SKIPE	T4
	ASH	T1,-4
>
	HLRS	T1		;SET TO UPDATE DEVDMP
	JRST	SETD2B		;FIX IOLIST AND START IO

;HERE IF COULDN'T GET ANY FREE CORE
PUNTXX:	SETZB	T1,P2		;NO IO LIST, CHNNUM=0 (SO
	JRST	SETLS5		; IOIMPM WILL LIGHT AT INTERRUPT)

>;END FTKI10 CONDITIONAL
;SUBROUTINE TO FIND A UNIT WHICH NEEDS HOME BLOCKS REREAD
;NON-SKIP RETURN IF NO SUCH UNITS
;SKIP RETURN IF FOUND, U POINTS AT UNIT
UNIRHB:	SKIPG	HOMFLG##	;ANY UNITS NEED REREADING?
	POPJ	P,		;NO
	MOVEI	T1,KONTAB##(J)	;YES, SET TO LOOK FOR UNIT
	HRLI	T1,-10
	MOVSI	T2,UNPRHB##	;BIT TO TEST
UNIRH1:	SKIPE	T3,(T1)		;GET A UNIT DATA BLOCK
	TDNN	T2,UNIDES##(T3);NEED REREADING?
	AOBJN	T1,UNIRH1	;NO
	JUMPGE	T1,CPOPJ##	;GO IF DIDNT FIND ANY (DIFFERENT KONTROLLER)
	MOVE	U,T3		;SET U TO UDB TO REREAD
	JRST	CPOPJ1##	;AND NON-SKIP

;SUBROUTINE TO SEE IF ANY UNIT NEEDS HOME BLOCKS READ
;STARTS IO TO READ HOME BLOCKS IF ANY UNITS ON THIS KONTROL NEED IT
;OTHERWISE RETURNS (DISMISSING INTERRUPT)
TSTRHB:	PUSHJ	P,UNIRHB	;ANY UNIT NEED REREADING?
	  JRST	POSDIA		;NO
	PUSHJ	P,FAKDDX	;YES, GET A DDB
	  JRST	POSDIA		;NO FREE CORE, TRY LATER
TSTRHX:	HRRM	U,DEVUNI##(F)	;SAVE U
	SETZM	DEVRHB##(F)	;WHOLE WORD=0 INDICATES REREADING HOME BLOCKS
	MOVE	T1,RHBIOW##	;IOWD TO READ 1ST 10 WORDS
	MOVEM	T1,DEVDMP##(F)	; INTO A BUFFER
	MOVEI	T1,1		;BLOCK 1IS 1ST HOME BLOCK
	MOVEM	T1,DEVBLK##(F)
	MOVE	S,[IOSMON,,IOACT] ;SET S
	MOVEM	S,DEVIOS(F)
	AOS	@KONCHN##(J)	;MARK CHANNEL BUSY
	PJRST	STRTIO		;START THE READ AND RETURN

;SUBROUTINE TO GET A DDB
FAKDDX:	PUSH	P,J		;FAKDDB WIPES OUT J
	PUSHJ	P,FAKDDB##	;GET ONE
	  JRST	JPOPJ##		;NO FREE CORE
	SETZ	J,		;CANT BLAME THIS READ ON ANY JOB,
	DPB	J,PJOBN##	; SO SET PJOBN = 0
	JRST	JPOPJ1##	;RESTORE J AND GOODNESS-RETURN
;HERE WHEN ALL POSITIONING INTERRUPTS HAVE BEEN HANDLED
POSDON:	POP	P,P2		;LH=UNIT #, RH = FUNCTION
	POP	P,P4		;CONI WORD
	POP	P,P3		;DATAI WORD
	HRRZ	U,KONCUA##(J)	;SET U TO UNIT ADDRESS
	JUMPE	U,CPOPJ##	;MIGHT BE SPURIOUS INTERRUPT FROM WRONG KONTROLLER
	HRRZ	F,UNICDA##(U)	; SET F TO FILE ADDRESS
	JUMPE	F,TSTRHB	;FREE INT IF 0 OR UNSOLICITED
	MOVE	S,DEVIOS(F)	;AND S TO S
IFN FTVM,<
	SKIPL	DEVSWP##(F)
	JRST	POSDNS
	HRRM	U,DEVUNI##(F)	;IF MULTIPLE SWAPS ON DIFFERENT DF10'S
	MOVE	T1,UNIBLK##(U)	; DEVBLK AND DEVUNI MAY BE CHANGED
	MOVEM	T1,DEVBLK##(F)	; SO RESTORE THEM TO RIGHT VALUES
POSDNS:
>
IFN	FT5UUO,<
;	SKIPE	UNISTS##(U)	;IF NOT A FREE INTERRUPT W/NO ACTIVE DRIVES,
	MOVEM	P4,DEVSTS(F)	;SAVE CONI WORD IN DDB
>
	TRNE	P2,OPPOS##	;POSITIONING INTERRUPT ONLY?
;THE FOLLOWING INSTRUCTION COUNTS ON THE FACT THAT RP10 DISKS
;DONT GET ERRORS ON SEEKS (EXCEPT SEEK INCOMPLETE, AND DPXKON LIES ABOUT THAT)
;AND THAT RP04 DISKS HAVE IMPLIED SEEKS IF OFF-CYLINDER WHEN IO IS STARTED
POSDIA:
IFE FTDHIA,<
	POPJ	P,		;YES
>
IFN FTDHIA,<
	JRST	[SKIPGE	(P1)
		 JRST	CHNIDL
		 POPJ	P,]
>
	SOS	CHNCFT##(P1)	;NO, DECREASE FAIRNESS COUNTS
	SOS	CHNCFP##(P1)
	PUSHJ	P,ADRINT	;SET UP R
	HRRZ	T4,P2		;ERROR BITS FROM DRIVER
	LDB	T1,UNYPUN##	;UNIT NUMBER
	HLRZ	T2,P2		;UNIT NUMBER THE DEVICE CODE RETURNED
	CAME	T1,T2		;DO THEY AGREE?
	TROA	P2,IOBKTL	;NO - LIGHT IOBKTL
	TRZ	P2,IOBKTL	;YES - ZERO IOBKTL
	SKIPE	CHNNUM##(P1)	;0 BLOCKS XFERRED?
	JRST	POSDN0		;NO (ERROR SETS LH -1 EVEN IF RH IS 0)
IFN FTKI10!FTKL10,<
	LDB	T1,PIOMOD##	;IF DUMP MODE CHNNUM=0
	CAIL	T1,SD		; IFF WE RAN OUT OF LOW-CORE BLOCKS
	JUMPGE	S,POSDN3	; AND WE WILL RECOVER AT DUMPG5
>
	TRO	S,IOBKTL	;YES, 1ST BUFFER HEADER ZAPPED BY USER
	JRST	POSER8		;STORE S, GET NEXT IO
POSDN0:	TRNE	P2,IOIMPM+IODTER+IODERR+IOBKTL	;ERROR?
	JRST	POSERR		;YES, RECOVER
	MOVE	T1,KONIOC##(J)	;GET ADDR OF INAD PAIR
IFN FTKL10,<
	MOVE	T2,CHB22B##(P1)
	TLNE	T2,CP.RH2##	;IS IT AN RH20?
	SKIPA	T1,2(T1)	;YES, TERMINATION WORD IS ELSEWHERE
>
	MOVE	T1,1(T1)	;GET TERMINATION CONTROL WORD
	CAMN	T1,CHNTCW##(P1)	;IS IT WHAT WE EXPECT
	JRST	POSDN1		;YES, CONTINUE
	AOS	UNICCT##(U)	;NO, INCREMENT TERMINATION ERROR COUNT
	TRO	P2,IODTER	;SET AS DATA ERROR TO GET INFO TO ERROR.SYS
				;FALL INTO ERROR HANDLING
;HERE ON ANY ERROR RETURN FROM THE DEVICE DEPENDENT ROUTINE
POSERR:	HRRZ	T1,DEVDMP##(F)	;ADR OF (POSSIBLE) DUMP IOWD
	TLNE	S,IOSMON	;MONITOR CALL?
	JUMPE	T1,POSR5A	;YES. NO ERR RECOVERY IF CHAN SKIP IOWD
	SKIPGE	UNIECT##(U)	;FIRST ERROR (UNFIR)?
	JRST	FSTERR		;YES
IFN FTRP04,<
	TRNE	P2,IOECCX	;ECC-CRRECTABLE ERROR?
	JRST	POSERF		;YES, COMPUTE THE NUMBER OF GOOD BLOCKS PASSED
>
	JRST	POSERA		;NO

;HERE ON FIRST ERROR (DON'T KNOW WHETHER IT WILL BE SOFT OR HARD)
FSTERR:	SETZM	UNIERR##(U)	;YES, ZERO LAST ERROR CONI STATUS
	MOVEM	P4,UNISOF##(U)	;SAVE FIRST CONI IN UNIT DB
	MOVEM	P3,UNISDI##(U)	;SAVE FIRST DATAI
IFN FTRP04,<
	TRNN	T4,IOBKTL+IODTER+IODERR  ;IF NOT DEVICE DETECTED,
	PUSHJ	P,@KONRRG##(J)	; READ THE MASSBUS REGISTERS
>
	PUSHJ	P,FSTREG	;COPY REGISTERS TO RH OF UDB
	MOVEI	T2,CHNDPE##	;ASSUME DATA PARITY ERR ON DEVICE
	TRNE	P2,IOCHMP	;IS IT A MEMORY PARITY ERROR?
	MOVEI	T2,CHNMPE##	;YES, SET FOR MEM PAR ERR
	TRNE	P2,IOCHNX	; IS IT A NXM ERROR
	MOVEI	T2,CHNNXM##	;YES, NXM ERROR
	MOVE	T1,KONIOC##(J)	;INITIAL CONTROL WORD ADR
	TRNN	P2,IOCHMP!IOCHNX	;CHANNEL DETECTED ERROR?
				;(MPE, NXM, OR DATA LATE)
	TRNE	P2,IODTER	;NO, DEVICE ERROR (DO NOT COLLECT CHAN DATA)?
	PUSHJ	P,(T2)		;GO STORE CHANNEL DATA FOR F.S. ON SOFT+HARD ERRORS
	SETZM	UNIECT##(U)	;SET RETRY COUNT TO 0
				;(FIRST ERROR FLAG -UNPFIR AND HARD ERROR FLAG -UNPHRD)
IFN FTRP04,<
	TRC	P2,IODTER!IODERR  ;IODERR AND IODTER BOTH ON
	TRCE	P2,IODTER!IODERR  ; MEAN FORMAT ERROR
	JRST	FSTER2		;NOT FORMAT ERROR
	HRRZ	T1,DEVACC##(F)	;FORMAT - IF A FILE IS OPEN,
	JUMPE	T1,FMTERR
	MOVE	T1,DEVREL##(F)	; AND WE ARE NOT AT FIRST BLOCK OF FILE,
	SOJLE	T1,FMTERR
	TRZ	P2,IODERR	;THEN CALL IT A DATA ERROR,
	JRST	FSTER2		; AND ATTEMPT RECOVERY
FMTERR:	TRO	S,IODERR!IODTER	;FORMAT ERR, NO FILE OR AT 1ST BLOCK
	JRST	POSER8		;INDICATE FORMAT ERR AND DON'T RETRY
FSTER2:>
IFN FT5UUO,<			;5 SERIES UUOS?
	MOVEI	T1,DEPDER	;ERROR-RETRY DISABLED?
	TDNN	T1,DEVSTA(F)	;BY OPEN UUO
	JRST	POSERF		;NO
	AOS	UNISCT##(U)	;YES, COUNT A SOFT ERROR
	PUSH	P,CHNNUM##(P1)	;SAVE NO OF BLOCKS XFERRED
	PUSHJ	P,CTGOOD	;COMPUTE BAD BLOCK
	ADD	T1,DEVBLK##(F)
	MOVEM	T1,UNIHBN##(U)	;SAVE IN UDB
	POP	P,CHNNUM##(P1)	;RESTORE NO OF BLOCKS
	JRST	POSR1A		;NO RETRY
>
POSERF:	PUSHJ	P,CTGOOD	;COMPUTE THE NUMBER OF GOOD DATA BLOCKS
	HRLM	P2,CHNNUM##(P1)	;STORE ERROR BITS IN CASE CHN MEM PAR
				; SO CAN CALL SWEEP AFTER LAST TRY
	ADD	T1,DEVBLK##(F)	;FIRST LOGICAL BLOCK OF THIS TRANSFER
	MOVEM	T1,UNIHBN##(U)	;STORE LOGICAL BLOCK OF ERROR FOR F.S.
				; TELL LATER IF HARD OR SOFT
POSERA:	MOVEI	T4,1		;SET BIT FOR ERR COUNTER, ASSUME DATA ERR
	TRNE	P2,IODERR	;DEVICE ERROR?
	MOVSI	T4,1		;YES, COUNT AS DEVICE ERROR
	ADDM	T4,UNIECT##(U)	;ACCUMULATE ERROR COUNT
IFN FTRP04,<
	TLNN	S,IO		;IF READING
	TRNN	P2,IOECCX	;AND AN ECC-CORRECTABLE ERROR
	JRST	NOECC1
IFN FTKI10!FTKL10,<
	SKIPGE	DEVSWP##(F)	;IF SWAPPER
	JRST	NOECC1
	PUSHJ	P,SVEUF##	;MAKE JOB ADDRESSABLE
>
	SOS	T1,UNIHBN##(U)	;GET BLOCK BEFORE THE BAD ONE
	SUB	T1,DEVBLK##(F)	;COMPUTE NUMBER OF GOOD BLOCKS
	PUSH	P,DEVDMP##(F)	;SAVE DEVDMP
	LDB	T2,PIOMOD##	;IF DUMP MODE
	CAIGE	T2,SD
	JUMPGE	S,ECC1		;OR MONITOR IO
	HRRM	T1,CHNNUM##(P1) ;SAVE NUMBER OF GOOD BLOCKS
	AOS	CHNNUM##(P1)	;+1 FOR THE BLOCK WE'LL CORRECT
	LSH	T1,BLKLSH##	;CONVERT TO WORDS
	HRLS	T1
	ADD	T1,KONDMP##(J)	;UPDATE THE IOWD
	JUMPL	S,ECC3		;IF NOT MONITOR IO
	MOVE	T2,T1		; UPDATE IOWD FOR THE DDB
	ADD	T2,[BLKSIZ,,BLKSIZ]
	CAMLE	T1,[MBLKSZ,,0]	;IF LESS THAN 1 BLOCK IN IOWD
	TLOA	T1,400000	;USET A FLAG
	TLZ	T1,-1
	MOVEM	T2,DEVDMP##(F)
	SUBI	T1,(R)		;UNRELOCATE THE IOWD
	JRST	ECC3		;AND CONTINUE
;HERE IF ERROR WAS IN BUFFERED MODE
ECC1:	HRRZ	T2,DEVIAD(F)	;LOC OF 1ST BUFFER
	JUMPE	T1,ECC2
IFN FTKA10,<
	TLO	T2,R
>
	EXCTUX	<HRR T2,@T2>	;ADVANCE THE NUMBER OF GOOD BUFFERS
	SOJG	T1,.-1
ECC2:	MOVEI	T1,1(T2)	;POINT T1 AT LOC-1 OF BAD BLOCK

;HERE WITH T1=UVA-1 OF START OF BAD BLOCK (OR EVA-1 IF MONITOR BUFFER)
ECC3:	PUSH	P,T1		;SAVE LOC
	HRRZ	T1,CHNNUM##(P1)
	JUMPE	T1,NOECC	;CHAN GOOFED IF NO BLOCKS XFERRED
	PUSHJ	P,@KONECC##(J)	;GET RELATIVE POSITION OF BAD WORD
	  JRST	NOECC		;OOPS
	CAILE	T1,BLKSIZ##-1	;IF THE ERROR IS IN THE ECC BYTE,
	JRST	[POP P,T1
		 POP P,(P)
		 JUMPGE T1,POSDN1
		 HRRZS	DEVDMP##(F)
		 JRST POSDN1]	;NO CORRECTION NEEDED
	LDB	T4,PIOMOD##	;IF DUMP MODE,
	CAIGE	T4,SD
	JUMPGE	S,ECC4
	HLRE	T4,DEVDMP##(F)	;WORD COUNT OF IOWD
	SUBI	T4,BLKSIZ##	; PRIOR TO BAD BLOCK
	ADDI	T4,(T1)		;+ POSITION OF ERROR BURST
	SKIPGE	(P)		;IF AT END OF IOWD
	HRRZS	DEVDMP##(F)	; MAKE SURE WE TERMINATE
	JUMPL	T4,ECC4		;CONTINUE IF HE'S READING THAT PART OF BLOCK
	MOVEI	T3,0		;NO CORRECTION FOR 2ND PART, HE'S NOT READING IT
	SKIPE	T4		;READING 1ST WORD OF ERROR BURST?
	MOVEI	T2,0		;NO, DON'T CORRECT 1ST PART EITHER
ECC4:	POP	P,T4		;RESTORE START OF BLOCK
	POP	P,(P)		;REMOVE SAVED DEVDMP FROM LIST
	ADDI	T1,1(T4)	;POINT T1 AT 1ST BAD WORD
	JUMPL	S,ECC6		;IF NOT MONITOR IO,
IFN FTKA10,<
	TLO	T1,R
>
	JUMPE	T2,ECC5		;NO, 1ST PART IF T2=0
	EXCTUX	<MOVS T4,@T1>	;THIS WILL HAVE TO BE MODIFIED IF WE GET
				; OTHER HARDWARE WHICH DOES ECC DIFFERENTLY
	XOR	T4,T2		;APPLY MASK
	EXCTXU	<MOVSM T4,@T1>	;AND SAVE RESULT
ECC5:	ADDI	T1,1
	JUMPE	T3,POSDN1	;NO 2ND PART IF T3=0
	EXCTUX	<MOVS T4,@T1>	;GET 2ND WORD
	XOR	T4,T3		;APPLY MASK
	EXCTXU	<MOVSM T4,@T1>	;AND SAVE RESULT
	JRST	POSDN1		;LOG A RECOVERED ERROR AND CONTINUE

;HERE IF ERROR IN MONITOR IO (INTO MON BUF)
ECC6:	JUMPE	T2,ECC7		;NO 1ST PART IF T2=0
	MOVS	T4,(T1)		;GET 1ST BAD WORD
	XOR	T4,T2		;APPLY MASK
	MOVSM	T4,(T1)		;AND SAVE
ECC7:	JUMPE	T3,POSDN1	;NO 2ND PART IF T3=0
	MOVS	T4,1(T1)	;GET 2ND BAD WORD
	XOR	T4,T3		;CORRECT
	MOVSM	T4,1(T1)	;AND SAVE
	JRST	POSDN1		;AND CONTINUE

;HERE IF WE REALLY COULDNT RECOVER THOUGH WE THOUGHT WE COULD
NOECC:	POP	P,(P)		;REMOVE RELATIVE ADDR FROM LIST
	POP	P,DEVDMP##(F)	;RESTORE ORIGINAL DEVDMP TO THE DDB
	MOVEI	T4,1		;COUNT DATA ERROR
NOECC1:>
	AOS	T1,CHNECT##(P1)	;UPDATE COUNT OF TRIES
	CAIN	T1,1		;FIRST RETRY OR 1ST AFTER RECAL?
	CAME	T1,CHNRCT##(P1)	;YES, THIS 1ST RECAL?
	JRST	POSERB		;NO
	MOVEM	P4,UNIERR##(U)	;YES. SAVE 2ND ("HARD") CONI WORD
	MOVEM	P3,UNIHDI##(U)	;SAVE "HARD" DATAI WORD
	PUSHJ	P,LSTER		;SAVE THE DRIVE REGISTERS NOW
POSERB:	MOVE	T2,UNISTS##(U)	;STATE OF UNIT
	CAIE	T2,TCOD		;IGNORE IF POSITIONING,
	POPJ	P,		;RETRY IF XFER (ERROR RECOVERY)
	MOVE	P4,T4		;SAVE ERROR-COUNT WORD
IFN FTRP04,<
	TRNN	T2,IOHDER	;HEADER ERROR
	TRNN	P2,IODERR	; OR DATA ERROR? (AND NOT FMT)
	SKIPG	T2,KONERR##(J)	;YES, WILL KONTROL ROUTINE TELL US WHAT TO DO?
	JRST	NOOFST		;NO
	HLRZ	T1,UNIECT##(U)	;USE LH(UNIECT) IF HEADER ERROR
	ADD	T1,UNIECT##(U)	; OR RH IF DATA ERROR
	HRRZS	T1		;ONE HALF MUST HAVE COUNTED UP
	PUSHJ	P,(T2)		;ASK THE KONTROLLER ROUTINE
	JRST	@ERRTBL(T1)	;AND GO DO WHAT IT SAID

ERRTBL:	STARTE			;(0) RETRY
	OFFSET			;(1) OFFSET
	POSERE			;(2) LAST TIME
	POSERG			;(3) GIVE UP
	POSERD			;(4) RECAL

NOOFST:>
	HRRZ	T2,UNISTR##(U)	;LOC OF STR DATA BLOCK
	LDB	T3,STYTRY##	;NO OF TIMES TO RETRY
	TRNE	P2,IODERR	;DEVICE (POSITIONING) ERROR?
	LDB	T3,STYSER##	;YES. USE A DIFFERENT PARAMETER
	SKIPN	T2		;UNIT IN AN STR?
	MOVEI	T3,^D10		;NO, USE 10
	SUB	T1,T3		;HAVE WE TRIED ENOUGH?
	JUMPL	T1,STARTE	;RETRY IF NEGATIVE
	TRNE	P2,IOBKTL	;WRONG UNIT?
	JRST	POSERC		;YES, DONT TRY RECAL
	AOS	T4,CHNRCT##(P1)	;UPDATE RECALIBRATE-COUNTER
	LDB	T3,STYRCL##	;NO OF TIMES TO RECAL
	SKIPN	T2		;UNIT IN AN STR?
	MOVEI	T3,^D10		;NO, USE 10
	SUB	T4,T3		;TRIED ENOUGH?
	JUMPGE	T4,POSER0	;TRIED ENOUGH IF T4=0
POSERD:	SETOM	CHNECT##(P1)	;NO. SET A SWITCH FOR RECALR
	PUSHJ	P,@KONRCL##(J)	;DO A RECALIBRATE
	  JRST	POSER0		;NOT A PACK OR UNIT DOWN
	PJRST	STOIOS##	;RECALIBRATING - RESET HUNG TIME, DISMISS INTERRUPT
POSER0:	JUMPG	T1,POSER1	;GO IF NOT LAST TIME
POSERE:	MOVEI	T1,KONRED##(J)	;LAST TIME - SET TO NO STOPPING
	TLNE	S,IO		; ON ERROR SO ALL OF DATA IS ATTEMPTED
				; TO BE TRANSFERRED ON LAST RETRY
	MOVEI	T1,KONWRT##(J)	
IFN FTRP04,<
	MOVSI	T2,DEPCPT##	;IF IN 10/11 COMPAT MODE,
	TDNE	T2,DEVCPT##(F)
	ADDI	T1,2		;CALL THE COMPATABILITY ENTRY
>
	PUSHJ	P,@(T1)		;CALL DEVICE DEPENDENT ROUTINE
	  PJRST	BADUNI		;UNIT NOT UP
	POPJ	P,		;DISMISS THE INTERRUPT
IFN FTRP04,<
;HERE WHEN ERROR IS DECLARED HARD FOR AN ERROR-PROGRAM KONTROLLER
POSERG:	PUSHJ	P,LSTER		;READ ALL DRIVE REGISTERS
	JRST	POSER1		;AND GO DECLARE HARD ERROR
>
IFN FTRP04,<
OFFSET:	HRROS	CHNECT##(P1)	;CHNECT=-1,,N TO INDICATE OFFSET IN PROGRESS
	PJRST	STOIOS##
>

;SUBROUTINE TO COUNT THE NUMBER OF GOOD DATA BLOCKS
;UPDATES CHNNUM IF NOT DUMP-MODE
;RETURNS NO OF GOOD BLOCKS IN T1
CTGOOD:	HRRZ	T1,KONIOC##(J)	;INITIAL CONTROL WORD ADDRESS
	PUSHJ	P,WRDCNT##	;COMPUTE NO. OF GOOD WORDS TRANSFERRED
IFN FTRP04,<
	TRNE	P2,IOECCX	;ECC CORRECTABLE ERROR?
	ADDI	T1,BLKSIZ##-2	;YES, MAKE SURE "BAD" BLOCK IS COUNTED
>
	LSH	T1,MBKLSH##	;CONVERT TO # OF GOOD BLOCKS
	SKIPGE	DEVSWP##(F)	;IF SWAPPER
	POPJ	P,		; DON'T CHANGE CHNNUM
	LDB	T2,PIOMOD##	;IS IT DUMP MODE?
	CAIGE	T2,SD		;(DONT CHANGE CHNNUM SINCE DEVDMD
				; HAS BEEN UPDATED COUNTING ON IT
	HRRZM	T1,CHNNUM##(P1)	;STORE NO. OF GOOD BLOCKS TRANSFERRED
				; ON FIRST TRY TO BE USED BY BUFAD
				; IF THIS PROVES TO BE HARD ERROR
				; FLAG ERROR SO NON-ZERO EVEN IF IN FIRST BLOCK
				; SO DIFFERENT FROM USER ZAP FIRST BUFFER
	POPJ	P,
;SUBROUTINE TO COPY THE DRIVE REGISTERS INTO THE RH OF THE UDB
;CALLED ON FIRST ERROR, WIPES OUT LH OF UDB REG'S
FSTREG:	SKIPN	T2,KONREG##(J)	;GET NUMBER OF DRIVE REGISTERS TO STORE
	POPJ	P,		;NONE - NOT A MASSBUS DEVICE
	ADDI	T2,UNIEBK##(U)	;POINT TO TOP OF BLOCK
	MOVSI	T1,KONEBK##(J)	;WHERE THEY WERE SAVED
	HRRI	T1,UNIEBK##(U)	;WHERE THEY ARE TO GO
	BLT	T1,-1(T2)	;SAVE THEM
	MOVE	T1,UNILAS##(U)	;LAST DATAO
	MOVEM	T1,(T2)		;SAVE IN THE UDB
	MOVE	T1,KONECR##(J)	;GET KONTROLLER
	MOVEM	T1,UNISCR##(U)	; CONTROL REG & DATA REG
	MOVE	T1,KONEDB##(J)	;AND SAVE IN UDB
	MOVEM	T1,UNISDR##(U)
	POPJ	P,		;AND RETURN


;SUBROUTINE TO SAVE THE DRIVE REGISTERS IN THE UDB
; RESPECTS T1,T4
LSTER:	PUSH	P,T1
	MOVN	T1,KONREG##(J)	;NUMBER OF REGISTERS TO SAVE
	JUMPE	T1,TPOPJ##
	HRLS	T1		;MAKE AN AOBJN WORD
	HRRI	T1,KONEBK##(J)
	MOVEI	T2,UNIEBK##(U)	;WHERE TO STORE
LSTER1:	MOVE	T3,(T1)
	HRLM	T3,(T2)		;SAVE A DRIVE REGISTER IN LH OF UDB WORD
	ADDI	T2,1
	AOBJN	T1,LSTER1	;GET ANOTHER WORD
	MOVE	T1,UNILAS##(U)	;LAST DATAO TO THE DRIVE
	HRLM	T1,(T2)		;SAVE IN UDB
	MOVE	T1,KONECR##(J)	;SAVE KONTROLLER (RH10)
	MOVEM	T1,UNIHCR##(U)	; CONTROL REG & DATA REG
	MOVE	T1,KONEDB##(J)	;IN UDB
	MOVEM	T1,UNIHDR##(U)
	JRST	TPOPJ##		;AND RETURN
;HERE ON HARD WRONG-UNIT
POSERC:	TLNE	S,IO		;IF READING, CONTINUE
	STOPCD	.,JOB,HWU,	;++HARD WRONG UNIT
;HERE ON HARD DEVICE OR DATA ERRORS
POSER1:	SOJE	T1,STARTE	;LAST RETRY, STOP ON ERROR, IF 1
	ADDM	P4,UNIHCT##(U)	;UPDATE HARD-ERROR WORD
	TLNE	P4,-1		;BEEN COUNTING IN LH (POSITION ERRS)?
	HLRZS	UNIECT##(U)	;YES. SAVE COUNT IN RH (UNIECT)
	MOVSI	T1,UNPHRD##	;HARD ERROR FLAG ON LAST ERROR ON THIS UNIT
	IORM	T1,UNIECT##(U)	;SET FOR DAEMON AND SYSERR
	PUSHJ	P,CHKCMP	;CHECK IF CHN MEM PAR ERR
				; IF YES, FLAG CPU MEM SWEEP

;HERE IF USER AVOIDING ALL RETRIES
POSR1A:
IFN FTKL10,<
	TLNN	S,IO		;IF INPUT,
	PUSHJ	P,CFDMP		;FLUSH CACHE SO BAD DATA WILL BE SEEN
>		;END IFN FTKL10
	JUMPL	S,POSR5A	;ONLY 1 IOWD IF MONITOR IO
	HRRZ	P4,CHNNUM##(P1)	;GET # OF GOOD BLOCKS FOR BUFAD
	PUSHJ	P,BUFAD		;ADVANCE THE GOOD BUFFERS
	  JFCL
POSR5A:	PUSH	P,P2		;SAVE ALL ERROR BITS
	ANDI	P2,IOIMPM+IODTER+IODERR+IOBKTL+IOCHMP+IOCHNX	;P2=ERROR BITS
	TRNN	P2,IOCHMP+IOCHNX ;CHAN-DETECTED ERROR?
	JRST	POSR5C		;NO
	SKIPL	DEVSWP##(F)	;SWAPPER?
	TRZ	P2,IOCHMP+IOCHNX ;NO, DON'T KEEP THESE ERR BITS
POSR5C:	OR	S,P2		;STORE ERROR BITS IN S
	POP	P,P2
	JUMPL	S,POSER6	;GO IF MONITOR IO
	LDB	T1,PIOMOD	;MODE
	CAIL	T1,SD		;WE DIDN'T STOP EARLY IF DUMP-MODE
	JRST	POSER6		; SO DON'T FIDDLE WITH DEVBLK, ETC.
	AOS	DEVBLK##(F)	;UPDATE DB LOCS WHICH POINT TO BLOCK
	AOS	DEVREL##(F)	; UPDATE DEVREL
	SOS	DEVLFT##(F)
	PUSHJ	P,(P3)		;ADVANCE 1 MORE BUFFER (THE BAD ONE)
	  JFCL
POSER6:	TRNN	S,IODTER	;PARITY ERROR?
	JRST	POSER7		;NO
	TLNE	S,IO		;YES. LIGHT ERR BIT IN LH(S)
	TLOA	S,IOSHWE##	; BECAUSE USER CAN CLEAR RH OF S (SETSTS)
	TLO	S,IOSHRE##
	MOVE	T1,KONIOC##(J)	;INITIAL CONTROL WORD ADR
	PUSHJ	P,CHNDPE##	;LOG THE ERROR
POSER7:	TRNE	S,IODERR	;DEVICE (POSITIONING) ERROR?
	TLO	S,IOSSCE##	;YES. LIGHT A BIT IN LH(S) SOFTWARE CHECKSUM
				; OR DEVICE ERROR
IFN FTDBBK,<
	SKIPE	DEVELB##(F)	;IF NOT ALREADY A BAD BLOCK,
	JRST	POSER8
	MOVE	T1,UNIHBN##(U)	;BAD BLOCK NO. STORED ON FIRST ERROR
	TRNE	P2,IODTER	;GET ERROR CODE
	TLO	T1,BAPDTR##	; (DATA ERR,HEADER ERR, OR OTHER)
	TRNE	P2,IOHDER	;HEADER ERR?
	TLO	T1,BAPHDR##	;YES
	TRNN	P2,IODTER+IOHDER ;NOT HEADER OR DATA?
	TLO	T1,BAPOTR##	;"OTHER"
	TRNN	P2,IOCHNX+IOCHMP  ;CHANNEL ERRORS?
	MOVEM	T1,DEVELB##(F)	;STORE BLOCK + CODE IN DDB
	LDB	T1,UNYLUN##	;AND SAVE THE LOGICAL UNIT NUMBER
	DPB	T1,DEYEUN##	;FOR ERRFIN
>
POSER8:	PUSHJ	P,STDIOD##	;YES. WAKE IT UP
IFN FTDAEM,<			;DAEMON CODE?
	MOVEI	T1,.ERDPE	;CODE FOR DISK ERROR
	HRL	T1,F		;PUT DDB ADDR IN LH FOR DAEMON
	SKIPN	DINITF		;DON'T TRY TO STOP THE JOB OR WAKE
				; DAEMON IF IN ONCE-ONLY
	PUSHJ	P,DAEERR##	;STOP JOB, WAKE UP DAEMON
>
POSER9:
IFN FTKI10!FTKL10,<
	SKIPE	T1,@KONIOC##(J)	;ARE THERE ANY IOWDS?
	PUSHJ	P,RTNIOW##	;YES, GIVE THEM BACK
>
	PJRST	SETIDL		;SET THIS FILE IDLE AND LOOK FOR ANOTHER
;HERE WHEN THERE WAS NO HARDWARE ERROR ON THE DATA TRANSFER
POSDN1:	SKIPG	T1,UNIECT##(U)	;NO. IS THIS A RECOVERED ERROR (UNPFIR)?
	JRST	POSDN2		;NO - NO ERROR AT ALL (USUAL)
	MOVSI	T2,1		;YES, FOR POSSIBLE UPDATE OF LH
	TLNN	T1,-1		;DEVICE ERROR?
	AOSA	UNISCT##(U)	;NO, DATA ERROR UPDATE RH(UNISCT)
	ADDM	T2,UNISCT##(U)	;YES, UPDATE LH(UNISCT)
	TLNE	T1,-1		;WERE WE COUNTING IN LH (POSITION ERRS)?
	HLRZS	T1,UNIECT(U)	;YES, SET UNIECT= NUMBER IN RH
IFN FTDAEM,<			;DAEMON CODE?
	MOVE	T2,CHNNUM##(P1)	;IF AN OVERRUN
	TLNE	T2,IOVRUN	; RECOVERED ON 1ST RETRY,
	SOJE	T1,[SKIPN ALLOVR##  ;DON'T CALL DAEMON
		   JRST POSD1A	; IF ALLOVR = 0
		   JRST .+1]
	MOVEI	T1,.ERDPE	;CODE FOR DISK ERROR
	HRL	T1,F		;PUT DDB ADDR IN LH FOR DAEMON
	SKIPN	DINITF		;DON'T TRY TO STOP THE JOB OR WAKE
				; DAEMON IF IN ONCE-ONLY
	PUSHJ	P,DAEERR##	;STOP JOB, WAKE UP DAEMON
>
	PUSHJ	P,CHKCMP	;CHECK IF THIS WAS A CHN MEM PAR
				; IF YES, FLAG FOR CPU SWEEP
POSD1A:	PUSHJ	P,CTGOOD	;RESET CHNNUM TO NO OF GOOD BLOCKS
	SKIPE	UNIERR##(U)	;IS THIS BEFORE FIRST RECAL?
	JRST	POSDN2		;NO, "HARD" CONI STUFF ALREADY STORED
	MOVEM	P4,UNIERR##(U)	;YES, SAVE "HARD" CONI
	MOVEM	P3,UNIHDI##(U)	;SAVE "HARD" DATAI
	PUSHJ	P,LSTER		;SAVE THE DRIVE REGISTERS AT END (THEY PROBABLY ARE
				; MEANINGLESS AT  THIS POINT SINCE THEY ARE ONLY
				; STORED AT ERROR TIME, BUT ITS BETTER THAN NOTHING
POSDN2:	TLNN	S,IO+IOSMON
	TLZN	S,IOSFIR	;TIME FOR CHECKSUMS?
IFN FTKL10,<
	JRST	.+2		;NOT TIME FOR CHECKSUMS, SO WE DON'T HAVE
				; TO SWEEP YET.
	JRST	POSD2A		;CHECKSUM TIME, SWEEP IMMEDIATELY
	MOVSI	T1,CP.SWF##	;GET READY TO SET THE BIT
	TLNN	S,IO		;READING?
	IORM	T1,CHB22B##(P1)	;YES, INDICATE THAT A SWEEP MUST BE
				; DONE BEFORE INTERRUPT EXIT TIME.
	JRST	POSDN3		;CONTINUE.
POSD2A:	PUSHJ	P,CFDMP		;CHECKSUM TIME, SO SWEEP THAT WE MAY SEE
				; THE DATA NOW.
>;END IFN FTKL10
IFN FTKA10!FTKI10,<
	JRST	POSDN3		;NO. CONTINUE
>;END IFN FTKA10!FTKI10
	PUSHJ	P,CHKSUM	;YES. COMPUTE CHECKSUM
	SKIPN	T2,@DEVRET##(F)	;PICK UP RETRIEVAL PNTR
	MOVE	T2,DEVRB1##(F)	;1ST PNTR, MORE IN RIB
	HRRZ	T3,UNISTR##(U)	;LOC OF STR DB
	LDB	T2,STYCKP##(T3)	;GET CHECKSUM
	CAMN	T2,T1		;DOES IT MATCH COMPUTED CHECKSUM?
	JRST	POSDN3		;YES. OK
	MOVE	T2,DEVACC##(F)	;LOC OF A.T.
	MOVE	T4,ACCNCK##(T2)	;ALWAYS-BAD-CHECKSUM WORD
	TRNE	T4,ACPNCK##	;FILE A DIRECTORY OR HAVE ABC?
	JRST	POSDN3		;YES. IGNORE ERROR
	MOVE	T4,DEVREL##(F)	;NO. RELATIVE BLOCK NUMBER
	MOVE	T2,ACCPT1##(T2)	;CURRENT 1ST POINTER
	LDB	T2,STYCKP##(T3)	;CHECKSUM BYTE
	CAMN	T2,T1		;MATCH?
	SOJE	T4,POSDN3	;YES, IF 1ST BLOCK FILE IS A UFD WHOSE 
				;CHECKSUM HAS CHANGED BETWEEN LOOKUP AND INPUT
	AOS	UNIMCT##(U)	;REAL CHKSUM ERR. COUNT SOFTWARE ERROR
	TDO	S,[XWD IOSSCE##,IOIMPM];LIGHT ERROR BIT (LH SINCE USER CAN CLEAR IOIMPM)
	PUSHJ	P,CTGOOD	;T1= NO. OF GOOD BLOCKS TRANSFERRED
	ADD	T1,DEVBLK##(F)	;FIRST LOGICAL BLOCK OF TRANSFER
	MOVEM	T1,UNIHBN##(U)	;STORE BAD BLOCK NO. FOR ERROR REPORTING
IFN FTDAEM,<			;DAEMON CODE?
	MOVEI	T1,.ERDPE	;SAY DISK ERROR FOR DAEMON
	HRL	T1,F		;PUT DDB ADDR IN LH FOR DAEMON
	PUSHJ	P,DAEERR##	;STOP JOB, WAKE UP DAEMON
>
POSDN3:	SKIPLE	DEVSWP##(F)	;FILE IN IOWAIT?
	PUSHJ	P,STDIOD##	;YES. WAKE JOB UP
	MOVEM	S,DEVIOS(F)	;SAVE S IN DDB
IFN FTKI10!FTKL10,<
	SKIPE	T1,@KONIOC##(J)	;RETURN THE FREE-CORE BLOCKS
	PUSHJ	P,RTNIOW##
>
	HRRZ	P4,CHNNUM##(P1)	;NO OF BLOCKS TRANSFERRED
IFN FTDSTT,<
	LDB	T1,PJOBN##	;JOB NUMBER
;	HRRZ	T1,JBTPDB##(T1)	;ADDR OF PDB FOR JOB
	TLNN	S,IO		;READING?
	ADDM	P4,JBTRCT##(T1)	;YES. UPDATE JOB READ COUNT
	TLNE	S,IO		;WRITING?
	ADDM	P4,JBTWCT##(T1)	;YES, INCREMENT NO BLOCKS WRITTEN BY THIS JOB
>
	JUMPL	S,SETMDL	;MONITOR IO? YES IF S NEG
	PUSHJ	P,BUFAD		;NO. UPDATE DDB, ADVANCE BUFFERS
	  JRST	SETIDL		;NEXT BUFFER NOT USABLE, OR DUMP MODE
				;AT LEAST 1 BUFFER IS AVAILABLE 
	PUSHJ	P,CHKNXT	;ANY MORE BLOCKS ON DISK NOW?
	  JRST	SETIDL		;NO. SET FILE, UNIT TO IDLE
	SKIPL	KONPOS##(J)	;YES. DOES KONTROLLER POSITION?
	JRST	SETPW		;YES. SET FILE TO PW STATE
	PUSHJ	P,SETTW0	;NO, SET FILE TO TW STATE
	JRST	PIKTRN		;AND LOOK FOR NEXT TRANSFER OPERATION

;ROUTINE TO INITIATE CPU MEM PAR SWEEP AFTER ALL RETRIES DONE
; IF ERROR WAS A MEM PARITY DETECTED BY THE CHANNEL
; MUST WAIT TILL END BECAUSE SWEEP CLEARS PARITY ERRORS
;CALL:	MOVE	P1,CHANNEL DATA BLOCK ADDRESS
;	PUSHJ	P,CHKCMP
;	RETURN

CHKCMP:	MOVE	T1,CHNNUM##(P1) ;LH=IO STATUS ERR BITS ON RETRIES
	HLLZ	T2,.CHCSR(P1)	;FLAG FOR THIS CHANNEL TO REQUEST CPU0 SWEEP
	TRO	T2,UE.PEF	;CAUSE PARITY (NOT NXM) SWEEP
	TLNE	T1,IOCHMP	;DID CHAN DETECT MEM PAR?
	IORM	T2,.C0AEF##	;YES, FLAG CPU0 TO DO A CORE SWEEP
	POPJ	P,		;RETURN

;HERE IF FILE IS ON A POSITIONING DEVICE
;SET FILE TO PW STATE, ADD TO PWQ
SETPW:	MOVEI	T1,PWCOD##	;SET FILE, UNIT TO PW
	PUSHJ	P,FILCOD
IFN FTDUAL,<
	PUSHJ	P,SECCOD	;SET STATE OF PRIME UNIT IF THIS ALTERNATE
>
	MOVEI	T1,UNIQUE##(U)	;SET T1=UNI PWQ
	PUSHJ	P,PUTQUE	;PUT FILE ON Q
	JRST	SETID1		;AND LOOK FOR ANY POSITIONING TO DO

;HERE WHEN MONITOR IO DONE - SET FILE TO IDLE
SETMDL:	MOVEI	T1,UNIMRC##(U)	;SET TO UPDATE UNIMRC (UNIMWC)
	PUSHJ	P,UPSTAT	;UPDATE STATISTICS FOR UNIT

;HERE TO SET FILE TO IDLE 
SETIDL:
IFN FTVM,<
	SKIPL	DEVSWP##(F)	;IF NOT THE SWAPPER
>
	TLZ	S,IOSMON	;MAKE SURE IOSMON IS OFF
	SKIPE	DEVRHB##(F)
	JRST	SETIDA
	SOSGE	HOMFLG##
	STOPCD	RERED1,DEBUG,RHN, ;++REREAD-HOMEBLOCK-COUNT NEGATIVE
	MOVSI	T2,UNPRHB##	;CLEAR REREAD HOME BLOCKS
	ANDCAM	T2,UNIDES##(U)	; FROM UDB
	TRNE	S,IOIMPM	;READ GO OK?
	JRST	RERED1		;WENT OFF-LINE AGAIN
	MOVS	T1,HOMBUF##	;IS 1ST WORD HOME,
	CAIN	T1,'HOM'
	TRNE	S,IODERR+IODTER; AND NO DATA ERRORS?
	JRST	RERED2		;CANT READ HOME BLOCK, CALL MDA
	MOVE	T1,UNIHID##(U)	;READ OK, IS IT WHAT WE EXPECT?
	CAME	T1,HOMBID##
	JRST	RERED2		;HOME BLOCKS DONT MATCH
RERED1:	PUSHJ	P,CLRDDB##	;ALL IS WELL, GIVE UP DDB
	SETZM	UNISTS##(U)	;SET UNIT IDLE
	JRST	SETIDD		;AND CONTINUE

;HERE IF CANT READ HOME BLOCKS OR NO MATCH
RERED2:
IFN FTMDA,<
	SKIPGE	UNIPTR##(U)	;IF UNIT HAS SWAPPING SPACE
	JRST	RERED3		; COMPLAIN TO THE OPERATOR
	PUSHJ	P,CALMDA	;OTHERWISE LET MDA HANDLE IT
	  JRST	RERED3		;NO MDA, COMPLAIN
	PUSHJ	P,CLRDDB##	;MDA IS RUNNING, RETURN DDB
	PUSHJ	P,SET4MD	;SET SO ONLY MDA CAN READ DRIVE
	JRST	SETIDC		;AND GO FIND SOMETHING ELSE TO DO
RERED3:>
;HERE IF UNIT HAD SWAPPING SPACE OR IF MDE ISNT RUNNING. COMPLAIN ABOUT UNIT
	PUSH	P,F		;SAVE F AND U
	PUSH	P,U
	HRRZ	U,OPRLDB##	;TALK TO OPR
	TRNN	S,IODTER+IODERR ;ERROR
	JRST	RERED4		;WRONG PACK
	PUSHJ	P,INLMES##	;WHO KNOWS
	BYTE	(7)7,7,7,7,7	
	ASCIZ	/
ERROR TRYING TO REREAD HOME BLOCKS ON /
	MOVSI	T2,'???'	;WHAT WE READ
	MOVEM	T2,HOMBID##
	JRST	RERED5
RERED4:	PUSHJ	P,INLMES##
	BYTE	(7)7,7,7,7,7	;SOME BELLS
	ASCIZ	/
WRONG PACK POWERED UP ON /
RERED5:	MOVE	T2,(P)		;UDB
	PUSH	P,UNIHID##(T2)	;SAVE WHAT WE THINK IT IS
	MOVE	T2,UNINAM##(T2);PHYSICAL NAME
	PUSHJ	P,PRNAME##
	PUSHJ	P,INLMES##
	ASCIZ	/ IS /
	MOVE	T2,HOMBID##	;WHAT WE READ
	PUSHJ	P,PRNAME##
	PUSHJ	P,INLMES##
	ASCIZ	/, SHOULD BE /
	POP	P,T2		;WHAT WE EXPECT
	PUSHJ	P,PRNAME##
	PUSHJ	P,INLMES##
	ASCIZ	/
PLEASE DISMOUNT IT AND MOUNT THE CORRECT PACK
/
	POP	P,U
	POP	P,F		;RESTORE ACS
	MOVEI	T1,OCOD##	;SET FOR ONCE-A-MINUTE GRUMP AT OPR
	MOVEM	T1,UNISTS##(U)	; AND NO IO TO START WHEN IT COMES UP
	PUSHJ	P,CLRDDB##	;RETURN THE FAKE DDB
	JRST	SETIDC		;AND FIND SOMETHING ELSE TO DO

IFN FTMDA,<
;SUBROUTINE TO SEND A MESSAGE TO THE MOUNTABLE DEVICE ALLOCATOR
CALMDA:	MOVE	T1,UNINAM##(U)	;SIXBIT /DEV NAME/
	MOVEI	T2,.TYDSK	;ITS A DSK
	PUSHJ	P,SNDMDC##	;TELL MDC
	  POPJ	P,		;MDC ISNT THERE
	JRST	CPOPJ1##	;MDC IS THERE
>
;HERE FROM BADUNI TO SET THE FILE IDLE. DON'T CLEAR IOSMON
SETIDA:	MOVEI	T1,ICOD##	;YES. SET FILE, UNIT TO I
	PUSHJ	P,FILCOD
SETIDD:
IFN FTDUAL,<
	SKIPN	UNIQUE##(U)	;THIS UNIT GOING IDLE?
	SKIPL	T1,UNI2ND##(U)	;YES, IS THERE A PRIME UNIT?
	JRST	SETIDB		;NO
	MOVE	T2,UNIQUE##(T1) ;YES, PUT POS. QUEUE OF PRIME UNIT
	HLLM	T2,UNIQUE##(U)	; ONTO THIS UNIT INSTEAD, TO START SEEK
	SETZM	UNIQUE##(T1)	; ON THIS UNIT NOW. PRIME UNIT HAS NO QUEUE NOW
SETIDB:>
	MOVEI	T1,PWCOD##
	SKIPE	UNIQUE##(U)	;ARE POSITIONS WAITING FOR UNIT?
	MOVEM	T1,UNISTS##(U)	;YES. PUT UNIT IN PW
SETIDC:
IFN FTDUAL,<
	SKIPN	UNIQUE##(U)
	PUSHJ	P,SECCOD
	SKIPGE	T1,UNI2ND##(U)	;IF ALT PATH TO DRIVE
	HRLZS	UNICDA##(T1)	;CLEAR UNICDA FOR MAIN PORT
>
	HRLZS	UNICDA##(U)	;NO CURRENT DDB
IFN FTVM,<
	SKIPGE	DEVSWP##(F)	;SWAPPER?
	JRST	SETID0		;YES, TELL VMSER ABOUT THE REQUEST
>
	PUSHJ	P,CLRACT##	;CLEAR IOACT
IFN FTSWAP,<
IFE FTVM,<
	SKIPL	DEVSWP##(F)	;IS IT THE SWAPPER?
>
	JRST	SETID1		;NO, CONTINUE
IFE FTVM,<
	PUSH	P,J		;SAVE J
	PUSHJ	P,SWPINT##	;CALL SWPINT
	POP	P,J		;RESET J
>	;END IFE FTVM
IFN FTVM,<
SETID0:	HRRZ	T1,CHNNUM##(P1)
	PUSHJ	P,SWPP1		;POINT PI AT SWPLST ENTRY
	PUSH	P,U
	PUSHJ	P,DONE##
	MOVEI	F,SWPDDB##
	POP	P,U
	MOVE	J,UNIKON##(U)	;DONE ZAPS EVERYBODY
	HRRZ	P1,KONCHN##(J)
	MOVSI	T1,UNPUNO##	;SINCE WE WILL TRY SWAP IF OFF-LINE
	ANDCAM	T1,UNIDES##(U)	; CLEAR UNPOFL,UNPUNS
>	;END FTVM
>	;END FTSWAP


SETID1:
IFN FTKI10!FTKL10,<
	JUMPE	P1,CHNIDX	;DONT START IO (CHANGE UBR) AT UUO LEVEL
>
IFN FTVM,<
	MOVEI	F,SWPDDB##	;MAKE SURE F POINTS TO THE SWAPPER
	SKIPE	SQREQ##		;SWAPPER GOING?
	PUSHJ	P,SWPSCN##	;YES, START SWAP SEEKS FIRST
>
	SKIPG	KONPOS##(J)	;DOES KONTROL POSITION?
	JRST	PIKTRX		;NO, LOOK FOR BEST TRANSFER
				;YES. START ANY WAITING POSITIONS

;HERE TO PICK THE BEST FILE ON EACH UNIT TO START POSITIONING
PIKPOS:
IFN FTDHIA,<
	SKIPLE	DIADSK##	;IF WAITING FOR DIAG
	CAME	P1,DIACHN##	; FOR THIS CHANNEL
	CAIA
	JRST	PIKTRX		;DON'T START ANOTHER SEEK
>
	PUSH	P,U		;SAVE U (LAST UNIT TO LOOK AT)
KONLUP:	HLR	U,UNIKON##(U)	;STEP TO NEXT UNIT IN RING
	SKIPE	T1,UNISTS##(U)	;GET STATE OF UNIT
	CAIE	T1,PWCOD##	;PW?
	CAIN	T1,SWCOD##	;OR SW?
	PUSHJ	P,UNIPOS	;YES, START UNIT POSITIONING
PIKPND:	CAME	U,(P)		;WAS THIS LAST UNIT ON KONTROL?
	JRST	KONLUP		;NO, STEP TO NEXT UNIT
	POP	P,T1		;YES, REMOVE U FROM PD LIST
	SKIPN	SQREQ##		;DON'T REREAD IF SWAPPER WANTS SERVICE
	PUSHJ	P,UNIRHB	;SEE IF ANY UNIT NEEDS REREADING HOME BLOCKS
	  JRST	PIKTRX		;NO, GO LOOK FOR FILE TO START DATA ON
	PUSHJ	P,FAKDDX	;YES, GET A DDB
	  JRST	PIKTRX		;NO SPACE NOW, PUSH ON AND TRY LATER
	PJRST	TSTRHX		;GOT 1, GO START TO READ HOME BLOCK
IFN FTDHIA,<
;ROUTINE TO START POSITIONING DURING CRANK-UP
CRNPOS:	MOVE	J,UNIKON##(U)	;MAKE SURE J IS RIGHT
	SETZM	UNISTS##(U)	;SO RIGHT UNISTS WILL BE STORED (UUOTWQ)
>

;SUBROUTINE TO PICK A FILE ON A UNIT AND START POSITIONING FOR THAT FILE
;ENTER WITH U=LOC OF UNIT DATA BLOCK
;EXIT CPOPJ, A FILE IS NOW POSITIONING ON THE UNIT
UNIPOS:	PUSHJ	P,SAVE4##	
IFE FTVM,<
	SKIPN	UNIQUE##(U)	;GET UNIQUE = PWQ FOR UNIT
	STOPCD	CPOPJ##,DEBUG,PQE,	;++POSITIONING QUEUE EMPTY
>
IFN FTVM,<
	SKIPE	UNIQUE##(U)
	JRST	UNIPS0
IFN FTDHIA&FTDUAL,<
	SKIPN	DIADSK##	;OK IF SHUTTING DOWN CHAN
>
	SKIPL	SCNCNT##	;IF NO DDB IN PWQ,
	POPJ	P,		; ITS OK IF SWPSCN PUT UNIT IN PW
	STOPCD	CPOPJ##,DEBUG,PQE,
UNIPS0:>
IFE FTDOPT,<			;NO DISK OPTIMIZATION
UNIPS1:	HLRZ	F,UNIQUE##(U)	;F=FIRST FILE IN UNIT POSITION QUEUE
	MOVEI	P2,DIFUDQ##(U)	;P2=PREDECESSOR
	SKIPE	T1,DEVUNI##(F)	;SKIP IF F/S HAS BEEN JERKED OUT
	PUSHJ	P,CYLCOM	;COMPUTE DISTANCE TO TARGET CYLINDER
	JUMPE	T1,PWQOK	;JUMP IF ON-CYLINDER, SET UNIT TO TW
>	;END CONDITIONAL ON FTDOPT
IFN FTDOPT,<
	PUSH	P,S		;SAVE S
UNIPS1:	HRLOI	P3,377777	;SET P3 (BEST DISTANCE) TO PLUS INFINITY
	HLRZ	F,UNIQUE##(U)	;F WILL GO THROUGH FILES ON U
	MOVEI	S,DIFUDQ##(U)	;INITIALIZE S (PREDECESSOR FILE)
	SKIPN	T1,DEVUNI##(F)	;JUST IN CASE F/S HAS BEEN JERKED OUT
	JRST	PWQLP1		;JERKED OUT - SET DISTANCE=0
				; (SO WILL IMMEDIATELY GO TO TWQ)
IFN FTDPRI,<
	PUSHJ	P,DFPRI		;PRIORITY OF FIRST FILE IN QUEUE
	MOVE	R,T2		;SAVE IN R
>
PWQLUP:	PUSHJ	P,CYLCOM	;COMPUTE DISTANCE TO TARGET CYLINDER
	MOVMS	T1		;ABSOLUTE VALUE
	SKIPL	DEVSWP##(F)	;IS THIS SWAPPER REQUEST?
	CAMGE	T1,P3		;NO, IS THIS BEST SO FAR?
PWQLP1:	PUSHJ	P,SVBST		;YES, SAVE POINTERS TO IT
				;P4=BEST F,P3=DIST TO BEST P2=PRED. F
	JUMPE	T1,PWQOK	;PERFECT IF 0 (SET UNIT TO TW)
	SKIPGE	DEVSWP##(F)	;SWAPPER REQUEST?
	JRST	SETPOS		;YES. USE THIS FILE
	MOVEM	F,S		;SAVE F AS PREDECESSOR POINTER
	HLRZ	F,DEVQUE##(F)	;AND STEP TO NEXT FILE ON UNIT
IFN FTDPRI,<
	JUMPE	F,SETPOS	;START POSITION IF LOOKED AT ALL IN QUEUE
	PUSHJ	P,DFPRI		;PRIORITY OF THIS FILE
	CAME	T2,R		;SAME AS FIRST IN QUEUE?
	JRST	SETPOS		;NO, START POSITION OF BEST IN PRIORITY GROUP
	JUMPG	R,PWQLUP	;YES, CHECK IT IF PRIORITY IS POSITIVE
				; (NEVER BY FAIR IF HI-PRI FILES IN QUEUE)
>
	SKIPLE	CHNCFP##(P1)	;IF TIME TO BE FAIR, FIRST IS BEST
	JUMPN	F,PWQLUP	;LOOP IF NOT END OF FILES, NOT FAIR TIME
SETPOS:	POP	P,S		;RESTORE S
;STILL IN FTDOPT CONDITIONAL
;HERE P4 HAS POINTER TO FILE, P2=PREDECESSOR
;REMOVE THIS FILE FROM PWQ, START IT POSITIONING
	MOVE	F,P4		;SET F TO FILE
>	;END CONDITIONAL ON FTDOPT
	PUSHJ	P,UNQUE0	;REMOVE FILE FROM Q
	MOVE	T1,UNISTS##(U)	;STATUS OF UNIT
	CAIN	T1,SWCOD##	;SEEK WAIT?
	AOJA	T1,STRPS0	;YES, SET STATE=SEEK AND START POSITION GOING
	PJRST	STRPOS		;NO, SET STATE=POSITION AND START POSITION GOING

;HERE IF A FILE IS ALREADY ON CYLINDER
PWQOK:	PUSHJ	P,UNQUE0	;REMOVE FILE FROM PWQ
	DSKOFF			;#TURN ALL DISK PI'S OFF
	MOVE	T1,UNISTS##(U)	;#STATUS OF UNIT
	CAIN	T1,SWCOD##	;#UNIT IN SEEK WAIT?
	JRST	PWQOK2		;#YES. DO SOMETHING ELSE
	HRRM	F,UNICDA##(U)	;SO SWAPPER WILL KNOW
IFN FTDUAL,<
	PUSH	P,U
>
	PUSHJ	P,UUOTWQ	;#PW STATE - ADD FILE TO TWQ,SET STATE TO TW
				;(UNLESS THERE WAS A SEEK WHICH WE FORGOT
				;BECAUSE OF A POSITION REQUEST, AND THE POSITION
				;IS ALREADY ON-CYLINDER. IN THAT CASE, I/O
				;WILL BE STARTED)
IFN FTDUAL,<
	POP	P,U		;IF WE STARTED IO ON ALTERNATE UNIT
	HRRZ	J,UNIKON##(U)	;THEN U, J AND P1 WERE CHANGED
	HRRZ	P1,KONCHN##(J)	;SO RESET THEM
>
PWQOK1:
IFN FTDOPT,<
	POP	P,S		;RESTORE S
>	;END CONDITIONAL ON FTDOPT
	POPJ	P,		;AND RETURN

;HERE IF POSITION REQUEST IS FOR A SEEK WHICH IS ON-CYLINDER
PWQOK2:	SKIPE	UNIQUE##(U)	;ANY POSITIONS WAITING?
	JRST	UNIPS1		;YES, START POSITION (FORGET SEEK)
	SETZM	UNISTS##(U)	;NO, SET UNIT IDLE
	JRST	PWQOK1		;RESTORE S AND RETURN
;SUBROUTINE TO UPDATE DDB, ADVANCE BUFFERS AFTER A DATA TRANSFER
;ENTER WITH P4=CHNNUM= NUMBER OF BUFFERS TO ADVANCE
;RETURNS WITH P3=ADVBFE OR ADVBFF DEPENDING ON INPUT OR OUTPUT
;CHANGES P3,P4
BUFAD:	MOVNM	P4,T1		;DECREASE NUMBER OF BLOCKS LEFT
	ADDM	T1,DEVLFT##(F)	;BY NUMBER OF BLOCKS XFERRED
	ADDM	P4,DEVBLK##(F)	;UPDATE FILE CURRENT BLOCK NUMBER
IFN FTDSUP,<	;SUPER USETI/USETO
	TLNE	S,IOSUPR	;IO FROM SUPER USETI/USETO?
	JRST	BUFAD0		;YES. DONT TOUCH A.T.
>
	ADDB	P4,DEVREL##(F)	;UPDATE CURRENT RELATIVE BLOCK NUMBER
	SOJN	P4,BUFAD4	;-1=HIGHEST BLOCK WRITTEN
	TLO	S,IOSFIR	;NEXT BLOCK = 1 - SET FOR CHKSUM
	MOVEM	S,DEVIOS(F)	;SAVE S IN DDB
BUFAD4:	HRRZ	T1,DEVACC##(F)	;ACCESS TABLE LOC
	MOVE	T3,ACCWRT##(T1)	;SET T3 NEGATIVE IF NEW
	SUB	T3,P4		; LAST BLOCK, 0 IF OLD LAST
	CAMLE	P4,ACCWRT##(T1)	;THIS BLOCK HIGHER THAN FORMER HIGHEST?
	MOVEM	P4,ACCWRT##(T1)	;YES, SAVE NEW HIGHEST WRITTEN
	SKIPE	ACCWRT##(T1)	;IF RIB, NOT LAST BLOCK
	CAME	P4,ACCWRT##(T1)	;LAST BLOCK?
	TLZA	P3,-1		;NO, LH(P3)=0
	HRL	P3,T1		;YES, LH(P3)=LOC OF A.T.
BUFAD0:	HRRZ	P4,CHNNUM##(P1)	;NUMBER OF BLOCKS TRANSFERRED AGAIN
	LDB	T2,PIOMOD##	;GET MODE
	MOVEI	T1,UNIDRC##(U)	;SET FOR DUMP-MODE STATS
	CAIL	T2,SD		;DUMP?
	PJRST	UPSTA		;YES. UPDATE UNIDRC(DWC) AND RETURN
	MOVEI	T1,UNIBRC##(U)	;NO. SET TO UPDATE BUFFERRED-MODE STATS
	PUSHJ	P,UPSTAT	;UPDATE UNIBRC(BWC)
	HRRI	P3,ADVBFE##	;SET TO ADVANCE BUFFERS
	TLNN	S,IO
	HRRI	P3,ADVBFF##
	JUMPE	P4,CPOPJ1##	;RETURN IF NO BUFFERS TO ADVANCE
BUFAD1:	SOJN	P4,BUFAD2	;GO IF NOT LAST BUFFER
	HLRZ	T2,P3		;LAST - LOC OF ACC IF READING LAST BLOCK OF FILE
	JUMPE	T2,BUFAD2	;NOT LAST BLOCK IF 0
;HERE WHEN DOING I/O TO LAST BLOCK OF THE FILE
IFN FTKI10!FTKL10,<
	PUSHJ	P,SVEUF##	;SETUP USER BASE REGISTER SO CAN ADDRESS BUFFERS
>
	TLNE	S,IO		;WRITING?
	JRST	BUFAD3		;YES, COMPUTE LBS
	LDB	T1,ACYLBS##	;READING LAST BLOCK - GET ITS SIZE
	MOVE	T2,DEVIAD(F)
IFN FTKA10,<
	MOVEI	T2,@T2
>
	EXCTXU	<MOVEM T1,1(T2)>
BUFAD2:	PUSHJ	P,(P3)		;ADVANCE BUFFERS
	  JUMPE	P4,CPOPJ##	;RAN OUT - RETURN IF LAST BUFFER
	JUMPG	P4,BUFAD1	;ADVANCE MORE IF NOT LAST BUFFER
	JRST	CPOPJ1##	;ANOTHER BUFFER AVAILABLE - SKIP RETURN

;HERE WHEN WRITING LAST BLOCK OF A FILE - COMPUTE LAST BLOCK SIZE
BUFAD3:	MOVE	T1,DEVOAD(F)	;ADDRESS OF LAST BUFFER
IFN FTKA10,<
	MOVEI	T1,@T1		;RELOCATE IF A KA
>
	EXCTUX	<MOVE T1,1(T1)>;GET WRDCNT OF LAST BUFFER
	CAILE	T1,BLKSIZ##	;TOO MANY WORDS?
	MOVEI	T1,BLKSIZ##	;YES, REDUCE COUNT
	JUMPL	T3,BUFAD5	;NEW LAST BLOCK IF NEGATIVE
	MOVE	T3,DEVACC##(F)	;LOC OF A.T.
	MOVE	T3,ACCSTS##(T3)	;STATUS WORD
	TRNN	T3,ACPUPD	;UPDATE MODE?
	TDZA	T3,T3		;NO
	LDB	T3,ACYLBS##	;YES, GET PREVIOUS LAST-BLOCK LENGTH
	CAILE	T3,BLKSIZ##	;TOO MANY WORDS?
	MOVEI	T3,BLKSIZ##	;REDUCE COUNT
	CAMLE	T3,T1		;CURRENT LENGTH LESS THAN PREVIOUS LENGTH?
	MOVE	T1,T3		;YES, SET CURRENT=PREVIOUS LENGTH
BUFAD5:	DPB	T1,ACYLBS##	;SAVE IN ACC
	JRST	BUFAD2		;AND GO ADVANCE BUFFERS
;SUBROUTINE TO COMPUTE A FOLDED CHECKSUM FROM THE FIRST DATA WORD
;CALL WHEN THE IO LIST HAS BEEN SET UP IN THE KONTROLLER DATA BLOCK
CHKSUM:
IFN FTKI10!FTKL10,<
	PUSHJ	P,SVEUF##	;MAKE ADDRESSABLE
	PUSH	P,J		;SAVE J
	PUSH	P,M		; AND M
	LDB	J,PJOBN##	;GET NEW JOB #
	HLRZ	M,DEVUVA##(F)	;GET L(1ST WORD)-1
	MOVEI	M,1(M)		;=ACTUAL I/O ADDRESS
	PUSHJ	P,GETWRD##	;GET WORD FROM USER AREA
	 STOPCD	.,STOP,CSE,	;++CHECKSUM ERROR
	MOVE	T2,T1		;MOVE IT
IFN FTKL10&FTMS,<
	MOVE	T1,M		;CHASE WORD FROM CACHE
	PUSHJ	P,OUCHE##	; IN CASE IT RUNS ON OTHER CPU
>
	POP	P,M		;RESTORE STUFF
	POP	P,J		;...
>
IFN FTKA10,<
	HLRZ	T2,DEVUVA##(F)	;ADDRESS OF THE CHECKSUM WORD
	TLO	T2,R		;RELOCATE
	HRRI	T2,1(T2)
	MOVE	T2,@T2		;GET CHECKSUM WORD
>
;SUBROUTINE TO COMPUTE A CHECKSUM FROM T2
;ENTER WITH T2=WORD TO BE CHECKSUMMED
;EXIT WITH T1 = CHECKSUM
CHKST1::HRRZ	T4,UNISTR##(U)	;LOC OF STR DB
	MOVE	T4,STYCKP##(T4)	;CHECKSUM POINTER
	LDB	T3,[POINT 6,T4,11]	;SIZE FIELD OF CHKSUM PNTR
	MOVNS	T3		;SET FOR LSH
	TLZA	T4,770000	;SET TO BIT 35
CHKSM1:	ADD	T2,T1		;NOT DONE. ADD BYTE TO REST OF WORD (FOLD CHKSUM)
	LDB	T1,T4		;GET A BYTE OF CHKSUM SIZE
	LSH	T2,(T3)		;THROW AWAY THE BYTE
	JUMPN	T2,CHKSM1	;FINISHED WHEN NO MORE OF ORIGINAL WORD
	POPJ	P,		;DONE - RETURN

;SUBROUTINE TO UPDATE UNIT STATISTICS AFTER IO
;ENTER WITH P4=NUMBER OF BLOCKS,  T1=UNIXRC(U)   (X=M,B OR D)
;UPSTA = UPSTAT,PRESERVES T3
UPSTA:
UPSTAT:	TLNE	S,IO		;WRITING?
	ADDI	T1,1		;YES. UNIXWC=UNIXRC+1
	ADDM	P4,(T1)		;UPDATE WORD IN UNIT BLOCK
IFN FTDMRB,<			;IF MULTIPLE RIBS
	SKIPL	DEVRIB##(F)	;IO TO FILE CURRENTLY USING EXTENDED RIB?
>	;END CONDITIONAL ON FTDMRB
	POPJ	P,		;NO, RETURN
IFN FTDMRB,<			;IF MULTIPLE RIBS
	HRRZ	T1,P4		;NUMBER OF BLOCKS TRANSFERRED
	TLNE	S,IO		;WRITING?
	HRLZ	T1,T1		;YES, MOVE NUMBER OF BLOCKS TO LEFT HALF
	ADDM	T1,UNIXRA##(U)	;AND STORE IN UNIT DATA BLOCK
	POPJ	P,		;AND RETURN
>	;END CONDITIONAL ON FTDMRB
;SUBROUTINE TO COMPUTE DISTANCE TO TARGET CYLINDER
;ENTER WITH TARGET BLOCK IN DEVBLK(F)
;ENTER AT CYLCM WITH T1=BLOCK NUMBER,  T4 PRESERVED
;EXIT WITH T1 =DISTANCE FROM CURRENT CYLINDER TO TARGET CYLINDER
CYLCOM:	MOVE	T1,DEVBLK##(F)	;#TARGET BLOCK
CYLCM::	LDB	T3,UNYBPY##	;#NUMBER OF BLOCKS PER CYLINDER
	IDIV	T1,T3		;#COMPUTE CYLINDER
	SUB	T1,UNICYL##(U)	;#-PRESENT CYLINDER
	POPJ	P,		;#EXIT T1=DISTANCE


;SUBROUTINE TO REMOVE A FILE FROM A QUEUE
;  UNQUEX FOR CHANNEL (XFER WAIT) QUEUES
;	P1=ADDR. OF CHAN. DATA BLOCK
;  UNQUE0 FOR UNIT (POSITION WAIT) QUEUES
;	U=ADDR. OF UNIT DATA BLOCK
;F HAS FILE    P2 HAS PREDECESSOR
UNQUEX:
IFN FTDSTT,<			;IF KEEPING DISK STATISTICS
	SOSA	CHNQUL##(P1)	;  DECREMENT XFER QUEUE LENGTH
>
UNQUE0:
IFN FTDSTT,<
IFE FTVM,<
	SOS	UNIQUL##(U)	;  OR POSITION WAIT QUEUE LENGTH
>
IFN FTVM,<
	SOSA	UNIQUL##(U)
>
>
IFN FTVM,<
	SOS	CHNWAT##(P1)
>
	HLLZ	T1,DEVQUE##(F)	;Q WORD OF FILE
	HLLM	T1,DEVQUE##(P2)	;SAVE IN Q WORD OF PREDECESSOR
	POPJ	P,		;AND RETURN
;SUBROUTINE  TO START UNIT POSITIONING
STRPOS::MOVEI	T1,PCOD##	;#PUT FILE AND UNIT INTO P STATE
STRPS0:	PUSHJ	P,FILCOD	;#
IFN FTDUAL,<
	PUSHJ	P,SECCOD	;SET MAIN UNIT TO SAME STATE
	HRRM	U,DEVCUR##(F)
>
STRPS1:
IFN FTMETR,<
	MOVE	T1,UNISTS##(U)	;GET STATE FOR METER POINT IN SETPAR
>
	PUSHJ	P,SETPRS	;#SET KONCUA, UNICDA, UNIBLK
	PUSHJ	P,@KONPOS##(J)	;#GO TO DEPENDENT ROUTINE
	  JRST	STRPS2		;UNIT NOT OK - PUT IT INTO T OR TW
				; (UNLESS CALLED FROM HNGDSK)
				;SO THAT STARTIO WILL CALL BADUNI
	DSKON			;# TURN ALL DISK PI'S BACK ON
	SCHEDULE
	POPJ	P,		;AND RETURN

;HERE IF UNIT NOT READY WHEN TRYING TO START POSITIONING
STRPS2:	MOVE	T1,UNISTS##(U)
	SETZM	UNISTS##(U)	;SO WE'LL STORE NEW UNISTS
	CAIN	T1,SCOD		;IF TRYING TO START A SEEK,
	POPJ	P,		; FORGET IT
IFE FTDUAL,<
	PJRST	SETTTW		;TRY TO START I/O
>
IFN FTDUAL,<
	PUSH	P,U
	PUSHJ	P,SETTTW
	POP	P,U		;IF WE STARTED IO ON ALTERNATE UNIT
	HRRZ	J,UNIKON##(U)	;THEN U, J AND P1 WERE CHANGED
	HRRZ	P1,KONCHN##(J)	;SO RESET THEM
	POPJ	P,
>

IFN FTDOPT,<
;SUBROUTINE TO SAVE POINTERS TO BEST FILE SO FAR
;P1-P3 CHANGED
SVBST:	MOVEM	F,P4		;P4 = BEST FILE SO FAR
	HRRZM	T1,P3		;P3 = DISTANCE TO BEST
	MOVEM	S,P2		;P2 = PREDECESSOR IN QUEUE
	POPJ	P,
>	;END CONDITIONAL ON FTDOPT
;SUBROUTINE TO SET UP PARAMETERS FOR DEPENDENT ROUTINE
SETPRS:
IFN FTRP04,<
	SKIPL	KONTAB##(J)	;#DONT STORE NEW KONCUA IF THIS IS A POSITION
				; STARTED WHILE XFER IS ACTIVE
>
SETPAR:	HRRM	U,KONCUA##(J)	;#SAVE CURRENT UNIT ADDR
	HRRM	F,UNICDA##(U)	;#SAVE FILE ADDR
IFN FTDUAL,<
	SKIPGE	T2,UNI2ND##(U)	;IF STARTING 2ND PORT,
	HRRM	F,UNICDA##(T2)	; MAKE SURE WORLD KNOWS ABOUT MAIN PORT
>
IFN FTMETR,
<				;SET UP T1 FOR METER POINT 3
				;SHOULD ALREADY CONTAIN
				; OPCODE 4 OR 6

	ROT	T1,-2		;#GET THE DISTINGUISHING BIT INTO BIT 0
>	;END CONDITIONAL ON FTMETR

	MOVE	T2,DEVBLK##(F)	;# SAVE LOGICAL BLOCK NR WITHIN UNIT
	MOVEM	T2,UNIBLK##(U)	;#IN UNIT DATA BLOCK
	HRR	T1,T2
	MOVE	S,DEVIOS(F)	;#GET S
IFN FTMETR,
<	SKIPL	T2,MP3##	;# POINT INITIALIZED
	PJRST	STOIOS##	;#NO, RESET HUNG TIME AND RETURN
	DPB	F,[POINT 11,T1,11] ;# STORE DDB ADR
	DPB	U,[POINT 8,T1,17] ;# AND UDB ADR
	PUSHJ	P,@MPDPRA##(T2) ;# CALL METER POINT ROUTINE
>	;END CONDITIONAL ON FTMETR

	PJRST	STOIOS		;# RESET HUNG TIME AND EXIT

;SUBROUTINE TO DETERMINE IF A POINTER TO THE NEXT BLOCK OF A FILE IS IN CORE
;ENTER WITH J,U,F SET UP
;RETURN CPOPJ IF NEXT BLOCK NOT AVAILABLE
;RETURN CPOPJ1 IF THE NEXT BLOCK HAS A POINTER ALREADY IN CORE
CHKNXT:	HRRZ	T1,DEVLFT##(F)	;NUMBER OF BLOCKS LEFT IN CURRENT GROUP
	SOJGE	T1,CHKNX2	;CPOPJ1 IF ANY LEFT AND WRITING
	MOVE	T1,DEVRET##(F)	;LOOK AT NEXT POINTER
CHKNX1:	SKIPE	T2,1(T1)	;IF ZERO (EOF)
	CAIL	T1,DEVRBN##(F)	;OR END OF POINTER BLOCK
	POPJ	P,		;NONE LEFT IN CORE
	HLRE	T1,DEVRSU##(F)	;DEVRSU
	CAMGE	T1,[-2]		;NO MORE IF MIGHT GET REDUNDANT RIB
	TLNN	T2,-1		;CHANGE OF LOGICAL UNITS?
	POPJ	P,		;NOT AVAILABLE SINCE IT IS ON ANOTHER UNIT
				;(CANT SWITCH UNITS ON INTERRUPT LEVEL SINCE STATE
				; OF OTHER UNITIS INDEPENDENT OF THIS ONE)
	PUSHJ	P,NXTBLK	;STORE NEW DEVBLK IN CASE THIS UNIT PUT BACK
				; IN PW AND NEW PNTR IS FOR A NEW CYLINDER
	  POPJ	P,		;NEW BLOCK PAST WRITTEN DATA - RETURN
CHKNX2:	HRRZ	T2,DEVACC##(F)	;SAME UNIT. LOC OF A.T.
	JUMPE	T2,CPOPJ##	;UNAVAILABLE IF F/S WAS JERKED
	MOVE	T2,ACCWRT##(T2)	;NUMBER OF BLOCKS WRITTEN
	TLNN	S,IO		;IF READING,
 	CAML	T2,DEVREL##(F)	;IS NEXT READ PAST THE EOF?
	AOS	(P)		;NO. OK
	POPJ	P,		;RETURN CPOPJ OR CPOPJ1
;HERE WHEN THE UNIT IS NOT READY
BADUNI:	HRRZS	UNISTS##(U)	;#CLEAR SIGN BIT (PHUNG)
IFN FTDAEM,<
	MOVEM	T2,UNIERR##(U)	;#YES, SAVE CONI, DATAI
	MOVEM	T3,UNIHDI##(U)	;#
>
IFN <FTKI10!FTKL10>,<
IFN FTVM,<
	SKIPGE	DEVSWP##(F)	;#DON'T RETURN IOWD'S IF THE SWAPPER
	JRST	BADUNA
>
	MOVE	T3,UNISTS##(U)	;IF THE UNIT IS IN T STATE
	SKIPG	UNIECT##(U)	; AND NOT IN ERROR-RECOVERY
	CAIE	T3,TCOD##
	JRST	BADUNA
	PUSH	P,T1		;SAVE T1
	SKIPE	T1,@KONIOC##(J)	;GET LOC OF FIRST CORE BLOCK
	PUSHJ	P,RTNIOW##	;RETURN THE FREE-CORE BLOCKS
	POP	P,T1		;RESTORE T1 AND CONTINUE
BADUNA:>	;END FTKI10!FTKL10
	MOVSI	T4,UNPOFL##
	TRNE	T1,KOPFUS##	;#UNIT GO FILE-UNSAFE?
	TLO	T4,UNPFUS##	;#YES, LIGHT ANOTHER BIT TOO
	TRNE	T1,KOPUSI##
	TLO	T4,UNPUSI##
	TRNN	T1,KOPOFL##	;#UNIT OFF-LINE?
	JRST	BADUN3		;# NO, CALL HNGSTP
	IORM	T4,UNIDES##(U)	;#YES - LIGHT UNIDES BIT
IFN FTDBAD,<
	SKIPE	DEVRHB##(F)	;#IF REREADING HOME BLOCKS,
	SKIPE	DINITF##	;#OR IF IN ONCE-ONLY CODE,
	JRST	BADUN6		;#DONT DO THIS OPR-WAIT STUFF
IFN FTDAEM,<
	MOVE	T2,UNIBLK##(U)	;#
	MOVEM	T2,UNIHBN##(U)	;#SAVE BLOCK NUMBER
	AOS	UNIHCT##(U)	;#BUMP ERROR COUNT
	PUSH	P,T1		;SAVE STATUS
	PUSHJ	P,LSTER		;COPY REGISTERS FROM KDB TO UDB
	MOVEI	T1,.ERDPE	;SAY DISK ERROR TO DAEMON
	HRL	T1,F		;PUT DDB ADDR IN LH FOR DAEMON
	PUSHJ	P,DAEERR##	;#AND CALL DAEMAN
	POP	P,T1		;#RESTORE STATUS
>	;END FTDAEM

	;STILL IN FTBAD CONDITIONAL
;HERE IF JOB NOT INTERCEPTING UNIT OFF-LINE

	TLO	P1,-1		;NO INTERCEPTS, INDICATE OFF-LINE
	MOVE	T1,[XWD TELOPC,1]	;#SET FOR CLOCK REQUEST
	CONO	PI,PIOFF##	;#SET TO STORE IN THE CLOCK QUEUE
	CONSO	PI,PIPROG##	;#ON PI LEVEL?
	JRST	BADLP2		;#NO
	IDPB	T1,CLOCK##	;#YES, TELL OPR ON NEXT TICK
	IDPB	U,CLOCK##	;SAVE ADR OF UDB
BADLP2:	CONO	PI,PION##	;#IN ORDER TO TYPE MESSAGE TO OPR
	CONSO	PI,PIPROG##	;#DONT TELL OPR NOW IF ON PI LEVEL
	PUSHJ	P,TELOPR	;#NOT ON PI LEVEL - TYPE MESSAGE NOW
IFN FTVM,<
	SKIPGE	DEVSWP##(F)	;#IF THE SWAPPER,
	JRST	BADU4A		; LET SWPHNG RECOVER
>
	MOVEI	T1,OWCOD##	;#SET UNIT IN OPR WAIT STATE
	MOVEM	T1,UNISTS##(U)	;#(ONLY GETS OUT OF OW ON "FREE" INTERRRUPT)
BADUN3:	JUMPL	S,BADUN4	;#IF NOT A MONITOR I/O OPERATION,
	MOVE	T1,KONDMP##(J)	;#GET IOWD BACK
	LDB	T2,PIOMOD##	;#MODE
	CAIL	T2,SD		;#DUMP?
	MOVEM	T1,DEVDMP##(F)	;#YES, RESET DEVDMP IN CASE "CONT" IS TYPED
	JUMPGE	P1,BADUN4	;#GO IF NOT OFF-LINE
	MOVEI	T4,.EROFL	;DISK OFF-LINE ERROR
	PUSHJ	P,SETINJ	;IS HE INTERCEPTING?
	  JRST	BADUN4		;NO
	MOVEI	T1,OW2COD##	;YES, SET UNIT O2 (OPR WAIT, NO MESSAGE EACH MINUTE)
	MOVEM	T1,UNISTS##(U)	; (ONLY GETS OUT OF O2 ON "FREE" INTERRUPT)
				;JOB IN IO WAIT?
	PUSHJ	P,STDIOD##	;YES, UNWAIT HIM
BADUN4:
IFN FTDUAL,<
	MOVE	T1,UNISTS##(U)
	CAIN	T1,TCOD##	;IF WRITE-LOCK
	SETZ	T1,		;MAKE OTHER PORT IDLE
	SKIPGE	T2,UNI2ND##(U)	;DUAL-PORTED UNIT?
	MOVEM	T1,UNISTS##(T2)	;YES, PUT OTHER PORT IN OPR WAIT TOO
>
	JUMPGE	P1,BADUN5	;#IF UNIT WENT OFF-LINE,
	PUSH	P,F		;#SAVE F
IFN FTKI10!FTKL10,<
	CONSO	PI,PIPROG##	;IF ON UUO LEVEL AND A SEEK FINISHED
	SETZ	P1,		; CAN'T START IT - CANT CALL SVEUB
>
	PUSHJ	P,SETID1	;#GO FIND SOMETHING ELSE TO DO
				;#THIS (UNIT WONT BE USED TILL COMES OUT OF OW)
	POP	P,F		;#RESTORE F
BADU4A:	DSKON
	POPJ	P,		;AND RETURN
;STILL IN FTDBAD CONDITIONAL
;HERE WHEN THE UNIT DID NOT GO OFF-LINE (PROBABLY WRITE-LOCK)
BADUN5:	DSKON			;#TURN ON DSK PI
IFE FTVM,<
	SKIPGE	DEVSWP##(F)	;CAN'T CALL HNGSTP IF THE SWAPPER
	JRST	BADUN6		;(P) = NULPDL
>
IFN FTVM,<
	SKIPGE	DEVSWP##(F)	;IF THE SWAPPER,
	JRST	BADU4A		; LET SWPHNG RECOVER
>
	CONSZ	PI,PIPROG##	;IS A PI IN PROGRESS (P-10 ONLY)
	JRST	BADU5A
	PUSHJ	P,SAVSTS	;MAKE A RECORD OF RESOURCES, RETURN THEM
	  JRST	BADUN6		;HAS MON-BUF OR PI IN PROGRESS, HE LOSES
	HRRZ	J,UNIKON##(U)	;SAVSTS CLOBBERS J
	PUSH	P,F		;NO, SAVE F
IFN FTKI10!FTKL10,<
	SETZ	P1,		; INTERRUPTED DONT START THE IO
				; (CANT CHANGE UBR AT UUO LEVEL)
>
	PUSHJ	P,SETIDA	;SET UNIT IDLE (DON'T CLEAR IOSMON), START NEXT OPERATION
	MOVE	F,(P)		;RESET DDB ADDR FOR HNGSTP
	PUSHJ	P,HNGSTP##	;"DEVICE DEV OK?"
	POP	P,F		;RESTORE F,
	POP	P,T3
	PUSHJ	P,RESSTS	; U, AND ANY RESOURCES JOB OWNED
	HRRZ	J,UNIKON##(U)	; AND J
	PJRST	UUOPW0		;HE SAID CONTINUE - TRY AGAIN
BADU5A:	TRO	S,IOIMPM
	PUSHJ	P,STDIOD##
	PJRST	SETIDA

>	;END CONDITIONAL ON FTDBAD
;HERE ON INTERRUPT LEVEL OR DURING ONCE-ONLY
;LIGHT AN ERROR BIT AND RETURN TO CALLER
BADUN6:	DSKON			;#TURN ON DSK PI'S
	MOVE	S,DEVIOS(F)	;GET CURRENT DEVIOS
	TRO	S,IOIMPM	;LIGHT IOIMPM
				;IN IO WAIT?
	PUSHJ	P,STDIOD##	;YES. TAKE OUT
	JUMPE	U,CLRACT##	;RETURN IF F/S WAS JERKED
	PUSH	P,F		;SAVE F
IFN FTKI10!FTKL10,<
	CONSO	PI,PIPROG##	;CANT CHANGE UBR IF
	SETZ	P1,		; AT UUO LEVEL
>
	PUSHJ	P,SETIDL	;SET UNIT IDLE,START NEXT OPERATION
	POP	P,F
	POPJ	P,		;RETURN
	IFN FTDBAD,<
;ROUTINE TO TYPE A MESSAGE TO THE OPR WHEN A DRIVE FOES OFF-LINE
;ENTER AT TELOPC FROM THE CLOCK, T1=UDB
;ENTER AT TELOPR WITH U SET CORRECTLY
TELOPC:	SETZ	J,		;SET J=0 AS A SWITCH
	MOVE	U,T1
IFN FTVM,<
	HRRZ	T1,UNICDA##(U)	;IF THE SWAPPER
	SKIPL	DEVSWP##(T1)	; UNIT IS STILL IN TW
>
	MOVE	T1,UNISTS##(U)	;GET STATUS OF UNIT
	CAIGE	T1,OWCOD##	;HAS UNIT ALREADY BEEN POWERED DOWN AND UP?
	POPJ	P,		;YES, DONT TYPE ANYTHING
				;NO, TELL OPR ABOUT BAD UNIT

	JRST	TELOP3		;SAVE ONLY U, AND TYPE
TELOPR:	PUSH	P,F
TELOP3:	PUSH	P,U
	HRRZ	U,OPRLDB##	;TYPE MESSAGE ON OPR'S TTY
	PUSHJ	P,INLMES##
	BYTE	(7) 7,7,7,7,7	;SOME BELLS
	ASCIZ	/
UNIT /
	MOVE	T2,(P)		;UNIT
	MOVE	T2,UNINAM##(T2)	;PHYSICAL UNIT NAME
	PUSHJ	P,PRNAME##	;TYPE IT
IFN FTDUAL,<
	MOVE	T2,(P)		;UDB ADDR
	SKIPL	T2,UNI2ND##(T2);IS THIS AN ALTERNATE PATH?
	JRST	TELO3A		;NO
	PUSH	P,T2		;YES, SAVE ITS ADDR
	PUSHJ	P,INLMES##	;TELL OPR THE NAME HE KNOWS
	ASCIZ	/ = /
	POP	P,T2
	MOVE	T2,UNINAM##(T2)
	PUSHJ	P,PRNAME##
TELO3A:>
	MOVE	T2,(P)
	MOVSI	T1,UNPUSI##
	TDNE	T1,UNIDES##(T2)
	JRST	TELO3B
	PUSHJ	P,INLMES##	;AND THE MESSAGE:
	ASCIZ	/ WENT OFF-LINE/
	JRST	TELO3C
TELO3B:	ANDCAM	T1,UNIDES##(T2)
	PUSHJ	P,INLMES##
	ASCIZ	/ STATUS INCONSISTENT/
TELO3C:	MOVE	T2,(P)		;UNIT
	MOVE	T2,UNIDES##(T2)	;UNIDES WORD
	TLNN	T2,UNPFUS##	;FILE UNSAFE?
	JRST	TELOP4
	PUSHJ	P,INLMES##	;YES
	ASCIZ	/ (FILE UNSAFE)/
;STILL IN FTDBAD
TELOP4:	MOVE	T2,(P)		;UDB AGAIN
	HRRZ	T2,UNIKON##(T2);KDB
	SKIPG	KONPOS##(T2)	;DOES THIS UNIT POSITION?
	JRST	TELOP5		;NO, HE CANT TURN IT OFF
	PUSHJ	P,INLMES##	;YES, ASK HIM TO
	ASCIZ	/
PLEASE POWER IT DOWN, THEN TURN IT ON AGAIN./
TELOP5:	PUSHJ	P,INLMES##
	ASCIZ	/
/
	POP	P,U		;RESTORE ACS
	JUMPE	J,CPOPJ##	;DIDN'T SAVE ACS IF J=0
	POP	P,F
	MOVE	S,DEVIOS(F)	;RESTORE S
	POPJ	P,		;AND RETURN


;ROUTINE CALLED ONCE A MINUTE
CHKUNI::SETZM	F
	SETOM	J
	HLRZ	U,SYSUNI##	;FIRST UNIT IN SYSTEM
CHKUN1:	MOVE	T1,UNISTS##(U)	;STATUS OF UNIT
	CAIE	T1,OCOD##
	CAIN	T1,OWCOD##	;WAITING FOR OPR?
	PUSHJ	P,TELOPR	;YES, REMIND HIM AGAIN
	HLRZ	U,UNISYS##(U)	;STEP TO NEXT UNIT
	JUMPN	U,CHKUN1	;AND TEST IT
	POPJ	P,		;DONE - RETURN
>	;END CONDITIONAL ON FTDBAD

;HERE TO START TRANSFER OF BEST (LATENCY DETERMINED) FILE IN TW
PIKTRN:
IFE FTVM,<
PIKTRX:
>
IFN FTVM,<
	MOVEI	F,SWPDDB	;MAKE SURE WE'RE POINTING AT SWAPPER
	SKIPE	SQREQ		;SWAPPER WANT SERVICE?
	PUSHJ	P,SWPSCN##	;YES, LET IT DO PART 1 (SET ASL UNITS IN TW STATE)
PIKTRX:	SKIPN	SQREQ##		;SWAPPER WAITING?
	JRST	PIKTR0		;NO
	MOVEI	F,SWPDDB	;YES, POINT F AT SWAPPER DDB
	PUSHJ	P,SWPPIK##	;GO FIND SOMETHING TO DO
	  CAIA			;COULDN'T, DO A FILE IO
	POPJ	P,		;SWAPPER IS GOING, EXIT
PIKTR0:>	;END FTVM
	PUSHJ	P,SAVE4##	;#
	DSKOFF
	MOVSI	T1,400000	;#SET OLD KONTROL IDLE
	ANDCAM	T1,KONTAB##(J)	;#
PIKTR1:	HLRZ	F,CHNQUE##(P1)	;#FIRST FILE DDB IN TW QUEUE FOR THIS CHAN
	JUMPE	F,CHNIDL	;#IF QUEUE EMPT, SET CHAN IDLE AND EXIT
	DSKON			;#


IFE FTDOPT,<
;HERE IF NO LATENCY OPTIMIZATION - PICK FIRST IN QUEUE
	MOVEI	P2,DIFCDQ##(P1)	;SET P2=PREDECESSOR
TWQLUP:	HRRZ	U,DEVUNI##(F)	;U=UNIT FILE IS ACCESSING
	JUMPE	U,SETBSY	;GO IMMEDIATELY IF F/S HAS BEEN JERKED OUT
	SKIPE	T2,UNISTS##(U)	;STATUS OF UNIT, SKIP IF IDLE
	CAIN	T2,TWCOD##	;SKIP IF NOT TRANSFER WAIT
	JRST	SETBSY		;IDLE OR TW, PICK THIS TRANSFER
	MOVEM	F,P2		;UNIT IS BUSY, LOOK FOR NEXT
	HLRZ	F,DEVQUE##(F)	;F=NEXT FILE IN QUEUE, P2=PREDECESSOR
	JUMPN	F,TWQLUP	;TEST NEXT FILE IF ANY
	PJRST	CHNIDL		;SET CHANNEL IDLE, NO WORK TO DO

;HERE WHEN A FILE IS FOUND, START IO
SETBSY:	MOVE	J,UNIKON##(U)	;J=CONTROLLER FILE IS ACCESSING
>	;END CONDITIONAL ON FTDOPT

IFN FTDOPT,<
;HERE TO FIND THE BEST FILE TO START TRANSFERRING
	HRLOI	P3,377777	;PRESET BEST LATENCY TIME
	PUSH	P,S		;SAVE S
	MOVEI	S,DIFCDQ##(P1)	;PRESET PREDECESSOR
IFN FTDPRI,<
	PUSHJ	P,DFPRI		;PRIORITY OF FIRST ITEM IN QUEUE
	MOVE	R,T2		;SAVE IN R
>
;STILL IN FTDOPT CONDITIONAL
TWQLUP:	MOVE	T1,DEVBLK##(F)	;BLOCK FILE NEEDS
IFE FTDUAL,<
	HRRZ	U,DEVUNI##(F)	;SET UP U
>
IFN FTDUAL,<
	SKIPE	U,DEVUNI##(F)
	HRRZ	U,DEVCUR##(F)
>
	JUMPE	U,TWQLP0	;JUST IN CASE F/S HAS BEEN JERKED OUT
	SKIPE	T2,UNISTS##(U)	;STATUS OF UNIT
	CAIN	T2,TWCOD##	;TW?
	SKIPA	J,UNIKON##(U)	;IDLE OR TW - SET J=KONTROLLER FOR UNIT
	JRST	TWQLP1		;UNIT IN TW - DONT GET ANOTHER FILE FOR TW
IFN FTDUAL,<
	SKIPGE	T2,UNI2ND##(U)	;IF THIS IS AN ALTERNATE PATH
	MOVE	T2,UNISTS##(T2)	; AND THE MAIN UNIT IS BUSY
	CAIN	T2,TCOD##
	JRST	TWQLP1		; THEN LEAVE ALTERNATE ALONE
>
	PUSHJ	P,@KONLTM##(J)	;COMPUTE LATENCY
TWQLP0:	  MOVEI	T1,0		;UNIT NOT OK- SET LATENCY=0
				; SO UNIT WILL BE GOTTEN RID OF IMMEDIATELY
	SKIPL	DEVSWP##(F)	;SWAPPER REQUEST?
	CAMG	T1,P3		;NO. BEST SO FAR?
	PUSHJ	P,SVBST		;YES, SAVE POINTERS TO FILE
				; P4=BEST F, P3=DIST TO BEST, P2=PRED.F
	JUMPE	T1,SETBSY
	SKIPGE	DEVSWP##(F)	;SWAPPER REQUEST?
	JRST	SETBSY		;YES. USE THIS FILE
TWQLP1:	MOVEM	F,S		;SAVE F AS PREDECESSOR
	HLRZ	F,DEVQUE##(F)	;STEP TO NEXT FILE
IFN FTDPRI,<
	JUMPE	F,TWQLP2	;START IO OR SET CHAN IDLE
	PUSHJ	P,DFPRI		;HAVE A QUEUE ENTRY - GET PRIORITY
	CAME	T2,R		;SAME AS FIRST IN QUEUE?
	JRST	TWQLP2		;NO, START IO
	JUMPG	R,TWQLUP	;YES, DON'T BE FAIR IF HI PRIORITY
>
	SKIPLE	CHNCFT##(P1)	;TIME TO BE FAIR?
	JUMPN	F,TWQLUP	;NO, TEST NEXT FILE
TWQLP2:	TLNN	P3,-1		;WAS ANY FILE FOUND TO START DATA?
	PJRST	SETBSY		;YES. GO
	JUMPN	F,TWQLUP	;NO. LOOK AGAIN IF THERE ARE MORE FILES
	POP	P,S		;NOTHING TO DO. RETORE S
	PJRST	CHNIDL		;SET CHAN IDLE
;STILL IN FTDOPT CONDITIONAL
;HERE WHEN BEST FILE IS FOUND START IO
SETBSY:	POP	P,S		;RESTORE S
	MOVE	F,P4		;SET F TO FILE
>	;END CONDITIONAL ON FTDOPT
	SKIPE	U,DEVUNI##(F)	;SET U TO UNIT DB ADR. IT IS ACCESSING
IFN FTDUAL,<
	HRRZ	U,DEVCUR##(F)
	SKIPE	U
>
	HRRZ	J,UNIKON##(U)	;SET J TO KONTROLLER DB UNIT IS ON
	DSKOFF			;#TURN ALL DISK PI'S OFF
	PUSHJ	P,UNQUEX	;#REMOVE FILE FROM TWQ
	JUMPE	U,UNIDWN	;JUST IN CASE F/S HAS BEEN JERKED OUT
IFN FTDSIM,<
SETBS1:	MOVE	T1,DEVUWZ##(F)	;ZEROING BLOCKS IN USETO CODE?
	TLNN	T1,DEPUWZ##
	JRST	SETBS2
	MOVE	T1,DEVACC##(F)	;YES, IS CURRENT BLOCK WRITTEN?
	MOVE	T1,ACCWRT##(T1)	; (IF USETO DECIDED TO ZERO BLOCK N WHILE
	CAML	T1,DEVREL##(F)	; ANOTHER JOB WAS IN DIOW FOR BLOCK N)
	JRST	NXTIO		;BLOCK NOW HAS GOOD DATA IN IT - GET OUT
SETBS2:>
	PUSHJ	P,STRTIO	;#SET FILE+UNIT TO T1, KONTROL TO B(CHAN ALREADY B)
				;COMPUTE CHAN COMMAND LIST AND START TRANSFER
	JRST	INTXIT		;AND DISMISS THE INTERRUPT
;HERE FROM BADUNI ON UUO LEVEL
CHNIDX:	MOVSI	T1,400000
	ANDCAM	T1,KONTAB##(J)
;HERE WHEN ALL PROCESSING FOR DATA INTERRUPT IS DONE
;RESET FAIRNESS COUNTS IF THEY ARE NEGATIVE AND DISMISS
;P1 IS STILL CHAN. DB ADR. WHICH CAUSED THIS DATA INTERRUPT
CHNIDL:	SETOB	T2,@KONCHN##(J)	;#SET CHAN IDLE
	DSKON			;#
IFN FTKI10!FTKL10,<
	JUMPE	P1,CPOPJ##	;DONE IF SETIDL CALLED BY
				; BADUNI AT UUO LEVEL
>
IFN FTKL10,<
	MOVSI	T1,CP.SWF##	;GET SWEEP FLAG
	TDNE	T1,CHB22B##(P1)	;DO WE HAVE TO DO A SWEEP BEFORE DISMISSING?
	PUSHJ	P,CFDMP		;YES, SO DO IT NOW SINCE NO I/O WILL
				; BE STARTED, AS WE ARE AT CHNIDL
>;END IFN FTKL10
	PUSH	P,U		;SAVE U FOR END TEST
CHNID1:	MOVE	T1,UNISTS##(U)	;STATUS OF UNIT
IFN FTDHIA,<
	CAIE	T1,TCOD##	;(PI'S ARE ON NOW)
	CAIN	T1,PCOD##	;IF ANY UNIT IS STILL DOING SOMETHING,
	AOJA	T2,CHNID2	; BUMP T2
	CAIN	T1,SCOD##
	AOJA	T2,CHNID2
>
IFN FTVM,<
	SKIPGE	SCNCNT		;UNITS ARE IN FUNNY STATE IF
				; IN SWPSCN ON UUO LEVEL
>
	CAIE	T1,TWCOD##	;IS UNIT IN TW?
	JRST	CHNID2		;NO
IFN FTDUAL,<
	SKIPE	T1,UNI2ND##(U)	;IF AN ALTERNATE UNIT,
	SKIPN	UNISTS##(T1)	; WHICH ISNT IDLE
	CAIA
	JRST	CHNID2		;IT'S OK
>
	SKIPE	T1,UNIQUE##(U)	;YES (UNKNOWN BUG)
	MOVEI	T1,PWCOD##	;PUT UNIT INTO PW OR IDLE
	MOVEM	T1,UNISTS##(U)	; SO SYSTEM WONT HANG
	AOS	UNIHNG##(U)	;INCREMENT SOFTWARE-HUNG COUNT
CHNID2:	HLR	U,UNICHN##(U)	;STEP TO NEXT UNIT ON CHAN
	CAME	U,(P)		;BACK WHERE WE STARTED?
	JRST	CHNID1		;NO, TEST THIS UNIT
	POP	P,U		;YES, REMOVE JUNK FROM PD LIST
IFN FTDHIA,<
	SKIPLE	F,DIADSK##	;TRYING TO SHUT DOWN IO?
	CAME	P1,DIACHN##	;YES, FOR THIS CHAN?
	JRST	CHNID4		;NO
	JUMPGE	T2,INTXIT	;YES, DISMISS IF ANY UNIT BUSY
CHNID3:	MOVE	S,DEVIOS(F)	;NO UNITS BUSY, SET UP S
	PUSHJ	P,STDIOD##	;AND WAKE UP THE JOB (TO START DIAGNOSTIC)
	PUSHJ	P,CLRACT##
IFN FTDUAL,<
	JRST	INTXIT
CHNID4:	JUMPLE	F,INTXIT	;GO IF NO DIAG WAITING
	MOVE	U,KONCUA##(J)	;UNIT WHICH INTERRUPTS
	SKIPL	T1,UNI2ND##(U)	;DIAG - DUAL PORTED DRIVE ?
	JRST	INTXIT
	HRRZ	T2,UNICHN##(T1)	;YES, IS 2ND PORT ON RIGHT CHAN ?
	SKIPGE	(T2)		; AND IS THAT CHAN IDLE ?
	CAME	T2,DIACHN##
	JRST	INTXIT		;NO, CAN'T START THE DIAG JOB
	HRRZ	T2,T1
	MOVEI	T3,PCOD##	;YES, ARE ALL UNITS ON THE CHAN IDLE ?
	MOVEI	T4,SCOD##
CHNID5:	CAME	T3,UNISTS##(T1)
	CAMN	T4,UNISTS##(T1)
	JRST	INTXIT		;NO, CAN'T START THE DIAG JOB
	HLRZ	T1,UNICHN##(T1)
	CAME	T1,T2
	JRST	CHNID5
	JRST	CHNID3		;YES, TAKE DIAG JOB OUT OF DIOW QUEUE
>
IFE FTDUAL,<
CHNID4:
>
>
INTXIT:
IFN FTKL10,<
	MOVSI	T1,CP.SWF##	;CLEAR SWEEP FLAG
	ANDCAM	T1,CHB22B##(P1)	;FOR NEXT INTERRUPT
>;END IFN FTKL10
	HRRZ	T1,CHNIFP##(P1)	;RESET FAIRNESS COUNTS IF THEY HAVE GONE NEGATIVE
	SKIPG	CHNCFP##(P1)	;CURRENT FAIRNESS COUNT FOR POSITIONING OPTIMIZATION
	MOVEM	T1,CHNCFP##(P1)
	HRRZ	T1,CHNIFT##(P1)
	SKIPG	CHNCFT##(P1)	;CURRENT FAIRNESS COUNT FOR TRANSFER OPTIMIZATION
	MOVEM	T1,CHNCFT##(P1)
	POPJ	P,		;AND DISMISS THE INTERRUPT


IFN FTKL10,<
;SUBROUTINE TO SWEEP CACHE
;CALL WITH P1=LOC OF CHANNEL DATA BLOCK
;PRESERVES ALL ACS  EXCEPT T1
CFDMP:
IFN FTKL10,<
	MOVE	T1,CHB22B##(P1)
	TLNN	T1,CP.RH2##	;IS IT AN RH20?
>
	SKIPE	DINITF##	; OR ARE WE IN ONCE-ONLY
	POPJ	P,		;YES, NO NEED TO SWEEP
	JRST	CSDMP##		;NO, SWEEP CACHE
>
UNIDWN:	PUSHJ	P,UNIYNK
	PJRST	PIKTR1		;START NEXT I/O IF CHAN NOT IDLE
				; IF IDLE, EXIT THE INTERRUPT
UNIYNK:
IFN FTDBAD,<
	PUSHJ	P,BADUN6	;FINISH THIS OPERATION, START NEXT
>
	MOVE	U,DEVFUN##(F)	;UNIT ADDR IS IN DEVFUN IF DEVUNI=0
	SKIPE	T1,UNIQUE##(U)	;MORE POSITIONS WAITING?
	MOVEI	T1,PWCOD##	;YES. RETURN UNIT TO PW STATE
	MOVEM	T1,UNISTS##(U)	;SET UNIT IDLE OR PW
	POPJ	P,




IFN FTDSIM,<
;HERE TO EXIT WITHOUT WRITING THE DISK
NXTIO:	MOVE	S,DEVIOS(F)	;SET UP S FOR STDIOD
	PUSHJ	P,STDIOD##	;WAKE UP JOB
	PUSHJ	P,CLRACT##	;NO LONGER IO ACTIVE
	DSKOFF
	CONSO	PI,PIPROG##	;#IF ON UUO LEVEL,
	JRST	[SETOM	@KONCHN##(J)  ;#INDICATE CHAN IS IDLE
		DSKON		;#TURN ON DSK PI
		POPJ	P,]	;#AND RETURN WITHOUT WIPING IT
IFN FTDUAL,<
	SKIPGE	T1,UNI2ND##(U) ;2ND PORT?
	HRR	U,T1		;YES, GO BACK TO MAIN
>
	SKIPE	T1,UNIQUE##(U)	;UNIT IS IDLE OR PW
	MOVEI	T1,PWCOD##
	MOVEM	T1,UNISTS##(U)
	JUMPE	T1,PIKTR1	;LOOK FOR A TRANSFER TO START IF NOT PW
	SETOM	UNICYL##(U)	;ENSURE A SEEK HAPPENS (ELSE COULD PDL OV)
	JRST	PIKPOS		; AND START A SEEK GOING
>
FILEND:	END