!TITLE Creating and Connecting Files
!
! [Maintenance Note]
! In addition to procedures for creating and connecting files,this module
! also contains the procedure to complete the loading process and hence
! must come first in the load module.  DIRFIX has to plant a jump
! instruction of the form X1B8000nn at the word 16 bytes from the start
! of the object file. This jump takes control to the first instruction
! in DIRLDR. nn is a number of half words, current value X32. The first
! two instructions in DIRLDR are X5883 and X6E09 respectively. DIRFIX
! also puts the length of the GLAP into the header.  On entry, B either
! contains the address of a record or, for an entry to SIGNAL, is 0.
!<BADPAGE
!%externalintegerfn BAD PAGE(%integer TYPE, FSYS, BITNO)
!
! This can be called by a privileged process with TYPE = 4 to clear bit
! BITNO in the bad pages list on fsys FSYS.  The result returned is the
! original value of the bit.
!>
!
! sorry about that, its for VIEW you know !
!
ROUTINE  DIRLDR
EXTERNALROUTINESPEC  DIRECTOR(INTEGER  DR0,DR1)
CONSTINTEGER  BDESC=X'18000020'

EXTERNALROUTINESPEC  SIGNAL
INTEGER  CODES, DR1
      *STD_(LNB +3); ! PLT DESCRIPTOR
      *ASF_9; ! ROOM FOR CODES
      *JAF_12,<DIRECT>; ! jump if B non-zero
!
      SIGNAL
      *OUT_0
DIRECT:
      *JLK_1
      *LSS_TOS 
      *USH_-18
      *USH_18
      *ST_CODES; ! start of code segment
!
      *LXN_CODES
      *LDTB_X'18000000'
      *LDB_(XNB +7); ! length of GLAP
      *LDA_(XNB +6); ! start of GLAP
      *INCA_CODES
      *CYD_0
      *LDA_(LNB +4); ! start of GLA
      *MV_L =DR ; ! copy GLAP to GLA
!
      *LSS_B 
      *ST_DR1
      DIRECTOR(BDESC, DR1)
      *OUT_0
END 
!
!
!
CONSTINTEGER  ALLOW IC INTS = X'FFFFF7FF'
CONSTINTEGER  ALLOW STACK = X'3000'
CONSTINTEGER  BADKEY = 6
CONSTINTEGER  BASEFILE SEG = 32
CONSTINTEGER  BATCH = 2; ! reason for STARTP
CONSTINTEGER  BITKEY = 0; ! SYSAD
CONSTINTEGER  CHERSH = 16; ! CODES
CONSTINTEGER  COMMS = 16; ! CODES2
CONSTINTEGER  DDTSTATES = X'3F0'; ! ie states 4 - 9 valid
CONSTINTEGER  DEFAULT MAXFILE = 1024; ! 1megabyte
CONSTINTEGER  DEFAULT MAXKB = X'8000'; ! 32 megabytes
CONSTINTEGER  DIRDACT = 5; ! special director async messages
CONSTINTEGER  DT = 1
CONSTINTEGER  ENDLIST = 255
CONSTINTEGER  EPAGE SIZE = 4
CONSTINTEGER  FEP OUTPUT REPLY MESS = 51
CONSTINTEGER  GRACE KIC = 3072; ! i.e. 3million instructions
CONSTINTEGER  HI D SEG = 31
CONSTINTEGER  INH IC INTS = X'800'; ! bit 20
CONSTINTEGER  INTER = 0; ! reason for STARTP
CONSTINTEGER  LEAVE = 8
CONSTINTEGER  LO D SEG = 16
CONSTINTEGER  LOG = 2; ! route PRINTSTRING to MAINLOG
CONSTINTEGER  LOUSEG = BASEFILE SEG
CONSTINTEGER  NNTKEY = 1; ! SYSAD
CONSTINTEGER  NO = 0
CONSTINTEGER  NO ARCH = 128
CONSTINTEGER  NORMAL STACK SEG = 4
CONSTINTEGER  NOT SLAVED = 1
CONSTINTEGER  OFFER = 2; ! CODES
CONSTINTEGER  OLDGE = 4; !CODES2
CONSTINTEGER  PON AND CONTINUE = 6
CONSTINTEGER  PRIVBIT = X'00040000'
CONSTINTEGER  READ ACR = 5
CONSTINTEGER  RESERVED = 1
CONSTINTEGER  STACK = 64; ! CODES2
CONSTINTEGER  STREAM CONTROL MESSAGE = X'370007'
CONSTINTEGER  TEMPFI = 4; ! CODES
CONSTINTEGER  TEMPFS = 12
CONSTINTEGER  TOP I VALUE = 5
CONSTINTEGER  TOP J VALUE = 512
CONSTINTEGER  UNAVA = 1; ! CODES
CONSTINTEGER  USER STOPS DACT = 67
CONSTINTEGER  VEC128 = X'38000000'
CONSTINTEGER  VIOLAT = 64; ! CODES
CONSTINTEGER  VTEMPF = 8; ! CODES
CONSTINTEGER  WRCONN = 1; ! CODES2
CONSTINTEGER  WSALLOW = 8; ! CODES2
CONSTINTEGER  YES = 1
! %CONSTINTEGERNAME EXPRESS = X'001C0048' ! WORD 18 OF SEG 7
!
!
!
      RECORDFORMAT  C 
ACF(LONGINTEGER  MUSECS, INTEGER  PTRNS, KINSTRS)
      RECORDFORMAT  C 
CBTF(INTEGER  DA, HALFINTEGER  AMTX, BYTEINTEGER  TAGS, LINK)
      RECORDFORMAT  C 
DRF(INTEGER  DR0, DR1)
      RECORDFORMAT  C 
PROPF(INTEGER  TRACKS, CYLS, PPERTRK, BLKSIZE, TOTPAGES, C 
      RQBLKSIZE, LBLKSIZE, ALISTSIZE, KEYLEN, SECTINDX)
      RECORDFORMAT  C 
SCTIF(INTEGER  DR0, DR1); ! horizontal, I-vector, format
      RECORDFORMAT  C 
SCTJF(INTEGER  TYPE, ACR, DRDR0, DRDR1); ! vertical, J-vector, format
!
INCLUDE  "PD22S_C03FORMATS"
!
!
      EXTERNALINTEGERFNSPEC  C 
ADESTROY(STRING (31)FULL, STRING (8)DATE, INTEGER  FSYS, TYPE)
      EXTERNALROUTINESPEC  C 
COMMSCLOSEDOWN
      EXTERNALROUTINESPEC  C 
DAP INTERFACE(INTEGER  ACT)
      EXTERNALINTEGERFNSPEC  C 
DDAP(INTEGERFN  A(INTEGER  A, B, C), INTEGER  ACT, ADR)
      EXTERNALINTEGERFNSPEC  C 
DDELAY(INTEGER  N)
      EXTERNALROUTINESPEC  C 
DERR2(STRING (31) S,INTEGER  FN, ERR)
      EXTERNALINTEGERFNSPEC  C 
DMAGCLAIM(STRING (6)TSN, INTEGERNAME  SNO, INTEGER  REQ, MODE)
      EXTERNALROUTINESPEC  C 
DOPER2(STRING (255)S)
      EXTERNALROUTINESPEC  C 
DOUTI(RECORD (PARMF)NAME  P)
      EXTERNALINTEGERFNSPEC  C 
DPON3I(STRING (6) USER,RECORD (PARMF)NAME  P, C 
      INTEGER  INVOC,MSGTYPE,OUTNO)
      EXTERNALROUTINESPEC  C 
DPONI(RECORD (PARMF)NAME  P)
      EXTERNALROUTINESPEC  C 
DREPORT(STRING (255)TEMPLATE)
      EXTERNALROUTINESPEC  C 
DRESUME(INTEGER  LNB, PC, ADR18)
      ROUTINESPEC  C 
DSTOP(INTEGER  N)
      EXTERNALINTEGERFNSPEC  C 
DUNLOCK(INTEGER  ADR)
      INTEGERFNSPEC  C 
FBASE2(INTEGER  FSYS, ADR)
      EXTERNALINTEGERFNSPEC  C 
FILE INDEX PERM(STRING (31)INDEX, INTEGER  FSYS)
      INTEGERFNSPEC  C 
HINDA(STRING (6)USER, INTEGERNAME  FSYS, INDAD, INTEGER  TYPE)
      EXTERNALINTEGERFNSPEC  C 
MOVESECTION(INTEGER  FSYS1, STARTP1, FSYS2, STARTP2, EPGS)
      EXTERNALINTEGERFNSPEC  C 
NEWAINDA(STRING (18)INDEX, INTEGER  FSYS, INTEGERNAME  AFINDAD)
      EXTERNALINTEGERFNSPEC  C 
S11OK(STRINGNAME  S11)
      EXTERNALROUTINESPEC  C 
STOP FEPS
      EXTERNALROUTINESPEC  C 
SYMBOLS(INTEGER  N, SYMBOL)
      EXTERNALINTEGERFNSPEC  C 
UFO(STRING (31)USER, FILE, STRINGNAME  UNAME, INAME, FNAME, INDEX, FULL)
      EXTERNALINTEGERFNSPEC  C 
UIO(STRING (31)USER, STRINGNAME  UNAME, INAME, INDEX)
!
!
!
!
EXTRINSICINTEGER  ACCTSA
EXTRINSICINTEGER  AEXPRESS
EXTRINSICINTEGER  AQD
EXTRINSICINTEGER  AREVS
EXTRINSICINTEGER  ASYNC INHIB 
EXTRINSICINTEGER  BLKSI
EXTRINSICINTEGER  CBTA0
EXTRINSICINTEGER  CBTASL0
EXTRINSICINTEGER  DAP STATE
EXTRINSICINTEGER  D CALLERS ACR; ! SET BY FN IN2
EXTRINSICINTEGER  D CALLERS PSR
EXTRINSICINTEGER  DEFAULT SS ACR 
EXTRINSICINTEGER  DINSTRS
EXTRINSICINTEGER  DIRFLAG
EXTRINSICINTEGER  DIRFN
EXTRINSICINTEGER  DIRLEVEL
EXTRINSICINTEGER  DIRLOGAD 
EXTRINSICINTEGER  DIRMON
EXTRINSICINTEGER  D TRYING 
EXTRINSICINTEGER  ENDSST
EXTRINSICINTEGER  FACILITYA
EXTRINSICINTEGER  FSYS WARN
EXTRINSICINTEGER  GOTSEMA
EXTRINSICINTEGER  HISEG
EXTRINSICINTEGER  HOTTOPA
EXTRINSICINTEGER  HOTTOPN
EXTRINSICINTEGER  INVOC; ! >=0
EXTRINSICINTEGER  LOG ACTION 
EXTRINSICINTEGER  OUTPAD
EXTRINSICINTEGER  OWNIND
EXTRINSICINTEGER  PAGEMON
EXTRINSICINTEGER  PROCESS
EXTRINSICINTEGER  PROCFSYS 
EXTRINSICINTEGER  SAINDAD
EXTRINSICINTEGER  SCTIAD
EXTRINSICINTEGER  SEMADDRHELD
EXTRINSICINTEGER  SEMANOHELD
EXTRINSICINTEGER  SESSINSTRS 
EXTRINSICINTEGER  SESSKIC 
EXTRINSICINTEGER  SIGMO 
EXTRINSICINTEGER  SST0
EXTRINSICINTEGER  TAPES CLAIMED
OWNINTEGERARRAY  DIRFNS(1:16)
!
!
!
CONSTRECORD (DIROWNF)NAME  DIROWN = X'C3000'
CONSTRECORD (UINFF)NAME  UINF = 9<<18
!
EXTRINSICSTRING (23)LOUTPSTATE
EXTRINSICSTRING ( 6)PROCUSER 
EXTRINSICSTRING (18)SELECTED INDEX
EXTRINSICSTRING (127)SELECTED NODE
EXTRINSICRECORD (FDF)LASTFD
EXTRINSICRECORD (PARMF)LOUTP
!
!
!
RECORDFORMAT  STE(INTEGER  APFLIM,USERA)
OWNRECORD (STE)ARRAYFORMAT  STF(0:255)
OWNRECORD (STE)ARRAYNAME  ST
!
RECORDFORMAT  HOTTOPF(STRING (18)INDEX, BYTEINTEGER  FSYS, INTEGER  PT)
OWNRECORD (HOTTOPF)ARRAYFORMAT  HOTTOPFS(0:1023)
OWNRECORD (HOTTOPF)ARRAYNAME  HOTTOP
OWNINTEGER  HOTTOPADR = 0
!
!-----------------------------------------------------------------------
!
OWNRECORD (CBTF)ARRAYFORMAT  CBTAF(0:512)
OWNRECORD (CBTF)ARRAYNAME  CBTA
OWNINTEGER  CBT1, CBT2, CBTOP
OWNBYTEINTEGERARRAYNAME  SSTB
OWNHALFINTEGERARRAYNAME  SSTH
OWNBYTEINTEGERARRAYFORMAT  BIFT(0:255)
OWNHALFINTEGERARRAYFORMAT  HIFT(0:1023)
OWNINTEGERARRAYFORMAT  IFT(0:254)
!
EXTRINSICBYTEINTEGERARRAY  FSYS USECOUNT(0:99)
!
OWNINTEGER  SEARCHENT=0; ! CONLIST(0) is not used, and CONLIST(1) contains "reserved".
EXTERNALRECORD (DRF)ARRAY  DRS LOCKED(0:2)
OWNINTEGERARRAY  SEGUSE(16:31) = -2(16); ! TO RECORD USE OF DIRECTORS INDEX SEGMENTS
OWNRECORD (ACF)NAME  ACCTS
OWNRECORD  (ACF)PREVA
!
!
OWNINTEGER  SESSION PENCE = -1
!
!
!-----------------------------------------------------------------------
!
ROUTINE  DMONW(BYTEINTEGER  FLAG, SEG, STRING (63)FILE)
INTEGER  J
RECORDFORMAT  HF(INTEGER  NEXT, RELST, MAX, A, B, C, CYCLIC, C 
      READ)
RECORD (HF)NAME  H
      RETURN  IF  PAGE MON = 0
      H == RECORD(PAGE MON)
      J = H_NEXT
      RETURN  UNLESS  J+16 < H_MAX; ! not enough for 3 long ints
      H_NEXT = J + 24
      J = PAGE MON + J
      BYTEINTEGER(J) = FLAG
      BYTEINTEGER(J+1) = SEG
      LENGTH(FILE) = 21 IF  LENGTH(FILE) > 21
      STRING(J+2) = FILE
END 
!
!
!
EXTERNALSTRINGFN  HTOS(INTEGER  VALUE, PLACES)
INTEGER  I
STRING (8)S
CONSTBYTEINTEGERARRAY  H(0:15) = C 
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
      PLACES = 8 IF  PLACES > 8
      I = 64 - 4 * PLACES
      *LD_S
      *LSS_PLACES
      *ST_(DR )
      *INCA_1
      *STD_TOS 
      *STD_TOS 
      *LSS_VALUE
      *LUH_0
      *USH_I
      *MPSR_X'24'
      *SUPK_L =8
      *LD_TOS 
      *ANDS_L =8,0,15
      *LSS_H+4
      *LUH_X'18000010'
      *LD_TOS 
      *TTR_L =8
      RESULT  = S
END 
!
!-----------------------------------------------------------------------
!
EXTERNALSTRINGFN  ITOS(INTEGER  N)
STRING (16)S
INTEGER  D0, D1, D2, D3
      *LSS_N
      *CDEC_0
      *LD_S
      *INCA_1
      *CPB_B 
      *SUPK_L =15,0,32
      *STD_D2
      *JCC_8,<WASZERO>
      *LSD_TOS 
      *ST_D0
      *LD_S
      *INCA_1
      *MVL_L =15,15,48
      BYTEINTEGER(D1) = '-' AND  D1 = D1 - 1 IF  N < 0
      BYTEINTEGER(D1) = D3 - D1 - 1
      RESULT  = STRING(D1)
WASZERO:
      RESULT  = "0"
END 
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  EQUAL(STRINGNAME  NAME1, NAME2)
INTEGER  L, A1, A2
!     result is 1 if equivalent else 0
!
      L = LENGTH(NAME1)
      RESULT  = 0 UNLESS  L = LENGTH(NAME2)
      RESULT  = 1 IF  L = 0; ! used to compare index names which are ususally 0
!
      A1 = ADDR(NAME1) + 1
      A2 = ADDR(NAME2) + 1
!
      *LDTB_X'18000000'
      *LDB_L
      *LDA_A1
      *STD_TOS 
      *LSD_TOS 
      *LDA_A2
      *CPS_L =DR ,32,0
      *JCC_8,<EQUIV>
      RESULT  = 0
EQUIV:
      RESULT  = 1
END ; ! EQUAL
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  WRS(STRING (255)S)
      PRINTSTRING(S)
      NEWLINE
END ; ! OF WRS
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  WRSS(STRING (255)S1, S2)
      PRINTSTRING(S1)
      PRINTSTRING(S2)
      NEWLINE
END ; ! OF WRSS
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  WRSNT(STRING (255)S, INTEGER  N, TYPE)
INTEGER  J
SWITCH  SW(0:3)
!
!     type & 3 = 0 decimal if small else hex
!                1 decimal
!                2 hex
!                3 decimal and hex
!     type & 4 = 1 dont put NL at end
!     type & X70 gives number of digits, 0=8
      PRINTSTRING(S)
      SPACE
      -> SW(TYPE & 3)
SW(0):   -> SW(2) UNLESS  -256 <= N <= 255
SW(3):
SW(1):   PRINTSTRING(ITOS(N))
         -> OUT IF  TYPE & 2 = 0
SW(2):   PRINTSTRING("X'")
         J = TYPE >> 4 & 7
         J = 8 IF  J = 0
         PRINTSTRING(HTOS(N, J))
OUT:
      NEWLINE IF  TYPE & 4 = 0
END ; ! OF WRSNT
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  WRSN(STRING (255)S, INTEGER  N)
      PRINTSTRING(S)
      SPACE
      WRITE(N, 1)
      NEWLINE
END ; ! WRSN
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  WRS3N(STRING (255)S1, S2, S3, INTEGER  N)
      PRINTSTRING(S1); SPACE
      PRINTSTRING(S2); SPACE
      PRINTSTRING(S3); SPACE
      WRITE(N, 1)
      NEWLINE
END ; ! WRS3N
!
!-----------------------------------------------------------------------
!
ROUTINE  PRX(INTEGER  I,PL)
      SPACES(1)
      PRINTSTRING(HTOS(I,PL))
END ; ! PRX
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  ACTIVE BLOCK(STRING (31)FN, FULL, INTEGER  FSYS)
      WRS3N(FN, " BLOCK STILL ACTIVE ", FULL, FSYS)
!      DIRMON = -1
END ; ! OF ACTIVE BLOCK
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  SST(INTEGER  N)
      IF  CBTOP > 255 C 
      THEN  RESULT  = SSTH(N) C 
      ELSE  RESULT  = SSTB(N)
END 
!
!
!
EXTERNALROUTINE  SETSST(INTEGER  N, VALUE)
      IF  CBTOP > 255 C 
      THEN  SSTH(N) = VALUE C 
      ELSE  SSTB(N) = VALUE
END 
!
!
!
EXTERNALROUTINE  INIT CBT
!    
!     With BLOCKSIZE=32 Epages (128K) a segment requires at most 2 CBT
!     entries. The CBT is therefore arranged with all single entries
!     packed at the front and all double entries packed at the back.
!     Two pointers are maintained, CBT1 and CBT2 to the single and
!     double entries adjacent to the free space in the middle. There
!     is always at least one single DIRGLA and one double DIRCODE entry.
!     The CBT is re-organised to this form at the first call of 
!     DISC SEG CONNECT (called by DIRECTOR to connect the SCT). We
!     assume the initial status of the CBT and SST is
!           SEGS 0, 1, 5 to 255 not used
!           SEG2, DIRCODE, CBT entries 0 and 1
!           SEG3, DIRGLA, CBT enty 2
!           SEG4, normal stack segment(32 to 63 Epages) CBT entry 3
!           CBTASL points to CBT entry 4, the head of the free chain
!
!
!     Intend to replace 'LINK' by 'FLAGS'
!     where
!        2**7 set=this block is a continuation block
!           6     advisory sequential, all blocks of file
!
INTEGER  THIS, NEXT
!
!
      ST == ARRAY(0, STF)
      SSTB == ARRAY(SST0, BIFT)
      SSTH == ARRAY(SST0, HIFT)
      CBTA == ARRAY(CBTA0, CBTAF)
      IF  CBTASL0 >> 18 = 0 C 
      THEN  NEXT = CBTASL0 C 
      ELSE  NEXT = INTEGER(CBTASL0)
!
      IF  NEXT > 4 THEN  THIS = NEXT ELSE  START 
         WHILE  NEXT # ENDLIST CYCLE 
            THIS = NEXT
            CBTA(THIS)_DA = 0
            NEXT = CBTA(NEXT)_LINK
         REPEAT 
      FINISH 
!
      CBT2 = THIS - 1; ! LAST DOUBLE ENTRY
      ENDSST = 255
      ENDSST = X'FFFF' IF  CBT2 > 255
      CBTOP = CBT2 - 2; ! WHERE TO START LOOKING FOR A FREE DOUBLE ENTRY
      CBT1 = 1; ! LAST SINGLE ENTRY
!
      RETURN  IF  SST(3) = 0; ! New format has been set up by Supervisor
!
      CBTA(CBT2) = CBTA(0); ! DIRCODE SEGMENT - 2 ENTRIES
      CBTA(CBT2)_LINK = 0
      CBTA(CBT2+1) = CBTA(1)
      CBTA(CBT2+1)_LINK = 128; ! continuation block
      SETSST(2, CBT2)
!
      CBTA(0) = CBTA(2); ! DIR GLA - 1 ENTRY
      CBTA(0)_LINK = 0
      SETSST(3, 0)
!
      CBTA(1) = CBTA(3); ! STACK
      CBTA(1)_LINK = 0
      SETSST(4, 1)
!
! OBSERVE THAT THERE WILL ALWAYS BE AT LEAST ONE SINGLE (DIRGLA) AND
! ONE DOUBLE (DIRCODE) CBT ENTRY
END ; ! OF INIT NEW CBT
!
!-----------------------------------------------------------------------
!
INTEGERFN  GET1
INTEGER  C
      C = 2; ! LOWEST POSSIBLE FREE ENTRY
LOOP:
      -> NO UNLESS  CBTA(C)_DA = 0
      CBT1 = C IF  CBT1 < C
      CBTA(C)_LINK = 0
      RESULT  = C
NO:
      C = C + 1
      -> LOOP UNLESS  C >=  CBT2
      RESULT  = -1
END 
!
!
!
INTEGERFN  GET2
INTEGER  C
      C = CBTOP; ! HIGHEST POSSIBLE FREE ENTRY
LOOP:
      -> NO UNLESS  CBTA(C)_DA = 0 AND  CBTA(C+1)_DA = 0
      CBT2 = C IF  CBT2 > C
      CBTA(C)_LINK = 0
      CBTA(C+1)_LINK = 0
      RESULT  = C
NO:
      C = C - 2
      -> LOOP UNLESS  C <= CBT1
      RESULT  = -1
END 
!
!-----------------------------------------------------------------------
!
ROUTINE  RECOVER CBT(INTEGER  C)
!
      CBTA(C)_DA = 0
      CBTA(C+1)_DA = 0 UNLESS  C < CBT2
END ; ! OF RECOVER CBT
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  FILL(INTEGER  LENGTH,FROM,FILLER)
      RETURN  UNLESS  LENGTH > 0
!
      *LDTB_X'18000000'
      *LDB_LENGTH
      *LDA_FROM
      *LB_FILLER
      *MVL_L =DR 
END ; ! OF FILL
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  MOVE(INTEGER  LENGTH, FROM, TO)
      RETURN  UNLESS  LENGTH > 0
!
      *LDTB_X'18000000'
      *LDB_LENGTH
      *LDA_FROM
      *CYD_0
      *LDA_TO
      *MV_L =DR 
END ; ! OF MOVE
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  PACKDT
                                        ! Result is DATE and TIME packed
                                        ! into a word :
                                        ! Y-70(6)M(4)D(5)H(5)M(6)S(6)
BYTEINTEGERARRAYNAME  X
BYTEINTEGERARRAYFORMAT  XF(0:255)
      X == ARRAY(X'80C00040', XF)
!
      RESULT  = (10*X(6) + X(7) - 86) << 26 ! C 
         (10*(X( 3)&15)+(X( 4)&15)) << 22 ! C 
         (10*(X( 0)&15)+(X( 1)&15)) << 17 ! C 
         (10*(X(12)&15)+(X(13)&15)) << 12 ! C 
         (10*(X(15)&15)+(X(16)&15)) <<  6 ! C 
         (10*(X(18)&15)+(X(19)&15))
END ; ! OF PACKDT
!
!-----------------------------------------------------------------------
!
INTEGERFN  DRANDOM(INTEGER  N)
                                        ! PRODUCES A RANDOM NUMBER IN RANGE 0-N
                                        ! WHERE N IS 2**M - 1
      *RRTC_0
      *STUH_B 
      *AND_N
      *EXIT_-64
END ; ! OF DRANDOM
!
!-----------------------------------------------------------------------
!
INTEGERFN  DIRAPF
INTEGER  ACR
      *LSS_(LNB +1); ! BITS 8-11 OF THIS WORD HAVE CURRENT ACR
      *ST_ACR
      ACR=(ACR>>20) & 15
      RESULT =X'100' ! (ACR<<4) ! READ ACR
END ; ! DIRAPF
!
!-----------------------------------------------------------------------
!
 EXTERNALROUTINE  GIVE APF(INTEGERNAME  SAPF,NOTDRUM,SLAVED,  C 
   INTEGER  SEG)
! ASSUMES SEG CONNECTED. RETURNS APF OF THE SEG, AND NOTDRUM IS SET
! NON-ZERO IF FILE IS NOT TO HAVE DRUM ALLOCATION, ELSE ZERO
INTEGER  TAGS,CELL
      CELL=SST(SEG)
      TAGS=CBTA(CELL)_TAGS
      SAPF=(ST(SEG)_APFLIM>>20) & X'1FF'
      NOTDRUM=TAGS & B'01000000'
      SLAVED=ST(SEG)_APFLIM & X'20000000'; ! NON-SLAVED BIT
END ; ! GIVE APF
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  HASH(STRING (6)USER, INTEGER  NNTHASH)
INTEGER  A, J, W
CONSTINTEGERARRAY  P(1:6) = 157, 257, 367, 599, 467, 709
      A = ADDR(USER)
      W = 0
      CYCLE  J = 1, 1, 6
         W = W + (BYTEINTEGER(A+J) - 47) * P(J)
      REPEAT 
      W = W - (W//NNTHASH)*NNTHASH
      W = W + NNTHASH IF  W < 0; ! just in case gash characters get through
      RESULT  = W
END ; ! HASH
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  VAL(INTEGER  ADR,LEN,RW,PSR)
! RESULT = 1  AREA OK (ACCESSIBLE)
!          0  AREA NOT OK (INACCESSIBLE)
!
! RW SHOULD BE SET  0  (READ ACCESS)
!               OR  1  (WRITE ACCESS)
!
! PARAM PSR IS USED IN THE VALIDATE, BUT IF ZERO, THE
! PSR OF THE CALLING ROUTINE IS USED
INTEGER  INSEG0,BEYOND SEG0,SEG0,SEG0 AD
INTEGER  DR0
      SEG0=ADR>>18
      RESULT =0 UNLESS  0<LEN<=32<<18; ! reject unreasonable values outright (e.g. > 32 segments)
      IF  PSR=0 START ; *LSS_(LNB +1); *ST_PSR; FINISH 
      IF  SEG0 # (ADR+LEN-1)>>18 START 
         SEG0 AD=SEG0<<18
         INSEG0=X'40000' - (ADR-SEG0 AD)
         BEYOND SEG0=LEN - INSEG0
         RESULT =VAL(ADR,INSEG0,RW,PSR) &  C 
            VAL(ADR+INSEG0,BEYOND SEG0,RW,PSR)
         FINISH 
! WE SHOULD ALSO CHECK THAT THE AREA LIES WITHIN USER SEGMENTS, AND
! NOT IN ANY HIGHER ACR SEGMENTS AS WELL.
      DR0=X'18000000' ! LEN
      *LDTB_DR0
      *LDA_ADR
      *VAL_PSR
      *JCC_8,<CCZER>
      *JCC_4,<CCONE>
      *JCC_2,<CCTWO>
! THEN CC=3, INVALID
      RESULT =0
CCZER:      ! READ AND WRITE PERMITTED
      RESULT =1; ! OK
CCONE:      ! READ, BUT NOT WRITE, PERMITTED
      IF  RW=1 THEN  RESULT =0; ! BAD
      RESULT =1; ! OK
CCTWO:      ! WRITE, BUT NOT READ, PERMITTED
      RESULT =0; ! BAD
END ; ! VAL
!
!-----------------------------------------------------------------------
!
ROUTINE  SETIC(INTEGER  KINSTRUCTIONS)
      INTEGER(AREVS)=KINSTRUCTIONS>>14
      KINSTRUCTIONS=KINSTRUCTIONS<<18>>8
      *LSS_KINSTRUCTIONS
      *ST_(6); ! IMAGE STORE 6 = IC
END ; ! SETIC
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  GETIC
INTEGER  INSTRUCTIONS, CARRYREVS, NET
      *LSS_(6); ! IMAGE STORE 6 = IC
      *ST_INSTRUCTIONS
      CARRYREVS = (INSTRUCTIONS >> 24) & 1
      NET = INTEGER(AREVS) - CARRYREVS
      RESULT  = (NET << 14) ! (INSTRUCTIONS << 8 >> 18); ! ie thousands of instructions
END ; ! GETIC
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  DDAYNUMBER
! gives a number which increases by 1 each day.  mod 7 -> mon=0 etc
CONSTLONGINTEGER  JMS = X'141DD76000'
      *RRTC_0
      *USH_-1
      *SHS_1
      *USH_1
      *IDV_JMS
      *STUH_B 
      *EXIT_-64
END ; ! DDAYNUMBER
!
!-----------------------------------------------------------------------
!
INTEGERFN  GETIT
INTEGER  ITUNITS,J
IT AGAIN:
      *LSS_(3); ! SSR
      *ST_J
      *LSS_(5); ! IT REGISTER
      *ST_ITUNITS
      ! This bit should not be set ever, when Director is executing, because
      ! the interrupt isn't then masked and an interrupt should take control
      ! immediately to the local controller. However, the bit has repeatedly
      ! been observed here on 2960 and 2970.
      IF  ITUNITS>>24#0 THEN  -> IT AGAIN
      RESULT =COM_ITINT*ITUNITS; ! IE. MICROSECONDS
END ; ! GETIT
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  FUNDS(INTEGERNAME  GPINDAD,INTEGER  INDAD)
! This function delivers the no of resource units to which the user owning
! index at INDAD has access. If the user has a "group resource-unit" holder,
! the parameter GPINDAD is set to the address of the group-holder's index 
! (else to the value of INDAD).
INTEGER  FSYS,J, IAD, GPIAD, NUTS
STRING (31) GPH
RECORD (HF)NAME  NH
      IAD = INDAD
L:
      NH == RECORD(IAD)
      NUTS = NH_INUTS
      -> OUT IF  NH_GPHOLDR = ""
      NUTS = 0
      GPH <- NH_GPHOLDR
      FSYS = NH_GPFSYS
      J = HINDA(GPH, FSYS, GPIAD, 0)
      UNLESS  J = 0 START 
         FSYS = -1
         J = HINDA(GPH, FSYS, GPIAD, 0)
         NH_GPFSYS = FSYS IF  J = 0
      FINISH 
!
      -> OUT IF  J > 0
      IAD = GPIAD
      -> L
OUT:
      GPINDAD = IAD
      RESULT  = NUTS
END ; ! FUNDS
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  IUPDATE(INTEGER  MODE,NEWCOUNT)
!  MODE  =  -2   INITIALISE INDEX FOR SESSION
!  MODE  =  -1   OUT OF DIRECTOR
!            0   IN OR INTO USER
!            1   INTO DIRECTOR
!
! WHEN CALLED WITH MODE -1, DO NOT UPDATE DIRECTOR RECORD UNLESS
! PREVIOUS MODE WAS 1 (WE DO NOT ATTEMPT TO RECORD EVERY EXIT FROM
! DIRECTOR).
RECORD (HF)NAME  NH
LONGINTEGER  TSINCELAST,TNOW
INTEGER  PSINCELAST,ISINCELAST,PNOW,INOW,J,NEWT,NEWP,NEWC,NEWI,MSG
INTEGER  DPENCE,CT SINCELAST,GPINDAD
INTEGERNAME  HINSTRS,HPTRNS,HMSECS,HCONNECTT
OWNINTEGER  INIT=0,PREV FRMN
OWNINTEGER  PFACTOR = 0; ! TO IMPLEMENT PRIORITY IN ACCOUNTING FIELDS
!
!
!
      RETURN  IF  OWNIND=0; ! SKIP UNTIL OWNIND SET UP AND FOR PROCESS1
!
      MSG=0
      NH == RECORD(OWNIND)
! I had to put this INIT in when we started to put the PREVIC value
! into the UINF segment: we call IUPDATE lots of times before getting
! that segment set up.
      IF  MODE=-3 START 
         J=PACKDT
!
         SIGMO = NH_SIGMON
         NH_SIGMON = 0
         IF  UINF_REASON = INTER C 
         THEN  NH_LASTLOGON = J C 
         ELSE  NH_LAST NON INT START = J
!
         INIT=1
         RETURN 
      FINISH 
!
      IF  MODE=-2 START 
         ACCTS==RECORD(ACCTSA)
         INTEGER(AREVS)=X'FFFF'
!
         PREVA_MUSECS=ACCTS_MUSECS-GETIT
         PREVA_PTRNS=ACCTS_PTRNS
!
         PREV FRMN=COM_SECSFRMN
         RETURN 
      FINISH 
!
      IF  INIT=0 THEN  RETURN 
!
! Initialisation entries previously completed
!
      IF  SESSION PENCE < 0 START 
         SESSION PENCE = 0; ! Any charges incurred during periods of scarcity
                                        ! are accumulated in this own integer
!
                                        ! initialised to -1
!
         UINF_FUNDS = FUNDS(GPINDAD, OWNIND)
         IF  UINF_REASON = BATCH ANDC 
             UINF_PRIORITY < 3 C 
         THEN  PFACTOR = 1
      FINISH 
!
      HCONNECTT == NH_CONNECTT
      IF  UINF_REASON = BATCH START 
         HINSTRS == NH_BINSTRS
         HPTRNS == NH_BPTRNS
         HMSECS == NH_BMSECS
      FINISH  ELSE  START 
         HINSTRS == NH_IINSTRS
         HPTRNS == NH_IPTRNS
         HMSECS == NH_IMSECS
      FINISH 
!
      INOW=GETIC
      UINF_PREVIC=INOW IF  UINF_PREVIC<INOW; ! PREVIC os zero at initialisation
      ISINCELAST=UINF_PREVIC - INOW
      UINF_PREVIC=INOW
!
      TNOW=ACCTS_MUSECS - GETIT
      TSINCELAST=(TNOW - PREVA_MUSECS)//1000
      ! A process start-up glitch in the sums somewhere:
      IF  0>TSINCELAST>=-200 THEN  TSINCELAST=0
! THE CONDITION BELOW FAILS ONLY IF WE GET TIME-SLICED BETWEEN
! READING IT AND SUBTRACTING THE RESULT OF THE FUNCTION GETIT FROM
! ACCTS_MUSECS
      PREVA_MUSECS=TNOW IF  TNOW>PREVA_MUSECS
!
      PNOW=ACCTS_PTRNS
      PSINCELAST=PNOW - PREVA_PTRNS
      PREVA_PTRNS=ACCTS_PTRNS
      IF  MODE<0 THEN  DINSTRS=DINSTRS+ISINCELAST
      NEWI=HINSTRS + ISINCELAST >> PFACTOR
      NEWT=HMSECS + TSINCELAST >> PFACTOR
      NEWP=HPTRNS + PSINCELAST >> PFACTOR
      CT SINCELAST=COM_SECSFRMN - PREV FRMN
      PREV FRMN=COM_SECSFRMN
      IF  CT SINCELAST<0 THEN  CT SINCELAST=CT SINCELAST+24*60*60
      NEWC=HCONNECTT
      IF  UINF_REASON#BATCH THEN  NEWC=NEWC+CT SINCELAST
! PREVENT OVERFLOW. ACCNTS SHOULD HAVE TAKEN IT BY NOW !
      IF  NEWI>>29#0 THEN  MSG=1 AND  NEWI=0
      IF  NEWT>>29#0 THEN  MSG=2 AND  NEWT=0
      IF  NEWP>>29#0 THEN  MSG=3 AND  NEWP=0
      IF  NEWC>>29#0 THEN  MSG=4 AND  NEWC=0
      HINSTRS=NEWI
      SESSINSTRS=SESSINSTRS + ISINCELAST
      ACCTS_KINSTRS=SESSINSTRS
      HMSECS=NEWT
      HPTRNS=NEWP
      HCONNECTT=NEWC
!
! Decrement Scarcity Ration according to site charging formula if we
! are in a time of scarcity.
      ! IF:
      ! no of interactive users >= current "scarcity" setting
      IF  COM_RATION&255 >= COM_RATION>>24 C 
            AND   C 
         (UINF_REASON=INTER C 
               OR  C 
         (UINF_REASON = BATCH AND  UINF_PRIORITY > 3) ) C 
      START 
!--------------------------------------------------------------------------------
!
!                      Site Formulae
!
!
         DPENCE = 0; ! in hundredths of pence
!
         IF  COM_OCPTYPE = 2 C     {KENT}
         THEN  DPENCE = INT(385 * (I SINCE LAST/COM_KINSTRS + C 
                  P SINCE LAST/250 + CT SINCE LAST/60))
!
         IF  COM_OCPTYPE = 4 C     {2988 see Newsletter July '83}
         THEN  DPENCE = INT(650 * (I SINCE LAST/COM_KINSTRS + C 
                  P SINCE LAST/700))
!
         IF  5 <= COM_OCPTYPE <= 6 C     {2972}
         THEN  DPENCE = INT(850 * (I SINCE LAST/COM_KINSTRS + C 
                  P SINCE LAST/600))
!
!----------------------------------------------------------------------------
!
         SESSION PENCE = SESSION PENCE + DPENCE
!
         J = UINF_FUNDS - DPENCE
         J = 0 IF  J < 0
         UINF_FUNDS = J; ! Note: the group holders figure is not adjusted until end of session
!
      FINISH 
      IF  NEWCOUNT>0 START 
         SETIC(NEWCOUNT)
         UINF_PREVIC=NEWCOUNT
      FINISH 
      IF  MSG#0 THEN  WRSN("IUPDATE", MSG)
END ; ! IUPDATE
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  IN2(INTEGER  FN)
INTEGER  SF, STACKSEG, SEGLIM, LNBHERE, J
      DIRLEVEL = DIRLEVEL + 1
      IF  1 <= DIRLEVEL <= 16 C 
      THEN  DIRFNS(DIRLEVEL) = FN C 
      ELSE  WRSN("IN2: Dirlevel? ", DIRLEVEL) AND  RESULT  = 8
!
      UNLESS  FACILITY A = 0 START 
         J = FACILITY A + (FN&255) << 2
         *LXN_J
         *INCT_(XNB +0)
      FINISH 
!
      *STLN_LNBHERE
      D CALLERS PSR = INTEGER(INTEGER(LNBHERE) + 4)
      D CALLERS ACR = D CALLERS PSR << 8 >> 28
!
      RESULT  = 0 IF  FN > 255; ! set '256' bit for 'dont inhibit'
!
      *LSS_(3)
      *OR_INH IC INTS
      *ST_(3)
!
      ASYNC INHIB = ASYNC INHIB + 1
!
      *STSF_SF; ! CHECK ENOUGH STACK
      STACKSEG = SF >> 18
      SF = SF & X'3FFFC'
      SEGLIM = (ST(STACKSEG)_APFLIM & X'3FF80') ! X'7F'
      RESULT  = 47 UNLESS  SF + ALLOW STACK < SEGLIM
!
      IUPDATE(1, 0)
!
      RESULT  = 0
END ; ! IN2
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  OUT(INTEGER  FLAG, STRING (63)TEMPLATE)
INTEGER  FN, LNB, LNB1, GLA, T
      FN = 0
      IF  1 <= DIRLEVEL <= 16 C 
      THEN  FN = DIRFNS(DIRLEVEL) C 
      ELSE  WRSN("OUT: Dirlevel? ", DIRLEVEL)
      DIRLEVEL = DIRLEVEL - 1
!
      IF  FN < 256 START 
         ASYNC INHIB = ASYNC INHIB - 1; ! un-inhibit
         IF  ASYNC INHIB <= 0 START ; ! and take any async messages
            DRESUME(-4, 0, 0) UNLESS  AQD = 0
!
            *LSS_(3); ! SSR
            *AND_ALLOW IC INTS; ! UNINHIBIT IC INTERRUPTS
            *ST_(3)
!
            IF  ASYNC INHIB < 0 THEN  WRS("OUT: ASYNC INHIB < 0")
!
!            IUPDATE(-1, 0)
         FINISH 
      FINISH 
!
      *STLN_LNB
      LNB1 = INTEGER(LNB)
      GLA = INTEGER(LNB1 + 16)
      T = INTEGER(LNB1 + 12) << 8 >> 8
      DIRFN = T + INTEGER(GLA + 12) + 12
      DIRFLAG = FLAG
      DREPORT(TEMPLATE) UNLESS  DIRMON = 0 OR  TEMPLATE = "NIL"
      RESULT  = FLAG
END ; ! OUT
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  DDT ENTRY(INTEGERNAME  ENTAD,INTEGER  FSYS)
INTEGER  J,FS,DLVN
INTEGERARRAYNAME  DIT
BYTEINTEGERARRAYNAME  DLVNA
RECORD (DDTF)NAME  DDT
INTEGERARRAYFORMAT  DITF(0:COM_NDISCS-1)
      DLVNA==ARRAY(COM_DLVNADDR,DLVNAF)
      DIT==ARRAY(COM_DITADDR,DITF)
      IF   0<=FSYS<=99 START 
         J=DLVNA(FSYS); ! pick up DIT entry number for FSYS
         IF  J<=250 START 
            DDT == RECORD(DIT(J))
            DLVN=DDT_DLVN
            FS=DLVN<<2>>2
            IF  (1<<DDT_STATE) & DDTSTATES > 0 AND  FSYS=FS START 
               ENTAD=ADDR(DDT)
               RESULT  = 0
            FINISH 
         FINISH 
      FINISH 
      RESULT  = 23
END ; ! DDT ENTRY
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  DISC USE COUNT(INTEGER  FSYS, INCR)
!
!     2**31  0=cck'd         1=not checked
!     2**30  0=available     1=closing
!
INTEGER  ADR, J, C
BYTEINTEGERNAME  N
RECORD (DDTF)NAME  DDT
      RESULT  = 8 UNLESS  INCR = 1 OR  INCR = -1
!
      J = DDT ENTRY(ADR, FSYS)
      RESULT  = J UNLESS  J = 0
!
      DDT == RECORD(ADR)
      ADR = ADDR(DDT_CONCOUNT)
      N == FSYSUSECOUNT(FSYS)
!
      IF  INCR = 1 START 
         N = N + 1
         *LXN_ADR
         *INCT_(XNB +0)
         RESULT  = 0
      FINISH 
!
      IF  N = 0 START ; ! !OOPS
         DOPER2("Negative count on " . ITOS(FSYS))
         RESULT  = 6
      FINISH 
!
      N = N - 1
      *LXN_ADR
      *TDEC_(XNB +0)
      *ST_C; ! original value
      IF  C > 0 START ; ! we haven't made it negative
         IF  C = 1 AND  DDT_DLVN << 1 < 0 START ; ! count now zero and
                                                  ! fsys closing
            DOPER2("Disc " . ITOS(FSYS) . " now free")
            DDT_DLVN = (DDT_DLVN << 2 >> 2) ! (1 << 31); ! disc reverts to
                                                         ! 'not-checked',
                                                         ! 'available'
         FINISH 
         RESULT  = 0
      FINISH 
!
      DOPER2("Negative sys count on " . ITOS(FSYS))
      *LXN_ADR
      *INCT_(XNB +0); ! put it back, Supervisor doesn't like if < 0
      RESULT  = 6
END ; ! DISC USE COUNT
!
!-----------------------------------------------------------------------
!
INTEGERFN  DSEGMENT(INTEGER  SEG,DSTRY)
! REQUESTS THE LOCAL CONTROLLER TO REMOVE ACTIVE MEMORY
! TABLE ENTRIES FOR SEGMENT SEG
RECORD (PARMF)NAME  P
INTEGER  J
      P==RECORD(OUTPAD)
      P=0
      P_P1=SEG
      P_P2=DSTRY
      *OUT_3; ! SET P_P2=1 FOR "DESTROYING SEGMENT"
      J = P_DEST
      IF  J=-1 THEN  WRS("DSEGMENT")
      RESULT  = J
END ; ! DSEGMENT
!
!-----------------------------------------------------------------------
!
 EXTERNALROUTINE  DCHAIN(INTEGER  SEG,DSTRY)
! Removes blocks for segment SEG from active memory and de-chains the
! CBT entries (putting them on the free list).
INTEGER  CELL, J
      CELL=SST(SEG)
      IF  CELL=ENDSST THEN  RETURN 
      IF  LODSEG<=SEG<=HIDSEG THEN   C 
          J = DISC USE COUNT(CBTA(CELL)_DA>>24,-1)
      J = DSEGMENT(SEG,DSTRY)
      IF  J = -1 START 
         WRSNT("DCHAIN SEG", SEG, 5)
         WRSN(" CELL", CELL)
      FINISH 
! CBT ENTRIES BACK TO FREE LIST
      SETSST(SEG, ENDSST)
      RECOVER CBT(CELL)
! CLEAR APF FIELD IN SEGMENT TABLE, BECAUSE 'VAL' WORKS ON IT,
! IRRESPECTIVE OF WHETHER THE ENTRY IS OTHERWISE VALID !
      *LSS_(1); ! pick up PSR
      *AND_X'FF0FFFFF'; ! remove ACR bits
      *OR_X'00100000'; ! set ACR  to one
      *ST_(1); ! and put it back
      ST(SEG)_APFLIM=ST(SEG)_APFLIM & X'E00FFFFF'; ! REMOVE APF FIELD
END ; ! DCHAIN
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  SYSBASE(INTEGERNAME  SYSTEM START, INTEGER  FSYS)
INTEGER  J, ENTAD
RECORD (DDTF)NAME  DDT
      J = DDT ENTRY(ENTAD, FSYS)
      IF  J = 0 START 
         DDT == RECORD(ENTAD)
         SYSTEM START = DDT_SBASE; ! this is the origin of 'fixed' sites for Supervisor etc
      FINISH 
      RESULT  = J
END ; ! SYSBASE
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  PP(INTEGER  SEMADDR,SEMANO, STRING (63)S)
!   Four things get semaphored:
!        file indexes/bitmap
!        arch index
!        #MSG
!        #DIRLOG
!
! PP is called from 
!
! CONN
!     DGETDA
!     DCHACCESS
!     DCONNECTI
!     DDDISCONNECTI
!     DCHSIZE
!     SMALLOC
!     DEAL
!     RETLIST
!     DCREATEF
!     DDESTROYF
!     DRENAME
!     DNEWGEN
! DIRECT
!     TXTMESS
!     DPERMISSIONI
!     DFINFO
!     DFSTATUS
!     DSFI
!     DOFFER
!     DTRANSFER
!     ARCHRECORD
!     OFILES
! XOP
!     D/PP
! MAG
!     DPRGP
!     GIVENEWSECTION
!     DSYSAD
!     DINDEX2
!     DINDEX (OBSOLETE)
!     PRINTSTRING
! If top bit is set in the semaphore number, we do not "express"
! the process. This (top bit) is currently being used
!   (1) in semaphoring the "#MSG" file.
!   (2) in semaphoring the Director logfile.
RECORD  (PARMF)P
      IF  GOTSEMA#0 START 
!
         DOPER2("PP ERROR")
      WRS(DIROWN_SEMA HOLDER." HAS: ".HTOS(SEMANOHELD,8)."@". HTOS(SEMADDRHELD, 8))
         WRS(S." WANTS: ".HTOS(SEMANO, 8)."@".HTOS(SEMADDR, 8))
!         PRINTMP(0, 0)
         DIRMON = -1
         RESULT  = 94
      FINISH 
!
      GOTSEMA=1
      DIROWN_SEMA HOLDER = S
      SEMANOHELD=SEMANO
      SEMADDRHELD = SEMADDR
      IF  SEMANO >= 0 AND  AEXPRESS # 0 C 
      THEN  INTEGER(AEXPRESS) = PROCESS
LOOP:
      *LXN_SEMADDR
      *INCT_(XNB +0)
      *JCC_8,<GOT>; ! GOT SEMA IF VALUE IS 0
      *JCC_4,<NOTGOT>
      DOPER2("SEMAP ".HTOS(SEMANO,8)."=".HTOS(INTEGER(SEMADDR),8)); ! disaster, sema -1 or less
      INTEGER(SEMADDR) = 0
      -> GOT
!      -> LOOP
NOTGOT:
      ! SOMEONE ELSE HAS SEMA. CALL SPVR
      P = 0
      P_DEST = X'70001'
      P_P1 = SEMANO
      DOUTI(P)
      INTEGER(SEMADDR) = 0 IF  P_SRCE = X'70004'; ! Sema was forced free
GOT:
      RESULT  = 0
END ; ! PP
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  VV(INTEGER  SEMADDR, SEMANO)
INTEGER  SEM
RECORD  (PARMF)P
      IF  GOTSEMA=0 START 
!
         DOPER2("VV ERROR")
         WRS("TRY TO FREE: ".HTOS(SEMANO,8)."@". HTOS(SEMADDR, 8))
!         PRINTMP(0, 0)
         DIRMON = -1
         RETURN 
      FINISH 
!
      IF  SEMANO # SEMANOHELD START 
         DOPER2("VV ERROR")
         WRS(DIROWN_SEMA HOLDER." HAS: ".HTOS(SEMANOHELD,8)."@". HTOS(SEMADDRHELD,8))
         WRS(" VRIJ: ".HTOS(SEMANO,8)."@". HTOS(SEMADDR,8))
         RETURN 
      FINISH 
!
      *LXN_SEMADDR
      *TDEC_(XNB +0)
      *JCC_8,<NOQ>; ! NO-ONE QUEUED ON THIS SEMA
      *JCC_4,<SOME WAITING>
      *ST_SEM
      INTEGER(SEMADDR) = -1
      DOPER2("VV ERROR")
      WRS( DIROWN_SEMA HOLDER . " frees " . HTOS(SEMANOHELD,8)."=".HTOS(SEM,8)); ! bad error, sema was -1 or less
      DIRMON = -1
      -> NOQ
SOME WAITING:
      P = 0
      P_DEST = X'70002'
      P_P1 = SEMANOHELD
      DPONI(P)
NOQ:
      IF  SEMANOHELD >= 0 AND  AEXPRESS # 0 C 
      THEN  INTEGER(AEXPRESS) = 0
      GOTSEMA=0
END ; ! VV
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  GET AV FSYS2(INTEGER  TYPE,  C 
   INTEGERNAME  N,INTEGERARRAYNAME  A)
! ARRAY A IS FILLED WITH N FSYS NUMBERS (AS MANY AS ARE ON-LINE)
!       TYPE  0   only discs which are consistency-checked and not closing
!             1   all on-line discs, checked or not, closing or not
!             2   only checked discs, but may be closing.
INTEGER  J, NDISCS, DITADDR, SLOAD FSYS, FS
RECORD (DDTF)NAME  DDT
      SLOAD FSYS = COM_SUPLVN
      A(0) = SLOAD FSYS
      DITADDR = COM_DITADDR
      NDISCS = COM_NDISCS
      N = 1
      CYCLE  J = 0, 1, NDISCS-1
         DDT == RECORD(INTEGER(DITADDR + J<<2))
         IF  (1 << DDT_STATE) & DDTSTATES > 0 AND   C 
            (TYPE=1 OR  (TYPE=0 AND  DDT_DLVN>>30=0) C 
                    OR  (TYPE=2 AND  DDT_DLVN>=0)) C 
         START 
            FS = DDT_DLVN<<2>>2
            UNLESS  FS = SLOAD FSYS START 
               A(N)=FS
               N=N+1
            FINISH 
         FINISH 
      REPEAT 
END ; ! GET AV FSYS2
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  AV(INTEGER  FSYS, TYPE)
! TYPE = 0 Only discs for which CCK has been done
! TYPE = 1 All on-line EMAS discs, whether consistency-checked or not and
!          whether closing or not.
!        2 Only consistency-checked discs, but they may be closing
! RESULT 1 IF FSYS AVAILABLE
!        0 IF NOT
INTEGER  DLVN,J
BYTEINTEGERARRAYNAME  DLVNA
INTEGERARRAYNAME  DIT
RECORD (DDTF)NAME  DDT
INTEGERARRAYFORMAT  DITF(0:COM_NDISCS-1)
      DLVNA==ARRAY(COM_DLVNADDR,DLVNAF)
      DIT==ARRAY(COM_DITADDR,DITF)
      RESULT =0 UNLESS  0<=FSYS<=99
      J=DLVNA(FSYS); ! pick up DIT entry number for FSYS
      IF  J>250 THEN  RESULT =0; ! off-line
      DDT == RECORD(DIT(J))
      DLVN=DDT_DLVN
      IF  FSYS = DLVN<<2>>2 START 
         IF  (1 << DDT_STATE) & DDTSTATES > 0 START 
             IF  TYPE=1 OR  C 
             (TYPE = 0 AND  DLVN >> 30  = 0) OR   C 
             (TYPE = 2 AND  DLVN>=0)  THEN  RESULT  = 1
         FINISH 
      FINISH 
      RESULT  = 0
END ; ! AV
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  SCONNECT(INTEGER  SEG,STARTP,LEN,CALLAPF,  C 
   NEWCOPY,NOTDRUM,NOTSLAV,FLAGS)
! (DISC) SECTION CONNECT.
!
! This function adds either a block to the specified segment or
! allocates 1 or 2 blocks to a new segment. It can add an arbitrary
! number of epages (up to the segment max). The only proviso is that
! the current last block (if any) should be full.
!
! We assume that the disc section size is greater than or equal to and
! a multiple of the CBT block size.
!
! Param LEN is the number of epages being added.
!
! Param STARTP is the start page of the first block being added and
! must have FSYS no. in LH byte.
!
! FLAGS
!        2**5  32 advisory 'multiple use'
!            set for the following
!              file use count > 1
!              an index/bitmap segment
!              subsys basefile
!        2**6  64 advisory sequential
!           7 128 continuation
!
! Possible error results :
!     2    invalid params ("should not occur")
!     28    CBT freelist empty
!
! TAGS field in CBT (8 bits) as follows
!     LH 3 BITS    BLOCK IS "NEW-COPY"  /DISC ONLY /  ABTX=ABT INDEX
!     RH 5 BITS    BLOCK LIMIT (EPAGES)
!
!
INTEGER  NEWBLKS, W, C1, C, J
      UNLESS  0 < SEG <= HISEG C 
               ANDC 
               0 < LEN <= 256 C 
               ANDC 
               0 < CALLAPF <= X'1FF' C 
      THEN  RESULT  = 2; ! FAULTY PARAMS
!
      NEWBLKS = (LEN+BLKSI-1)//BLKSI
!
      *LSS_(1); ! PICK UP PSR
      *AND_X'FF0FFFFF'; ! REMOVE ACR BITS
      *OR_X'00100000'; ! AND SET TO 1
      *ST_(1); ! PUT BACK
!
      NOTDRUM = B'01000000' UNLESS  NOTDRUM = 0
      NEWCOPY = B'10000000' UNLESS  NEWCOPY = 0
      IF  NOTSLAV # 0 # COM_OCPPORT1 C 
      THEN  NOTSLAV = X'20000000' C 
      ELSE  NOTSLAV = 0
!
      W = ST(SEG)_APFLIM
      C1 = SST(SEG)
!
      J = 28
      IF  C1 = ENDSST START 
                                        ! NEW SEGMENT
         IF  LEN <= BLKSI START 
                                        ! ONE BLOCK
            C = GET1
            -> RES IF  C < 0
            CBTA(C)_DA = STARTP
            CBTA(C)_AMTX = 0
            CBTA(C)_TAGS = (LEN-1) ! NOTDRUM ! NEWCOPY
            CBTA(C)_LINK = FLAGS
         FINISH  ELSE  START 
                                        ! TWO BLOCKS
            C = GET2
            -> RES IF  C < 0
            CBTA(C)_DA = STARTP
            CBTA(C)_AMTX = 0
            CBTA(C)_TAGS = (BLKSI-1) ! NOTDRUM ! NEWCOPY
            CBTA(C)_LINK = FLAGS
            CBTA(C+1)_DA = STARTP + BLKSI
            CBTA(C+1)_AMTX = 0
            CBTA(C+1)_TAGS = (LEN-BLKSI-1) ! NOTDRUM ! NEWCOPY
            CBTA(C+1)_LINK = FLAGS ! 128
         FINISH 
         ST(SEG)_APFLIM = (W & X'800C007F') ! C 
            NOTSLAV ! CALLAPF<<20 ! X'40000380' ! C 
            (((LEN * EPAGESIZE - 1) & X'FF') << 10)
         SETSST(SEG, C)
      FINISH  ELSE  START 
                                         ! ADD A BLOCK
         C = GET2
         -> RES IF  C < 0
         CBTA(C) = CBTA(C1)
         CBTA(C+1)_DA = STARTP
         CBTA(C+1)_AMTX = 0
         CBTA(C+1)_TAGS = (LEN-1) ! NOTDRUM ! NEWCOPY
         CBTA(C+1)_LINK = CBTA(C)_LINK ! 128
         ST(SEG)_APFLIM = (W & X'FFFC03FF')  ! C 
            ((((W >>10) + LEN*EPAGESIZE) & X'FF') << 10)
         SETSST(SEG, C)
         RECOVER CBT(C1); ! RECOVER OLD SINGLE ENTRY
      FINISH 
      J = 0
RES:
      RESULT =J
END ; ! SCONNECT
!
!-----------------------------------------------------------------------
!
INTEGERFN  ALLOC DSEG(INTEGER  PGNO,INTEGERNAME  DSEG)
! THE PARAM PGNO SHOULD BE "SEGMENT-ALIGNED" ON THE DISC, IE.
!  (PGNO*EPAGE SIZE)<<10 SHOULD BE A SEGMENT-START ADDRESS.
! FOR REPLACEMENT OF DIRECTOR INDEX SEGMENTS, SEE NOTE IN FN  CINDA
INTEGER  SEG,ENT,MIN,PT,SU, J, N
OWNINTEGER  STAMP
      RESULT =8 IF  (((PGNO<<8>>8)*EPAGE SIZE)<<10)&X'3FFFF'#0
!
      STAMP = STAMP + 1
      MIN = STAMP
      J = 0
      N = 0; ! COUNT SEGS STILL AVAILABLE FOR USE
      SEG = LODSEG
      WHILE  SEG<=HIDSEG CYCLE 
         ENT = SST(SEG)
         -> ALREADY CONN IF  ENT # ENDSST AND  CBTA(ENT)_DA = PGNO
!
         SU = SEGUSE(SEG)
!
         -> GOT HOLE IF  SU = -2
!
         IF  SU >= 0 START 
            N = N + 1
            MIN = SU AND  PT = SEG IF  SU < MIN
         FINISH 
         SEG = SEG + 1
      REPEAT 
!
      DOPER2("ALLOC DSEG: <3") IF  N < 3; ! ON THE VERGE OF DISASTER
!
      DOPER2("ALLOC DSEG: OUT") AND  RESULT  = 3 IF  MIN = STAMP
      SEG = PT
      DCHAIN(SEG, 0); ! DROP EXISTING SEGMENT DISC ADDRESSES
GOT HOLE:
      J = SCONNECT(SEG,PGNO,64,DIRAPF&255,0,0,NOTSLAVED,32); ! anticipate multiple use
      J = DISC USE COUNT(PGNO>>24,+1) IF  J = 0
ALREADY CONN:
      SEGUSE(SEG) = STAMP IF  SEGUSE(SEG) # -1; !  unless fixed
      DSEG = SEG
      RESULT  = J
END ; ! ALLOC DSEG
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  SYSAD(INTEGER  KEY, FSYS)
!
! Use of first few words of bitmap (at least 40 bytes available)
!
! adr use          semano
!   0 bitmap sema  fsys<<16
!   8 fsys
!  16 time of CCK
!  20 date
!  24 nnt sema     2-----4        (one day)
! On the IPL disc, the next 248 bytes are also available - see
! record format DIRCOMF above.
! Note: the semanos used for DIRLOG and FEP are X8-----1 and x4-----2
!
! KEY = 0 BIT MAP 
!       1 NNT 
!       3 NNT SEMA
!       4 DATE WORD
!       5 DIRCOM
!       6 BAD PAGES BIT MAP
!
INTEGER  J, SEG, BB, BIT
RECORD (DISCDATAF)DATA
      FSYS = COM_SUPLVN IF  FSYS < 0 OR  KEY = 5
!
      J = FBASE2(FSYS, ADDR(DATA))
      -> OUT UNLESS  J = 0
!
      BIT = -1
      BIT = 0 IF  KEY = 0
      BIT = DATA_NNTSTART IF  KEY = 1
      BIT = 4 IF  KEY = 3
      BIT = 16 IF  KEY = 4
      BIT = 40 IF  KEY = 5
      BIT = X'5000' IF  KEY = 6
      J = -1 AND  -> OUT IF  BIT < 0
!
      BB = FSYS << 24 ! DATA_START
      J = ALLOC DSEG(BB, SEG)
      RESULT  = SEG << 18 + BIT IF  J = 0
OUT:
      WRSNT("SYSAD fails", J, 5)
      WRSNT(" KEY", KEY, 5)
      WRSN(" FSYS", FSYS)
      RESULT  = -1
END ; ! SYSAD
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  BAD PAGE(INTEGER  TYPE, FSYS, BITNO)
!     TYPE = 0    CLEAR BAD PAGES LIST FOR FSYS
!            1    NEW BAD PAGE, BITNO IN FSYS
!            2    GOOD PAGE, BITNO IN FSYS
!            3    IS BITNO IN FSYS A BAD PAGE? (RESULT 1 IF SO, 0 IF NOT)
!            4    LIKE 2 BUT CALLED FROM A PROGRAM
! When a new bad page is recorded, we want the bitmap bit to be set. But
! not in this routine, because it may be executed on the signal stack
! and we need totake the bitmap semaphore. (Still, when it's executed
! on the signal stack (progerr 18) the page belongs to a file anyway).
! The bit is set in fn MOVE SECTION on getting a flag from the bulk mover.
INTEGER  J, B, BITSINBITMAP
RECORD (DISCDATAF)DATA
      IF  0 < TYPE < 3 START 
         IF  FSYS < 0 THEN  FSYS = BITNO >> 24
         BIT NO = (BIT NO << 8) >> 8
      FINISH 
!
      RESULT  = 93 IF  TYPE & 1 = 0 ANDC 
            0 <= DTRYING << 11; ! ie types 1 and 3 not priv
!
      J = AV(FSYS, 1)
      RESULT  = 2 IF  J = 0
!
      J = FBASE2(FSYS, ADDR(DATA))
      RESULT  = 2 UNLESS  J = 0
!
      B = SYSAD(BADKEY, FSYS)
!
      IF  TYPE = 0 START 
                                        ! CLEAR BAD PAGES LIST
         FILL(DATA_BITSIZE, B, 0)
         RESULT  = 0
      FINISH 
!
      BITS IN BITMAP = DATA_BITSIZE << 3
      *LDTB_BITS IN BITMAP; ! SET UP DR AND B
      *LDA_B; ! TO ADDR THE BIT
      *LB_BIT NO; ! WE WANT
!
      IF  TYPE = 1 START 
                                        ! NEW BAD PAGE
         *LSS_(DR +B ); ! PICK UP BIT
         *JAT_5,<SET>; ! J IF > 0, ALREADY SET
         *LSS_1
         *ST_(DR +B )
         DOPER2( "BAD PAGE ".HTOS(FSYS<<24!BITNO,8)." RECORDED")
         RESULT  = 0
      FINISH 
!
      IF  TYPE = 2 START 
                                        ! MAKE A BAD PAGE GOOD
         *LSS_(DR +B )
         *JAT_4,<NOT SET>; ! J IF = 0, ALREADY GOOD
         *LSS_0
         *ST_(DR +B )
         DOPER2("CLEARED")
         RESULT  = 0
      FINISH 
!
      IF  TYPE = 3 START 
                                        ! TEST BIT
         *LSS_(DR +B ); ! RESULT NOW IN ACC
         *EXIT_-64; ! RETURN
      FINISH 
!
      IF  TYPE = 4 START 
                                        ! EXTERNAL CALL TO CLEAR BIT
         *LSS_(DR +B )
         *JAT_4,<ALREADY CLEAR>
         *LSS_0
         *ST_(DR +B )
         RESULT  = 1
ALREADY CLEAR:
         RESULT  = 0
      FINISH 
!
      RESULT  = 2; ! INVALID CALL
!
SET:
      DOPER2("ALREADY SET"); RESULT  = 0
NOT SET:
      DOPER2("ALREADY CLEAR"); RESULT  = 0
END ; ! OF BAD PAGE
!
!-----------------------------------------------------------------------
!
INTEGERFN  DEAL(INTEGER  FSYS,STARTP,PAGE,PAGES)
! Can de-allocate up to 32 epages comprising the whole or the
! last part of the section.
INTEGER  J, MASK, WORD, WITHIN, BITS, BADS
RECORD (PARMF)NAME  P
      STARTP = PAGE IF  STARTP = 0; ! same most of the time
      WITHIN = PAGE & 31
!
      J = 8; ! BAD PARAM
      -> OUT UNLESS  PAGES > 0
      -> OUT UNLESS  PAGE >= STARTP
      -> OUT UNLESS  (STARTP >> 5) = (PAGE >> 5)
      -> OUT UNLESS  WITHIN + PAGES <= 32
!
      BITS = SYSAD(BITKEY, FSYS)
      BADS = SYSAD(BADKEY, FSYS); ! BAD PAGES BIT LIST
      WORD = (PAGE>>3) & (¬3)
!
      MASK = ((-1) << (32 - PAGES)) >> (PAGE & 31)
!
      J = 22; ! bits already clear flag
      IF  INTEGER(BITS+WORD) & MASK # MASK START 
         WRSNT("DEAL: B+W", BITS+WORD, 2)
         WRSNT("INTG(B+W)", INTEGER(BITS+WORD), 2)
         WRSNT("MASK     ", MASK, 2)
         -> OUT
      FINISH 
! OUT to local controller to check that the block whose start page is
! STARTP is not still active. This is because an ordinary disconnect does
! not wait until all page-outs are complete.
      J = 91
      P==RECORD(OUTPAD)
      P=0
      P_DEST=(FSYS<<24) ! STARTP
      *OUT_17
      -> OUT IF  P_DEST = -1;! P_DEST=-1 IS ERROR RESULT
!
      IF  INTEGER(BITS+8) # FSYS START 
         WRS("BITMAP/FSYS2")
         DIRMON = -1
      FINISH 
!
! Clear the bits
      J = PP(BITS, FSYS << 16, "DEAL")
      IF  J = 0 START 
         INTEGER(BITS+WORD) = (INTEGER(BITS+WORD) & (¬MASK)) C 
            ! INTEGER(BADS + WORD); ! ENSURE THAT ANY BAD PAGES JUST DETECTED ARE PUT INTO BITLIST
         VV(BITS, FSYS << 16)
      FINISH 
OUT:
      RESULT  = J
END ; ! DEAL
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  NINDA(INTEGER  FSYS,INDNO,INTEGERNAME  INDAD)
! TO BE USED ONLY BY
!     CINDA
!     NEWUSER
!     CLEAR FSYS
!     CCK
! POSSIBLE ERROR RESULTS :
!     28    CBT FREELIST EMPTY
!     26    SCONNECT - INVALID PARAMS ("SHOULD NOT OCCUR")
!
INTEGER  J,DSEG,PGNO
RECORD (DISCDATAF)DATA
      J = FBASE2(FSYS, ADDR(DATA))
      -> OUT UNLESS  J = 0
!
      PGNO = INDNO >> 2; ! index numbers are 1K's
      J = ALLOC DSEG((FSYS<<24) ! (DATA_START + (PGNO>>6<<6)), DSEG)
      -> OUT UNLESS  J = 0
!
      INDAD = DSEG<<18 + (INDNO&255)<<10
!
      J = 97
      -> OUT IF  BADPAGE(3, FSYS, DATA_START+PGNO)=YES
!
      J = 0
OUT:
      RESULT =J
END ; ! NINDA
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  FIND NNT ENTRY(STRING (31)INDEX,
      INTEGERNAME  FSYS, NNAD, INTEGER  TYPE)
                                        ! SEARCHES NNT OF ONE OR MORE FSYS'S FOR AN ENTRY
                                        ! HOLDING USER. IF FOUND, RETURNS FSYS AND ADDR OF NNT ENTRY
                                        !     POSSIBLE ERRORS
                                        !     23 FSYS NOT AVAILABLE
                                        !     37 USER NOT FOUND
                                        ! THE SEARCH IS SPEEDED UP (USUALLY) BY MAINTAINING A
                                        ! COLLECTION OF UP TO 16 USER+FSYS+NNTI COMBINATIONS
!       TYPE is set 0 or 2, in calls of this function.
!          0   when only consistency-checked and not-closing discs are to be
!              searched,
!          2   when only consistency-checked discs are to be searched and we
!              don't mind if they are closing (calls originating from
!              DDISCONNECT, DDESTROY and PROCESS STOPS(call of DINDA)
!
INTEGER  HTI, FSYS GIVEN, FINDAD
INTEGER  PT,N,I,J,FLAG,NNA,STOP
INTEGERARRAY  A(0:99)
STRING (6)UNAME
STRING (11)INAME
STRING (18)IND
RECORD (FF)NAME  F
RECORD (NNF)ARRAYFORMAT  NNTF(0:16384)
RECORD (NNF)ARRAYNAME  NNT
RECORD (NNF)NAME  NN
RECORD (HOTTOPF)NAME  HT
OWNINTEGER  TIMES,SAVES
RECORD (DISCDATAF)DATA
!
      J = UIO(INDEX, UNAME, INAME, IND)
      RESULT  = J UNLESS  J = 0
!
      FSYS GIVEN = FSYS
      TIMES = TIMES + 1
      IF  (UNAME="FTRANS" OR  UNAME="SPOOLR" OR  UNAME="MAILER" OR  UNAME="VOLUMS") C 
                AND  FSYS=-1 C 
      THEN  FSYS = COM_SUPLVN
!
!
      -> INVALID IF  HOTTOPN = 0; ! hot top not yet initialised
!
      UNLESS  HOTTOPADR = HOTTOPA START ; ! new address specified, remap
         TIMES = 1
         SAVES = 0
         HOTTOPADR = HOTTOPA
         HOTTOP == ARRAY(HOTTOPADR, HOTTOPFS)
         CYCLE  I = 0,1,HOTTOPN; HOTTOP(I)=0; REPEAT 
      FINISH 
!
      CYCLE  I = 0, 1, HOTTOPN
         HTI = I
         HT == HOTTOP(I)
         -> INVALID IF  HT_INDEX = ""; ! Examined all used entries
         IF  EQUAL(IND, HT_INDEX) = YES ANDC 
            (FSYS=-1 OR  FSYS=HT_FSYS) START 
            ! FOUND, BUT CHECK JUST IN CASE
            NNA = SYSAD(NNTKEY,HT_FSYS)
            NNT == ARRAY(NNA, NNTF)
            PT = HT_PT
            NN == NNT(PT)
            -> INVALID UNLESS  NN_NAME = UNAME; ! reuse this entry
            FSYS = HT_FSYS
            ! give 'not found' for TYPE=0 when the FSYS is closing
            RESULT  = 85 IF  TYPE = 0 = AV(FSYS, 0)
            NNAD = ADDR(NN)
            RESULT  = 0
         FINISH 
      REPEAT 
!
      ! Hot top is full and didnt find reqd entry, pick
      ! one at random to re-use
      HTI = DRANDOM(HOTTOPN)
      HT == HOTTOP(HTI)
INVALID:
      ! either now HOTTOPN = 0 or HT is mapped to record in which
      ! to save user/fsys/pt
!
      FLAG=23; ! DISC N/A
      IF  FSYS=-1 START ; ! LOOK IN ALL AVAILABLE FILE SYSTEMS
         GET AV FSYS2(TYPE,N,A)
         N = N - 1
      FINISH  ELSE  START ; ! LOOK ONLY IN "FSYS"
         IF  AV(FSYS, TYPE)=0 THEN  -> NOT FOUND
         A(0)=FSYS
         N=0
      FINISH 
!
      FOR  J = 0, 1, N CYCLE 
         FSYS = A(J)
         FLAG = FBASE2(FSYS, ADDR(DATA))
         -> NOT FOUND UNLESS  FLAG = 0
!
         NNA=SYSAD(NNTKEY,FSYS)
         NNT==ARRAY(NNA,NNTF)
         PT=HASH(UNAME, DATA_NNTHASH)
         STOP=PT
         UNTIL  PT=STOP CYCLE 
            NN == NNT(PT)
            IF  NN_NAME=UNAME START 
               IF  INAME = "" START ; ! looking for a process index
                  -> FOUND IF  NN_TAG = 0
               FINISH  ELSE  START ; ! looking for a file index
                  IF  NN_TAG > 0 START 
                     I = NN_INDNO
                     J = NINDA(FSYS, I, FINDAD)
                     RESULT  = J UNLESS  J = 0
                     F == RECORD(FINDAD)
                     RESULT  = 87 UNLESS  F_OWNER = UNAME
                     -> FOUND IF  EQUAL(F_NAME, INAME) = YES
                  FINISH 
               FINISH 
            FINISH 
            EXIT  IF  NN_NAME = ""
            PT = PT + 1
            PT = 0 IF  PT > DATA_NNTTOP
         REPEAT 
      REPEAT 
!
      FLAG = 37
NOT FOUND:
      FSYS = FSYS GIVEN
      RESULT  = FLAG
FOUND:
      NNAD = ADDR(NN)
      UNLESS  HOTTOPN = 0 START 
         SAVES = SAVES + 1
         HT_INDEX = IND
         HT_FSYS = FSYS
         HT_PT = PT
         UNLESS  DIRMON = 0 START 
            WRS3N("HOTTOP", IND, ITOS((SAVES*100)//TIMES)."%", HTI)
         FINISH 
      FINISH 
      RESULT  = 0
END ; ! FIND NNT ENTRY
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  EMPTY DVM
! Used to disconnect the index area of the VM and to empty the HOTTOP
INTEGER  SEG,I
      CYCLE  SEG=HIDSEG,-1,LODSEG
         IF  SEGUSE(SEG)#-1 START 
            DCHAIN(SEG,0)
            SEGUSE(SEG)=-2
         FINISH 
      REPEAT 
!
      IF  HOTTOPN > 0 {in use} ANDC 
          HOTTOPA = HOTTOPADR {and mapped} C 
      START 
         CYCLE  I=HOTTOPN,-1,0
            HOTTOP(I)_INDEX=""
         REPEAT 
      FINISH 
END ; ! EMPTY DVM
!
!-----------------------------------------------------------------------
!
INTEGERFN  CLOSING BIT(INTEGER  FSYS)
INTEGER  ENTAD, J
RECORD (DDTF)NAME  DDT
      J = DDT ENTRY(ENTAD,FSYS)
      RESULT  = 0 UNLESS  J = 0
      DDT==RECORD(ENTAD)
      RESULT =(DDT_DLVN>>30)&1; ! pass back bit 1 of DLVN
END ; ! CLOSING BIT
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  SET CLOSING BIT(INTEGER  FSYS)
INTEGER  ENTAD
RECORD (DDTF)NAME  DDT
      ! Give "not available if SLOAD disc specified.
      RESULT  = 23 IF  DDTENTRY(ENTAD,FSYS)#0 OR  AV(FSYS, 0)=0 ORC 
          FSYS=COM_SUPLVN
      DDT==RECORD(ENTAD)
      DDT_DLVN=DDT_DLVN ! (1<<30)
      RESULT =0
END ; ! SET CLOSING BIT
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  CONSEG(STRING (30)FULL,
      INTEGER  FSYS, INTEGERNAME  GAP)
! RESULT IS  SEG NO WHICH FILE IS CONNECTED AT.
! If the file is connected but is not to be disconnected, have its size or access
! changed then the top bit is set in the result value.
! GAP is equal to the gap reserved for the file at CONNECT, or the
! default gap which was used if GAP was specified zero.
! Result is zero if the file is not connected.
INTEGER  SEG, LSEG, LGAP, EQ
RECORD (CTF)NAME  C
      LSEG = 0
      LGAP = 1
      CYCLE  SEG = 4, 1, HISEG
         C == DIROWN_CONLIST(DIROWN_CPOINT(SEG))
         EQ = 0
         EQ = 1 IF  EQUAL(C_FULL, FULL) = YES ANDC 
                   { C_PREFIX = PREFIX %ANDC  }
                    C_FSYS = FSYS
         IF  LSEG # 0 START 
            IF  EQ # 0 OR  C_FULL = "RESERVED" C 
            THEN  LGAP = LGAP + 1 C 
            ELSE  EXIT 
         FINISH 
         ! Pass "no disconnect" bit over into result seg number.
         LSEG = SEG ! (C_NODISCO << 31) IF  EQ # 0 = LSEG
      REPEAT 
      GAP = LGAP
      RESULT  = LSEG
END ; ! CONSEG
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  DNINDA(INTEGER  FSYS, INDNO, INTEGERNAME  INDAD)
INTEGER  J
      J = IN2(51)
      -> OUT UNLESS  J = 0
!
      J = 45
      -> OUT IF  VAL(ADDR(INDAD), 4, 1, DCALLERSPSR) = NO
!
      J = NINDA(FSYS, INDNO, INDAD)
OUT:
      RESULT  = OUT(J, "II")
END 
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  HINDA(STRING (6)UNAME, INTEGERNAME  FSYS,INDAD, INTEGER  TYPE)
! Called to get the address of a 'process index' NOT a file index
! UNAME must be a 6 ch name
! FSYS may be specific or -1 (-1 means IPL disc for executives else first found)
! TYPE 0: flag 85 is returned if fsys found is closing and procuser himself
!         is on a closing disc
!      2: disc may be closing
!
INTEGER  J, NNAD, DSEG, SAVESEG
RECORD (HF)NAME  H
RECORD (NNF)NAME  NN
STRING (12) W
      TYPE = 2 IF  TYPE = 0 # CLOSING BIT(PROCFSYS)
!
      J = 0
      SAVESEG = 0
      IF  UNAME = PROCUSER AND  (FSYS = PROCFSYS OR  FSYS = -1) START 
         IF  OWNIND # 0 START ; ! have already found (and locked) procuser
            INDAD = OWNIND
            FSYS = PROCFSYS
            -> GOT OWN
         FINISH 
         SAVESEG = 1
      FINISH 
!
      J = FIND NNT ENTRY(UNAME, FSYS, NNAD, TYPE)
      -> OUT UNLESS  J = 0
!
      NN == RECORD(NNAD)
      J = NINDA(FSYS, NN_INDNO, INDAD)
      -> OUT UNLESS  J = 0
!
      DSEG = INDAD>>18
      UNLESS  SAVESEG = 0 START 
         SEGUSE(DSEG) = -1
         SEGUSE(SYSAD(BITKEY,FSYS)>>18) = -1
      FINISH 
GOT OWN:
      H == RECORD(INDAD)
      W <- H_OWNER
      UNLESS  H_OWNER = UNAME START 
         WRS3N("HINDA", UNAME, W, 87)
         J = 87; ! wrong index found
      FINISH 
      J = 99 IF  H_MARK = 0
OUT:
      RESULT  = J
END ; ! HINDA
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  FINDA(STRING (31)INDEX, INTEGERNAME  FSYS, FINDAD,
      INTEGER  TYPE)
INTEGER  J, NNAD
STRING (6)WOWNER, UNAME
STRING (11)WNAME, INAME
STRING (18)WFULL, IND
RECORD (NNF)NAME  NN
RECORD (FF)NAME  F
      J = UIO(INDEX, UNAME, INAME, IND)
      -> OUT UNLESS  J = 0
!
      TYPE = 2 IF  TYPE = 0 # CLOSING BIT(PROCFSYS)
!
      J = 0
      IF  IND = PROCUSER ANDC 
          (FSYS = PROCFSYS OR  FSYS = -1) ANDC 
          OWNIND # 0 C 
      START 
         FINDAD = OWNIND
         FSYS = PROCFSYS
         -> GOTOWN
      FINISH 
!
      J = FIND NNT ENTRY(IND, FSYS, NNAD, TYPE)
      -> OUT UNLESS  J = 0
!
      NN == RECORD(NNAD)
      J = NINDA(FSYS, NN_INDNO, FINDAD)
      -> OUT UNLESS  J = 0
!
GOTOWN:
      FINDAD = FINDAD + 512 IF  INAME = ""
      F == RECORD(FINDAD)
      UNLESS  UNAME = F_OWNER AND  EQUAL(INAME, F_NAME) = YES START 
         J = 87
         WOWNER <- F_OWNER
         WNAME <- F_NAME
         WFULL = WOWNER
         WFULL = WOWNER . ISEP . WNAME UNLESS  WNAME = ""
         WRS3N("FINDA", IND, WFULL, 87)
      FINISH 
OUT:
      RESULT  = J
END ; ! FINDA
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  MAP FILE INDEX(STRINGNAME  INDEX,
      INTEGERNAME  FSYS, FINDAD, STRING (31)TXT)
INTEGER  J
RECORD (FF)NAME  F
      J = FINDA(INDEX, FSYS, FINDAD, 0)
      RESULT  = J UNLESS  J  =  0
!
      F == RECORD(FINDAD)
      RESULT  = PP(ADDR(F_SEMA), F_SEMANO, TXT)
END ; ! MAP FILE INDEX
!
!-----------------------------------------------------------------------
!
INTEGERFN  NEW FILE DEAL(INTEGER  FINDAD, SD, PAGES)
INTEGER  NSD, FSYS, NP, DA, LINK, J
RECORD (FF)NAME  F
INTEGERARRAYNAME  SDS
      F == RECORD(FINDAD)
      SDS == ARRAY(FINDAD + F_SDSTART, SDSF)
      NSD = (F_FDSTART - F_SDSTART) >> 2
      FSYS = F_FSYS
      J = 0
      WHILE  PAGES > 0 CYCLE 
         NP = PAGES
         NP = 32 IF  NP > 32
         PAGES = PAGES - NP
         DA = SD << 13 >> 13
         LINK = SD >> 19
         J = J ! DEAL(FSYS, 0, DA, NP) UNLESS  DA = X'7FFFF'
         EXIT  UNLESS  1 <= LINK <= NSD
         SD = SDS(LINK)
         SDS(LINK) = 0
      REPEAT 
      J = 87 UNLESS  PAGES = 0 = LINK
      RESULT  = J
END ; ! NEW FILE DEAL
!
!-----------------------------------------------------------------------
!
externalintegerfn  NEWFIND(integer  FINDAD, DA, stringname  FILE)
! Searches the file index at the specified address for
! the file of the given name, ignoring old gens if DA=0 or
! ignoring any with the wrong disc address if DA # 0. If found,
! returns index (>0) into array of FDs else 0
INTEGER  J, PREFIX
integer  I, NFD
STRING (255)W
record (FDF)name  FD
record (FF)name  F
record (FDF)arrayname  FDS
!
      F == RECORD(FINDAD)
      FDS == ARRAY(FINDAD+F_FDSTART, FDSF)
      NFD = (F_SIZE<<9 - F_FDSTART) // FDSIZE; ! NO OF FDS IN INDEX
      DA = DA << 13
!
         cycle  I = 1, 1, NFD
            FD == FDS(I)
            result  = 0 if  FD_NAME = ""; ! reached a never-used entry
            if  EQUAL(FD_NAME, FILE) = YES start 
                  -> OUT IF  (DA=0=FD_CODES2&OLDGE OR  FD_SD<<13=DA)
            FINISH 
         REPEAT 
         RESULT  = 0
OUT:
      LASTFD = FD
      RESULT  = I
end ; ! NEWFIND
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  FILL STACK ENTS(INTEGER  INDAD, STRING (3)SUFF)
INTEGER  FINDAD, NFD, J, CELL, SD, DA, PGS
STRING (11)FILE
RECORD (FF)NAME  F
RECORD (FDF)ARRAYNAME  FDS
RECORD (FDF)NAME  FD
INTEGERARRAYNAME  SDS
      CELL = SST(4)
      CBTA(CELL)_LINK = 0
!
      FILE = "#STK" . SUFF
      FINDAD = INDAD + 512
      F == RECORD(FINDAD)
      FDS == ARRAY(FINDAD + F_FDSTART, FDSF)
      SDS == ARRAY(FINDAD + F_SDSTART, IFT)
      NFD = (F_SIZE << 9 - F_SDSTART) // FDSIZE
!
      CYCLE  J = 1, 1, NFD
         FD == FDS(J)
         IF  EQUAL(FD_NAME, FILE) = YES START 
            SD = FD_SD >> 19
            IF  SD > 0 START ; ! more than one block
               DA = F_FSYS << 24 ! (SDS(SD) << 13 >> 13)
               PGS = FD_PGS
               J = SCONNECT(4, DA, PGS - 32, X'FF', 1, 0, 0, 128)
               IF  J = 0 START 
                  *LSS_(1)
                  *AND_X'FF0FFFFF'
                  *OR_X'00100000'
                  *ST_(1)
                  ST(4)_APFLIM = (ST(4)_APFLIM & X'FFFC0FFF') ! ((PGS-1)<<12)
               FINISH  ELSE  WRSN("Fill stack ents", J)
            FINISH 
            RETURN 
         FINISH 
      REPEAT 
      WRS("Fill stack ents: No #STK !!")
END ; ! FILL STACK ENTS
!
!-----------------------------------------------------------------------
!
INTEGERFN  NEWCONENT
! returns a free CONLIST entry number, or zero if CONLIST full.
INTEGER  J
      IF  SEARCHENT=0 START 
         DIROWN_CONLIST(0)_FULL="RUBBISH"
         DIROWN_CONLIST(RESERVED)_FULL="RESERVED"
         SEARCHENT=2
      FINISH 
      J=SEARCHENT
      WHILE  J<=TOPCONENT CYCLE 
         IF  DIROWN_CONLIST(J)_FULL="" THEN  DIROWN_CONLIST(J) = 0 AND  RESULT =J
         J=J+1
      REPEAT 
      RESULT =0; ! no free entry found
END ; ! NEWCONENT
!
!-----------------------------------------------------------------------
!
 EXTERNALINTEGERFN  DISCSEG CONNECT(INTEGER  FSYS,SITE,SEG,APF,EPAGES,
      FLAGS)
! Used to connect the System Call Table and basefiles
! Epages must be in the range 1 to 64 (for EPAGE SIZE=4)
! The segment is connect as "slaved".
! WE ARE NOT CONNECTING MORE THAN ONE SEGMENT ALTOGETHER.
INTEGER  J,ENT
      J = 29
      ENT=NEWCONENT
      UNLESS  ENT = 0 START 
         J = SCONNECT(SEG,SITE!(FSYS<<24),EPAGES,APF,0,0,0,FLAGS)
         IF  J = 0 START 
            SEARCHENT=ENT+1
            DIROWN_CPOINT(SEG)=ENT
            DIROWN_CONLIST(ENT)_FULL="DISCSITE"
         FINISH 
      FINISH 
      RESULT  = J
END ; ! DISCSEG CONNECT
!
!-----------------------------------------------------------------------
!
externalintegerfn  NEWFILEPERM(integer  FINDAD, record (FDF)name  FD,
      string (6)USER)
INTEGER  ARCHIVE
record (FF)name  F
record (PDF)arrayname  PDS
record (PDF)name  PD
integer  LINK, J, CH, NPD, N, P
!
!                   gives USER's permitted access modes to FILE
!
      RESULT  = 7 IF  USER = "DIRECT" OR  USER = "FCHECK"
!
      F == RECORD(FINDAD)
      IF  F_NAME = "#ARCH" START 
         ARCHIVE = 1
      FINISH  ELSE  START 
         P = FD_OWNP
         -> OUT if  USER = F_OWNER
         ARCHIVE = 0
      FINISH 
!
      PDS == ARRAY(FINDAD+F_PDSTART, PDSF)
      NPD = (F_SDSTART - F_PDSTART) // PDSIZE
      LINK = FD_PHEAD
      N = 0
      while  NPD >= LINK > 0 cycle ; ! explicit permission to file
         PD == PDS(LINK)
         P = PD_PERM
         -> OUT IF  USER = PD_NAME
         LINK = PD_LINK
         N = N + 1
         exit  if  N > 15
      repeat 
!
      LINK = FD_PHEAD
      N = 0
      while  NPD >= LINK > 0 cycle ; ! implicit permission to file
         PD == PDS(LINK)
         cycle  J = 1, 1, 6
            CH = CHARNO(PD_NAME, J)
            -> NO1 unless  CH='?' or  CH = CHARNO(USER, J)
         repeat 
         P = PD_PERM
         -> OUT
NO1:
         LINK = PD_LINK
         N = N + 1
         exit  if  N > 15
      repeat 
!
      P = FD_EEP
      -> OUT IF  P > 0
      RESULT  = -1 IF  ARCHIVE = 1
!
      LINK = F_FIPHEAD
      N = 0
      while  NPD >= LINK > 0 cycle ; ! explicit permission to index
         PD == PDS(LINK)
         P = PD_PERM
         -> OUT IF  USER = PD_NAME
         LINK = PD_LINK
         N = N + 1
         exit  if  N > 15
      repeat 
!
      LINK = F_FIPHEAD
      N = 0
      while  NPD >= LINK > 0 cycle ; ! implicit permission to index
         PD == PDS(LINK)
         cycle  J = 1, 1, 6
            CH = CHARNO(PD_NAME, J)
            -> NO2 unless  CH='?' or  CH = CHARNO(USER, J)
         repeat 
         P = PD_PERM
         -> OUT
NO2:
         LINK = PD_LINK
         N = N + 1
         exit  if  N > 15
      repeat 
!
      RESULT  = -1
OUT:
      P = P & 7
      P = P ! 1 IF  P & 6 > 0
      RESULT  = P
end ; ! NEWFILEPERM
!
!-----------------------------------------------------------------------
!
INTEGERFN  NOM(INTEGER  SEG)
! NOMINATE (DE-NOMINATE IF SEG=0) SEGMENT SEG.
INTEGER  J
RECORD (PARMF)NAME  P
      P==RECORD(OUTPAD)
      P=0
      P_P1=3; ! STACKNO
      P_P2=SEG
      IF  SEG=0 START ; *OUT_13; FINISH  ELSE  START ; *OUT_12; FINISH 
      J=P_DEST
      J=36 IF  J<0; ! NOM/DE-NOM FAILS
      RESULT =J
END ; ! NOM
!
!-----------------------------------------------------------------------
!
externalintegerfn  DCONNECTI(string (255)FILE,
      integer  FSYS, MODE, APF, integername  SEG, GAP)
!  ORDER OF MODE BITS:         EXECUTE      WRITE      READ
! BITS IN MODE MAY BE SET AS FOLLOWS:
!     2**0  READ
!        1  WRITE
!        2  EXECUTE
!        3  WRITE-SHARED ALLOW
!        4  NEW-COPY
!        5  COMMS MODE
!        6  DISC-ONLY
!        7  NEW STACK SEGMENT
!        8  DISCONNECT, CHANGE ACCESS, CHANGE SIZE NOT ALLOWED
!        9  (advisory) sequential file
!       31  NON-SLAVED SEGMENT
constintegerarray  MG(0:7) = c 
      B'01111101011100000011000000000000',
      B'01110000000000000000000000000000',
      B'01111100011100000011000000000000',
      B'01110000000000000000000000000000',
      B'00010000000000000001000000000000',
      B'00000000000000000000000000000000',
      B'00010000000000000001000000000000',
      B'00000000000000000000000000000000'
integer  FINDAD, FI
integer  J,CONENT,NODISCO
integer  SECTLEN,RELSEG, NSD, S,  DA, NSECTS, N
integer  K,PRM,NEWCOPY,WSA,NEWSTACK,A,MODEGOOD,NONSLAVED
integer  TOPSEG,FSEGS,TOPFSEG,XSEG,XGAP,IS ROOM,TOTSEGS
integer  PAGS,CODES,CODES2,NOTDRUM
integer  FLAGS
STRING (31)UNAME, INAME, FNAME, INDEX, FULL
record (CTF)name  CT
record (FF)name  F
record (FDF)arrayname  FDS
integerarrayname  SDS
integername  SD
record (FDF)name  FL
conststring (8)FN = "DCONNECT"
      J = IN2(11)
      -> OUT UNLESS  J = 0
!
      APF=X'100' ! (D CALLERS ACR<<4) ! D CALLERS ACR if  APF = 0
!
      J=8; ! BAD PARAM
      NODISCO = (MODE&X'100') >> 8
      FLAGS = (MODE&X'200')>>3; ! ie set FLAGS to 64 for 'adv seq'
      MODE=MODE&(¬(X'300'))
      NONSLAVED=MODE>>31
      MODE=MODE<<1>>1; ! Drop LH bit
      unless  0<=MODE<=255 then  -> OUT
!
      ! TYPE 0, VECTOR
      ! SIZE=0, BIT
      ! A=0, USC=0, BCI=0
      A=ADDR(MG(0))
      *LDTB_256
      *LDA_A
      *LB_MODE
      *LSS_(dr +b )
      *ST_MODEGOOD
      if  MODEGOOD=0 then  -> OUT
!
      J = UFO("", FILE, UNAME, INAME, FNAME, INDEX, FULL)
      -> OUT UNLESS  J = 0
!
      J = MAP FILE INDEX(INDEX, FSYS, FINDAD, FN); ! to get fsys
      -> OUT UNLESS  J = 0
!
      F == RECORD(FINDAD)
!
      J = 32; ! NOT EXIST
      FI = NEW FIND(FINDAD, 0, FNAME)
      -> VOUT if  FI=0
!
      J = CONSEG(FULL,FSYS, K); ! check if file already connected
      UNLESS  J = 0 START  
         SEG = J & 255
         GAP = K
         J = 34
         -> VOUT
      finish 
!
      FDS == ARRAY(FINDAD+F_FDSTART, FDSF)
      SDS == ARRAY(FINDAD+F_SDSTART, SDSF)
      NSD = (F_FDSTART - F_SDSTART) >> 2
      FL == FDS(FI)
!
      J = 32; ! NO ACCESS
      PRM = NEW FILE PERM(FINDAD, FL, PROCUSER)
      IF  PRM = -1 START 
         PRM = F_EEP & 7
         PRM = PRM ! 1 IF  PRM & 6 > 0
      FINISH 
      -> VOUT if  MODE&PRM < MODE&7; ! what you can have < what wanted
!
      PAGS=FL_PGS
      J = 87
      -> VOUT IF  PAGS <= 0
!
      NSECTS = (PAGS + 31) >> 5
      CODES = FL_CODES; ! make local copies
      CODES2 = FL_CODES2
!
      J=5; ! NOT READY
      if  CODES&UNAVA#0 then  -> VOUT
!
      J=20; ! ON OFFER
      if  CODES&OFFER#0 then  -> VOUT
!
      NEWCOPY=CODES & VIOLAT
      ! Reject WS-ALLOW for VIOLAT file
      J=33; ! conflicting mode
      if  MODE&B'00001000'#0 and  NEWCOPY#0 then  -> VOUT
!
      NOTDRUM=MODE & B'01000000'
      NEWSTACK=MODE&B'10000000'
      GAP=2 if  NEWSTACK#0 and  GAP<2
      NEWCOPY=NEWCOPY ! (MODE&B'00010000'); ! INCLUDE SPECIFIC RQ FOR NEWCOPY
      if  FL_USE#0 start 
         ! Does requested mode conflict with existing mode?
         ! Reject WRITE connection or WS-ALLOW connection if Ws-allow not set
         if  MODE&B'0001010'#0 and  CODES2&WSALLOW=0 then  -> VOUT
         !
         ! If already connected WS-ALLOW, this request must also have it
         if  MODE&B'1000'=0 and  CODES2&WSALLOW#0 then  -> VOUT
         !
         ! Reject any further connection if already conected W-mode and WS-ALLOW not set
         if  CODES2&WRCONN#0 and  CODES2&WSALLOW=0 then  -> VOUT
         !
         ! Reject any further connection if already connected in COMMS mode or STACK mode
         if  CODES2&(COMMS!STACK)#0 then  -> VOUT
         !
         ! Reject NEW-COPY connection if already connected at all
         ! and reject any further connection for a VIOLAT file.
         -> VOUT IF  NEWCOPY # 0
      finish 
      WSA=MODE&B'1000'
      MODE=MODE & 7
      if  MODE&6#0 then  MODE=MODE ! 1; ! add R if X or W set
!
!      %if MODE&4=0 %then APF=APF & (¬X'100'); ! trim APF, not X
      if  MODE&2=0 then  APF=APF & (¬X'F0'); ! not W
      if  MODE&1=0 then  APF=APF & (¬X'0F'); ! not R
!
      J=29; ! no free CONLIST entry
      CONENT=NEWCONENT
      if  CONENT=0 then  -> VOUT; ! no free CONLIST entry
!
      FSEGS = (PAGS*4+255) >> 8; ! set SEG and GAP if req
      J = 35
      XGAP = GAP
      if  XGAP < FSEGS start 
!         -> VOUT %IF XGAP > 0; ! too small a GAP specified - BUT SS CAN'T COPE !!!!!
         XGAP = FSEGS
         GAP = FSEGS
      finish 
      TOTSEGS = XGAP
!
      XSEG=SEG
      if  NEWSTACK#0 and  XSEG&1#0 then  -> VOUT; ! MUST BE EVEN FOR STACK MODE
      if  XSEG=0 start 
         XSEG=LOUSEG
         while  XSEG<=HISEG cycle 
            IS ROOM=1
            cycle  K=0,1,GAP-1
               ! First condition below checks that we're not going beyond
               ! the VM limit.
               if  XSEG+K>HISEG or   c 
                   DIROWN_CPOINT(XSEG+K)#0 then  IS ROOM=0  c 
                      and  exit 
            repeat 
            if  IS ROOM#0 then  exit ; ! FOUND HOLE
            XSEG=XSEG+1
            if  NEWSTACK#0 then  XSEG=XSEG+1; ! TO GO UP IN TWO'S
         repeat 
         if  IS ROOM=0 then  -> VOUT
         SEG=XSEG
      finish 
      TOPFSEG=XSEG+FSEGS-1
      TOPSEG=XSEG+TOTSEGS-1
!------ SEG & GAP SELECTED AND SET UP ---------------------------
! VALID SEG NOS, START AND FINISH ?
      unless  0<=XSEG and  TOPSEG<=HISEG then  -> VOUT
! CHECK THESE SEGMENTS ARE FREE
      cycle  K=XSEG,1,TOPSEG
         unless  DIROWN_CPOINT(K)=0 then  -> VOUT; ! VM GAP TOO SMALL
      repeat 
! CHECK ENOUGH CBT FREE LIST CELLS
      K=1; ! binary switch for segment count
      RELSEG=XSEG
      SECTLEN = 32
      N = 0
      SD == FL_SD
      while  N < NSECTS cycle 
         N = N + 1
         SECTLEN = PAGS - (NSECTS - 1) << 5 if  N = NSECTS
         DA = (SD << 13) >> 13
         J = 87 and  -> VOUT if  DA = (-1) >> 13; ! space never allocated
         J = SCONNECT(RELSEG, DA ! (FSYS << 24), SECTLEN, C 
               APF, NEWCOPY, NOTDRUM, NONSLAVED, FLAGS); ! called for each section
         -> VOUT unless  J = 0
         FLAGS = FLAGS ! 128
         K = K + 1
         RELSEG = RELSEG + K & 1
         S = SD >> 19
         exit  unless  1 <= S <= NSD
         SD == SDS(S)
      repeat 
      J = 87 and  -> VOUT unless  N = NSECTS and  S = 0
!
!------------- UPDATE USECOUNT IN DISC TABLE ------------------------
      J = DISC USE COUNT(FSYS,+1)
      -> VOUT UNLESS  J = 0
!
!------------ UPDATE CON TABLE ----------------------------
! mark not-to-be-disconnected in FSYS if required.
      CT == DIROWN_CONLIST(CONENT)
      CT_FULL = FULL
      CT_FSYS=FSYS
      CT_NODISCO = NODISCO
!
      DMONW(X'F0', SEG, FULL) IF  PAGE MON # 0 AND  INTEGER(PAGE MON) > 32
!
      SEARCHENT=CONENT+1
      J=XSEG
      while  J<=TOPSEG cycle 
         if  J<=TOPFSEG then  DIROWN_CPOINT(J)=CONENT  c 
            else  DIROWN_CPOINT(J)=RESERVED
         J=J+1
      repeat 
!----------------------------- UPDATE FILE DESCRIPTOR -----------------
      if  MODE&2#0 start ; ! WRITE MODE
         FL_ARCH=(FL_ARCH&(¬2)) ! 1; ! SET BITS 2**0 AND CLEAR 2**1
         FL_CODES2=FL_CODES2 ! WRCONN
      finish 
      if  WSA#0 then  FL_CODES2=FL_CODES2 ! WSALLOW
      FL_ARCH=FL_ARCH ! 4; ! HAS-BEEN-CONNECTED BIT
      FL_CCT=FL_CCT + 1 unless  FL_CCT=255
      FL_USE=FL_USE+1
      J = DDAYNUMBER & 255
      J = 1 IF  J = 0
      FL_DAYNO = J
      if  NEWSTACK#0 start 
         J=NOM(0); ! IGNORE FLAG
         J=NOM(SEG)
         if  J#0 then  -> VOUT
         FL_CODES2=FL_CODES2 ! STACK
      finish 
      J=0
VOUT:
      VV(ADDR(F_SEMA), F_SEMANO)
OUT:
      RESULT  = OUT(J, "SIIIJJ")
end ; ! DCONNECTI
!
!-----------------------------------------------------------------------
!
ROUTINE  REF NEW BLOCKS(INTEGER  SEG)
! This routine arranges to reference a page in each block of segment SEG for which
! the new-copy bit is set in the CBT entry.
INTEGER  ENT,DUMMY,VADDR,BLOCK BYTES
      BLOCK BYTES=(BLKSI*EPAGE SIZE)<<10; ! BLOCK SIZE IN BYTES
      VADDR=SEG<<18
      ENT = SST(SEG)
      DUMMY = 0
      UNLESS  ENT = ENDSST START 
         UNLESS  CBTA(ENT)_TAGS & X'80' = 0 START 
            DUMMY = INTEGER(VADDR)
         FINISH 
         IF  ENT > CBT1 START 
            ! DOUBLE ENTRY
            UNLESS  CBTA(ENT+1)_TAGS & X'80' = 0 START 
               DUMMY = DUMMY ! INTEGER(VADDR+BLOCK BYTES)
            FINISH 
         FINISH 
      FINISH 
      WRSN("REF NEW BLOCKS SEG", SEG) UNLESS  DUMMY=0
END ; ! REF NEW BLOCKS
!
!-----------------------------------------------------------------------
!
externalintegerfn  DDISCONNECTI(string (31)FILE, integer  FSYS, LO)
! Bits in parameter LO are as follows:
!
!
!     2**0 = 0 User call, disconnect only from user segments
!            1 from DSTOP, allow all disconnects and destroy
!              temp and vtemp files
!
!     2**1 = 1 disconnect and destroy, but only if this is the
!              only connection, flag 42 otherwise
!
integer  J,DESTROY,DDD,PRIV VIOL
integer  FINDAD, SD, PI, NPD
integer  SEG,CODES,CODES2,CPT
integer  LOSEG,FLAG,PAGES,NKB
string (31)UNAME, INAME, FNAME, INDEX, FULL
record (CTF)name  CT
record (PDF)arrayname  PDS
record (PDF)name  PD
record (FF)name  F
record (FDF)arrayname  FDS
record (FDF)name  FL
conststring (10)FN = "DISCONNECT"
      FLAG = IN2(18)
      -> OUT UNLESS  FLAG = 0
!
      FLAG = UFO("", FILE, UNAME, INAME, FNAME, INDEX, FULL)
      -> OUT UNLESS  FLAG = 0
!
      FLAG = FINDA(INDEX, FSYS, FINDAD, 2)
      -> OUT UNLESS  FLAG = 0
!
      F == RECORD(FINDAD)
!
      FLAG = PP(ADDR(F_SEMA),F_SEMANO,FN)
      -> OUT unless  FLAG = 0
!
      LOSEG=LOUSEG
      LOSEG=4 if  LO&1#0
      cycle  SEG=LOSEG,1,HISEG
         CPT = DIROWN_CPOINT(SEG)
         CT == DIROWN_CONLIST(CPT)
         -> GOTFI if  EQUAL(CT_FULL, FULL) = YES and  CT_FSYS=FSYS
      repeat 
      FLAG=39; ! NOT IN CONLIST
      -> VOUT
GOTFI:
      ! top bit set in FSYS field means do not disconnect (except at
      ! DSTOP).
      FLAG=84; ! restricted connect
      -> VOUT UNLESS  CT_NODISCO = 0 OR  LO & 1 > 0; ! restricted connect
      ! Check that segment being disconnected does not contain the PC from
      ! which fn DDISCONNECT was called, else flag 21. (If this fn DDISCONNECTI
      ! was not called from DDISCONNECT butfrom somewhere in Director, this
      ! test is equally valid. A possibility not catered for at present
      ! is that the PC of the call of DDISCONNECT could be in the second
      ! segment of a PD file. This case would escape the test which follows).
      FLAG=21; ! not allowed to disconnect calling file
      *STLN_J
      J=INTEGER(INTEGER(J)+8)>>18; ! PC from link DR 1 display down.
      if  J=SEG and  LO&1=0 then  -> VOUT; ! and OK from DSTOP
!
      FDS == ARRAY(FINDAD+F_FDSTART, FDSF)
      PDS == ARRAY(FINDAD+F_PDSTART, PDSF)
      NPD = (F_SDSTART - F_PDSTART) // 9
      FLAG = 78
      J=NEW FIND(FINDAD,CBTA(SST(SEG))_DA<<8>>8,FNAME); ! FIND SPECIFIC GEN OF A FILE
      -> VOUT if  J=0
!
      FL==FDS(J)
      CODES = FL_CODES
      CODES2 = FL_CODES2
      if  FL_USE=0 then  monitor  else  FL_USE=FL_USE-1
      FLAG = 0; ! ANTICIPATE SUCCESS
      DESTROY=0
      if  FL_USE=0 start 
         if  CODES2&STACK#0 then  J=NOM(0); ! DE-NOMINATE
! Clear STACK, DISC ONLY, COMMS, WSALLOW, and WRCONN bits
         FL_CODES2 = CODES2&B'10000110'
! Set DESTROY non-zero for destroy:
!  1 for TEMPFI and VTEMPFI, and 2 for OLDGEN.
! We destroy vtempfiles anyway, and tempfiles if this is the
! closedown call of DISCONNECT (LO&1=1). AlsO we destroy if 2**1 is set in param LO.
         if  CODES&VTEMPF#0 or  (LO&1#0 and  CODES&TEMPFI#0)  c 
            or  LO&2#0 then  DESTROY=1
!
                                        ! IF 'NO ACCESS' OR 'DONT DESTROY'
                                        ! THEN DONT DESTROY !!
         if  CODES2 & OLD GE > 0 c 
         then  DESTROY = 2 c 
         else  start 
            if  DESTROY = 1 c 
                and  c 
                (FL_OWNP = 0 c 
                or  c 
                FL_OWNP & 8 > 0) c 
            then  DESTROY = 0 and  FLAG = 51
         finish 
!
      finish 
! now either destroy the file or reference blocks in the file for which
! the new-copy bit is set in the corresponding CBT entry, to ensure that
! zero pages go back to the disc.
! we want to release the semaphore as soon as possible, as disconnection
! may involve disc transfers.
      SD = 0
      PRIV VIOL=CODES & VIOLAT
      if  DESTROY=0 start 
         FL_CODES=FL_CODES & (¬VIOLAT)
      finish  else  start 
         PAGES=FL_PGS
!
         if  DESTROY = 1 start 
            ! in the case of 'generation destroy', all adjustments to
            ! counts etc have already been done in NEWGEN
            NKB = PAGES * 4
            F_FILES = F_FILES - 1
            F_TOTKB = F_TOTKB - NKB
!
            unless  FL_CODES & TEMPFS = NO start 
               F_TEMPFILES = F_TEMPFILES - 1
               F_TEMPKB = F_TEMPKB - NKB
            finish 
!
            unless  FL_CODES & CHERSH = NO start 
               F_CHERFILES = F_CHERFILES - 1
               F_CHERKB = F_CHERKB - NKB
            finish 
         finish 
!
         SD = FL_SD
         PI = FL_PHEAD
         FL = 0
         FL_NAME = ".NULL"
         while  0 < PI <= NPD cycle 
            PD == PDS(PI)
            PI = PD_LINK
            PD = 0
         repeat 
      finish 
VOUT:
      VV(ADDR(F_SEMA), F_SEMANO)
      -> OUT UNLESS  FLAG = 0 OR  FLAG = 51
!
! Clear conlist entries
      DMONW(X'E0', SEG, FULL) IF  PAGE MON # 0 AND  INTEGER(PAGE MON) > 32
!
      DDD=0; ! "DESTROY" FLAG TO DCHAIN
      DDD=1 if  DESTROY#0
      SEARCHENT=CPT if  CPT<SEARCHENT; ! new search-start point
      while  DIROWN_CPOINT(SEG)=CPT cycle 
         REF NEW BLOCKS(SEG) if  DESTROY=0 and  PRIV VIOL#0
         DCHAIN(SEG,DDD)
         DIROWN_CPOINT(SEG)=0
         SEG=SEG + 1
      repeat 
      CT=0
      while  DIROWN_CPOINT(SEG)=RESERVED cycle 
         DIROWN_CPOINT(SEG)=0
         SEG=SEG+1
      repeat 
      if  SD>0 start 
         J = NEW FILE DEAL(FINDAD,SD,PAGES)
         ACTIVE BLOCK("DISCONNECT", FULL, FSYS) if  J = 91
      finish 
      J = DISC USE COUNT(FSYS,-1); ! decrement FSYS use_count in disc table.
      UNLESS  FSYSWARN = 0 START 
         CYCLE  J = 99, -1, 0
            EMPTYDVM AND  EXIT  IF  FSYSUSECOUNT(J)#0=AV(J, 0)
         REPEAT 
      FINISH 
OUT:
      FLAG = 42 IF  LO & 2 > 0 = DESTROY AND  FLAG = 0
      RESULT  = OUT(FLAG, "SII")
end ; ! DDISCONNECTI
!
!-----------------------------------------------------------------------
!
ROUTINE  RECOVER2(INTEGER  STARTSEG,ENDSEG,NEW LAST BLK,  C 
   NEW LAST BLKLEN)
! Recovers CBT entries for blocks beyond NEWLAST BLK in segment SEG.
! NEW LAST BLK is a blockno counting from 0 in STARTSEG.
! NEW LAST BLKLEN is no of epages to be left in new last block.
! The routine also deals with more then one segment if necessary (ie. if
! ENDSEG#STARTSEG) and provided ENDSEG>STARTSEG, NEW LAST BLK may be
! greater than the number of blocks per segment.
INTEGER  R,CUR,BLKS PER SEG,NEW ENDSEG
INTEGER  CELL, C
      BLKS PER SEG=256//(BLKSI*EPAGE SIZE)
      MONITOR  UNLESS  NEW LAST BLK//BLKS PER SEG<=ENDSEG-STARTSEG
! First deal with whole segments (if any) beyond the new last segment
      NEW ENDSEG=STARTSEG + NEW LAST BLK//BLKS PER SEG
      CUR=ENDSEG
      WHILE  CUR>NEW ENDSEG CYCLE 
         DCHAIN(CUR,1); ! destroy option - pages are going to be de-allocated
         CUR=CUR - 1
      REPEAT 
! Now set NEW LAST BLK to be new last block number within new last
! segment
      NEW LAST BLK=NEW LAST BLK - (NEW ENDSEG-STARTSEG)*BLKS PER SEG
      R = DSEGMENT(NEWENDSEG,0)
      CELL=SST(NEW ENDSEG)
      IF  R = -1 START 
         WRSN("RECOVER2", NEW END SEG)
      FINISH 
!
      C = CELL + NEW LAST BLK
      CBTA(C)_TAGS = (CBTA(CELL)_TAGS & X'E0') ! NEW LAST BLK LEN - 1
!
      IF  NEW LAST BLK = 0 AND  CELL > CBT1 START 
                                        ! HAVE TO SHRINK FROM TWO BLOCKS TO ONE
         C = GET1
         MONITOR  AND  DSTOP(24) IF  C < 0
         CBTA(C) = CBTA(CELL)
         IF  STARTSEG = NEW END SEG START 
            CBTA(C)_LINK = CBTA(C)_LINK&127; ! Remove cont block bit
         FINISH 
         SETSST(NEW END SEG, C)
         RECOVER CBT(CELL)
      FINISH 
!
! reduce the seg table limit field for the new last segment
      *LSS_(1); ! pick up PSR
      *AND_X'FF0FFFFF'; ! remove ACR bits
      *OR_X'00100000'; ! set ACR  to one
      *ST_(1); ! and put it back
      ST(NEW ENDSEG)_APFLIM=(ST(NEW ENDSEG)_APFLIM&X'FFFC0FFF') !  C 
         ((((NEW LAST BLK<<5) + NEW LAST BLK LEN)<<2 -1)<<10)
END ; ! RECOVER2
!
!-----------------------------------------------------------------------
!
ROUTINE  BLOCK EXTEND(INTEGER  SEG,BLKNO,NEWDA,ADDPAGES)
! Call supplies new disc address and new (and not smaller!) number of pages
! for the block. A call with ADDPAGES zero is used to replace the disc address for the 
! block: required in the case where SECTSI>BLKSI, as the section has been moved.
! ADDPAGES can be non-zero only when BLONK specifies the last block of SEG.
INTEGER  TAGS,ENT,DA
      *LSS_(1); ! pick up PSR
      *AND_X'FF0FFFFF'; ! remove ACR bits
      *OR_X'00100000'; ! set ACR  to one
      *ST_(1); ! and put it back
      ENT=SST(SEG) + BLKNO
! Replace the disc address. Tag bits to remain the same.
! Limit to be increased.
      DA=CBTA(ENT)_DA
      CBTA(ENT)_DA=(DA>>24<<24) ! NEWDA; ! PRESERVE FSYS NO
      TAGS=CBTA(ENT)_TAGS
      CBTA(ENT)_TAGS=(TAGS&X'E0') ! ((TAGS&31)+ADDPAGES)
! Increase segment limit in segment table
      ST(SEG)_APFLIM=(ST(SEG)_APFLIM&X'FFFC03FF') !  C 
         ((ST(SEG)_APFLIM>>10)&255 + ADD PAGES*EPAGE SIZE)<<10
END ; ! BLOCK EXTEND
!
!-----------------------------------------------------------------------
!
ENDOFLIST 
!
!
!
!
!
!
! Here are the routines to create EXAC7
!
! TCH
! The descriptor in ACC points to a table of check bits. Successive
! bytes in the DR string are checked against the table as follows
!        - pick up the bit addressed by the byte
!        - proceed along the DR string until get
!          a byte with a check bit of 1
!        - DR is left at that byte
!
!
!
!%EXTERNALROUTINE EXAC7(%STRING(255)S)
!!
!%EXTERNALROUTINESPEC DEFINE(%STRING(255)S)
!%EXTERNALROUTINESPEC CLEAR(%STRING(255)S)
!%EXTERNALROUTINESPEC PROMPT(%STRING(255)S)
!%EXTERNALROUTINESPEC RSTRG(%STRINGNAME S)
!!
!%SYSTEMROUTINESPEC PHEX(%INTEGER N)
!!
!%INTEGERARRAY T(0:55); ! 7*8-1
!%INTEGER BD, AD, BYTE, W, CON, I, INDEX
!%STRING(31)FILE
!!
!      BD = 7*256
!      AD = ADDR(T(0))
!      FILL(56*4, AD, 0)
!!
!      %CYCLE BYTE = 1, 1, 254
!         W = BYTE << 24
!         CON = 0
!         %CYCLE I = 0, 1, 7
!            CON = CON + 1 %IF W >= 0
!            %IF W<0 %OR I=7 %START
!               %IF CON>0 %START; ! GOT FROM 1 TO 7 CONSECUTIVE ZERO BITS
!                  INDEX = 256*(CON-1)+BYTE
!                  *LDTB_BD
!                  *LDA_AD
!                  *LB_INDEX
!                  *LSS_1
!                  *ST_(%DR+%B)
!                  CON = 0
!               %FINISH
!            %FINISH
!            W = W << 1
!         %REPEAT
!      %REPEAT
!!
!      PROMPT("FILE:        ")
!      RSTRG(FILE)
!      DEFINE("1,".FILE)
!      SELECTOUTPUT(1)
!      PRINTSTRING("%CONSTINTEGERARRAY EXAC7(0:55) = %C
!")
!!
!      I = 0
!      %CYCLE W = 0, 1, 55
!         I = I + 1
!         PRINTSTRING("X'")
!         PHEX(T(W))
!         %IF W = 55 %C
!         %THEN PRINTSTRING("'") %C
!         %ELSE PRINTSTRING("',")
!         NEWLINE %AND I = 0 %IF I = 4
!      %REPEAT
!!
!      SELECT OUTPUT(0)
!      CLOSESTREAM(1)
!      CLEAR("")
!%END
!%ENDOFFILE
!
!
! Here follow the routines (commented out) which were used to create the
! array in fn FIND SMALL HOLE
!%INTEGERFN CONSEC HOLES(%INTEGER BYTE,HOLESIZE)
!! Result is  0  ("No") if there are not HOLESIZE consecutive zero bits in BYTE
!!            1  ("Yes") if the is a group of HOLESIZE zero bits in BYTE
!%INTEGER N,CONSEC
!      BYTE=(¬BYTE)<<24; ! TO LH BYTE OF WORD
!      N=0
!      CONSEC=0
!      %WHILE N<=7 %CYCLE
!         %IF BYTE<0 %START
!            ! Top bit set
!            CONSEC=CONSEC+1
!            %IF CONSEC=HOLESIZE %THEN %RESULT=1
!         %FINISH %ELSE CONSEC=0
!         BYTE=BYTE<<1
!         N=N+1
!         %REPEAT
!      %RESULT=0
!%END; ! CONSEC HOLES
!
!-----------------------------------------------------------------------
!
!%ROUTINE SETBIT(%INTEGER TABAD,TABNO,BITNO,BITVAL)
!%INTEGER I,BD,INDEX
!      BD=7*256
!      INDEX=256*TABNO + BITNO
!!-----------------------------------------------------
!      ! TYPE 0, VECTOR
!      ! SIZE=0, BIT
!      ! A=0, USC=0, BCI=0
!      *LDTB_BD
!      *LDA_TABAD
!      *LB_INDEX
!      *LSS_BITVAL
!      *ST_(%DR+%B)
!%END; ! SETBIT
!
!-----------------------------------------------------------------------
!
!%EXTERNALROUTINE MAKE 7 TABLES(%STRING(255) S)
!%INTEGERARRAY TABLES(0:7*8-1)
!%INTEGER HOLESIZE,TABNO,N,YES OR NO,J,TABAD
!%STRING(31) FILE
!      TABAD=ADDR(TABLES(0))
!      %CYCLE HOLESIZE=1,1,7
!         TABNO=HOLESIZE-1
!         %CYCLE N=0,1,255
!            YES OR NO=CONSEC HOLES(N,HOLESIZE)
!            SETBIT(TABAD,TABNO,N,YES OR NO)
!            %REPEAT
!         %REPEAT
!      PROMPT("FILE: ")
!      RSTRG(FILE)
!      DEFINE("1,".FILE)
!      SELECT OUTPUT(1)
!      PRINTSTRING(%C
!"%CONSTINTEGERARRAY TAB7(0:7*8-1)=  %C
!")
!      N=0
!      %CYCLE J=0,1,7*8-1
!         N=N+1
!         PRINTSTRING("X'")
!         PRHEX(TABLES(J))
!         %UNLESS J=7*8-1 %THEN PRINTSTRING("',") %ELSE PRINTSTRING("'")
!         %IF N=6 %THEN NEWLINE
!         %IF N=8 %THEN NEWLINE %AND N=0
!         %REPEAT
!      NEWLINE
!      SELECT OUTPUT(0)
!      CLOSE STREAM(1)
!      CLEAR("")
!%END; ! MAKE 7 TABLES
!
!-----------------------------------------------------------------------
!
LIST 
INTEGERFN  SMALL HOLE(INTEGER  LO, HI, EPAGES, INTEGERNAME  MASK)
!
! This function is used to find space for sections of 1 to 7 EPAGES within
! a byte.  It tries first to find an 'exact' hole and, if that fails,
! to find a sufficiently large hole.  If a hole is found, it constructs
! a MASK which is to be OR'd into a word in the bitmap.  The result is
! the address of the byte containing the hole or zero.
!
CONSTINTEGERARRAY  TABLE(0:111) = C 
X'26362F36',X'26FF2F36',X'FFFFFFFF',X'FFFFFFFF',
X'26362F36',X'FFFFFFFF',X'2636FFFF',X'26FF2F36',
X'08483848',X'FFFFFFFF',X'08FF3848',X'0F483848',
X'0848FFFF',X'0F483848',X'08FF3848',X'0F483848',
X'0080FFFF',X'30804080',X'0F804080',X'30804080',
X'00FF4080',X'30804080',X'0F804080',X'30804080',
X'00FF8000',X'40008000',X'30008000',X'40008000',
X'0F008000',X'40008000',X'30008000',X'40008000',
X'0F000000',X'80000000',X'40000000',X'80000000',
X'30000000',X'80000000',X'40000000',X'80000000',
X'30000000',X'00000000',X'80000000',X'00000000',
X'40000000',X'00000000',X'80000000',X'00000000',
X'40000000',X'00000000',X'00000000',X'00000000',
X'80000000',X'00000000',X'00000000',X'00000000',
X'FFFFFFFF',X'FFFFFFFF',X'FFFFFFFF',X'FFFFFFFF',X'FFFFFFFF',X'FFFFFFFF',
X'FFFFFFFF',X'FFFFFFFE',
X'FFFFFFFF',X'FFFFFFFF',X'FFFFF8C8',X'FFC8F8C8',X'FFFFFFFF',X'FFC8F8C8',
X'FFFFF8C8',X'FFC8F8C8',
X'FFFFFFFF',X'F080C080',X'FF80C080',X'F080C080',X'FFFFC080',X'F080C080',
X'FF80C080',X'F080C080',
X'FFFF8000',X'C0008000',X'F0008000',X'C0008000',X'FF008000',X'C0008000',
X'F0008000',X'C0008000',
X'FF000000',X'80000000',X'C0000000',X'80000000',X'F0000000',X'80000000',
X'C0000000',X'80000000',
X'F0000000',X'00000000',X'80000000',X'00000000',X'C0000000',X'00000000',
X'80000000',X'00000000',
X'C0000000',X'00000000',X'00000000',X'00000000',X'80000000',X'00000000',
X'00000000',X'00000000'
INTEGER  DR0, DR1, ADR0, ADR1, W0, W1, L, B, CON, T, M, J
      L = HI - LO
      DR0 = X'58000000' ! L
      DR1 = LO
      ADR0 = X'00000100'
      ADR1 = ADDR(TABLE(0)) + ((EPAGES - 1) << 5)
!
      CYCLE  T = 0, 1, 1
         *LD_DR0
         *LSD_ADR0
         *TCH_L =DR 
         *JCC_8,<NOTFOUND>
         *STD_W0
!
         B = BYTEINTEGER(W1) << 24
         CON = 0
         M = (-1) << (32 - EPAGES) >> ((W1 & 3) << 3)
         MASK = M; ! save this mask
         CYCLE  J = 0, 1, 7
            CON = CON + 1 IF  B >= 0; ! count consecutive zero bits
            M = M >> 1
            IF  B < 0 OR  J = 7 START ; ! see how many we've got
               RESULT  = W1 IF  CON = EPAGES OR  (T = 1 AND  CON >= EPAGES)
               CON = 0
               MASK = M
            FINISH 
            B = B << 1
         REPEAT 
NOT FOUND:
         ADR1 = ADR1 + 224; ! second part of table
      REPEAT 
      RESULT  = 0
END ; ! SMALL HOLE
!
!-----------------------------------------------------------------------
!
INTEGERFN  FINDHOLE(INTEGER  STARTAD, ENDAD)
! This function finds a zero byte in
! the interval STARTAD to ENDAD.
! Result is   0  if no hole is found
!            #0  = address of byte containing the (start of the) hole
INTEGER  DR0,DR1,LEN
      LEN=ENDAD-STARTAD
      DR0=X'58000000' ! LEN
      DR1=STARTAD
      *LD_DR0
      *LB_0; ! test char = 0 (to find holes)
      *SWNE_L =DR 
! condition code now set as follows
!     0  reference byte not found
!     1  reference byte found, address in bottom half of DR
      *JCC_8,<NOTFD>
      *STD_DR0
      RESULT =DR1
NOTFD:
      RESULT =0
END ; ! FINDHOLE
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  SMALLOC(INTEGERNAME  STARTPG, INTEGER  FSYS, EPAGES, ZEROPAGS)
! For allocations up to 7 epages the alignment is arbitrary.
! For other allocations, align on a byte boundary.
! Search from bottom each time.
!
! Possible improvements:
!
! 1. Do not restrict small holes to lie within bytes
!
! 2. Do not restrict large holes to start on byte boundary
!
! 3. Do not restrict holes to lie within words
!
! 4. Compact 'old last sections' {Dchsize}
!
INTEGER  B, TIMES, LOB, HIB, CAT, M, AD, AD3
INTEGER  LOAD, HIAD
INTEGER  MASK
INTEGER  J
INTEGER  OFFSET, WDAD
RECORD (DISCDATAF)DATA
CONSTINTEGERARRAY  SIZE(1:5) = 32, 24, 16, 8, 7
      J = FBASE2(FSYS, ADDR(DATA))
      RESULT  = J UNLESS  J = 0
!
      LOB = ((DATA_START + DATA_FILESTART + 31) & (-32))
      HIB = DATA_END & (-32)
      TIMES = 0
      B = SYSAD(BITKEY, FSYS)
      LOAD = B + LOB >> 3
      HIAD = B + HIB >> 3
!
      CAT = 5; ! compute 'category' of required hole
      CYCLE 
         EXIT  IF  EPAGES <= SIZE(CAT)
         CAT = CAT - 1
      REPEAT 
SMOVER:
      TIMES = TIMES + 1
!
      IF  CAT = 5 START ; ! find a hole and the corresponding word mask
         AD = SMALL HOLE(LOAD, HIAD, EPAGES, M)
      FINISH  ELSE  START 
         MASK = (-1) << (32 - EPAGES)
         AD = LOAD - 1
         CYCLE 
            AD = FINDHOLE(AD+1, HIAD)
            EXIT  IF  AD = 0; ! no zero byte found
            AD3 = AD & 3
            M = MASK >> (AD3 << 3)
            EXIT  IF  CAT = 4; ! only wanted a zero byte
            EXIT  IF  AD3 < CAT AND  M & (INTEGER(AD &(-3))) = 0
         REPEAT 
      FINISH 
!
      IF  AD = 0 START ; ! No hole found
         DOPER2("FSYS ".ITOS(FSYS)." FULL")
         RESULT  = 10; ! FULL
      FINISH 
!
      J = PP(B,FSYS << 16, "SMALLOC"); ! Take the semaphore
      -> SMOVER UNLESS  J = 0
!
      WDAD = AD & (¬3); ! Set bits if all clear
      IF  INTEGER(WDAD)&M = 0 C 
      THEN  INTEGER(WDAD)=INTEGER(WDAD) ! M C 
      ELSE  M = 0
!
      VV(B, FSYS << 16)
      -> SMOVER IF  M = 0; ! Some bits were already set, try again
!
      *LSS_M; ! we know mask is non-zero
      *SHZ_OFFSET; ! count zero bits at top
      STARTPG = (WDAD-B)<<3 + OFFSET
!
      UNLESS  ZEROPAGS = 0 START ; ! Clear the new pages
         J = MOVESECTION(-1, 0, FSYS,STARTPG,EPAGES)
         IF  J#0 START 
            -> SMOVER IF  TIMES<10
            RESULT  = J; ! 10 disc zero-writes failed
         FINISH 
      FINISH 
!
      RESULT  = 0
END ; ! SMALLOC
!
!-----------------------------------------------------------------------
!
!
externalintegerfn  DCREATEF(string (255)FILE,
      integer  FSYS, NKB, ALLOC, LEAVE, INTEGERNAME  DA)
! IF ALLOC 2**0 = 0  THEN MAKE THE INDEX ENTRY BUT DO NOT ALLOCATE
!                      THE PAGES
!                 1  NORMAL FILE CREATE
! AND IF   2**1 = 0  MAKE FD AVAIABLE ON COMPLETION
!                 1  LEAVE FD UNAVAILABLE
! AND IF 2**2 SET THEN TEMPFI
! AND IF 2**3 SET THEN VTEMPF
! AND IF 2**4 SET THEN ZERO PAGES
! AND IF 2**5 SET THEN CREATE 'CHERISHED'
! AND IF 2**6 SET THEN SET EEP TO TOP BYTE OF 'ALLOC'
! POSSIBLE ERROR RESULTS
!     16    ALREADY EXISTS
!     17    INSUFFICIENT FREE CELSS
!     18    INVALID NAME
!     26    SCONNECT ERROR
!     28    INSUFFICIENT FREE CBT CELLS
!     37    USER NOT KNOWN
!     41    INVALID SIZE
!
integer  NSD, NFD, S, NS, LINK
integer  J, STARTPG,MAXKB,MAXFILE,FINDAD,FI
integer  NUM,EP,R1,NP,EPAGES
STRING (31)UNAME, INAME, FNAME, INDEX, FULL
record (FF)name  F
record (FDF)arrayname  FDS
integerarrayname  SDS
record (FDF)name  FL
integername  SD
conststring (9)FN = "DCREATEF "
      DA = -1
      J=IN2(13)
      -> OUT unless  J = 0
!
      J=8
      -> OUT UNLESS  0 < NKB <= (64 << 10); ! ie a max of 64Mbytes
      EPAGES=(NKB+3) >> 2
      NKB=EPAGES << 2
!
      J = UFO("", FILE, UNAME, INAME, FNAME, INDEX, FULL)
      -> OUT UNLESS  J = 0
!
      J=MAP FILE INDEX(INDEX, FSYS, FINDAD, FN)
      -> OUT UNLESS  J = 0
!
      F == RECORD(FINDAD)
      FDS == ARRAY(FINDAD + F_FDSTART, FDSF)
      SDS == ARRAY(FINDAD + F_SDSTART, SDSF)
!
      J=41; ! single file limit exceeded
      MAXFILE=F_MAXFILE
      MAXFILE=DEFAULT MAXFILE if  MAXFILE=0
      unless  EPAGES<=MAXFILE>>2 then  -> VOUT; ! single file limit exceeded
!
      J=83; ! total (non-temp) filespace limit exceeded
      MAXKB=F_MAXKB
      if  MAXKB=0 then  MAXKB=DEFAULT MAXKB
      if  ALLOC & (TEMPFI ! VTEMPF) = 0 c 
          andc 
          F_TOTKB-F_TEMPKB+NKB>MAXKB c 
      then  -> VOUT
!
      NUM=(EPAGES+31) >> 5
      J = 0
      NFD = (F_SIZE << 9 - F_FDSTART) >> 5
      for  FI = LEAVE, 1, NFD cycle 
         FL == FDS(FI)
         if  FL_NAME = "" or  FL_NAME = ".NULL" start 
            J = FI IF  J = 0; ! save first free entry
            EXIT  IF  FL_NAME = ""; ! reached end of used entries
         FINISH 
!
         IF  EQUAL(FL_NAME, FNAME) = YES AND  FL_CODES2 & OLDGE = 0 START 
            DA = (FSYS<<24) ! (FL_SD<<13>>13)
            J = 16; ! file already exists
            -> SAVE FD
         FINISH 
      REPEAT 
!
      J = 15 AND  -> VOUT IF  J = 0; ! no free FDs
!
      FL == FDS(J)
      FL = 0; ! for safety
      FL_NAME = FNAME
      FL_USE = 1 IF  LEAVE < 3; ! #STK, #LCSTK and #DGLA do not get connected
      FL_SD = (-1) >> 13
      FL_PGS = EPAGES
      FL_CODES = UNAVA
!
      if  NUM > 1 start ; ! more than 1 section, so need some SDs
         J = 43; ! in case there are no free SDs
         NS = NUM - 1
         NSD = (F_FDSTART - F_SDSTART) >> 2; ! number of SDs
         SD == FL_SD
         cycle  S = 1, 1, NSD
            if  SDS(S) = 0 start ; ! found a free SD
               SD = SD ! (S << 19)
               SD == SDS(S)
               SD = (-1) >> 13
               NS = NS - 1
               exit  if  NS = 0
            finish 
         repeat 
!
         UNLESS  NS = 0 START ; ! could not get enough
            S = FL_SD
            CYCLE 
               LINK = S >> 19
               EXIT  UNLESS  1 <= LINK <= NSD
               S = SDS(LINK)
               SDS(LINK) = 0
            REPEAT 
            FL = 0
            FL_NAME = ".NULL"
            -> VOUT
         FINISH 
!
      finish 
      SD == FL_SD
      VV(ADDR(F_SEMA), F_SEMANO)
      if  ALLOC&1#0 start 
         EP=EPAGES
         while  EP>0 cycle 
            NP=EP
            NP=32 if  NP>32
            R1=SMALLOC(STARTPG,FSYS,NP,ALLOC&16)
!
            if  R1 # 0 start ; ! allocate fails
               J = NEW FILE DEAL(FINDAD, FL_SD, EPAGES)
               ACTIVE BLOCK(FN, FULL, FSYS) if  J = 91
               J = PP(ADDR(F_SEMA), F_SEMANO, FN)
               -> OUT unless  J = 0
               FL = 0
               FL_NAME = ".NULL"
               J = R1
               -> VOUT
            finish 
!
            DA = (FSYS<<24) ! STARTPG IF  DA = -1
            SD = ((SD >> 19) << 19) ! STARTPG
            LINK = SD >> 19
            SD == SDS(LINK) if  LINK > 0
            EP=EP - NP
         repeat 
      finish ; ! ALLOC # 0
      J = PP(ADDR(F_SEMA), F_SEMANO,FN)
      -> OUT unless  J = 0
      F_TOTKB=F_TOTKB + NKB
!
      unless  ALLOC & TEMPFS = NO start 
         ! temporary
         F_TEMPKB = F_TEMPKB + NKB
         F_TEMPFILES = F_TEMPFILES + 1
      finish 
!
      unless  ALLOC & 32 = NO start 
         ! cherished
         F_CHERKB = F_CHERKB + NKB
         F_CHERFILES = F_CHERFILES + 1
      finish 
!
      F_FILES=F_FILES+1
      ! Clear UNAVA unless 2**1 set in ALLOC
      FL_CODES=0 if  ALLOC&2=0
      if  ALLOC&4#0 then  FL_CODES=FL_CODES ! TEMPFI
      if  ALLOC&8#0 then  FL_CODES=FL_CODES ! VTEMPF
      if  ALLOC&16=0 then  FL_CODES=FL_CODES ! VIOLAT
      if  ALLOC&32#0 then  FL_CODES=FL_CODES ! CHERSH
      if  ALLOC&64#0 then  FL_EEP=ALLOC>>24
      IF  ALLOC & 128 # 0 THEN  FL_CODES = FL_CODES ! NOARCH
      FL_OWNP = 7
      J = DDAYNUMBER & 255
      J = 1 IF  J = 0
      FL_DAYNO = J
      J=0
SAVE FD:
      LAST FD = FL
VOUT:
      VV(ADDR(F_SEMA), F_SEMANO)
OUT:
      RESULT  = OUT(J, "SIIX")
end ; ! DCREATEF
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  DMON(STRING (255)S)
EXTERNALINTEGERFNSPEC  DLOCK(INTEGER  A,L, LONGINTEGERNAME  LI)
EXTERNALINTEGERFNSPEC  DUNLOCK(INTEGER  A)
INTEGER  J, SEG, GAP, DA, ENT
LONGINTEGER  LI
CONSTSTRING (17)FULL = "VOLUMS.PAGEFAULTS"
STRING (63)FILE
CONSTINTEGER  NKB = 128  {16}
CONSTINTEGER  WR = 3
RECORDFORMAT  HF(INTEGER  NEXT, RELST, MAX, A, B, C, CYCLIC, C 
      READ)
RECORD (HF)NAME  H
RECORD (PARMF)NAME  P
      P == RECORD(OUTPAD)
      P = 0
      J = 99; ! silly fail flag
!
      IF  S = "" START ; ! monitor OFF
         UNLESS  PAGE MON = 0 START ; ! not already off
            J = 0
            *OUT_20; ! inform Supervisor
!            %RESULT = 98 %IF P_DEST < 0; ! ignore failures
            J = DUNLOCK(PAGE MON)
            PAGE MON = 0
            J = DDISCONNECTI(FULL, -1, 0)
            WRSS(FULL, " OFF")
         FINISH 
         RESULT  = J
      FINISH 
!
      IF  S = "SWITCHON" START 
         CYCLE  J = 4, 4, NKB-4
            ENT = INTEGER(PAGEMON + J<<10)
         REPEAT 
         J = 0 
         -> SWITCH ON
      FINISH 
!
!   *** turn monitor ON ***
!
      RESULT  = J UNLESS  PAGE MON = 0
!
      SEG = 0
      GAP = 0
      J = DCREATEF(FULL, -1, NKB, (3<<24)+64+1, LEAVE, DA)
      RESULT  = J IF  0 # J # 16
      J = DCONNECTI(FULL, -1, WR, DIRAPF, SEG, GAP)
      RESULT  = J IF  0 # J # 34
!
      PAGE MON = SEG << 18
      WRSS(FULL, " ON")
      H == RECORD(PAGE MON)
      H = 0
      H_NEXT = 32
      H_RELST = 32
      H_CYCLIC = 32
      H_MAX = NKB << 10
      H_READ = 32
!
      J = DLOCK(PAGE MON, NKB << 10, LI); ! lock down
!
      IF  S = "LATER" START 
         PAGE MON = 0 UNLESS  J = 0
         RESULT  = J
      FINISH 
SWITCH ON:
      CYCLE  SEG = 35, 1, HISEG; ! list current segments
         ENT = SST(SEG)
         UNLESS  ENT = ENDSST START 
            FILE = DIROWN_CONLIST(DIROWN_CPOINT(SEG))_FULL
            DMONW(X'F0', SEG, FILE); ! 'F0' = connect
         FINISH 
      REPEAT 
!
      IF  J = 0 START 
         P_P1 = PAGE MON
         *OUT_20; ! inform Supervisor
         J = 98 IF  P_DEST < 0
      FINISH 
      RESULT  = J
END 
!
!-----------------------------------------------------------------------
!
externalintegerfn  DDESTROYF(string (31)FILE, integer  FSYS, DEALLOC)
! BITS IN DEALLOC :
!     2**0     DEALLOCATE PGS IF SET, always do this
!     2**1     DESTROY EVEN IF "UNAVA" OR "OFFER" SET (FOR FN DTRANSFER)
!     2**2     DESTROY EVEN IF USE IS NON--ZERO OR UNAVA SET (FOR PROCESS
!              1 TO DESTROY SIGSTK"S - NEVER DISCONNECTED)
integer  FINDAD, FI, SD, PI, NPD
integer  J,PAGES, NKB, K
STRING (31)UNAME, INAME, FNAME, INDEX, FULL
record (FDF)name  FL
record (FF)name  F
record (FDF)arrayname  FDS
record (PDF)arrayname  PDS
record (PDF)name  PD
conststring (10)FN = "DDESTROYF "
!
      J = IN2(16)
      -> OUT UNLESS  J = 0
!
      J = UFO("", FILE, UNAME, INAME, FNAME, INDEX, FULL)
      -> OUT UNLESS  J = 0
!
      J = FINDA(INDEX, FSYS, FINDAD, 2)
      -> OUT UNLESS  J = 0
!
      F == RECORD(FINDAD)
      J = PP(ADDR(F_SEMA),F_SEMANO,FN.FULL)
      -> OUT unless  J = 0
!
      FDS == ARRAY(FINDAD + F_FDSTART, FDSF)
      PDS == ARRAY(FINDAD + F_PDSTART, PDSF)
      NPD = (F_SDSTART - F_PDSTART) // 9
!
      J=32; ! FILE DOES NOT EXIST
      FI=NEW FIND(FINDAD,0,FNAME)
      if  FI=0 then  -> VOUT; ! FILE DOES NOT EXIST
!
      FL==FDS(FI)
      if  DEALLOC&4=0 start ; ! SET ONLY BY PROCESS 1 ON DESTROYING SIGSTKS
         J = 40; ! FILE CONNECTED
         if  FL_USE#0 then  -> VOUT
         if  DEALLOC&2=0 start ; ! set only by DTRANSFER
!                                   BOB THINKS 'UNLESS CALLER PRIVI'
            if  (FL_OWNP = 0 or  FL_OWNP & 8 > 0) and  DTRYING >= 0 c 
            then  J = 51 and  -> VOUT; ! NO ACCESS OR DONT DESTROY
            J=20; ! file is on offer
            if  FL_CODES&OFFER#0 then  -> VOUT
            J=5; ! FILE NOT READY
            if  FL_CODES&UNAVA#0 then  -> VOUT
         finish 
      finish 
      PAGES=FL_PGS
!
      NKB = PAGES * 4
!
      F_TOTKB = F_TOTKB - NKB
!
      unless  FL_CODES & TEMPFS = NO start 
         ! temporary
         F_TEMPKB = F_TEMPKB - NKB
         F_TEMPFILES = F_TEMPFILES - 1
      finish 
!
      unless  FL_CODES & CHERSH = NO start 
         ! cherished
         F_CHERKB = F_CHERKB - NKB
         F_CHERFILES = F_CHERFILES - 1
      finish 
!
      F_FILES = F_FILES-1
!
      PI = FL_PHEAD
      while  0 < PI <= NPD cycle 
         PD == PDS(PI)
         PI = PD_LINK
         PD = 0
      repeat 
!
      SD = FL_SD
      FL = 0
      FL_NAME = ".NULL"
!
      VV(ADDR(F_SEMA), F_SEMANO)
      J = NEW FILE DEAL(FINDAD,SD,PAGES)
      ACTIVE BLOCK("DESTROY", FULL, FSYS) if  J = 91
      J = 0
      -> OUT
VOUT:
      VV(ADDR(F_SEMA), F_SEMANO)
OUT:
      UNLESS  FSYSWARN = 0 START 
         CYCLE  K = 99, -1, 0
            IF  FSYSUSECOUNT(K) # 0 = AV(K, 0) C 
            THEN  EMPTYDVM AND  EXIT 
         REPEAT 
      FINISH 
!
      RESULT  = OUT(J, "SII")
end ; ! DDESTROYF
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  OUTPUT MESSAGE TO FEP(RECORD (FEPF)ARRAYNAME  FEPS,
   INTEGER  FE, TYPE, MESSAGE ADDR, MESSAGE LENGTH, STRM ID, PROTOCOL)
INTEGER  CURSOR, ADD, TOTAL LEN, BUFF LEN, FLAG, ZERO, J
INTEGER  SEMADR, SEMANO
RECORD (PARMF)P
RECORD (DIRCOMF)NAME  DIRCOM
!
!
!
ROUTINE  PUT(INTEGER  ADR, LEN)
INTEGER  L
      L = BUFF LEN - CURSOR; ! space remaining in buffer, >0
      IF  LEN > L START ; ! have to split
         MOVE(L, ADR, ADD + CURSOR)
         MOVE(LEN - L, ADR + L, ADD)
         CURSOR = LEN - L
      FINISH  ELSE  START 
         MOVE(LEN, ADR, ADD + CURSOR)
         CURSOR = CURSOR + LEN
         CURSOR = 0 IF  CURSOR >= BUFF LEN
      FINISH 
END ; ! PUT
!
!
!
      ZERO = 0
      TOTAL LEN = 1+1+2+MESSAGE LENGTH+1
      IF  FEPS(FE)_AVAILABLE # YES THEN  RETURN 
      !
      ! This routine is called by DIRECT and in user processes by DSETMODE.
      ! The fep buffers are in the same segment as the record format FEPS and
      ! the field OUT BUFF CONN ADDR gives the offset into it.
      !
      ADD = ADDR(FEPS(0))>>18<<18 + FEPS(FE)_FEP DETAILS(PROTOCOL)_OUT BUFF CON ADDR<<14>>14
      J = INTEGER(ADD); ! page fault on buffer before claiming semaphore
      !
      DIRCOM == RECORD(SYSAD(DIRCOMKEY, -1))
      SEMADR = ADDR(DIRCOM_FEPSEMA)
      SEMANO = X'40000002'
      FLAG = PP(SEMADR, SEMANO, "FEPoutmsg")
      !
      CURSOR = FEPS(FE)_FEP DETAILS(PROTOCOL)_OUTPUT CURSOR
      BUFF LEN = FEPS(FE)_FEP DETAILS(PROTOCOL)_OUT BUFF LENGTH
      PUT(ADDR(TOTAL LEN)+3,1)
      PUT(ADDR(TYPE)+3,1)
      PUT(ADDR(STRM ID)+2,2)
      PUT(ADDR(ZERO),1)
      PUT(MESSAGE ADDR, MESSAGE LENGTH)
      P = 0
      P_DEST = STREAM CONTROL MESSAGE
      P_SRCE = FE<<8!FEP OUTPUT REPLY MESS
      P_P1 = FEPS(FE)_FEP DETAILS(PROTOCOL)_OUTPUT STREAM
      P_P2 = CURSOR
      J = DPON3I("",P,0,0,7)
      FEPS(FE)_FEP DETAILS(PROTOCOL)_OUTPUT CURSOR = CURSOR
      IF  FLAG = 0 THEN  VV(SEMADR, SEMANO)
END ; ! OUTPUT MESSAGE TO FEP
!
!-----------------------------------------------------------------------
!
!<DCHACCESS
externalintegerfn  DCHACCESS(string (31)FILE INDEX, FILE,
      integer  FSYS, NEWMODE)
!
! This procedure is used to change the access mode (Segment table access
! permission field, APF) for connected file FILE belonging to file index
! FILE INDEX on disc-pack FSYS (0-99). Bits in NEWMODE have the following
! meanings (as in the MODE parameter to procedure DCONNECT):
!         2**0 set - read access to be allowed     
!         2**1 set - write access to be allowed
!         2**2 set - execute access to be allowed
!         2**9 set - advisory sequential(not set, then clear)
! The bottom 3 bits of NEWMODE must have one of the values 1, 3
! or 5.  The file must be permitted to the caller in the appropriate
! mode.
!>
integer  J, I, PRM, AS, C, FINDAD, W
integer  SEG, READAPF, EXECBIT, WRITEAPF, OLDWRITEAPF
STRING (31)UNAME, INAME, FNAME, INDEX, FULL
RECORD (CTF)NAME  CT
record (FF)name  F
record (FDF)arrayname  FDS
record (FDF)name  FL
conststring (10)FN = "DCHACCESS "
      J = IN2(7)
      -> OUT unless  J = 0
!
      J = 8
      W = NEWMODE & 7
      -> OUT unless  (W=1 or  W=3 or  W=5)
      -> OUT unless  NEWMODE & X'FFFFFDF8' = 0
      -> OUT if  FSYS<0
      AS = (NEWMODE & X'200') >> 3; ! 2**6 for advisory-sequential
!
      J = UFO(FILE INDEX, FILE, UNAME, INAME, FNAME, INDEX, FULL)
      -> OUT UNLESS  J = 0
!
      J = MAP FILE INDEX(INDEX, FSYS, FINDAD, FN)
      -> OUT UNLESS  J = 0
!
      F == RECORD(FINDAD)
      FDS == ARRAY(FINDAD + F_FDSTART, FDSF)
!
      J = 32; ! not exist/no access
      I = NEW FIND(FINDAD, 0, FNAME)
      -> VOUT if  I = 0
      FL == FDS(I)
!
      PRM = NEW FILE PERM(FINDAD, FL, PROCUSER)
      IF  PRM = -1 START 
         PRM = F_EEP & 7
         PRM = PRM ! 1 IF  PRM & 6 > 0
      FINISH 
      -> VOUT if  W & PRM < W
!
      J = 33
      -> VOUT if  NEWMODE&2#0 and  FL_USE>1 and  FL_CODES2&WSALLOW=0
!
      *LSS_(1); ! pick up PSR
      *AND_X'FF0FFFFF'; ! remove ACR bits
      *OR_X'00100000'; ! set ACR  to one
      *ST_(1); ! and put it back
!
      J=39; ! file not connected
      cycle  SEG=LOUSEG,1,HISEG
         CT == DIROWN_CONLIST(DIROWN_CPOINT(SEG))
         if  EQUAL(CT_FULL, FULL) = YES and  CT_FSYS=FSYS start 
            J = 84
            -> VOUT unless  CT_NODISCO = 0; ! not allowed to change access
            READAPF=ST(SEG)_APFLIM & X'00F00000'
            EXECBIT=0
            EXECBIT=X'10000000' if  NEWMODE&4#0
            WRITEAPF=0
            WRITEAPF=READAPF<<4 if  NEWMODE&2#0
            OLDWRITEAPF=ST(SEG)_APFLIM & X'0F000000'
            ST(SEG)_APFLIM = (ST(SEG)_APFLIM & X'E0FFFFFF') ! EXECBIT ! WRITE APF
            C = SST(SEG); ! CBT index
            CBTA(C)_LINK = CBTA(C)_LINK & X'BF' ! AS
            if  C > CBT1 c 
            then  CBTA(C+1)_LINK = CBTA(C+1)_LINK & X'BF' ! AS
         finish  else  start 
            if  J = 84 start ; ! file has been found
               ! Remove WR-CONNected bit if new mode doesn't contain write
               ! and usecount is one.
               if  NEWMODE&2=0 and  FL_USE=1 c 
               then  FL_CODES2= FL_CODES2&(¬WRCONN)
               ! do a null
               ! do an OUT to get an activate to clear the slaves.
               *OUT_4
               J=0
               exit 
            finish 
         finish 
      repeat 
VOUT:
      VV(ADDR(F_SEMA), F_SEMANO)
OUT:
      RESULT  = OUT(J, "SSII")
end ; ! DCHACCESS
!
!-----------------------------------------------------------------------
!
!<DCHSIZE
externalintegerfn  DCHSIZE(string (31)FILE INDEX, FILE,
       integer  FSYS, NEWKB)
!
! The physical size of file FILE belonging to file index FILE INDEX on
! disc-pack FSYS (or -1) is altered (if necessary) so that its new size
! is NEWKB Kbytes.  The size may not be reduced to zero.  The file may
! be connected in the caller's virtual memory (only).  If the caller is
! not the file owner, he must either have W access to the file index or
! be privileged.
!>
!
!
!
integer  OLDPGS; !number of pages to start with
integer  NEWPGS; ! number of pages required
integer  XPGS; ! number of pages to be added or removed
integer  XKB; ! number of Kbytes to be added or removed
integer  MAXKB; ! file space limit from file index
integer  MAXFILE; ! also from file index
integer  PGS IN OLD LAST SECT
integer  PGS IN NEW LAST SECT
integer  OLDSEGS; ! number of segments to start with
integer  NEWSEGS; ! number of segments required
integer  OLDSECTS; ! number of sections to start with
integer  NEWSECTS; ! number of sections required
integer  XSECTS; ! number of sections to be added or removed
integer  B, W; ! used in relocating last section when no change in size
!
integer  J,K,L,N,LASTSEG,CON,GAP
integer  RELSEG,APF,NOTDRUM
integer  ADD TO LAST SECT
integer  SLAVEBIT,ZERALLOC
integer  STARTP
integer  CELL, FLAGS
integer  S, FINDAD, DA, LINK, NSD
STRING (31)UNAME, INAME, FNAME, INDEX, FULL
record (FDF)name  FL
integerarrayname  SDS
record (FF)name  F
record (FDF)arrayname  FDS
integername  SD0, SD, SDX
conststring (8)FN = "DCHSIZE "
      K=IN2(9)
      -> OUT UNLESS  K = 0
!
      K = UFO(FILE INDEX, FILE, UNAME, INAME, FNAME, INDEX, FULL)
      -> OUT UNLESS  K = 0
!
      K=93
      -> POK IF  UNAME = PROCUSER
      -> POK IF  DTRYING < 0
      -> POK IF  FILE INDEX PERM(INDEX, FSYS) & 2 > 0
      -> OUT
POK:
      K=8; ! BAD PARAM - SILLY SIZE
      NEWPGS=(NEWKB + 3) >> 2
      NEWKB = NEWPGS << 2; ! ie rounded up to next page
      NEWSEGS = (NEWPGS + 63) >> 6
      -> OUT UNLESS  NEWPGS > 0
!
      K=MAP FILE INDEX(INDEX, FSYS, FINDAD, FN)
      -> OUT UNLESS  K = 0
!
      F == RECORD(FINDAD)
      FDS == ARRAY(FINDAD + F_FDSTART, FDSF)
      SDS == ARRAY(FINDAD + F_SDSTART, SDSF)
      NSD = (F_FDSTART - F_SDSTART) >> 2
      MAXFILE=F_MAXFILE
      MAXFILE=DEFAULT MAXFILE if  MAXFILE=0
      MAXKB = F_MAXKB
      MAXKB = DEFAULT MAXKB if  MAXKB = 0
!
      K = 32; ! file does not exist
      J = NEWFIND(FINDAD, 0, FNAME)
      -> VOUT if  J = 0
!
      FL == FDS(J)
      OLDPGS = FL_PGS
      K = 87
      -> VOUT IF  OLDPGS = 0
      OLDSEGS = (OLDPGS + 63) >> 6
      OLDSECTS = (OLDPGS + 31) >> 5
      NEWSECTS = (NEWPGS + 31) >> 5
      XSECTS = NEWSECTS - OLDSECTS
      PGS IN OLD LAST SECT = OLDPGS - (OLDSECTS - 1) << 5
      PGS IN NEW LAST SECT = NEWPGS - (NEWSECTS - 1) << 5
!
      K = 41; ! single file limit exceeded
      -> VOUT if  NEWKB > MAXFILE
!
      XPGS = NEWPGS - OLDPGS; ! number to increase by (-, +, or 0)
      XKB = XPGS << 2
!
      K = 83; ! total file space limit exceeded
      -> VOUT if  XPGS > 0 andc   {increasing size of file}
                  FL_CODES & TEMPFS = 0 andc   {and its not temporary}
                  F_TOTKB - F_TEMPKB + XKB > MAXKB andc   {and limit exceeded}
                  FNAME # "#ARCH"  {but its not #ARCH}
!
      CON = CONSEG(FULL, FSYS, GAP); ! is file connected in this VM?
!
      IF  FL_USE > 0 START ; ! file is in use somewhere
         K = 0
         -> VOUT IF  XPGS = 0; ! if we are just fiddling, exit
         K = 84
         -> VOUT IF  CON < 0; ! restricted connect
         K = 42
         -> VOUT IF  CON = 0; ! connected in another VM
         -> VOUT IF  FL_USE > 1; ! ditto
      FINISH 
!
      if  CON > 0 start ; ! file is connected in this VM
         CELL = SST(CON)
         FLAGS = CBTA(CELL)_LINK ! 128; ! any subs blocks will be conts
         LASTSEG = CON + OLDSEGS - 1
         if  XPGS > 0 start ; ! connected and extending
            K = 28; ! insufficient CBT cells
            -> VOUT if  XSECTS > (CBT2 - CBT1 - 1)
            K = 35; ! gap too small
            -> VOUT if  NEWSEGS > GAP
         finish 
      finish 
!
      SD0 == FL_SD
      SD == SD0; ! Now find last SD in chain
      J = 0
      while  J < OLDSECTS cycle 
        J = J + 1
         S = SD >> 19
         exit  unless  1 <= S <= NSD
         SD == SDS(S)
      repeat 
      K = 87
      -> VOUT unless  J = OLDSECTS and  S = 0
!
      if  XSECTS>0 start ; ! extend the chain of SDs
         J = XSECTS
         SDX == SD
!
         cycle  S = 1, 1, NSD
            if  SDS(S) = 0 start ; ! a free SD
               SDX = SDX ! (S << 19); ! link to new SD
               SDX == SDS(S)
               SDX = (-1) >> 13; ! set DA to '-1' in case allocate fails
               J = J - 1
               -> ENOUGH if  J = 0
            finish 
         repeat 
!
         LINK = SD >> 19;     ! failed to get enough new SDs
         SD = SD << 13 >> 13; ! so tidy up
         while  LINK > 0 cycle 
            SDX == SDS(LINK)
            LINK = SDX >> 19
            SDX = 0
         repeat 
         K = 43; ! not enough SDs
         -> VOUT
!
      finish 
ENOUGH:
      FL_CODES = FL_CODES ! UNAVA; ! mark file UNAVAILABLE while we 'adjust' it
      VV(ADDR(F_SEMA), F_SEMANO)
      -> INCREASE if  XPGS > 0
!
      if  CON > 0 > XPGS start ; ! file is connected and we are shrinking
         RECOVER2(CON, LASTSEG, NEWSECTS-1, PGS IN NEW LAST SECT); ! recover blocks
         J = LASTSEG; ! and amend conlist
         while  J >= CON + NEWSEGS cycle 
            DIROWN_CPOINT(J) = RESERVED
            J = J - 1
         repeat 
      finish 
!
      SD == FL_SD; ! de-allocate surplus pages
      N = 0
      while  N < OLDSECTS cycle 
         N = N + 1
         LINK = SD >> 19
         if  N >= NEWSECTS start 
            STARTP = (SD << 13) >> 13
            K = STARTP
            L = 32
            L = PGS IN OLD LAST SECT if  N = OLDSECTS
            if  N = NEWSECTS start 
               SDX == SD; ! leave SDX pointing at new last SD and
                                        ! discard any beyond
               SD = STARTP; ! clear link in new last sect
               L = L - PGS IN NEW LAST SECT
               STARTP = STARTP + PGS IN NEW LAST SECT
            finish 
            J = 0
            J = DEAL(FSYS, K, STARTP, L) if  L > 0
            ACTIVE BLOCK("CHSIZE1", FULL, FSYS) if  J = 91
            SD = 0 if  N > NEWSECTS
         finish 
         exit  unless  0 < LINK <= NSD
         SD == SDS(LINK)
      repeat 
      K = 87 and  -> OUT unless  LINK = 0 and  N = OLDSECTS
!
      IF  XPGS < 0 START ; ! we are reducing size of file
         -> RELOCATE IF  PGS IN NEW LAST SECT < 9
         -> UPDATE INDEX
      FINISH 
!
      B = SYSAD(BITKEY, FSYS); ! size remaining same, get addr of bitmap
      K = SDX; ! first page of last sect
      W = INTEGER(B + (K>>5<<2)); ! word in bitmap containing sect
!
      J = K & 31; ! start page within word
      -> RELOCATE IF  J > 0 ANDC   {sect not at start of word}
                      W << (J-1) >= 0  {previous page is free}
!
      J = (K + PGS IN NEW LAST SECT) & 31; ! page after this sect
      -> RELOCATE IF  J > 0 ANDC   { sect not at end of word}
                      W << J >= 0 { following page free}
!
      K = 0; ! release file and exit
      FL_CODES = FL_CODES & (¬UNAVA)
      -> OUT
RELOCATE:
!      WRSN("Dchsize RELOCATES " . FULL, PGS IN NEW LAST SECT) %IF XPGS = 0
!
      if  CON > 0 start 
         LASTSEG = CON + NEWSEGS - 1
         J = DSEGMENT(LASTSEG, 0)
         if  J = -1 start 
            WRSN("DCHSIZE RELOCATE SEG", LASTSEG)
         finish 
      finish 
!
      cycle  N = 1, 1, 10; ! have 10 attempts to move
         J = SMALLOC(STARTP, FSYS, PGS IN NEW LAST SECT, NO); ! NO = DONT CLEAR
         exit  unless  J = 0; ! give up
!
         J = MOVESECTION(FSYS, SDX, FSYS, STARTP, PGS IN NEW LAST SECT)
         if  J = 0 start 
            J = DEAL(FSYS, 0, SDX, PGS IN NEW LAST SECT)
            ACTIVE BLOCK("CHSIZE3", FULL, FSYS) if  J = 91
            SDX = STARTP
            if  CON > 0 c 
            then  BLOCK EXTEND(LASTSEG, (NEWSECTS - 1) & 1, c 
               STARTP, 0); ! adjust DA field in CBT entry
            exit ; ! success
         finish 
         J = DEAL(FSYS, 0, STARTP, PGS IN NEW LAST SECT)
         ACTIVE BLOCK("CHSIZE4", FULL, FSYS) if  J = 91
      repeat 
      -> UPDATE INDEX
!
INCREASE:   !-------------------------- INCREASE-----------------------
!
                                        !Get here with :
                                        ! - SD mapped to old last SD, with
                                        !   (possibly) more new SD's
                                        !   hanging on
      if  CON>0 start 
         if  OLDPGS & 63 > 0 start ; ! last segment not full
            REF NEW BLOCKS(LAST SEG)
            J = DSEGMENT(LAST SEG,0)
            if  J = -1 start 
               PRINTSTRING("DCHSIZE")
               WRITE(LASTSEG, 1)
               WRITE(SST(LASTSEG), 1)
               NEWLINE
            finish 
         finish 
         GIVE APF(APF,NOTDRUM,SLAVEBIT,CON)
      finish 
!
      if  OLDPGS & 31 > 0 start ; ! last section not full, so pages
                                  ! will be allocated to it. 
                                  ! Re-allocate the space and move
         ADD TO LAST SECT = XPGS; ! if no new sections reqd
         ADD TO LAST SECT = 32 - (OLDPGS & 31) if  XSECTS > 0; ! just fill up old....
         K=SMALLOC(STARTP,FSYS,PGS IN OLD LAST SECT+ADD TO LAST SECT,0); ! don't zero the pages
         if  K#0 then  -> INC RECOVER; ! FSYS full
         LINK = SD >> 19
         DA = (SD << 13) >> 13
         K=MOVE SECTION(FSYS,DA,FSYS,STARTP,PGS IN OLD LAST SECT)
         -> INC RECOVER unless  K = 0
         K = MOVESECTION(-1, 0, FSYS, STARTP+PGS IN OLD LAST SECT, c 
               ADDTO LAST SECT)
         -> INC RECOVER unless  K = 0
!
         J=DEAL(FSYS, 0,DA,PGS IN OLD LAST SECT)
         ACTIVE BLOCK("CHSIZE5", FULL, FSYS) if  J = 91
         SD = (LINK << 19) ! STARTP
!
         if  CON>0 start 
            ! Replace disc address for last block
            BLOCK EXTEND(LASTSEG, (OLDSECTS-1)&1, STARTP, c 
                  ADD TO LAST SECT)
         finish ; ! CON > 0
      finish 
!
      if  XSECTS > 0 start ; ! new sections are being added,
                             ! allocate them and then make and
                             ! fill in new cells
         SDX == SD; ! point to old last
         ZERALLOC = NO
         ZERALLOC = YES if  CON = 0; ! clear new bits only if file not connected
         cycle  J = 1, 1, XSECTS
            L=32
            if  J=XSECTS then  L=PGS IN NEW LAST SECT
            K=SMALLOC(STARTP,FSYS,L,ZERALLOC); ! Zero pgs if not connected else newcopy
            if  K#0 then  -> INC RECOVER; ! FSYS full
            SDX == SDS(SDX >> 19); ! move to next
            SDX = ((SDX >> 19) << 19) ! STARTP; ! and insert DA
            if  CON>0 start ; ! file is connected, connect new sections
               RELSEG=(OLDSECTS+J-1) >> 1
               K=SCONNECT(CON+RELSEG,(FSYS<<24)!STARTP,L,APF,1, c 
                NOTDRUM, SLAVEBIT, FLAGS)
               if  K#0 then  WRSN("DCHSIZE", K)
            finish 
         repeat 
      finish 
!
      if  CON>0 start ; ! update con table
         J=LASTSEG + 1
         while  J<CON + NEWSEGS cycle 
            DIROWN_CPOINT(J)=DIROWN_CPOINT(CON)
            J=J+1
         repeat 
      finish 
      -> UPDATE INDEX
!
INC RECOVER:
      if  CON > 0 c 
      then  RECOVER2(CON,CON+NEWSEGS-1,OLD SECTS-1,PGS IN OLD LAST SECT)
      LINK = SD >> 19
      SD = (SD << 13) >> 13; ! mark old last SD as end of list
      while  LINK > 0 cycle 
         SD == SDS(LINK)
         LINK = SD >> 19
         SD = 0
      repeat 
      FL_CODES = FL_CODES & (¬UNAVA)
      -> OUT
!
!
!
UPDATE INDEX:
      K = PP(ADDR(F_SEMA),F_SEMANO,FN)
      -> OUT unless  K = 0
      FL_PGS=NEWPGS
      F_TOTKB=F_TOTKB + XKB
!
      unless  FL_CODES & TEMPFS = NO start 
         ! temporary
         F_TEMPKB = F_TEMPKB + XKB
      finish 
!
      unless  FL_CODES & CHERSH = NO start 
         ! cherished
         F_CHERKB = F_CHERKB + XKB
      finish 
!
      FL_CODES=FL_CODES ! VIOLAT if  XSECTS>0 and  CON>0
      FL_CODES=FL_CODES & (¬UNAVA)
VOUT:
      VV(ADDR(F_SEMA), F_SEMANO)
OUT:
      RESULT  = OUT(K, "SSII")
END ; ! DCHSIZE
!
!-----------------------------------------------------------------------
!
!<DCONNECT
externalintegerfn  DCONNECT(string (31)FILE INDEX, FILE,
      integer  FSYS, MODE, APF, integername  SEG, GAP)
!
! Provided that the file is suitably permitted to the caller, the file of
! name FILE belonging to file index FILE INDEX on disc-pack FSYS (or -1)
! is connected into the caller's virtual memory.
!
! The bits in the parameter MODE have the following meanings (when set):
!     2**0     Read access required
!        1     Write access required
!        2     Execute access required
!        3     Write access by other processes to be allowed
!        4     New copy of file to be written
!        5     Communications mode
!        6     Not to be allocated space on the drum
!        7     Segment to be used as a process stack
!
! The purpose of bit 2**3 is to allow (read and) write access by more
! than one process to be achieved only when each user specifically allows
! the situation (by setting the bit in his request).
!
! Bits 2**1 or 2**3 may not be set in the request if bit 2**2 (execute
! access) is also set.
!
! SEG either specifies the segment number at which the file is to be
! connected (in the range 34 to 127), or is zero, indicating that the
! choice of segment number is to be left to Director.  If the result of
! the function is 0 or 34 (file already connected), SEG is set to the
! chosen segment number.
!
! GAP specifies the number of segments which are to be reserved for the
! file, even though the current size of the file may be less than that
! number of segments.  Attempts to specify a value of SEG which conflicts
! with this GAP, in subsequent connect requests before this file is
! disconnected, will be rejected.  
!
! If GAP is set to zero then no segments of virtual memory, other than
! those required by the current file size, are reserved for the file.
! If the result of the function is 0 or 34 (file already connected), GAP
! is set to the number of segments reserved for the file.
!
! APF may be used to specify the access permission field in the
! segment(s) being connected.  The bottom 9 bits are significant:
!
!                   1      4      4
!                  EXE-  WRITE   READ
!                  CUTE   ACR    ACR
!
! The read and write ACR values supplied must be greater than or equal to
! the ACR at which the calling program (subsystem) is running. If the APF
! parameter is set to zero, a value of X'1nn' is used, where n is the ACR
! at which the caller is executing.
!>
INTEGER  J,WAP,RAP
STRING (31)UNAME, INAME, FNAME, INDEX, FULL
      J = IN2(11)
      -> OUT UNLESS  J = 0
!
      J = UFO(FILE INDEX, FILE, UNAME, INAME, FNAME, INDEX, FULL)
      -> OUT UNLESS  J = 0
!
      J = 93
      ! MODE<0 means segment to be non-slaved
      IF  DTRYING<<7>=0 AND   C 
         (CHARNO(FNAME, 1)='#' OR  MODE<0) C 
         THEN  -> OUT
!
      J = 45
      -> OUT UNLESS  VAL(ADDR(SEG), 4, 1, DCALLERS PSR) = YES
      -> OUT UNLESS  VAL(ADDR(GAP), 4, 1, DCALLERS PSR) = YES
!
      RAP=APF&15
      WAP=(APF>>4)&15
      IF  RAP=0 THEN  RAP=DCALLERS ACR
      IF  WAP=0 THEN  WAP=DCALLERS ACR
      J = 8
      UNLESS  WAP>=DCALLERS ACR AND  RAP>=DCALLERS ACR THEN  -> OUT
      APF=X'100' ! (WAP<<4) ! RAP
      J=DCONNECTI(FULL,FSYS,MODE,APF,SEG,GAP)
      RESULT  = OUT(J, "NIL")
OUT:
      RESULT  = OUT(J, "SSIIIJJ")
END ; ! DCONNECT
!
!-----------------------------------------------------------------------
!
!<DCPUTIME
externalintegerfn  DCPUTIME
!>
      RESULT =(ACCTS_MUSECS - GETIT)//1000
END ; ! DCPUTIME
!
!-----------------------------------------------------------------------
!
!<DCREATE2
externalintegerfn  DCREATE2(string (31)FILE INDEX, FILE,
      integer  FSYS, NKB, TYPE, integername  DA)
!
! A file of name FILE is created, in file index FILE INDEX on disc-pack
! FSYS, of E Epages, where E is the smallest number of Epages containing
! NKB Kbytes.  The maximum size of file allowed is 16 Mbytes.  Subsystems
! requiring larger files should arrange that they be made up of subfiles
! comprising files created by this procedure.
!
! Bits in TYPE may be set:
!
!     2**0     For a temporary file (destroyed when the creating process
!              stops if the file was connected, or at System start-up).
!
!     2**1     For a very temporary file (destroyed when the file is
!              disconnected).
!
!     2**2     For a file which is to be zeroed when created.
!
!     2**3     To set "CHERISHed" status for the file.
!
!
! Temporary files are made into ordinary files (that is, the "temporary"
! attribute is removed) on being RENAMEd, OFFERed, TRANSFERred or
! PERMITted, and also explicitly by an appropriate call on procedure
! DFSTATUS.
!
! The disc address of the first section of the file is returned in DA.
!>
INTEGER  J
STRING (31)UNAME, INAME, FNAME, INDEX, FULL
      J = IN2(13)
      -> OUT UNLESS  J = 0
!
      J = UFO(FILE INDEX, FILE, UNAME, INAME, FNAME, INDEX, FULL)
      -> OUT UNLESS  J = 0
!
      J = 18
      -> OUT IF  CHARNO(FNAME, 1) = '#' C 
            AND  D CALLERS ACR >= DEFAULT SS ACR
!
      J = 93
      -> POK IF  UNAME = PROCUSER
      -> POK IF  DTRYING < 0
      -> POK IF  FILE INDEX PERM(INDEX, FSYS) & 2 > 0
      -> OUT
POK:
      J=8
      -> OUT UNLESS  0<=TYPE<=15 AND  TYPE&3#3
!
      J=DCREATEF(FULL,FSYS,NKB,(TYPE<<2) ! 1, LEAVE, DA)
      RESULT  = OUT(J, "NIL")
OUT:
      RESULT  = OUT(J, "SSIII")
END ; ! DCREATE2
!
!-----------------------------------------------------------------------
!
!<DCREATE
externalintegerfn  DCREATE(string (31)FILE INDEX, FILE, 
      integer  FSYS, NKB, TYPE)
INTEGER  DA
      RESULT  = DCREATE2(FILE INDEX, FILE, FSYS, NKB, TYPE, DA)
END ; ! DCREATE
!
! This is simply a call on DCREATE2
!>
!
!-----------------------------------------------------------------------
!
!<DDESTROY
externalintegerfn  DDESTROY(string (31)FILE INDEX, FILE, string (8)DATE,
      integer  FSYS, TYPE)
!
! File FILE belonging to file index FILE INDEX on disc-pack FSYS, is
! destroyed.  TYPE should be set to 1 to destroy a file from archive
! storage, otherwise it should be set to zero.  When TYPE=1, DATE should
! be set to the archive date.  DATE is ignored if TYPE=0.
!
! The procedure fails if 'OWNP' for the file is either zero (no access)
! or 8 (do not destroy).
!>
!  TYPE  =  0     destroy on-line file
!           1     destroy archive file
!
INTEGER  J
STRING (31)UNAME, INAME, FNAME, INDEX, FULL
      J = IN2(16)
      -> OUT UNLESS  J = 0
!
      J = UFO(FILE INDEX, FILE, UNAME, INAME, FNAME, INDEX, FULL)
      -> OUT UNLESS  J = 0
!
      -> POK IF  UNAME = PROCUSER
      -> POK IF  DTRYING < 0
      -> POK IF  FILE INDEX PERM(INDEX, FSYS) &2 > 0
      J = 93
      -> OUT
POK:
      IF  TYPE=0 C 
      THEN  J=DDESTROYF(FULL,FSYS,1)  C 
      ELSE  J=ADESTROY(FULL,DATE,FSYS,TYPE-1)
      RESULT  = OUT(J, "NIL"); ! monitoring done in support proc
OUT:
      RESULT  = OUT(J, "SS")
END ; ! DDESTROY
!
!-----------------------------------------------------------------------
!
!<DDISCONNECT
externalintegerfn  DDISCONNECT(string (31)FILE INDEX, FILE, 
      integer  FSYS, DSTRY)
!
! The file of name FILE belonging to file index FILE INDEX on disc-pack
! FSYS is disconnected from the caller's virtual memory.  Parameter
! DESTROY should be set either to 0 or 1.  If set to 1 the file will be
! destroyed, provided that it belongs to the process owner (not necessary
! if the process is privileged) and the "use-count" for the file is zero
! after disconnection.  Otherwise the  parameter is ignored.
!>
INTEGER  J
STRING (31)UNAME, INAME, FNAME, INDEX, FULL
      J=IN2(18)
      -> RES UNLESS  J = 0
!
      J=8
      -> RES UNLESS  0<=DSTRY<=1
!
      J = UFO(FILE INDEX, FILE, UNAME, INAME, FNAME, INDEX, FULL)
      -> RES UNLESS  J = 0
!
      J = 93
      IF  DTRYING << 7 >= 0 START ; ! not a privileged user
         -> RES IF  CHARNO(FNAME, 1) = '#'; ! can't disconnect #... files
         IF  UNAME # PROCUSER AND  DSTRY # 0 START 
            -> RES UNLESS  FILE INDEX PERM(INDEX, FSYS) & 2 > 0
         FINISH 
      FINISH 
!
      DSTRY=2 IF  DSTRY#0
      J = DDISCONNECTI(FULL, FSYS, DSTRY)
      RESULT  = OUT(J, "NIL")
RES:
!
      RESULT  = OUT(J, "SSII")
END ; ! DDISCONNECT
!
!-----------------------------------------------------------------------
!
!<DGETDA
externalintegerfn  DGETDA(string (31)FILE INDEX, FILE,
      integer  FSYS, ADR)
!
! This procedure provides the disc addresses of the sections of file FILE
! belonging to file index FILE INDEX on disc-pack FSYS.  Data is written
! from address ADR in the format
!
!     (%integer SECTSI, NSECTS, LASTSECT, SPARE, %integerarray DA(0:255))
!
! where SECTSI      is the size (in epages) of the sections (except
!                   possibly the final section)
!
!       NSECTS      is the number of sections, and hence the number
!                   of entries returned in array DA
!
!       LASTSECT    is the size (in epages) of the final section
!
! In each entry in the DA array, the top byte contains the FSYS number.
!>
integer  J,N,S,NSD,NSECTS
integer  FINDAD, FI
integer  PGS IN LAST SECT
integer  PAGS
STRING (31)UNAME, INAME, FNAME, INDEX, FULL
record (FF)name  F
record (FDF)name  FD
record (FDF)arrayname  FDS
integername  SD
integerarrayname  SDS
integerarrayname  UA
conststring (7)FN = "DGETDA "
      J = IN2(30)
      -> OUT unless  J = 0
!
      J = UFO(FILE INDEX, FILE, UNAME, INAME, FNAME, INDEX, FULL)
      -> OUT UNLESS  J = 0
!
      J = MAP FILE INDEX(INDEX, FSYS, FINDAD, FN)
      -> OUT UNLESS  J = 0
!
      F == RECORD(FINDAD)
      FDS == ARRAY(FINDAD+F_FDSTART, FDSF)
      SDS == ARRAY(FINDAD+F_SDSTART, SDSF)
      NSD = (F_FDSTART - F_SDSTART) >> 2
      J=32; ! FILE DOES NOT EXIST
      FI=NEW FIND(FINDAD, 0, FNAME)
      if  FI=0 then  -> VOUT; ! NOT EXIST
!
      FD == FDS(FI)
!
      J=5; ! FILE NOT READY
      if  FD_CODES&UNAVA#0 then  -> VOUT
!
      PAGS=FD_PGS
      J = 87
      -> VOUT IF  PAGS <= 0
      NSECTS = (PAGS + 31) >> 5
      PGS IN LAST SECT = PAGS - (NSECTS - 1) << 5
!
      J=45; ! USER AREA NOT AVAILABLE
      -> VOUT if  VAL(ADR,NSECTS<<2+16,1,DCALLERS PSR) = NO
!
      UA==ARRAY(ADR+16,IFT)
      INTEGER(ADR)=32
      INTEGER(ADR+4)=NSECTS
      INTEGER(ADR+8)=PGS IN LAST SECT
      INTEGER(ADR+12)=32; ! SPARE
!
      SD ==FD_SD; ! proceed down sections chain
      J = 87
      N=0
      while  N < NSECTS cycle 
         UA(N) = (FSYS<<24) ! ((SD<<13)>>13)
         N=N+1
         S = SD >> 19
         exit  unless  1 <= S <= NSD
         SD == SDS(S)
      repeat 
      J = 0 if  N = NSECTS and  S = 0
VOUT:
      VV(ADDR(F_SEMA), F_SEMANO)
OUT:
      RESULT  = OUT(J, "SSI")
end ; ! DGETDA
!
!-----------------------------------------------------------------------
!
!<DMDC
externalroutine  DMDC
!>
INTEGER  SEG, J, CELL
      CYCLE  SEG = LODSEG, 1, HISEG
         CELL = SST(SEG)
         IF  CELL # ENDSST START 
            J =  DSEGMENT(SEG, 0)
            WRS("DMDC") IF  J = -1
         FINISH 
      REPEAT 
END ; ! MDC
!
!-----------------------------------------------------------------------
!
!<DMODE
externalintegerfunction  dmode(integer  set,adr,command)
!
! ****** Kent version of DMODE ******
!
! If  'set'  =  1, then 'adr' specifies the address of N bytes (N<65) of
! TCP command data, the first being a length byte containing N-1,  which
! are  to be dispatched (via the buffers and control streams attached to
! the executive process DIRECT) to the TCP.  A copy of the  current  TCP
! mode settings is retained, and is used to return current mode settings
! to the user if requested.  The GETMODE action (function code 4) is not
! required,  since the local copy of the mode settings is always correct
! (as long as no other mechanism but DMODE is used to  set  TCP  modes).
! For  compatibility  with  ERCC,  GETMODE is accepted, but treated as a
! no-op.
!
! If 'set' = 0, then 'command' specifies a TCP command  byte  for  which
! the   corresponding   mode   data  are  required. 'adr'  specifies  an
! eight-byte area into which  the  relevant  mode  settings  are  to  be
! placed.   This interface routine, which accesses the local copy of the
! mode settings, is provided  to  enable  changes  to  be  made  to  the
! internal representation of the data without requiring user programs to
! be modified.
!
!   Possible error results: 8, 45, 61
!>
!
! Format of the record used to hold the local copy of  the  current  TCP
! mode  settings.  This record is 40 bytes long (8 bytes longer than the
! ERCC one), so it cannot be held in the UINF record (nor does  it  need
! to be, since no other process requires access to it).  Instead, an own
! copy of the record is maintained.
!
constantinteger  tmodelen = 40;         ! Length of TCP bulk setmode transfer
                                        ! (there are two extra bytes as well)
recordformat  tmodef(byteinteger  len,function,flags1,flags2,pads,dummy,
linelimit,pageleng,byteintegerarray  tabvec(0:15),byteinteger  del,can,
byteintegerarray  rawmask(0:15))      
!
integer  pt,step,flag,seg,gap,j,len,v,rawset,i
record (logf hdf)name  logh
record (fepf)arrayname  feps
record (procdatf)arrayname  proclist
constantrecord (uinff)name  uinf = 9<<18
!
! Bits in TCP mode byte 'flags1' (this is two bytes at ERCC)
! Bits not mentioned here are not used
!
constantbyteinteger  echo    = b'00000010'   { bit 1 }
constantbyteinteger  graph   = b'00000100'   { bit 2 }
constantbyteinteger  bin in  = b'00010000'   { bit 4 }
!
! Bits in TCP mode byte 'flags2' (this is two bytes at ERCC)
! Bits not mentioned here are not used
!
constantbyteinteger  flow    = b'00000001'   { bit 0 }
constantbyteinteger  video   = b'00001000'   { bit 3 }
constantbyteinteger  xtab    = b'00100000'   { bit 5 }
constantbyteinteger  raw     = b'01000000'   { bit 6 }
!
! Default  settings  for  TCP  modes.   These are used to initialise the
! local copy of the current settings.
!
constantbyteintegerarray  defmodes(1:tmodelen) = c 
0,video,0,0,80,24,1,6,9,12,15,18,40,80,160(8),127,24,0(16)
!
constantinteger  topg = 23, tops = 34
switch  dg(1:topg),ds(1:tops)
ownrecord (tmodef) t
owninteger  prev = 0;                   ! Time of previous call, or zero
owninteger  n = 0;                      ! Number of calls in rapid succession
owninteger  firstcall = 1;              ! Used to initialise local copy of modes
!
      flag = in2(43)
      -> out if  flag # 0
      !
      flag = 8
      -> out unless  0 <= set <= 1
      !
      flag = 45
      len = 1
      len = 16 if  set = 0
      -> out if  val(adr,len,1-set,0) = 0
      !
      if  firstcall = 1 then  start ;   ! Set up local copy of TCP modes
         firstcall = 0
         t_len = 35;                    ! Red tape header
         t_function = 22;               ! Bulk setmode function code
         move(tmodelen,addr(defmodes(1)),addr(t_flags1))
      finish 
      !
      -> set if  set = 1
      !
      ! Code to return a current mode setting
      ! -------------------------------------
      !
      flag = 0
      unless  0 < command <= topg then  command = 0
      -> dg(command)
      !
dg(*):
      flag = 8
      -> out
dg(1):  v=(¬(t_flags1)) & echo;        -> nonz;    ! Echo mode (0=off, 1=on)
dg(2):  v=t_pageleng;                  -> setv;    ! Page size
dg(3):  v=t_linelimit;                 -> setv;    ! Max line size
dg(7):  v=t_del;                       -> setv;    ! Delete char
dg(8):  v=t_can;                       -> setv;    ! Cancel char
dg(9):  v=t_flags1 & bin in;           -> nonz;    ! Binary input (0=off, 1=on)
dg(10): move(16,addr(t_tabvec(0)),adr); -> out;    ! Tab settings
dg(11): v=t_flags1 & graph;            -> nonz;    ! Graph mode (0=off, 1=on)
dg(14): v=t_pads;                      -> setv;    ! Number of pad chars
dg(15): v=t_flags2 & video;            -> nonz;    ! Video mode (0=off, 1=on)
dg(17): v=t_flags2 & flow;             -> nonz;    ! Flow control (0=off, 1=on)
dg(21): v=t_flags2 & xtab;             -> nonz;    ! Hard tabs mode (0=off, 1=on)
dg(23): v=t_flags2 & raw;              -> nonz;    ! Raw (Screed) mode (0=off, 1=on)
      !
nonz: v = 1 if  v # 0
setv: byteinteger(adr) = v
      -> out
      !
      ! Code to change TCP mode settings
      ! --------------------------------
      !
set:
      flag = 8
      len = byteinteger(adr)
      -> out unless  0 < len < 64
      flag = 45
      -> out if  val(adr,len+1,0,0) = 0
      flag = 8
      rawset = 0
      pt = 1
      !
      cycle 
         command = byteinteger(adr+pt)
         -> out unless  0 < command <= tops
         step = 1
         if  command = 10 then  start ; ! Set tabs
            -> out if  pt + 16 > len;   ! Must be last command
            step = len - pt
         finish  else  c 
         if  command = 22 then  step = tmodelen else  c 
                                        { Bulk setting of all modes
         if  command = 34 then  step = 16
                                        ! Bulk setting of raw mask
         -> out if  pt + step > len;    ! Not enough data
         !
         v = byteinteger(adr+pt+1);     ! First qualifier byte
         -> ds(command)
         !
ds(*):   -> out
ds(1):   if  v = 0 then  t_flags1 = t_flags1!echo else  c 
                         t_flags1 = t_flags1 & (¬echo); -> next
ds(2):   t_pageleng = v; -> next
ds(3):   t_linelimit = v; -> next
ds(4):   -> next;                       ! Getmode - null at Kent
ds(7):   t_del = v; -> next
ds(8):   t_can = v; -> next
ds(9):   if  v = 0 then  t_flags1 = t_flags1 & (¬bin in) else  c 
                         t_flags1 = t_flags1!bin in; -> next
ds(10):  move(16,adr+pt+1,addr(t_tabvec(0))); -> next
ds(11):  if  v = 0 then  t_flags1 = t_flags1 & (¬graph) else  c 
                         t_flags1 = t_flags1!graph; -> next
ds(14):  t_pads = v; -> next
ds(15):  if  v = 0 then  t_flags2 = t_flags2 & (¬video) else  c 
                         t_flags2 = t_flags2!video; -> next
ds(17):  if  v = 0 then  t_flags2 = t_flags2 & (¬flow) else  c 
                         t_flags2 = t_flags2!flow; -> next
ds(21):  if  v = 0 then  t_flags2 = t_flags2 & (¬xtab) else  c 
                         t_flags2 = t_flags2!xtab; -> next
ds(22):  move(tmodelen,adr+pt+1,addr(t_flags1)); -> next
ds(23):  if  v = 0 then  t_flags2 = t_flags2 & (¬raw) else  c 
                         t_flags2 = t_flags2!raw; -> next
ds(24):
ds(25):
ds(26):
ds(27):  t_rawmask(command-24) = v; rawset = 1; -> next
ds(28):  move(tmodelen,addr(defmodes(1)),addr(t_flags1)); -> next
ds(30):  v = x'ff' unless  v = 0
         t_rawmask(i) = v for  i = 0,1,15
         if  v = 0 then  t_flags2 = t_flags2 & (¬raw) else  c 
                         t_flags2 = t_flags2!raw; -> next
ds(34):  move(16,adr+pt+1,addr(t_rawmask(0))); -> next
         !
next:    pt = pt + step + 1
      repeat  until  pt >= len
      !
      ! If the user altered the raw mask, clear the bits he may not know about.
      !
      if  rawset # 0 then  start 
         t_rawmask(j) = 0 for  j = 4,1,15
      finish 
      !
      if  uinf_streamid>>24 # x'0e' then  start 
                                        ! Ignore if not a front end
         flag = 0
         -> out
      finish 
      !
      j = com_secsfrmn - prev;          ! Time since last call (or time of day)
      j = j + 24*60*60 if  j < 0;       ! Adjust for new day
      if  j < 15 then  n = n + 1 else  n = 0
!     j = ddelay(60-j) %if n > 20;      ! Wait until it's at least a min since last call
      prev = com_secsfrmn;              ! Remember time of this call
      !
      ! Now dispatch the data
      !
      seg = 0; gap = 0
      flag = dconnecti("VOLUMS.#LOGMAP",-1,11,0,seg,gap)
      -> out if  flag # 0
      logh == record(seg<<18+x'10000')
      proclist == logh_proclist
      feps == logh_feps
      output message to fep(feps,uinf_streamid<<8>>24,2,addr(t),tmodelen+2,c 
         uinf_streamid<<16>>16, proclist(uinf_pslot)_protocol)
      !
      flag = ddisconnecti("VOLUMS.#LOGMAP",-1,0)
out:
      result  = out(flag,"I")
end ;   ! of DMODE

!-----------------------------------------------------------------------
!
!<DNEWGEN
externalintegerfn  DNEWGEN(string (31)FILE INDEX, FILE, NEWGEN OF FILE,
      integer  FSYS)
!
! This procedure provides a means of introducing an updated version
! (i.e. a new generation) of file FILE belonging to file index FILE INDEX
! even though it may be connected in other users' virtual memories.
!
! If FILE is not connected in any virtual memory, a call on DNEWGEN is
! equivalent to destroying FILE and then renaming NEWGEN OF FILE to FILE,
! except that the new version of FILE retains the former FILE's access
! permissions.
!
! If FILE is connected in some virtual memory, then the filename
! NEWGEN OF FILE "disappears", and any subsequent connection of FILE into
! a virtual memory yields the contents of the new generation formerly
! held in NEWGEN OF FILE.
!
! When the number of users of a former copy of FILE becomes zero
! (i.e. when it is not connected in any virtual memory), that copy is
! destroyed.
!>
!
!
!
integer  FINDAD,J
integer  SD, PI, NPD
integer  OLDPGS, NEWPGS, OLDA, NEWA, OLDKB, NEWKB
STRING (31)UNAME, INAME, FNAME, INDEX, FULL
record (FF)name  F
record (FDF)arrayname  FDS
record (PDF)arrayname  PDS
record (PDF)name  PD
record (FDF)name  NEWFL,OLDFL
conststring (8)FN = "DNEWGEN "
      J=IN2(47)
      -> OUT UNLESS  J = 0
!
      J = UFO(FILE INDEX, FILE, UNAME, INAME, FNAME, INDEX, FULL)
      -> OUT UNLESS  J = 0
!
      J = 18
      -> OUT IF  S11OK(NEWGEN OF FILE)#0
      -> OUT IF  EQUAL(FNAME, NEWGEN OF FILE) = YES; ! INVALID NAME
!
      -> POK IF  UNAME = PROCUSER
      -> POK IF  DTRYING < 0
      -> POK IF  FILE INDEX PERM(INDEX, FSYS) &2 > 0
      J = 93
      -> OUT
POK:
      J=MAP FILE INDEX(INDEX,FSYS,FINDAD,FN)
      -> OUT UNLESS  J = 0
!
      F == RECORD(FINDAD)
      FDS == ARRAY(FINDAD + F_FDSTART, FDSF)
      PDS == ARRAY(FINDAD + F_PDSTART, PDSF)
      NPD = (F_SDSTART - F_PDSTART) // PDSIZE
!
      J=32; ! OLDNAME DOES NOT EXIST
      OLDA=NEWFIND(FINDAD,0,FNAME)
      if  OLDA=0 then  -> VOUT; ! DOES NOT EXIST
      OLDFL==FDS(OLDA)
!
      NEWA=NEWFIND(FINDAD,0,NEWGEN OF FILE)
      if  NEWA=0 then  -> VOUT
      NEWFL==FDS(NEWA)
!
      J=40; ! FILE IS CONNECTED
      if  NEWFL_USE#0 then  -> VOUT; ! NEW FILE IS CONNECTED
      J=5
      if  NEWFL_CODES&VIOLAT#0 or  OLDFL_CODES&VIOLAT#0 then  -> VOUT
      J=6
      if  NEWFL_CODES&OFFER#0 or  OLDFL_CODES&OFFER#0 then  -> VOUT
!
! There are now no more potential failures so do the
! common actions before splitting on file in use or no
!
!
      OLDPGS = OLDFL_PGS
      NEWPGS = NEWFL_PGS
!
      OLDKB = OLDPGS * 4
      NEWKB = NEWPGS * 4
!
      F_FILES = F_FILES - 1
      F_TOTKB = F_TOTKB - OLDKB
!
      unless  OLDFL_CODES & TEMPFS = NO start 
         ! old file temp
         F_TEMPFILES = F_TEMPFILES - 1
         F_TEMPKB = F_TEMPKB - OLDKB
      finish 
!
      unless  NEWFL_CODES & TEMPFS = NO start 
         F_TEMPFILES = F_TEMPFILES - 1
         F_TEMPKB = F_TEMPKB - NEWKB
      finish 
!
      unless  NEWFL_CODES & CHERSH = NO start 
         ! new file is cherished and its attributes are about to be lost
         F_CHERFILES = F_CHERFILES - 1
         F_CHERKB = F_CHERKB - NEWKB
      finish 
!
      unless  OLDFL_CODES & CHERSH = NO start 
         F_CHERKB = F_CHERKB - OLDKB + NEWKB
      finish 
!
      PI = NEWFL_PHEAD; ! discard any permissions on NEWFL
      while  0 < PI <= NPD cycle 
         PD == PDS(PI)
         PI = PD_LINK
         PD = 0
      repeat 
!
      if  OLDFL_USE=0 start 
         OLDFL_PGS = NEWPGS
         SD=OLDFL_SD
         OLDFL_SD=NEWFL_SD
         OLDFL_CODES=OLDFL_CODES & (¬TEMPFS)
         OLDFL_ARCH=OLDFL_ARCH ! 1; ! WRITTEN TO
         NEWFL = 0
         NEWFL_NAME = ".NULL"
         VV(ADDR(F_SEMA), F_SEMANO)
         J = NEWFILE DEAL(FINDAD,SD,OLDPGS)
         ACTIVE BLOCK(FN, FULL, FSYS) if  J = 91
         -> OUT
      finish 
!
!
! Then old file was connected. OLDFL is newest gen of the file (currently
! not marked OLDGE). Change that to OLDGE.
! Rename NEWGEN OF FILE to be FILE.
! Attributes to be same as previous newest generation.
! Move contents (excl name pointer) of old FD to new FD
!
      SD = NEWFL_SD; ! save it
      NEWFL = OLDFL; ! copy across all attributes
      NEWFL_SD = SD; ! but then reset SD (the disc addresses)
      NEWFL_PGS = NEWPGS; ! and its size
      NEWFL_USE = 0
      NEWFL_CODES = NEWFL_CODES & (¬TEMPFS)
      NEWFL_CODES2 = NEWFL_CODES2 & (¬(OLDGE!WRCONN!WSALLOW))
      NEWFL_ARCH = NEWFL_ARCH ! 1; ! written to
!
      OLDFL_CODES2 = OLDFL_CODES2 ! OLDGE
      OLDFL_PHEAD = 0; ! any permissions have been transferred to NEWFL
      J = 0
VOUT:
      VV(ADDR(F_SEMA), F_SEMANO)
OUT:
      RESULT  = OUT(J, "SSSI")
END ; ! DNEWGEN
!
!-----------------------------------------------------------------------
!
!<DRENAME
externalintegerfn  DRENAME(string (31)FILE INDEX, OLDNAME, NEWNAME, 
      integer  FSYS)
!
! File OLDNAME belonging to file index FILE INDEX on disc-pack FSYS is
! renamed NEWNAME.
!
! A file may not be renamed while it is connected in any virtual memory.
!>
!
!
!
integer  FI, FINDAD, J
STRING (31)UNAME, INAME, FNAME, INDEX, FULL
record (FF)name  F
record (FDF)name  FL
record (FDF)arrayname  FDS
conststring (8)FN = "DRENAME "
      J=IN2(70)
      -> OUT UNLESS  J = 0
!
      J = UFO(FILE INDEX, OLDNAME, UNAME, INAME, FNAME, INDEX, FULL)
      -> OUT UNLESS  J = 0
!
      J = S11OK(NEWNAME)
      -> OUT UNLESS  J = 0
!
      IF  D CALLERS ACR >= DEFAULT SS ACR AND  C 
         BYTEINTEGER(ADDR(NEWNAME)+1)='#' THEN  -> OUT; ! INVALID NAME
!
      -> POK IF  UNAME = PROCUSER
      -> POK IF  DTRYING < 0
      -> POK IF  FILE INDEX PERM(INDEX, FSYS) &2 > 0
      J = 93
      -> OUT
POK:
      J = MAP FILE INDEX(INDEX, FSYS, FINDAD, FN)
      -> OUT UNLESS  J = 0
!
      F == RECORD(FINDAD)
      FDS == ARRAY(FINDAD + F_FDSTART, FDSF)
!
      J =16; ! NEWNAME ALREADY EXISTS
      if  NEWFIND(FINDAD,0,NEWNAME)>0 then  -> VOUT; ! ALREADY EXISTS
      FI=NEWFIND(FINDAD,0,FNAME)
      J=32; ! OLDNAME DOES NOT EXIST
      if  FI=0 then  -> VOUT; ! DOES NOT EXIST
      FL==FDS(FI)
!
      J=5; ! FILE NOT READY
      if  FL_CODES&UNAVA#0 then  -> VOUT
!
      J=40; ! FILE IS CONNECTED
      if  FL_USE#0 then  -> VOUT; ! FILE IS CONNECTED
!
      FL_NAME = NEWNAME; ! No more failures, so can rename
      unless  FL_CODES&TEMPFS = NO start 
         ! renaming a temporary file has the curious effect
         ! effect of making it permanent !!
         F_TEMPKB=F_TEMPKB - FL_PGS*4
         F_TEMPFILES = F_TEMPFILES - 1
      finish 
!
      FL_CODES<-FL_CODES &(¬(UNAVA!TEMPFS))
      FL_ARCH=FL_ARCH ! 1; ! WRITTEN TO
      J=0
VOUT:
      VV(ADDR(F_SEMA), F_SEMANO)
OUT:
      RESULT  = OUT(J, "SSSI")
END ; ! DRENAME
!
!-----------------------------------------------------------------------
!
!<DSETIC
externalintegerfn  DSETIC(integer  KINSTRUCTIONS)
!
! This procedure is used to set a number of K instructions (1K=1024)
! which may be executed before an instruction-counter program error
! contingency is generated. The value of the parameter is subject to the
! following constraints:
!
!    it must lie between the nominal values for the number of K
!    instructions which the machine will execute in 1 second and
!    two hours.  The field KINSTRS in the public segment 48
!    communications record format (see Ref. 11) is required for
!    this calculation.
!
!    it must not, when added to the number of K instructions so far
!    executed this session, exceed the session K instruction limit
!    (value available in the UINF record of the process, as
!    described in this manual).
!>
! ALLOW RANGE APPROX 1 SEC TO TWO HOURS
! RESULT  =  0   OK
!            8   PARAM OUT OF RANGE
INTEGER  FLAG
      FLAG = IN2(256 + 77)
      -> OUT UNLESS  FLAG = 0
!
      *LSS_(3); ! SSR
      *OR_INH IC INTS
      *ST_(3)
!
      ASYNC INHIB = ASYNC INHIB + 1
!
      FLAG=8
      -> OUT UNLESS  COM_KINSTRS <= KINSTRUCTIONS <= 7200*COM_KINSTRS
!
      FLAG=0
      IF  SESSINSTRS + KINSTRUCTIONS >= SESSKIC + GRACE KIC START 
         KINSTRUCTIONS = SESSKIC + GRACE KIC - SESSINSTRS
         FLAG = 57 AND  -> OUT IF  KINSTRUCTIONS <= 0; ! END OF SESSION
      FINISH 
!
      IUPDATE(0,KINSTRUCTIONS)
OUT:
      ASYNC INHIB = ASYNC INHIB - 1
      *LSS_(3); ! SSR
      *AND_ALLOW IC INTS; ! UNINHIBIT IC INTERRUPTS
      *ST_(3)
!
      RESULT  = OUT(FLAG, "")
END ; ! DSETIC
!
!-----------------------------------------------------------------------
!
!<DSTOP
externalroutine  DSTOP(integer  REASON)
!
! This is the means by which a subsystem terminates its process.
! Director disconnects all files, disconnects interactive terminal
! streams, unlocks locked-down areas and destroys temporary files.  The
! integer REASON, which conventionally should be 100 for a normal stop or
! greater than 100 for an abnormal stop, is printed in a stopping message
! in the log file.  Values less than 100 are generated by Director itself
! in abnormal circumstances:
!
!    REASON     Meaning
!
!      0        Error condition noted in monitor printing (Dirlog).
!
!      1        A contingency occurred, but the Director procedure PRIME
!               CONTINGENCY (used to specify a subsystem contingency
!               procedure to be executed)had not previously been called.
!
!      2        A program error has occurred during execution of the
!               subsystem's contingency handling procedure and before
!               a call of Director procedure DRESUME.  (A call of DRESUME
!               indicates that diagnostic actions are complete, and more
!               specifically that the contingency procedure itself is
!               again ready to handle further contingencies.)
!
!      3        The number of program error and virtual store
!               contingencies for the process has exceeded a certain
!               fixed number, currently 32.  The purpose of this limit is
!               to terminate the contingency loop which will occur if the
!               subsystem contingency procedure executes satisfactorily
!               but the computation repeatedly "resumed to" immediately
!               fails.
!
!      4        The number of instructions executed by the process
!               exceeds the limit specified for the session.  In the case
!               of an interactive session this is currently a very large
!               number, but may be subject to the System Manager's
!               control.  In the case of a batch session, it is the
!               number specified when the batch job was submitted to the
!               SPOOLR process.  A subsystem should normally arrange,
!               through use of Director procedures DSETIC and DSFI
!               (TYPE=21), that this session limit is not violated, in
!               order to initiate the termination under its own control.
!
!      5        Not used.
!
!      6        A program error has occurred during execution of a
!               Director procedure.  Currently this may be caused by
!               supplying the wrong number of parameter words to a
!               procedure, later machine modification levels will enable
!               this condition to generate a "subsystem program error"
!               contingency.
!
!      7        Illegal call of Director procedure DRESUME: the value of
!               the LNB parameter is >0 but is not at least 5 words
!               below the machine register LNB contents at the time of
!               the call of DRESUME.
!
!      8        Illegal call of Director procedure DRESUME: the parameter
!               LNB specifies Director's contingency stack segment, which
!               is reserved.
!
!      9        Illegal call of Director procedure DRESUME, specifying
!               resumption of a computation in which a virtual store
!               error (address error) has just occurred.
!
!     10        Illegal call of Director procedure DRESUME, specifying
!               resumption of a computation when a contingency has not in
!               fact occurred.
!
!     11        A processor stack-switch has failed to occur, perhaps
!               when a call of Director procedure DRESUME has specified
!               (through parameter LNB) a segment which is not the normal
!               or other nominated stack segment.
!
!     12        Illegal call of Director procedure DASYNC INH to despatch
!               (accept) a queued asynchronous contingency, either when
!               no contingency is queued, or before the subsystem
!               contingency-handling procedure has indicated, through a
!               suitable call of Director procedure DRESUME, that it is
!               able to accept further contingency notifications.
!
!     13        An "emergency stop" command has been sent to the process
!               Director from the machine Operator console.  This REASON
!               for stopping may be printed in addition to one of the
!               above-specified stopping messages when certain of the
!               above failures occur during processing using the
!               subsystem-nominated processor stack segment.  In this
!               case the message printed previously specifies the true
!               reason for stopping.
!
!     14        A "stop" command has been sent from the Operator console
!               or from DIRECT as part of the System automatic close-down
!               sequence, or from the interactive communications system
!               following line or network-processor failure.
!>
INTEGER  SEG,J,STAKAD
STRING (31)FULL, FNAME, INDEX
RECORD (PARMF)P, Q
RECORD (PARMF)NAME  PP
RECORD (DIRCOMF)NAME  DIRCOM
RECORD (CTF)NAME  CT
OWNINTEGER  TIMES=0
OWNINTEGER  USECOUNTS = 1
SWITCH  ENTRY(0:3)
RECORD (HF)NAME  NH
INTEGER  GPINDAD
INTEGER  K, L
OWNSTRING (14)LOGMAP = "VOLUMS.#LOGMAP"
INTEGERFN  DRAT(INTEGER  A,C,B)
RESULT  = 0
END 
!
!
!
      IF  SESSION PENCE > 0 C 
          ANDC 
          OWNIND # 0 C 
      START 
         J = FUNDS(GPINDAD, OWNIND)
         J = J - SESSION PENCE
         J = 0 IF  J < 0
         NH == RECORD(GPINDAD)
         NH_INUTS = J
      FINISH 
!
      UNLESS  PAGE MON = 0 START 
         PP == RECORD(OUTPAD)
         PP = 0
         *OUT_20
         PAGE MON = 0
         WRS("PAGEFAULTS DSTOP")
      FINISH 
!
      CYCLE  J=0,1,2
         IF  DRS LOCKED(J)_DR0#0 THEN  K=DUNLOCK(DRS LOCKED(J)_DR1)
      REPEAT 
!
      J = DMAGCLAIM("", J, 1, 0) IF  TAPES CLAIMED > 0
      J = DDAP(DRAT, 0, 0) IF  DAP STATE > 0
      J = NEWAINDA("", -1, J) UNLESS  SAINDAD = 0
!
      -> ENTRY(TIMES)
ENTRY(0):
      UNLESS  GOTSEMA = 0 START 
         VV(SEMADDRHELD, SEMANOHELD)
         WRSS( DIROWN_SEMA HOLDER, " had sema at DSTOP")
      FINISH 
!
! IF CURRENT STACK IS NOT THE NORMAL STACK THEN PON AN XST, TO GET THE DISCONNECTS
! ETC DONE ON THE NORMAL STACK
      ! THIS TO BE REPLACED BY AN OUT TO STACK SWITCH TO LOCAL 4 SOMETIME
      *STSF_STAKAD
      IF  STAKAD>>18#NORMAL STACK SEG START 
         Q=0
         Q_DEST=((COM_ASYNC DEST + PROCESS)<<16) ! DIRDACT
         Q_S="XSTOP"
         Q_P6 = REASON
         DOUTI(Q)
         -> ENTRY(3); ! (BUT SHOULD NOT RETURN FROM THE PON)
      FINISH 
      TIMES=TIMES + 1 IF  TIMES < 3
      ASYNC INHIB=100; ! we can't treat async events when files have been disconnected
! INHIBIT IC INTERRUPTS
      *LSS_(3); ! SSR
      *OR_INH IC INTS
      *ST_(3)
!
      HOTTOPN = 0; ! prevents any further accesses to HOTTOP 
                   ! which may be a file
      OWNIND = 0; ! switch off IUPDATE
      FACILITYA = 0; ! ...and procedure counting
!
      COMMS CLOSEDOWN
!
                                        ! DIVERT ANY FURTHER MSGS TO
                                        ! MAINLOG AS WE ARE ABOUT TO
                                        ! DISCONNECT DIRLOG
      IF  PROCESS = 1 START 
         WRS("DSTOP DISCONNECTING")
         STOP FEPS
      FINISH 
      LOG ACTION = DT ! LOG
      DIRLOG AD = 0
!
      IF  "FCHECK"#PROCUSER#"DIRECT" AND  UINF_REASON = BATCH START 
         REASON = (UINF_PRIORITY<<24) ! (REASON<<8>>8)
         DAP INTERFACE(3) IF  UINF_DAPSECS > 0; ! its a DAP batch job
      FINISH 
ENTRY(1):
      IF  USECOUNTS = 1 START 
         CYCLE  SEG = 7, 1, HISEG
            CT == DIROWN_CONLIST(DIROWN_CPOINT(SEG))
            FULL = CT_FULL
            IF  FULL->INDEX.(".").FNAME AND  C 
                (PROCESS # 1 OR  EQUAL(FULL, LOGMAP) = NO) C 
            START 
               ! We want the logmap file to be (disconnected and) destroyed
               ! only by a consistency check.
               J=DDISCONNECTI(FULL,CT_FSYS,1)
               IF  J#0 THEN  DERR2(FULL, 6, J)
            FINISH 
         REPEAT 
      FINISH 
ENTRY(2):
      CYCLE  SEG = 7, 1, HISEG
         DCHAIN(SEG, 0)
      REPEAT 
ENTRY(3):
      IF  PROCUSER # "DIRECT" AND  USECOUNTS = 1 START 
         USECOUNTS = 0
         IF  PROCUSER # "FCHECK" C 
         THEN  J = DISC USE COUNT(PROCFSYS,-1); ! for #SIGSTK. All other files have been
                                      ! dealt with.
         CYCLE  J=99,-1,0
            K = FSYS USECOUNT(J)
            IF  K # 0 C 
            THEN  DOPER2("Fsys ".ITOS(J)." usecount".ITOS(K))
            WHILE  K > 0 CYCLE 
               L = DISC USE COUNT(J, -1)
               K = K - 1
            REPEAT 
         REPEAT 
      FINISH 
!
      P=0
      P_DEST=X'FFFF0000' ! USER STOPS DACT
      P_P1=PROCESS
      J=DPON3I("VOLUMS",P,0,1,PONANDCONTINUE) UNLESS  PROCUSER="VOLUMS"
      ! SEND PROCESS-STOPPING NOTIFICATION TO PROCESS 1
      PP==RECORD(OUTPAD)
      PP=0
      ! DEST is set up by the local controller, which PONs the message
      PP_P1=SESSINSTRS; ! Kinstructions
      PP_P2=INVOC
      STRING(ADDR(PP_P3))=PROCUSER
      PP_P5=REASON
      PP_P6=ACCTS_PTRNS
!
      IF  PROCESS = 1 START 
         WRS("PROCESS 1 CLOSING DOWN")
         CYCLE  L = 0, 1, 63; ! 4096 CHS TO MAINLOG TO FLUSH IT OUT
            SYMBOLS(64, '*')
            NEWLINE
         REPEAT 
      FINISH 
!
      *OUT_0
END ; ! DSTOP
!
!-----------------------------------------------------------------------
!
!<FBASE
externalintegerfn  FBASE(integername  LO, HI, integer  FSYS)
!
! This procedure returns the characteristics of an on-line disc.
!
!        LO is set to X40 for an ordinary disc and X800 for an IPL disc
!
!        HI is set as follows:
!           EDS  80   X3F1F
!           EDS 100   X59F3
!           EDS 160   X8F6F
!           EDS 200   XB3E7
!           EDS 640   X24797
!>
RECORD (DDTF)NAME  DDT
RECORD (PROPF)NAME  PROP
INTEGER  ENTAD, J
      J = DDT ENTRY(ENTAD, FSYS)
      IF  J = 0 START 
         DDT == RECORD(ENTAD)
         LO = DDT_BASE; ! bitmap+indexes+filespace
         PROP == RECORD(DDT_PROPADDR)
         HI = PROP_TOTPAGES - 1
      FINISH 
      RESULT  = J
END ; ! FBASE
!
!-----------------------------------------------------------------------
!
!<FBASE2
externalintegerfn  FBASE2(integer  FSYS, ADR)
!
! This returns the characteristics of an on-line disc in a record
! of format DISCDATAF at address ADR
!>
RECORD (DDTF)NAME  DDT
RECORD (PROPF)NAME  PROP
INTEGER  J, ENTAD, HIBIT, TYPE, K
RECORD (DISCDATAF)NAME  DATA
CONSTINTEGER  TOPTYPE = 5
CONSTINTEGERARRAY  BITSIZE(1:TOP TYPE) = X'1000'(2), X'2000'(2), X'5000'
CONSTINTEGERARRAY  NNTSTART(1:TOP TYPE) = X'7000'(4), X'A000'
CONSTINTEGERARRAY  NNTSIZE(1:TOP TYPE) = X'4000'(4), X'1FF8'
CONSTINTEGERARRAY  NNTTOP (1:TOP TYPE) = 1364(4), 681
CONSTINTEGERARRAY  NNTHASH(1:TOP TYPE) = 1361(4), 667
CONSTBYTEARRAY  INDEXSTART(1:TOP TYPE) = 12(5)
CONSTINTEGERARRAY  FILESTART(1:TOP TYPE) = 1024(5)
CONSTINTEGERARRAY  HI(1:TOP TYPE) = X'3F1F', X'59F3', X'8F6F',
                  X'B3E7', X'24797'
      J = DDT ENTRY(ENTAD, FSYS)
      -> OUT UNLESS  J = 0
!
      DDT == RECORD(ENTAD)
      PROP == RECORD(DDT_PROPADDR)
      HIBIT = PROP_TOT PAGES - 1
      TYPE = -1
      CYCLE  K = 1, 1, TOP TYPE
         TYPE = K AND  EXIT  IF  HIBIT = HI(K)
      REPEAT 
      J = 8 AND  -> OUT IF  TYPE < 0
!
      DATA == RECORD(ADR)
!
      DATA_START = DDT_BASE
      DATA_BITSIZE = BITSIZE(TYPE)
      DATA_NNTSTART = NNTSTART(TYPE)
      DATA_NNTSIZE = NNTSIZE(TYPE)
      DATA_NNTTOP  = NNTTOP(TYPE)
      DATA_NNTHASH = NNTHASH(TYPE)
      DATA_INDEXSTART = INDEX START(TYPE)
      DATA_FILESTART  = FILE START(TYPE)
      DATA_END = HIBIT
OUT:
      RESULT  = J
END ; ! FBASE2
!
!-----------------------------------------------------------------------
!
!<GETAVFSYS
externalroutine  GET AV FSYS(integername  N, integerarrayname  A)
!
! This procedure supplies the numbers of the disc-packs currently
! on-line.  Array A, which should be declared (0:99), is filled from
! A(0), A(1), .....  with as many numbers as there are on-line EMAS
! disc-packs, and N is set to the number of entries returned.
! By on-line we mean that the disk must be
!     - mounted
!     - consistency checked (CCK'd)
!     - not closing
! The IPL disc is always placed first in the list.
!>
INTEGER  J
INTEGERARRAY  LA(0:99)
!
      J = IN2(88)
      -> OUT UNLESS  J = 0
!
      -> OUT UNLESS  VAL(ADDR(N), 4, 1, DCALLERS PSR) = YES
!
      GET AV FSYS2(0,N,LA)
!
      -> OUT UNLESS  N > 0
      -> OUT UNLESS  VAL(ADDR(A(0)), 4*N, 1, DCALLERS PSR) = YES
!
      MOVE(N<<2, ADDR(LA(0)), ADDR(A(0)))
OUT:
      J = OUT(J, "")
END ; ! GET AV FSYS
!
!-----------------------------------------------------------------------
!
!<PRINTMP
externalroutine  PRINTMP(integer  SEG1, SEG2)
!
! PRINTMP stands for PRINT Master Page tables. The procedure lists
! details of the segments SEG1 to SEG2 to DIRLOG. If SEG2 is zero,
! then HISEG is used, a value supplied by Supervisor.
!>
INTEGER  SEG,ENT
INTEGER  J, SAVEDT
!
!
!
ROUTINE  PSECTION
INTEGER  DA, J
RECORD (DISCDATAF)DATA
      WRITE(SEG, 3)
      PRINTSTRING(" X")
      PRINTSTRING(HTOS(SEG, 2))
      DA = CBTA(ENT)_DA
      PRX(DA,8)
      PRX(CBTA(ENT)_AMTX, 4)
      SPACE
      PRX(CBTA(ENT)_TAGS, 2)
      SPACES(2)
      PRX(CBTA(ENT)_LINK, 2)
      SPACE
      PRX(ST(SEG)_APFLIM, 8)
!
      IF  SEG >= LODSEG AND  32#SEG#33 START 
         J = FBASE2(DA>>24, ADDR(DATA))
!
         UNLESS  DATA_FILESTART <= DA<<8>>8 <= DATA_END C 
         THEN  PRINTSTRING("*** OUT OF RANGE ***")
      FINISH 
END 
!
!
!
      J = IN2(92)
      -> OUT UNLESS  J = 0
!
      SEG2=HISEG IF  SEG2=0
      WRS("-------------- MASTER PAGE TABLES -------------------")
!
      SAVE DT = LOG ACTION & DT
      LOG ACTION = LOG ACTION - SAVE DT
!
      WRS(" SEGMENT CBTENTRY")
      WRS(" DEC HEX DISCADDR AMTX TAGS LINK  APFLIM   USER.FILE")
!
      CYCLE  SEG = SEG1,1,SEG2
         ENT=SST(SEG)
         IF  ENT#ENDSST START 
            PSECTION
            SPACES(2)
            WRS(DIROWN_CONLIST(DIROWN_CPOINT(SEG))_FULL)
            IF  ENT > CBT1 THEN  ENT = ENT+1 AND  PSECTION; ! DOUBLE SECTION
            NEWLINE
         FINISH 
      REPEAT 
!
      LOG ACTION = LOG ACTION ! SAVE DT
OUT:
      J = OUT(J, "")
END ; ! PRINTMP
!
!-----------------------------------------------------------------------
!
INCLUDE  "PD22S_B05PROCS"
ENDOFFILE