CONSTSTRING (16) VSN = "GROPE23 1/4/85"
RECORDFORMAT  PARMF(INTEGER  DEST,SRCE,P1,P2,P3,P4,P5,P6)
CONSTBYTEINTEGERARRAY  HEXDS(0:15)='0','1','2','3','4','5','6','7',
                                   '8','9','A','B','C','D','E','F'
EXTERNALINTEGERFNSPEC  REALISE(INTEGER  VAD)
EXTERNALSTRINGFNSPEC  HTOS(INTEGER  N,PL)
EXTERNALROUTINESPEC  PRHEX(INTEGER  N)
EXTERNALSTRINGFNSPEC  STRINT(INTEGER  N)
EXTERNALROUTINESPEC  WAIT(INTEGER  MILLISECS)
EXTERNALROUTINESPEC  DUMPTABLE(INTEGER  T,A,L)
EXTERNALINTEGERSPEC  NDISCS
CONSTINTEGER  REAL0ADDR=X'81000000'
!
! LP repertoire addresses and lengths for each of 16 cartidge settings
OWNINTEGERARRAY  REPERTOIRE ADDR(0:15)
OWNINTEGERARRAY  REPERTOIRE LEN(0:15)
!----------------------------------------------------------------------------------------------------
! %CONSTINTEGERARRAY LP96REP(0:23)=%C follows
ENDOFLIST 
CONSTINTEGERARRAY  LP96REP(0:23)=C 
X'F0F1F2F3',X'F4F5F6F7',X'F8F94B60',X'7B6B614E',X'C1C2C3E9',
X'C4E4E2E3',X'C7C57DC8',X'D1D2D85C',X'D4D5D6D7',X'D9C9C6D3',
X'E5E6E7E8',X'7E4D505D',X'4C6D3F6E',X'5B7A7C4F',X'6C5E7F6F',
X'4AE05F5A',X'A8A979F0',X'81828384',X'85868788',X'89919293',
X'94959697',X'9899A2A3',X'A4A5A6A7',X'C06AA1D0'
!
LIST 
! %CONSTINTEGERARRAY LP384REP(0:95)= %C follows
ENDOFLIST 
CONSTINTEGERARRAY  LP384REP(0:95)=  C 
X'F0F1F2F3',X'F4F5F6F7',X'F8F94B9C',X'7B6B614E',
X'C1C2C3E9',X'C4E4E2E3',X'C7C57DC8',X'D1D2D85C',
X'D4D5D6D7',X'D9C9C6D3',X'E5E6E7E8',X'7E4D505D',
X'6C5E7F6F',X'4AE05F5A',X'4C6D3F6E',X'5B7A7C4F',
X'81828384',X'85868788',X'89919293',X'F0F1F2F3',
X'F4F5F6F7',X'F8F94B60',X'94959697',X'9899A2A3',
X'A4A5A6A7',X'A8A979F0',X'9EADEFCA',X'7B6B614E',
X'C1C2C3E9',X'C4E4E2E3',X'C7C57DC8',X'D1D2D85C',
X'D4D5D6D7',X'D9C9C6D3',X'E5E6E7E8',X'7E4D505D',
X'6C5E7F6F',X'4AB7A05A',X'F0F1F2F3',X'F4F5F6F7',
X'F8F94B60',X'4CF08B6E',X'5B7A7C4F',X'C06A75D0',
X'9A6D749B',X'FCEAAFED',X'ACAB8F8E',X'8DB5B4B3',
X'787776DC',X'DDDEDFB8',X'B9BABBB0',X'7B6B614E',
X'C1C2C3E9',X'C4E4E2E3',X'C7C57DC8',X'D9C9C6D3',
X'D1D2D85C',X'F0F1F2F3',X'F4F5F6F7',X'F8F94B60',
X'D4D5D6D7',X'E5E6E7E8',X'7E4D505D',X'6C5E7F6F',
X'4AE05F5A',X'4CF08B6E',X'5B7A7C4F',X'A8A979F0',
X'81828384',X'85868788',X'89919293',X'94959697',
X'9899A2A3',X'A4A5A6A7',X'B1B2FAFB',X'C1C2C3E9',
X'F0F1F2F3',X'F4F5F6F7',X'F8F94B60',X'7B6B614E',
X'C4E4E2E3',X'C7C57DC8',X'D9C9C6D3',X'D1D2D85C',
X'D4D5D6D7',X'E5E6E7E8',X'7E4D505D',X'6C5EDBCB',
X'4AB7A05A',X'4CF08B6E',X'5B7A7C4F',X'EBBCA1BD',
X'8CAEBFBE',X'B6AAFDFE',X'9DEE80DA',X'C06D6AD0'
LIST 
IF  SSERIES=YES START 
EXTERNALROUTINE  DCU GROPE(RECORD (PARMF)NAME  P)
EXTRINSICINTEGER  FEP MAP
EXTERNALINTEGERFNSPEC  PINT
EXTERNALROUTINESPEC  OPMESS(STRING (63)S)
EXTERNALROUTINESPEC  RETRY REPORTING(INTEGER  PARM)
ROUTINESPEC  FIRE IO(INTEGER  PORT,LONGINTEGER  ACT)
ROUTINESPEC  FORM TABLES(INTEGER  TABAD,TOP TAB ENT)
ROUTINESPEC  FORMAT COMMS AREA(INTEGER  TABAD,DCUNO,CAA)
ROUTINESPEC  INVALIDATE(INTEGER  ENT)
ROUTINESPEC  NEW ENTRY(INTEGER  DEVTYPE,SPSSM,PROPS0,PROPS1,AUTO)
ROUTINESPEC  REMEMBER(INTEGER  INF)
ROUTINESPEC  DO(INTEGER  COMMAND,DATAD,LEN)
ROUTINESPEC  FORGETMENOT
ROUTINESPEC  INIT RES PIC(INTEGER  A,L)
SYSTEMROUTINESPEC  MOVE(INTEGER  L,F,T)
RECORDFORMAT  ISTF(INTEGER  LNB, PSR, PC, SSR, SF, IT, IC, SP)
RECORD (ISTF)NAME  IST
RECORD (ISTF) SAVE IST
RECORDFORMAT  DCUTF(BYTEINTEGER  FLAGS,DEVTYPE,SPAREB,LINK, C 
   INTEGER  PROPS0,PROPS1,DEV ENT BASE,UTAD,SPSSM,MNEMONIC,  C 
   BYTEINTEGER  MECHINDEX,PROPS03,SERVRT,STATE)
RECORDFORMAT  UTEF(INTEGER  PD,PP,BYTEINTEGER  FMN,SP,STRM,FLAGS,  C 
         INTEGER  TCBA,A1,A2,A3,A4,IDEST,I1,I2,I3,S1,S2,L1,L2)
CONSTINTEGER  SLOTSI=32; ! =LENGTH OF ABOVE FORMAT
RECORDFORMAT  TCBF(INTEGER  COMMAND,STE,LEN,DATA,NTCB,RESP, C 
      INTEGERARRAY  PREAMBLE,POSTAMBLE(0:3))
CONSTINTEGERARRAY  ADAPTOR BYTES(0:15)=C 
   0, 0, 0,160,512, 480, 600, 0, 0, 0, 0, 0, 0, 0, 600, 0
!  NA PT PR CP CR   MT   LP   GP OP GU DR NA CT SU FE   NA
! ABOVE, THE NO OF BYTES FOR LPADAPTORS INCLUDES THE 256 BYTES FOR A
! TRANSLATE TABLE
!***
!     USE ENTFORM FROM GDC ***
!***
RECORDFORMAT  ENTFORM(INTEGER    C 
   SER, SPSSM, PROPADDR, SECS SINCE, CAA, TCBA,   C 
   BYTEINTEGER  MECH,ATTN,HALFINTEGER  ALTRT, INTEGER  SPARE1,  C 
   STATE, RESP0, RESP1, SENSE1, SENSE2, SENSE3, SENSE4,  C 
   REPSNO, BASE, ID, DLVN, MNEMONIC,  C 
   ENTSIZE, SPARE2, SPARE3, UTCB AD, SENSDAT AD, LOGMASK, TRTAB AD,  C 
   UA SIZE, UA AD, TIMEOUT,PROPS0,PROPS1)
CONSTINTEGER  ENT FORM BYTES=128; ! =LENGTH OF ABOVE RECORD FORMAT
! THIS NEXT CONSTANT IS IN WORDS, AND INCLUDES
!     LENGTH OF ENTFORM                 =32
!     LENGTH OF DCU'S TCB               =14
!     TOTAL                              46
CONSTINTEGER  DEV ENTRY BASIC=46; ! WORDS, SIZE OF FIXED PART OF COMMS AREA RECORD FORMAT
CONSTHALFINTEGERARRAY  TIMEOUT SECONDS(0:15)=  C 
   10, 60, 60,600,300, 10, 60, 10, 10, 10, 10, 10, 10, 10, 3, 10
!  NA  PT  PR  CP  CR  MT  LP  GP  OP  GU  DR  NA  CT  SU  FE NA
OWNBYTEINTEGERARRAYFORMAT  BIFT(0:511)
OWNBYTEINTEGERARRAYFORMAT  LBIFT(0:2047)
OWNINTEGERARRAYFORMAT  IFT(0:1023)
CONSTINTEGER  MT=5, LP=6, OP=8, FE=14
CONSTINTEGER  DISC PCM=9,EDS100=X'33',EDS200=X'35',EDS80=X'37',FDS160=X'39',FDS640=X'3B'
CONSTINTEGER  EDS ADAPTOR BYTES=1120;   ! 14 TCBs
CONSTINTEGER  EDS Q SPACE=32;           ! instead of _PROPS0,PROPS1 then:
         ! PROPS,STATS1,STATS2,bytint QSTATE,PRIO,SP1,SP1, c
         ! LQLINK,UQLINK,CURCYL,SEMA,TRLINK,SPARE)
CONSTINTEGER  REAL0 SEG=X'2040'; ! PUBLIC 64(DEC) MAPPED TO REAL 0
!
!
! ------ IDLES -----
!                             FF00      TOO MANY DCU'S (>8)
!                             FF01      TOO MANY SLOTS (>256) OR
!                                       SUPPLIED TABLE TOO SMALL
!                             FF02      TOO MANY SLOTS ON ONE DCU
!                                       (ARRAY IN THIS RT)
!                             FF03      TOO MANY MAGTAPE STREAMS (>32)
!                                       (IDLE IN 'FORM TABLES' ABOVE)
!
!                             FF04      TOO MANY OPER STREAMS (>7)
!                                       (IDLE IN 'FORM TABLES' ABOVE)
!                             FF05      SUPPLIED TABLE TOO SMALL
!                                       (RT CHECKLIM IN RT FORM TABLES)
!                             FF06      TCB/SENSDAT
!                                       NOT COMPATIBLE (CHECK ON THIS PROGRAM!)
!
!                             FF07      DCU ACTIVATE FAILED
!
INTEGER  CAA,STRM,RSTRM,TOPSTRM
INTEGER  MECH,MPROP,AUTO,FORM STYLE,FORM LEN,NEW CAA
INTEGER  I,J,K,L,PT,DCUHN,DCU2,RESP0,RESP1
INTEGER  DEV,DACT
INTEGER  TABAD,CURNR
INTEGER  PROPDATADDR,SENSDATADDR
INTEGER  PROPS,PROPS1
INTEGER  START STREAM,LAST STREAM
INTEGER  AWORDA
INTEGER  TCBA
INTEGER  CART,A,S
RECORD (TCBF)NAME  TCB,TCB2
RECORD (UTEF)NAME  UT
INTEGERARRAY  ACT(0:1)
LONGINTEGER  LA
RECORDFORMAT  RF(INTEGER  STREAM,RESP0,RESP1,PROPS0,PROPS1,  C 
      SENS0,SENS1,SENS2)
RECORD (RF)NAME  R
INTEGERARRAYNAME  TABLE
!
!
! BYTE OFFSETS FROM TCB ADDRESS --
CONSTINTEGER  PROPDAT OFFSET=X'40'
CONSTINTEGER  SENSDAT OFFSET=X'48'
CONSTINTEGER  LP REP OFFSET=X'58'
CONSTINTEGER  LP4B=X'41'
CONSTINTEGER  REMENTSI=8; ! NO OF WORDS REMEMBERED BY 'REMEMBER'
CONSTINTEGER  MAXDCUNO=7
CONSTINTEGER  CONNECT=0,SENDPROP=X'2C40400E',SENSE=X'2C404004'
CONSTINTEGER  INITIALISE=X'2C404081',LOAD REP=X'2C4040A5'
CONSTINTEGER  WRITE CONTROL=X'2C404085',READ=X'2C404002'
OWNLONGINTEGER  STATUS POLL=X'0016161604212105'
               ! see PSD 4.2.13 sect. 3.6.4
               ! only guessing tho!!
CONSTINTEGER  TERMINATED=X'10000000'
CONSTINTEGER  CR80=X'0C000000'
CONSTINTEGER  MAX RESPONSE BYTES=X'C00'; !***PROTEM
!%CONSTINTEGER MAX RESPONSEBYTES=X'1000'; ! LIMIT TO ARRAY RESPONSES
CONSTINTEGER  MT6PROP=X'00000100'; ! BIT IN BYTE 2 OF MT PROP CODES
CONSTINTEGER  ZX=11;                    ! dummy device
!
OWNINTEGER  SETUP=0
!
! ENOUGH FOR 128 TAPE DECKS @ 8 WORDS EACH
INTEGERARRAYNAME  RESPONSES
OWNINTEGER  NR
OWNINTEGER  TOP TAB ENT
OWNINTEGER  DCUNO=-1
OWNINTEGER  LHWDCU=MAXDCUNO+1
OWNINTEGER  HHWDCU
OWNINTEGER  UTAD=UTVA;                  ! unit table base
OWNINTEGER  DDT NO
OWNINTEGER  LP INIT WORD=0
OWNINTEGER  SPARE SLOT
! G2NEXT IS INCREMENTED AT EACH DACT=2 ENTRY TO DCU GROPE.
! G2ZERO GIVES THE ORIGINAL VALUE, IE. THESE TWO MUST START OFF THE SAME.
CONSTINTEGER  G2ZERO=8
OWNINTEGER  G2NEXT=8; ! TABLE ENTRY AT WHICH FINAL C/A ADDRS START
!
SWITCH  GROPE(1:3)
SWITCH  GDEV(0:15)
      !* P_P1 :- %BYTEINTEGER STRMS,CCA SEG,DCU NO.,SCU PORT
      !* DCU2s have STRMS zero
!*
      RESPONSES==ARRAY(REAL0 SEG<<18 + X'2000',IFT)
      DACT=P_DEST&X'FFFF'
      UNLESS  0<DACT<=3 THEN  ->OUT
      TABAD=P_P2
      TABLE==ARRAY(TABAD,IFT)
      IF  SETUP=0 START 
         SETUP=1
         CYCLE  J=0,1,MAX RESPONSEBYTES>>2-1
            RESPONSES(J)=X'88888888'
         REPEAT 
         FOR  J=0,1,15 CYCLE 
            REPERTOIRE ADDR(J) = ADDR(LP96REP(0))
            REPERTOIRE  LEN(J) = 96
         REPEAT 
         REPERTOIRE ADDR(3) = ADDR(LP384REP(0))
         REPERTOIRE  LEN(2) = 48
         REPERTOIRE  LEN(3) = 384
         REPERTOIRE  LEN(4) = 64
         TABLE(0)=47
         TABLE(1)=48;                   ! start of DCU table
         TABLE(2)=-1;                   ! no. of slots
         TABLE(3)=0;                    ! no. of DCUs
      FINISH 
      PT=P_P1&255
      IF  P_P1>>24=0 THEN  DCU2=YES ELSE  DCU2=NO AND  AWORDA=X'60000000'!PT<<22
      DCUHN=P_P1>>8&15
      -> GROPE(DACT)
!*
GROPE(1):                               ! initailise & grope
!*
      CAA=P_P3
      TOP TAB ENT=P_P4
      DCUNO=DCUNO+1
      IF  DCUNO>MAXDCUNO START 
         *IDLE_X'FF00'
      FINISH 
      TABLE(3)=TABLE(3)+1
      TCBA=CAA+32
      TCB==RECORD(TCBA)
      TCB=0
      PROPDATADDR=TCBA+PROPDAT OFFSET
      SENSDATADDR=TCBA+SENSDAT OFFSET
      IF  DCU2=YES START 
         START STREAM=1
         LAST STREAM=255
         TABLE(24+DCUNO)=32;            ! no CCA required
      FINISH  ELSE  START 
         ACT(0)=X'1400';                !temp CCA
         ACT(1)=REALISE(TCBA&X'FFFC0000')!X'80000001'
         J=0
         I=PINT AND  J=J+1 UNTIL  I=0 OR  J=100;   !lose outstanding ints.
         LA=LONGINTEGER(ADDR(ACT(0)))
         *LSD_LA; *LB_AWORDA; *ADB_X'20'; *ST_(0+B ); ! set CCA
         J=P_P5+8+(P_P1&X'FF')*8;       !DCU table
         J=P_P5+INTEGER(J+4)&X'FFFF';   !stream tables
         START STREAM=BYTEINTEGER(J+7)
         LAST STREAM=START STREAM+BYTEINTEGER(J+6)
         TABLE(24+DCUNO)=32+32*P_P1>>24*4
      FINISH 
      TABLE(16+DCUNO)=DCUHN
      LHWDCU=DCUHN IF  DCUHN<LHWDCU
      HHWDCU=DCUHN IF  HHWDCU<DCUHN
      ! field SYSERRs during grope (usually DCU failures)
      *LSS_(3); *USH_-26; *AND_3; *ST_I
      IST==RECORD(X'80000000'!I<<18)
      SAVE IST=IST
      *JLK_<SYSERR>; *LSS_TOS ; *ST_I
      IST_PC=I
      IST_SSR=X'0180FFFE'
      *STLN_I; IST_LNB=I
      *STSF_I; IST_SF=I
      RETRY REPORTING(-1);              ! retry reporting on
      STRM=START STREAM
      MECH=0
      SPARE SLOT=0
      UNTIL  STRM>LAST STREAM CYCLE 
         CURNR=NR
         IF  DCU2=YES START 
            UT==RECORD(UTAD);           ! set up unit entry
            UT=0
            UT_PD=X'E7000000'
            UT_FMN=PT
            UT_STRM=STRM
            UT_FLAGS=X'81'
            UT_IDEST=X'000E4000';       ! peri -> unit
            DO(X'2C41400E',PROPDATADDR,8); ! send stream props
            IF  RESP0>>30=3 THEN  ->NEXT STREAM; ! fire fails
            IF  DEV>>4=0 THEN  ->NEXT STREAM; ! non-existent stream
            IF  DEV>>4=1 THEN  EXIT ;   ! no more streams
            FIRE IO(0,1);               ! reserve stream
            IF  TCB_RESP>>30=3 THEN  ->NEXT STREAM; ! reserve fails
            J=0
            I=PINT AND  J=J+1 UNTIL  I#0 OR  J>100
            DO(SENDPROP,PROPDATADDR,8);    ! send device props
            IF  RESP0=0 OR  RESP0>>30=3 THEN  ->NODEV 
            DO(SENSE,SENSDATADDR,12)
            IF  RESP0=0 OR  RESP0>>30=3 THEN  ->NODEV
            RESP1=UTAD;                 ! save UT AD
         FINISH  ELSE  START 
            DO(CONNECT,0,0)
            DO(SENDPROP,PROPDATADDR,8) UNLESS  DEV=-1
            DO(SENSE,SENSDATADDR,12) UNLESS  DEV=-1 OR  RESP0=0
         FINISH 
         IF  DEV=DISC PCM START 
            I=BYTEINTEGER(PROPDATADDR+2)
            UNLESS  I=0 START 
               INTEGER(PROPDATADDR+4)=0; ! lest alternate route
               IF  I=EDS100 OR  I=EDS200 THEN  DEV=I ELSE  START 
                  ! EDS80 family identified thus:-
                  ! n2 = FDS640
                  ! n3 = FDS160
                  ! n8 = EDS80
                  ! where n = 4 for single channel & n = C for dual channel
                  IF  I>>7#0 START ;    ! dual channel
                     J=0
                     WHILE  J<NR CYCLE ;! find other interface
                        R==RECORD(ADDR(RESPONSES(J)))
                        IF  EDS80<=R_PROPS0>>24<=FDS640 START 
                           IF  INTEGER(PROPDATADDR)>>8&X'FFFF'=R_PROPS0>>8&X'FFFF' START 
                              R_PROPS1=PT<<8!STRM; ! remember alternate route
                              ->NODEV
                           FINISH 
                        FINISH 
                        J=J+REMENTSI
                     REPEAT 
                  FINISH 
                  I=I&15
                  IF  I=8 THEN  DEV=EDS80 ELSE  C 
                     IF  I=3 THEN  DEV=FDS160 ELSE  DEV=FDS640
               FINISH 
               BYTEINTEGER(PROPDATADDR)=DEV
            FINISH  ELSE  DEV=-1
         FINISH 
         ->DDEV IF  EDS100<=DEV<=FDS640
         ->NODEV UNLESS  0<=DEV<=15
         ->NODEV IF  DEV=12;            ! forget comms lines
         FORGETMENOT
         ->GDEV(DEV)
GDEV(6):                                !LINE PRINTER
         UNLESS  PROPS>>8&LP4B=0 START 
            IF  DCU2=YES THEN  UTAD=UTAD-64; ! further I/Os required so step
                                             ! back to right UTAD
                                             ! (FORGETMENOT updates it)
            FORM STYLE=PROPS&255
            FORM LEN=(FORM STYLE>>4)*10+FORM STYLE&15
            FORM LEN=66 IF  FORM LEN=0
            CART=PROPS1>>16&15
            A=REPERTOIRE ADDR(CART)
            S=REPERTOIRE LEN(CART)
            I=0
            WHILE  I<384 CYCLE ;        ! fill the repertoire buffer
               J=A
               WHILE  J<A+S CYCLE 
                  INTEGER(CAA+LP REP OFFSET+I)=INTEGER(J)
                  I=I+4; J=J+4
               REPEAT 
            REPEAT 
            INVALIDATE(CURNR)
            LP INITWORD=X'10'
            DO(INITIALISE,ADDR(LP INITWORD),4)
            UNLESS  CART=0 AND  PROPS1&X'100000'=0 THEN  C 
                  DO(LOADREP,CAA+LP REP OFFSET,384)
            LP INIT WORD=X'FC10'
            DO(INITIALISE,ADDR(LP INIT WORD),4)
            LP INIT WORD=(FORMLEN-1)<<24
            DO(WRITE CONTROL,ADDR(LP INIT WORD),1) UNLESS  FORM LEN=99
            DO(SENDPROP,PROPDATADDR,8)
            DO(SENSE,SENSDATADDR,12)
            IF  DCU2=YES THEN  RESP1=UTAD; ! dont forget UTAD
            FORGETMENOT
         FINISH 
         ->NEXT STREAM
SYSERR:  ! report error & terminate grope on this DCU
         *JLK_TOS 
         *LSS_TOS ; *LSS_TOS ; *ST_I
         OPMESS("DCU ".HTOS(DCUHN,2)." SEI ".HTOS(I,8).TOSTRING(17))
         EXIT 
NODEV:                                  ! invalid devices
         IF  DCU2=YES START 
            FIRE IO(0,5);               ! release stream
            J=0
            I=PINT AND  J=J+1 UNTIL  I#0 OR  J>100
         FINISH 
         ->NEXT STREAM
DDEV:                                   !DISCS
         RESP0=X'18400000';             ! cannot fail now
         FORGETMENOT
         NDISCS=NDISCS+1
         ->NEXT STREAM
GDEV(12):                               ! communications line
   ->NEXT STREAM
GDEV(0):GDEV(1):GDEV(2):GDEV(3):GDEV(4):GDEV(5):GDEV(7):GDEV(8):
GDEV(9):GDEV(10):GDEV(11):GDEV(13):GDEV(14):GDEV(15):
NEXT STREAM:
         STRM=STRM+1
      REPEAT 
      IST=SAVE IST
      RETRY REPORTING(0);               ! retry reporting off
      J=0
      I=PINT AND  J=J+1 UNTIL  I=0 OR  J>100; ! lest any abterms lurking
      ->OUT
!*
GROPE(3):                               ! form GDC table
      ! P_P2 = address of TABLE
!*
      J=0
      TOPSTRM=-1
      WHILE  J<NR CYCLE 
         R==RECORD(ADDR(RESPONSES(J)))
         RSTRM=R_STREAM
         IF  RSTRM>>30#0 THEN  ->NEXTR;    !INVALIDATED
         IF  R_RESP0=-1 THEN  ->NEXTR;     !CONNECT FAILED
         MPROP=R_PROPS0
         IF  MPROP=0 THEN  ->NEXTR;        !NO PROP CODES
         DEV=MPROP>>24
         IF  DEV=0 THEN  ->NEXTR
         IF  R_RESP0&TERMINATED=0 THEN  ->NEXTR
         !* SAID TO EXCLUDE 7905 !
         IF  DEV=MT AND  R_RESP0&CR80=0 THEN  ->NEXTR
         !* SHOULD EXCLUDE HANS CHRISTIAN ANDERSON!
         !* (NO "SHORT BLOCK"  ON 12 BYTE SENSE)
         IF  DEV=MT THEN  MPROP=MPROP&X'FFF0FFFF'; ! ensure mech 0
         AUTO=MPROP
         TOPSTRM=RSTRM>>8&X'FF';        !HIGHEST STREAM SO FAR
         NEW ENTRY(DEV,RSTRM,MPROP,R_PROPS1,AUTO)
         IF  DEV=MT START 
            K=3;                        !3 MORE SLOTS FOR MT4
            K=7 IF  R_PROPS0&MT6PROP#0;  !7 MORE FOR MT6
            CYCLE  L=1,1,K
               RSTRM=RSTRM+1
               MPROP=MPROP+X'10000'
               NEW ENTRY(DEV,RSTRM,MPROP,R_PROPS1,MPROP)
            REPEAT 
         FINISH 
         R_STREAM=RSTRM!X'40000000'; !PREVENT 2ND INSPECTION
NEXTR:
         J=J+REMENTSI
      REPEAT 
      FORM TABLES(TABAD,TOP TAB ENT)
      -> OUT
!*
GROPE(2):      ! FORMAT CA'S
      ! P_P2 IS TABLE ADDRESS
      ! P_P3 IS VIRTUAL ADDRESS OF OLD C/A SEGMENT
      ! P_P4 IS VIRTUAL ADDRESS OF NEW C/A SEGMENT
      ! P_P6 = ADDR(TEMP DDT POINTER AREA
      NEW CAA=P_P4
      TABLE(G2NEXT)=NEW CAA
      IF  G2NEXT=G2ZERO START ;         ! init operlog
         TABLE(41)=TABLE(41)+NEW CAA
         INIT RES PIC(TABLE(41),48*41)
      FINISH 
      FORMAT COMMS AREA(TABAD,G2NEXT - G2ZERO,NEW CAA)
      G2NEXT=G2NEXT + 1
      ->OUT
OUT:
      P_P1=0
      RETURN 
ROUTINE  FIRE IO(INTEGER  PORT,LONGINTEGER  ACT)
INTEGER  ACTW
INTEGER  I
LONGINTEGER  TCB DESC,UT DESC
   IF  DCU2=YES START 
      TCB DESC=TCBA&X'0FFFFFFFF'!LENGTHENI(X'2800000E')<<32
      UT DESC=UTAD&X'0FFFFFFFF'!LENGTHENI(X'B0000001')<<32
      *PRCL_4
      *LSS_ACT+4
      *SLSD_TCB DESC
      *ST_TOS 
      *LD_UT DESC
      *RALN_8
      *CALL_(DR )
      *ST_I
      IF  I#0 THEN  TCB_RESP=X'C0000000'!I; ! fire failed
   FINISH  ELSE  START 
      I=PINT
      ACTW=X'60000000'!PORT<<22
      *LB_ACTW
      *LSD_ACT
      *ST_(0+B )
ACTOK:
      *MPSR_X'12'
      *L_(0+B )
      *MPSR_X'11'
      *JAF_4,<ACTOK>
   FINISH 
END 
ROUTINE  FORM TABLES(INTEGER  TABAD,TOP TAB ENT)
!-----------------------------------------------------------------------
! FORMAT OF TABLE IS
! +=ALREADY SET UP                   
!         WORD
!     +     0     LAST WORD CURRENTLY USED
!     +     1     POINTER TO SLOT TABLE
!     +     2     'LASTSLOT' NUMBER
!     +     3     NUMBER OF DCU'S
!           4     WORD WHERE STRMQ ARRAY STARTS
!           5                DCU & STREAM TO SLOT (SPSS)
!           6                HDCU TO LDCU
!           7                (MAG TAPES) MECHINDEX
!     +     8-15  C/A ADDRESSES FOR DCU'S 0-7
!     +     16-23 H/W DCU NO. FOR DCU'S 0-7
!     +     24-31 C/A SIZES (BYTES) REQD FOR DCU'S 0-7
!           32-39 STARTS AND LIMITS OF OPER BUFFERS IN COMMS AREA
!                 FOR OPER STREAMS 0-6 (SUCCESSIVE OPER STREAMS AS FOUND
!                 IN DCU TABLE).
!                 LH HALFWORD = OFFSET FROM RELEVANT COMMS AREA
!                 RH HALFWORD = NO OF BYTES ALLOCATED.
!           40    ADDRESS OR START OR TABLE AREA FOR TAPE ROUTINE
!                 RELATIVE TO START OF FIRST COMMS AREA. THIS AREA IS AT THE
!                 BACK OF THE COMMS AREA FOR DCU0, FOLLOWING THE OPER
!                 BUFFERS(IF ANY).
!           41    SPARE (FOR FEP OR EQUIVALENT)
!           42     SPARE
!           43-46  GROPE VSN (STRING)
!           47     SPARE
!
! THEN FOLLOW:
!     DCU TABLE
!     STRMQ
!     DCU & STREAM TO SLOT (SPSS)
!     HDCU TO LDCU
!     MECHINDEX
!------------------------------------------------------------------------
!
INTEGER  NOPERSTRMS,PROP,OPERBYTES,CUR OFF,MAGSLOTS
INTEGER  NDCUS,J,N,LOSPSS,HISPSS,TAD,TEND,DCUT BASE,SLOTNO,MECH
INTEGER  LASTSLOT,SPSS,I,MBASENO,STRM,DCUNO
!
ROUTINESPEC  CHECKLIM(INTEGER  WORDS REQ)
BYTEINTEGERARRAYNAME  SPSS TO SLOT
BYTEINTEGERARRAYNAME  HDCU TO LDCU
BYTEINTEGERARRAYNAME  MECHSLOTS
INTEGERARRAYNAME  TABLE
RECORD (DCUTF)NAME  G
INTEGERNAME  CA SIZE
!
      TABLE==ARRAY(TABAD,IFT)
      NDCUS=TABLE(3)
INTEGERARRAY  GS TO MI(0:NDCUS*256);    !256 WORDS/DCU
!
! STRMQ - NEED 64 WORDS PER DCU
      N=TABLE(0)
      TABLE(4)=N+1; ! START ENTRY IN TABLE OF STRMQ ARRAY
      J=1
      WHILE  J<=NDCUS<<6 CYCLE 
         TABLE(N+J)=X'FFFFFFFF'
         CHECKLIM(1)
         J=J+1
         REPEAT 
!
! SPSS TO SLOT
      N=TABLE(0)+1
      LOSPSS=LHWDCU<<8; ! EG. X'0500' FOR SCU 0 DCU 5
      HISPSS=HHWDCU<<8!255
      TAD=ADDR(TABLE(N)); ! ADDRESS OF START OF SPSS TO SLOT ARRAY
      TABLE(5)=N; ! START ENTRY IN TABLE OF DITTO
      TEND=TAD + HISPSS - LOSPSS; ! ADDRESS OF LAST BYTE OF SPSS ARRAY
      CYCLE  J=TAD,1,TEND
         BYTEINTEGER(J)=255; ! SET=UNUSED
         REPEAT 
      J=(HISPSS-LOSPSS+1)>>2
      CHECKLIM(J)
      DCUT BASE=ADDR(TABLE(TABLE(1)))
      LASTSLOT=TABLE(2)
      SPSS TO SLOT==ARRAY(TAD,LBIFT)
!
! (FOR MULTI-MECHANISM STREAMS, THE ENTRY WOULD BE SET UP
! - HENCE THE TEST, AT THE ASSIGNMENT TO SPSS TO SLOT, BELOW).
      SLOTNO=0
      WHILE  SLOTNO<=LASTSLOT CYCLE 
         G==RECORD(DCUT BASE + SLOTNO*SLOTSI)
         SPSS=G_SPSSM
         MECH=SPSS&15
         SPSS=SPSS>>8&X'FFFF'
         SPSS TO SLOT(SPSS - LOSPSS)<-SLOTNO C  
             IF  SPSS TO SLOT(SPSS-LOSPSS)=255
         SLOTNO=SLOTNO + 1
         REPEAT 
!
! HDCU TO LDCU
      N=TABLE(0)+1
      TABLE(6)=N
      HDCU TO LDCU==ARRAY(ADDR(TABLE(N)),BIFT)
!* ONE BYTE PER DCU - INDEXED BY (H/W DCU NO. - LOWEST H/W DCU NO.)
!*                    TO GIVE LOGICAL DCU NO.
      J=HHWDCU
      CHECKLIM(J)
      J=0
      WHILE  J<=NDCUS-1 CYCLE 
         HDCU TO LDCU(TABLE(16+J)-LHWDCU)=J
         ! HOLES IN THIS ARRAY WILL BE LEFT UNASSIGNED
         J=J+1
         REPEAT 
!
!-----------------------------------------------------------------------
! THE (MAG TAPES) MECHINDEX ARRAY.
! EACH TAPE STREAM HAS AN EIGHT-BYTE ENTRY IN THIS ARRAY.
! EACH MAG TAPE SLOT CONTAINS THE ENTRY NUMBER FOR ITS STREAM
! (G_MECHINDEX). BYTE N OF THE ENTRY CONTAINS THE SLOT NUMBER
! FOR MECHANISM N.
!
      SLOTNO=0
      N=0
      MAGSLOTS=0
      CYCLE  J=0,1,NDCUS*256; GS TO MI(J)=255; REPEAT 
! FIRST LOOK THROUGH ALL THE SLOTS LOOKING FOR MAG TAPES. IN GS TO MI,
! INDEXED BY (DCUNO<<8+STRM), FOR EACH DISTINCT STREAM WE PUT AN ENTRY
! NUMBER IN THE MECHSLOTS ARRAY TO BE CREATED. AND N COUNTS THE NUMBER
! OF DISTINCT MAG TAPE STREAMS.
! ALSO COUNT THE NUMBER OF MAGNETIC TAPE SLOTS, TO ALLOCATE SPACE (AT
! 172 BYTES PER SLOT) FOR THE TAPE ROUTINE.
      WHILE  SLOTNO<=LASTSLOT CYCLE 
         G==RECORD(DCUT BASE + SLOTNO*SLOTSI)
         IF  G_DEVTYPE=MT START 
            MAGSLOTS=MAGSLOTS + 1
            DCUNO=(G_SPSSM>>24)&15
            STRM=(G_SPSSM>>8)&255
            I=DCUNO<<8 + STRM
            IF  GS TO MI(I)=255 START 
               GS TO MI(I)=N
               N=N+1
               FINISH 
            FINISH 
         SLOTNO=SLOTNO + 1
         REPEAT 
      IF  N>32 START ; *IDLE_X'FF03'; FINISH 
! N IS THE NUMBER OF MAG TAPE STREAMS. AT 2 WORDS PER STREAM
! NOW FOR EACH MAG TAPE HANDLER, WE FIND THE 'BASE' IN MECHSLOTS FOR
! ITS STREAM FROM GS TO MI AND PUT THE SLOT NUMBER INTO THE MECHSLOTS
! ENTRY FOR THAT STREAM.
      I=TABLE(0)+1
      TABLE(7)=I
      CHECKLIM(N<<1)
      J=I
      WHILE  J<I+N<<1 CYCLE ; TABLE(J)=X'FFFFFFFF'; J=J+1; REPEAT 
      MECHSLOTS==ARRAY(ADDR(TABLE(I)),BIFT)
      SLOTNO=0
      WHILE  SLOTNO<=LASTSLOT CYCLE 
         G==RECORD(DCUT BASE + SLOTNO*SLOTSI)
         IF  G_DEVTYPE=MT START 
            DCUNO=(G_SPSSM>>24)&15
            STRM=(G_SPSSM>>8)&255
            I=DCUNO<<8 + STRM
            MBASENO=GS TO MI(I)<<3
            MECH=G_SPSSM&15
            MECHSLOTS(MBASENO+MECH)=SLOTNO
            G_MECHINDEX=MBASENO
            FINISH 
         SLOTNO=SLOTNO + 1
         REPEAT 
!---------------- SPACE ALLOCATION IN COMMS AREAS ------------------------
!
! NOW BASIC AOMUNT FOR EACH DEVICE, PLUS WORK AREAS FOR DEVICE ADAPTORS, ACCORDING TO ARRAY ADAPTOR BYTES.
      SLOTNO=0
      WHILE  SLOTNO<=LASTSLOT CYCLE 
         G==RECORD(DCUT BASE + SLOTNO*SLOTSI)
         DCUNO=(G_SPSSM>>24)&15
         CA SIZE==TABLE(24+DCUNO)
         CA SIZE=(CA SIZE+7) & (¬7); ! EACH AREA TO BE DOUBLE-WORD ALIGNED
         CA SIZE=CA SIZE+DEV ENTRY BASIC<<2
         IF  G_DEVTYPE>15 THEN  I=EDS ADAPTOR BYTES+EDS Q SPACE ELSE  C 
               I=ADAPTOR BYTES(G_DEVTYPE)
         CA SIZE=CA SIZE+I
         SLOTNO=SLOTNO + 1
         REPEAT 
!
! NOW CALCULATE SPACE REQUIRED FOR THE OPER BUFFERS,CURRENTLY
!     576 BYTES PLUS 984 BYTES PER SCREEN (IF MORE THAN ONE SCREEN, LEAVE
!     SPACE FOR 6). THUS
!        ONE SCREEN             1560 BYTES  (X618)
!        MORE THEN ONE SCREEN   6480 BYTES  (X1950)
      NOPERSTRMS=0
      SLOTNO=0
      WHILE  SLOTNO<=LASTSLOT CYCLE 
         G==RECORD(DCUT BASE + SLOTNO*SLOTSI)
         IF  G_DEVTYPE=OP START ; ! OPER
!                                       +++++++++++++++++++++++++++++++++++++++FF04
            IF  NOPERSTRMS>=7 START ; *IDLE_X'FF04'; FINISH ; ! TOO MANY
            PROP=G_MECHINDEX
            G_MECHINDEX=G_MECHINDEX ! (NOPERSTRMS<<4)
            OPERBYTES=1560; ! ONE SCREEN ONLY
            IF  PROP&15>1 THEN  OPERBYTES=6480; ! MORE THAN ONE SCREEN
! ALLOCATE SPACE NOW IN COMMS AREA FOR THIS DCU. THIS INVOLVES ADDING
! TO TABLE(24+DCUNO).
            DCUNO=(G_SPSSM>>24)&15
            CUR OFF=TABLE(24+DCUNO)
            TABLE(32+NOPERSTRMS)=CUR OFF<<16 + OPERBYTES
! INCREASE C/A SIZE REQUIRED
            TABLE(24+DCUNO)=CUR OFF + OPERBYTES
            NOPERSTRMS=NOPERSTRMS + 1
            FINISH 
         SLOTNO=SLOTNO + 1
         REPEAT 
!-----------------------------------------------------------------------
! AND NOW THE SPACE AT THE BACK OF THE FIRST COMMS AREA 
!   1.   FOR THE TAPE TABLE
!   2.   SPARE (EX FEP)
!
! RELATIVE ADDRESS OF TAPE TABLE AREA = CURRENT SIZE (BYTES) OF COMMS AREA :
      TABLE(40)=TABLE(24)
! INCREASE 'SIZE REQUIRED', FOR FIRST COMMS AREA, AT 172+64=236 BYTES PER
! MAG TAPE SLOT, WITH AN EXTRA 256-64=192 BYTES FOR EACH OF THE FIRST TWO STREAMS
      TABLE(24)=TABLE(24) + 236*MAGSLOTS
      J=MAGSLOTS
      J=2 IF  J>2
      TABLE(24)=TABLE(24) + J*192
! ***** FOLLOWING FEW LINES LEFT IN FOR INFO (EX FEP)
! WORK AREA FOR LINK AND FOR FE ADAPTOR. 512 BYTES EACH, AND CONTIGUOUS,
! IN FACT. TABLE(41) TO MARK START OF THE PAIR OF AREAS, 2ND TO BE
! 512 BYTES ON FROM FIRST.
!      TABLE(24)=(TABLE(24)+7) & (¬7); ! LINK AREA TO BE DOUBLE-WORD ALIGNED
!      TABLE(41)=TABLE(24); ! REL START (BYTES) OF WORK AREA FOR LINK
!      TABLE(24)=TABLE(24) + 1024; ! 512 BYTES FOR EACH
!      TABLE(42)=TABLE(24); ! LIMIT FOR LINK WORK AREA (REL TO C/A)
! TEMP COMPAT
TOPSTRM=255
! ALLOCATE MAX OF THAT REQD FOR SLOTS AND X40 BYTES FOR STREAMS ZERO
! TO HIGHEST STREAM FOUND (NEW SCHEME)
      DCUNO=0
      WHILE  DCUNO<NDCUS CYCLE 
         J=TABLE(24+DCUNO)
         K=(TOPSTRM+1)<<6 + X'120'
         IF  K>J THEN  TABLE(24+DCUNO)=K
         DCUNO=DCUNO+1
         REPEAT 
      J=TABLE(24);                      ! allocate operlog space
      J=(J+3)&(-4);                     ! in 1st comms area
      TABLE(41)=J
      TABLE(24)=J+1976
      RETURN 
ROUTINE  CHECKLIM(INTEGER  WORDS REQ)
!                                       +++++++++++++++++++++++++++++++++++++++++FF05
      IF  TABLE(0)+WORDS REQ>TOP TAB ENT START 
        *IDLE_X'FF05'
     FINISH 
      TABLE(0)=TABLE(0) + WORDS REQ
END ; ! CHECKLIM
      END ; ! FORM TABLES
ROUTINE  FORMAT COMMS AREA(INTEGER  TABAD,DCUNO,CAA)
! CALLED ONCE FOR EACH COMMS AREA AFTER AREA HAS BEEN ALLOCATED
! (IE. AT DACT=2 ENTRY TO DCU GROPE).
INTEGER  LASTSLOT,J,SLOTNO,DCUT BASE, DEV OFFSET,DEV ENT BASE
INTEGER  REPAD,REPLEN,CH,IX,DEVTYPE,GNO,EDS EXTRA
LONGINTEGER  A
CONSTINTEGER  EDS TIMEOUT=3
CONSTINTEGER  HL=32;                    !CA HEADER LENGTH
CONSTINTEGER  LP ILLCHAR=X'07'
!
RECORD (ENTFORM)NAME  D
RECORD (DCUTF)NAME  G
!
BYTEINTEGERARRAYNAME  REP,TRTAB
INTEGERARRAYNAME  TABLE
INTEGERARRAYNAME  DDTP
!
RECORDFORMAT  CAHF(INTEGER  ACTW,SEMA)
RECORD (CAHF)NAME  CAH
RECORD (TCBF)NAME  DCUS TCB
      CAH==RECORD(CAA)
      CAH_ACTW=AWORDA;                  !DCU INT/ACT WORD ADDRESS
      CAH_SEMA=-1;                      ! multi ocp semaphore
   IF  DCU2=YES THEN  ->SKIP
      J=REALISE(CAA);                   !SET DCU CCA
      A=LENGTHENI(J+HL)<<32!J!X'080000001'
      J=P_P1>>24;                       !NO. OF STREAMS
      J=(J+3)&(-4)//4
      J=0 IF  J>15
      A=A!LENGTHENI(J<<28)<<32
      *LSD_A
      *LB_AWORDA
      *ADB_X'20'
      *ST_(0+B )
SKIP:
      TABLE==ARRAY(TABAD,IFT)
      DDTP==ARRAY(P_P6,IFT)
      LASTSLOT=TABLE(2)
      DCUT BASE=ADDR(TABLE(TABLE(1)))
      SLOTNO=0
      DEV OFFSET=HL+32*P_P1>>24*4;      !HEADER + DCU CCA SIZE
      WHILE  SLOTNO<=LASTSLOT CYCLE 
         G==RECORD(DCUT BASE + SLOTNO*SLOTSI)
         GNO=(G_SPSSM>>24) & 15
         IF  GNO=DCUNO START 
            ! IF THE SLOT RELATES TO THIS DCU (IE. THIS COMMS AREA) THEN
            ! FORMAT THE DEVICE ENTRY.
            DEVTYPE=G_DEVTYPE
            IF  EDS100<=DEVTYPE<=FDS640 THEN  EDS EXTRA=EDS Q SPACE C 
                                       ELSE  EDS EXTRA=0
            DEV ENT BASE=CAA + DEV OFFSET
            D==RECORD(DEV ENT BASE)
            G_DEV ENT BASE=DEV ENT BASE
            D_SPSSM=G_SPSSM
            D_PROPS0=G_PROPS0
            D_PROPS1=G_PROPS1
            UNLESS  EDS EXTRA=0 THEN  D_ALTRT<-G_PROPS1; ! alternate route
            D_PROPADDR=ADDR(D_PROPS0)
            D_CAA=CAA
            D_TCBA=DEV ENT BASE + ENT FORM BYTES+EDS EXTRA
            D_MNEMONIC=G_MNEMONIC
            D_LOGMASK=1 IF  DEVTYPE#MT AND  DEVTYPE#LP
            D_SENSDAT AD=ADDR(D_SENSE1)
            D_TIMEOUT=EDS TIMEOUT
            D_TIMEOUT=TIMEOUT SECONDS(DEVTYPE) UNLESS  DEVTYPE>15
            DCUS TCB==RECORD(D_TCBA)
            DCUS TCB=0
            DCUS TCB_COMMAND=X'2F00400A';  ! set up IDENTIFY
            DCUS TCB_STE=REALISE(ADDR(D)&X'FFFC0000')!1
            DCUS TCB_LEN=2
            DCUS TCB_DATA=ADDR(D_MECH)
!-------------------------------------------------
            D_UA AD=DEV ENT BASE + DEV ENTRY BASIC<<2+EDS EXTRA
            IF  EDS EXTRA=0 THEN  D_UA SIZE=ADAPTOR BYTES(DEVTYPE) C 
                            ELSE  D_UA SIZE=EDS ADAPTOR BYTES
            D_ENT SIZE=DEV ENTRY BASIC<<2 + D_UA SIZE
            UNLESS  EDS EXTRA=0 START 
               DDTP(DDT NO)=DEV ENT BASE
               DDT NO=DDT NO+1
               D_PROPADDR=(DEVTYPE-EDS100)*20
            FINISH 
            IF  DEVTYPE=LP START 
               D_UA SIZE=D_UA SIZE - 256; ! TAKE OFF SIZE OF TRANSLATE TABLE
               D_TRTAB AD=D_UA AD + D_UA SIZE
               TRTAB==ARRAY(D_TRTAB AD,BIFT)
               CART=G_PROPS1>>16&15
               ! create the translate table, based on the repertoire
               IF  CART=0 OR  BYTEINTEGER(D_PROPADDR+2)&LP4B=0 START 
                  FOR  IX=0,1,255 CYCLE ; TRTAB(IX)=IX; REPEAT 
               FINISH  ELSE  START 
                  REPAD=REPERTOIRE ADDR(CART)
                  REP==ARRAY(REPAD,BIFT)
                  REPLEN=REPERTOIRE LEN(CART)
                  FOR  IX=0,1,255 CYCLE 
                     CH=LP ILLCHAR; ! del (07) for ERCC, UKC may use back '?'
                     J=0
                     WHILE  J<REPLEN CYCLE 
                        IF  IX=REP(J) THEN  CH=IX AND  EXIT 
                        J=J+1
                     REPEAT 
                     ! insert 'format effectors' at own values
                     ! and also turn  lf (x'25') into newline (x'15')
                     IF  IX=X'15' THEN  CH=X'15'
                     IF  IX=X'25' THEN  CH=X'15'
                     IF  IX=X'0C' THEN  CH=X'0C'; ! NEWLINE
                     IF  IX=X'0D' THEN  CH=X'0D'
                     IF  IX=X'40' THEN  CH=X'40'; ! SPACE
                     ! If value IX was not found in repertoire (CH still LP ILLCHAR),
                     ! was it a lower case letter? If so, change it to upper case.
                     ! (We do not search to see if the upper case letter is in the
                     ! repertoire (surely it is)).
                     IF  CH=LP ILLCHAR AND   C 
                     (X'81'<=IX<=X'89' OR  X'91'<=IX<=X'99' OR   C 
                        X'A2'<=IX<=X'A9') THEN  CH=IX ! X'40'
                     TRTAB(IX)=CH
                  REPEAT 
               FINISH ; ! cartridge setting non-zero
            FINISH ; ! LP DEVICE
            DEV OFFSET=DEV OFFSET + D_ENT SIZE
            FINISH ; ! SLOT BELONGS TO THIS DCU
         SLOTNO=SLOTNO+1
         REPEAT 
      END ; ! FORMAT COMMS AREA
!
ROUTINE  DO(INTEGER  COMMAND,DATAD,LEN)
INTEGER  I,J
LONGINTEGER  A
CONSTINTEGER  CONNECT STREAM=X'03000000',START STREAM=X'01000000'
CONSTINTEGER  CONNECT TERM=X'201000'
      A=LENGTHENI(TCBA)<<32!STRM
      IF  COMMAND=0 START ;             !CONNECT STREAM
         A=A!CONNECT STREAM
         FIRE IO(PT,A)
         CYCLE  J=0,1,5;                !WAIT FOR TERMINATION
            I=PINT;                     ! take interrupt
            IF  I>>24=DCUHN&15 AND  I&X'FF'=STRM C 
               AND  I&CONNECT TERM=CONNECT TERM START 
               DEV=0 
               RETURN 
            FINISH 
         REPEAT 
         DEV=-1
         RETURN 
      FINISH 
      A=A!START STREAM
      IF  DCU2=YES THEN  A=2
      TCB_COMMAND=COMMAND
      TCB_STE=REALISE(DATAD&X'FFFC0000')!1
      TCB_LEN=LEN
      TCB_DATA=DATAD
      TCB_RESP=0
      FIRE IO(PT,A)
      J=0
      J=J+1 UNTIL  TCB_RESP#0 OR  J>100000
      RESP0=TCB_RESP
      IF  DCU2=NO THEN  RESP1=0
      IF  INTEGER(PROPDATADDR)=0 AND  SPARE SLOT=0 START ; ! set up spare slot
         SPARE SLOT=1
         INTEGER(PROPDATADDR)=ZX<<24
         RESP0=X'1000'
      FINISH 
      DEV=INTEGER(PROPDATADDR)>>24
END ; ! DO
ROUTINE  FORGETMENOT
INTEGER  I
      I=DCUNO<<24!DCUHN<<16!STRM<<8!MECH
      REMEMBER(I)
      REMEMBER(RESP0)
      REMEMBER(RESP1)
      PROPS=INTEGER(PROPDATADDR)
      PROPS1=INTEGER(PROPDATADDR+4)
      REMEMBER(PROPS)
      REMEMBER(PROPS1)
      RESP0=0
      LONGINTEGER(PROPDATADDR)=0
      CYCLE  I=0,4,8
         REMEMBER(INTEGER(SENSDATADDR+I))
         INTEGER(SENSDATADDR+I)=-1
      REPEAT 
      IF  DCU2=YES THEN  UTAD=UTAD+64;  ! next UT entry
END 
ROUTINE  INVALIDATE(INTEGER  ENT)
      RESPONSES(ENT)=RESPONSES(ENT) ! X'80000000'
END ; ! INVALIDATE
ROUTINE  NEW ENTRY(INTEGER  DEVTYPE,GSPSSM,PROPS0,PROPS1,AUTO)
OWNINTEGERARRAY  MNEMONIC(1:15)=  C 
   M'PT0', M'PR0', M'CP0', M'CR0', M'M00',
   M'LP0', M'GP0', M'OP0', M'GU0', M'DR0',
   M'ZX0', M'CT0', M'SU0', M'FE0', M'LK0'
! THE TCBS ARRAY IS INDEXED BY DEVTYPE AND SPECIFIES THE NUMBER
! OF TCB'S TO BE ASSIGNED TO EACH DEVICE TYPE.
RECORD (DCUTF)NAME  G
INTEGER  NEXT,MD,STRM
      NEXT=TABLE(0) + 1
      G==RECORD(ADDR(TABLE(NEXT)))
      G_DEVTYPE=DEVTYPE
      G_SPSSM=GSPSSM
! SUPPLY LAST BYTE OF PROP CODES FOR OPER ONLY
      IF  DEVTYPE=OP THEN  G_MECHINDEX<-PROPS0
! FIGURE OUT MNEMONIC. CURRENT ARRAY ENTRIES HOLD THE
! MNEMONICS NEXT TO BE USED. FOR NON-MAG TAPES, AND FOR MAG TAPES
! ON THE SAME STREAM AS PREVIOUS ONE (IF ANY), JUST USE THE ARRAY
! ENTRY AND INCREMENT IT.
! FOR A MAG TAPE, WE USE THE TAPE HANDLER (WIRED, UNIQUE) ADDRESS
! OUT OF BYTE ONE (0-1-2-3) OF THE PROPERTY CODES
! SIMILAR ARRANGEMENT FOR DISCS
      IF  DEVTYPE>15 START 
         IF  DEVTYPE<FDS160 THEN  MD=M'ED' ELSE  MD=M'FD'
         MD=MD<<16!HEXDS(PROPS0>>20&15)<<8!HEXDS(PROPS0>>16&15)
      FINISH  ELSE  MD=MNEMONIC(DEVTYPE)
      STRM=(GSPSSM>>8) & 255
! 'M' PLUS BOTTOM 7 BITS OF BYTE 1 OF PROPERTY CODES AS 2 ISO CHARS, FOR MT
      IF  DEVTYPE=MT THEN  MD=PROPS0<<9>>29<<8 + PROPS0<<12>>28 + M'M00'
      IF  DEVTYPE=FE START 
         MD=PROPS0<<8>>24
         FEP MAP=FEP MAP!1<<MD
         MD=MD+M'FE0'
      FINISH 
      G_MNEMONIC=MD
      G_PROPS0=PROPS0
      G_PROPS1=PROPS1
      G_PROPS03<-AUTO; ! THIS IS BYTE 3 OF PROPS, EXCEPT FOR LP, WHEN
                     ! IT'S 1ST TERTIARY STATUS BYTE (CONTAINING AUTO BIT)
      G_UTAD=R_RESP1;                   ! 0 or UT entry address
      IF  MD&255='9' THEN  MD=MD+'A'-'9' ELSE  MD=MD+1
      MNEMONIC(DEVTYPE)=MD UNLESS  DEVTYPE>15
!                                       ++++++++++++++++++++++++++++++++++++++++++++FF05
      IF  TABLE(0)+8>TOP TAB ENT START ; *IDLE_X'FF05'; FINISH 
      TABLE(0)=TABLE(0) + 8; ! 8 WORDS ADDED TO ARRAY FOR THE DEVICE SLOT
      TABLE(2)=TABLE(2)+1; ! INCREMENT 'LASTSLOT'
      END ; ! NEW ENTRY
ROUTINE  REMEMBER(INTEGER  INF)
      IF  NR>=MAX RESPONSEBYTES>>2 START ;
        *IDLE_X'FF02';                ! +++++++++++++++++++++++++++++++++++++FF02
         FINISH 
      RESPONSES(NR)=INF
      NR=NR+1
      END ; ! REMEMBER
ROUTINE  INIT RES PIC(INTEGER  A,L)
CONSTBYTEINTEGERARRAY  BL(0:40)=64(40),21; ! blank line
   INTEGER(A)=L
   INTEGER(A+4)=-1
   MOVE(41,ADDR(BL(0)),A+8)
   MOVE(L-41,A+8,A+8+41)
END 
!------------------------**GROPE ROUTINE**------------------------------
END 
FINISH  ELSE  START 
!
! GPC grope is in three parts
!
!
! part 1 is called for each GPC. it sets TABLE(3) = no of GPCs and table(16+GPCno) = pt.
! it tries to initialise the GPC and, if that fails, returns. it then attempts to connect
! all streams and builds an array of responses. finally it works through the array and builds slots. (note the 
! array of responses is easily identified in a hardware dump)
!
!
!part 2 (form tables) is called once only. builds the strmq, pts to slot and pt to gpc. if there are no slots, it 
! returns. cycles through slots to build mechslots and assigns values to pts to slot. allocates
! space in communication areas for device entries
!
!
! part 3 (format comms area) is called for each GPC. if no slots, returns. cycles through slots :
!           if for this GPC
!              formats device entry
!              if LP, insert translation table
!              if OP, put oper no in slot and allocate
!                     space in CA for work area
!
! grope builds a communications area for each GPC and a 'TABLE'. the format
! of the table is:
!
!
!
!
!         word
!           0     last word currently used
!           1     word which is start of slots
!           2     'LASTSLOT' 
!           3     number of GPC's
!           4     word where strmq array starts
!           5                pts to slot
!           6                pt to gpc
!           7                (mag tapes) mechindex
!           8-15  CA addresses for GPC's 0-7
!           16-23 port-trunk for GPC's 0-7
!           24-31 CA sizes (bytes) reqd for GPC's 0-7
!           32-39 starts and limits of oper buffers in comms area
!                 for oper streams 0-6 (successive oper streams as found
!                 in GPC table).
!                 lh halfword = offset from relevant comms area
!                 rh halfword = no of bytes allocated.
!           40-42 spare
!           43-47  grope vsn (string)
!
! then follow:
!     slots
!         FLAGS/DEVTYPE/X/LINK
!         PROPS0
!         PROPS1
!         DEV ENT BASE
!         C STATUS
!         GPTSM
!         MNEMONIC
!         MECHINDEX/PROPS03/X/STATE
!     strmq
!         16 bytes for each GPC. each byte is pointer to a slot
!         for a device with a chain in progress (or 'FF')
!     pts to slot
!         16 bytes for each pt from lowest to highest, i.e. may be more
!         pt's than gpc's. gives rapid translation from pts to (first) slot
!     pt to gpc
!         1 byte for each pt from lowest to highest
!         translates pt to logical GPC number
!     mechindex
!         8 bytes for each MT stream. bytes contain slot numbers. mechindex
!         field in slot refers to start of relevant 8 byte array
!
!
! if grope detects a fatal error it idles as follows:
!
!
!                             FF00      too many GPC's (>8)
!                             FF01      too many slots (>256) or
!                                       supplied table too small
!                             FF02      too many entries in 'response' array
!                             FF03      too many magtape streams (>32)
!                                       (in 'FORM TABLES')
!                             FF04      too many oper streams (>7)
!                                       (in 'FORM TABLES')
!                             FF05      supplied table too small
!                                       (in 'CHECKLIM')
!
!
!
!
! for non fatal errors and incidents, a message is placed in the responses array and queued for the oper:
! GPC GROPE nn dd/mm/yy                 when grope is entered
! PAW=response PT=pt                    if paw is non zero
! SAW=response PTS=pts                  if saw is non zero
! RES=response PTS=pts                  if stream response is non zero
! GPC pt INIT RES=response              if a GPC fails to init
! GET CA address                        if the routine GET CA failed to get control
! GPC GROPE EXIT
!----------------------------------------------------------------------------------------------------
EXTERNALINTEGERFNSPEC  GPC INIT(INTEGER  CA VA,PT,CHOPSUPE)
EXTERNALROUTINESPEC  GET PSTB(INTEGERNAME  PSTB0,PSTB1)
EXTERNALROUTINESPEC  OPMESS(STRING (63) S)
EXTERNALSTRINGFNSPEC  STRHEX(INTEGER  N)
SYSTEMROUTINESPEC  MOVE(INTEGER  S,FROM,TO)
!-----------------------------------------------------------------------
RECORDFORMAT  GPCTF(BYTEINTEGER  FLAGS,DEVTYPE,SPAREB,LINK, C 
   INTEGER  PROPS0,PROPS1,DEV ENT BASE,SPAREI,GPTSM,MNEMONIC,  C 
   BYTEINTEGER  MECHINDEX,PROPS03,SERVRT,STATE)
OWNRECORD (GPCTF)NAME  G
RECORDFORMAT  RCBF(INTEGER  LIMFLAGS,LSTBA,LB BYTES,LBA,AL BYTES, C 
      ALA,INITWORD,SLOTNO)
OWNRECORD (RCBF)NAME  RCB
RECORDFORMAT  SEF(INTEGER  SAW0,SAW1,RESP0,RESP1)
OWNRECORD (SEF)NAME  SENT
RECORDFORMAT  CAF(INTEGER  MARK,PAW,PIW0,PIW1,CSAW0,CSAW1,  C 
      CRESP0,CRESP1,RECORD (SEF)ARRAY  SENTRY(0:15));!LENGTH X120 BYTES
OWNRECORD (CAF)NAME  CA
! The following are secondary status byte masks determining what
! abnormal terminations the GPC routine is to print monitor dumps for.
! ZX is a dummy device
CONSTINTEGERARRAY  LOGMASK(0:15)=  C 
   0, 0, 0,  0,  0,    0, X'00', 0, 0, 0, 0, 0, 0, 0,X'1FF', 0
!  NA PT PR  CP CR   MT   LP    GP OP GU DR ZX CT SU FE    NA
!
CONSTINTEGERARRAY  ADAPTOR BYTES(0:15)=  C 
   0, 0, 0,600,600, 200, 600, 0, 1368, 0, 0, 600, 0, 600, 600, 0
!  NA PT PR CP CR   MT   LP   GP OP    GU DR ZX   CT SU   FE   NA
! above, the no of bytes for LP adaptors includes the 256 bytes for a
! translate table
! CDM'able devices must have the same adaptor byte size (600).
!
RECORDFORMAT  ENTFORM(INTEGER    C 
   SER, GPTSM, PROPADDR, SECS SINCE, CAA, GRCB AD, LBA, ALA,  C 
   STATE, RESP0, RESP1, SENSE1, SENSE2, SENSE3, SENSE4,  C 
   REPSNO, BASE, ID, DLVN, MNEMONIC,  C 
   ENTSIZE, PAW, USAW0, URCB AD, SENSDAT AD, LOGMASK, TRTAB AD,  C 
   UA SIZE, UA AD, TIMEOUT,PROPS0,PROPS1)
OWNRECORD (ENTFORM)NAME  D
!
CONSTINTEGER  ENT FORM BYTES=128; ! =length of above record format
! This next constant is in words, and includes
!     length of ENTFORM                 =32
!     length of GPC's RCB               = 8
!     length of GPC's LB                = 2
!     length of GPC's AL                = 2
!                                       -----
!     total                              44
!----------------------------------------------------------------------------------------------------
CONSTINTEGER  AL OFFSET = X'48'; ! bytes from RCB A
CONSTINTEGER  CONNECT = 0
CONSTINTEGER  DEV ENTRY BASIC=44; ! words, size of fixed part of comms area record format
CONSTINTEGER  DO CONTROLLER REQUEST = X'04000000'
CONSTINTEGER  DO STREAM REQUEST = X'01000000'
CONSTINTEGER  FE = 14
CONSTINTEGER  GPC DEST = X'40000800'
CONSTINTEGER  INITIALISE = 4
CONSTINTEGER  INIT CONTROLLER = X'32000010'
CONSTINTEGER  LB OFFSET = X'20'; ! bytes from RCB A
CONSTINTEGER  LOADREP = 3
CONSTINTEGER  LOGICAL STREAM = X'F00F0'
CONSTINTEGER  LP = 6
CONSTINTEGER  LP REP OFFSET = X'280'
CONSTINTEGER  LST RA = X'8080'
CONSTINTEGER  MAX GPC NO = 7
CONSTINTEGER  MAX RESPONSE WORDS = X'3E0'
CONSTINTEGER  MT = 5
CONSTINTEGER  MT6PROP = X'100'
CONSTINTEGER  ONE RCB OFFSET = X'120'
CONSTINTEGER  OP = 8
CONSTINTEGER  PROP DAT OFFSET = X'90'; ! =144 bytes from RCB A
CONSTINTEGER  SENDPROP = 1
CONSTINTEGER  SENS DAT OFFSET = X'98'; ! =152 bytes from RCB A
CONSTINTEGER  SLOTSI = 32; ! slot size
CONSTINTEGER  SU=13;                    ! Switch unit
CONSTINTEGER  TOPLSEG = 5
CONSTINTEGER  WRITECONTROL = 5
CONSTINTEGER  ZX=11;   ! dummy device
!----------------------------------------------------------------------------------------------------
EXTRINSICINTEGER  FEP MAP
OWNINTEGER  CAA
OWNINTEGER  COUNT
OWNINTEGER  DEVTYPE
OWNINTEGER  GPC COUNT; ! used for part 3
OWNINTEGER  GPCNO
OWNINTEGER  GPCT BASE
OWNINTEGER  GPTSM
OWNINTEGER  J
OWNINTEGER  LASTSLOT
OWNINTEGER  MAGSLOTS
OWNINTEGER  NO OF RESPONSES
OWNINTEGER  OPSLOTS
OWNINTEGER  PAWSAWFAILS
OWNINTEGER  PROPDATADDR
OWNINTEGER  PROPS
OWNINTEGER  PROPS1
OWNINTEGER  PT
OWNINTEGER  RCBA
OWNINTEGER  RESP0
OWNINTEGER  RESP1
OWNINTEGER  SENSDATADDR
OWNINTEGER  SETUP
OWNINTEGER  STRM
OWNINTEGER  TOP TABLE ENTRY
OWNINTEGER  TRUNKADDR
OWNINTEGER  SPARE SLOT
OWNINTEGERARRAYNAME  RESPONSES
OWNINTEGERARRAYNAME  TABLE
!
CONSTHALFINTEGERARRAY  TIMEOUT SECONDS(0:15)=  C 
   10, 60, 60,600,300, 30, 60, 10, 10, 10, 10, 10, 10, 10,  3, 10
!  NA  PT  PR  CP  CR  MT  LP  GP  OP  GU  DR  ZX  CT  SU  FE  NA
!
CONSTINTEGERARRAY  GPCS LOGIC BLOCK(0:1)=  C 
 X'04F10800',   X'00F00400'
! CONNECT        SENSE
! COMMD CHAIN
!
OWNBYTEINTEGERARRAYFORMAT  BIFT(0:511)
OWNINTEGERARRAYFORMAT  IFT(0:1023)
OWNINTEGERARRAY  LBE(0:5)=  C 
X'04F10800',X'04F00E00',X'00F00402',X'80F02504',X'80F00106',X'82F00500'
! CONNECT   PROP CODES  SENSE       LOAD REP    INITIALISE  WRITECONTROL
! COMMDCHAIN  COMMDCHAIN            OUTWARDS    OUTWARDS    OUTWARDS,LITERAL(ZERO)
!
!
!
EXTRINSICINTEGER  LP ILLCHAR;           ! SET UP IN GPC - ERCC VALUE=X'07'
                                        ! UKC MAY USE BACK '?'
!
!----------------------------------------------------------------------------------------------------
!
ROUTINE  CHECKLIM(INTEGER  WORDS REQ)
      IF  TABLE(0) + WORDS REQ > TOP TABLE ENTRY START 
         *IDLE_X'FF05'; !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++FF05
      FINISH 
      TABLE(0) = TABLE(0) + WORDS REQ
END ; ! OF CHECK LIM
!
ROUTINE  SEND CHFLAG(INTEGER  PT)
      TRUNKADDR=GPC DEST ! (PT<<16)
      *LB_TRUNKADDR
      *LSS_1
      *ST_(0+B )
      END ; ! SEND CHFLAG
!
ROUTINE  MSG(STRING (31)TXT)
INTEGER  A, I
      A = ADDR(RESPONSES(NO OF RESPONSES))
      FOR  I=0,1,LENGTH(TXT) CYCLE 
         BYTEINTEGER(A+I) = BYTEINTEGER(ADDR(TXT)+I)
      REPEAT 
      BYTEINTEGER(A) = X'80'; ! to 'invalidate' the entry
      NO OF RESPONSES = NO OF RESPONSES + 8
      OPMESS(TXT)
END ; ! OF MSG
!
INTEGERFN  GET CA(INTEGER  CAA)
RECORD (CAF)NAME  CA
INTEGERNAME  MARK
      CA == RECORD(CAA)
      MARK == CA_MARK
      COUNT = 0
LOOP:
      COUNT = COUNT + 1
      IF  COUNT > 100000 THEN  -> ERROR
      *INCT_(MARK)
      *JCC_8,<OUT>; ! =-1
      *JCC_5,<LOOP>; ! >-1
      ! drop through if <-1
ERROR:
      MARK = 0; ! force free
      MSG("Get CA ".HTOS(CAA,8))
      RESULT  = 1
OUT:
      RESULT  = 0
END ; ! GET CA
!
ROUTINE  REMEMBER(INTEGER  INF)
      IF  NO OF RESPONSES>=MAX RESPONSEWORDS START ;
        *IDLE_X'FF02';                ! +++++++++++++++++++++++++++++++++++++++++++++++++FF02
      FINISH 
      RESPONSES(NO OF RESPONSES)=INF
      NO OF RESPONSES=NO OF RESPONSES+1
      END ; ! REMEMBER
!
ROUTINE  INVALIDATE(INTEGER  ENT)
      RESPONSES(ENT)=RESPONSES(ENT) ! X'80000000'
END ; ! INVALIDATE
!
ROUTINE  DO(INTEGER  COMMAND)
INTEGER  J, CURNR
      DEVTYPE = 0; ! so that if get CA fails, grope(1) doesnt run amok
      RCB_LBA=ADDR(LBE(COMMAND))
      IF  GET CA(CAA) > 0 THEN  -> OUT
      IF  CA_PAW#0 START 
         MSG("PAW=".HTOS(CA_PAW,8)." pt=".HTOS(PT,2))
         PAWSAWFAILS=PAWSAWFAILS+1
      FINISH 
      CA_PAW=DOSTREAM REQUEST ! STRM
      CA_CRESP0=0
      IF  SENT_SAW0#0 START 
         MSG("SAW=".HTOS(SENT_SAW0,8)." pts=".HTOS((PT<<4)!STRM,3))
         PAWSAWFAILS=PAWSAWFAILS+1
      FINISH 
      IF  SENT_RESP0#0 START 
         MSG("RES=".HTOS(SENT_RESP0,8)." pts=".HTOS((PT<<4)!STRM,3))
      FINISH 
      SENT=0
      SENT_SAW0=X'30000020'; ! SAW flags + RCB bound
      SENT_SAW1=RCBA
      CA_MARK=-1
      SEND CHFLAG(PT); 
WAIT:
      COUNT=0
      COUNT=COUNT+1 UNTIL  SENT_RESP0#0 OR  COUNT>100000 
      IF  GET CA(CAA) > 0 THEN  -> OUT
      CA_PIW0=CA_PIW0 & (¬(X'80000000'>>STRM))
      RESP0=SENT_RESP0
      RESP1=SENT_RESP1
      SENT_RESP0=0
      CA_MARK=-1
                                        ! remember 8 words 
      IF  INTEGER(PROPDATADDR)=0 AND  SPARE SLOT=0 START ;  !set up spare slot
         SPARE SLOT=1
         INTEGER(PROPDATADDR)=ZX<<24
         RESP0=X'1000'
      FINISH 
      CUR NR = NO OF RESPONSES
      REMEMBER((GPCNO<<16)!(PT<<8)!(STRM<<4))
      REMEMBER(RESP0)
      REMEMBER(RESP1)
      PROPS=INTEGER(PROPDATADDR)
      REMEMBER(PROPS)
      INTEGER(PROPDATADDR) = 0
      PROPS1=INTEGER(PROPDATADDR+4)
      REMEMBER(PROPS1)
      INTEGER(PROPDATADDR+4)=0
      FOR  J=0,4,8 CYCLE 
         REMEMBER(INTEGER(SENSDATADDR + J))
         INTEGER(SENSDATADDR + J)=0
      REPEAT 
      DEVTYPE=PROPS>>24
                                        ! check that response is useful else 'invalidate'
      IF  (RESP0 >> 20) & 15 = 1 START ;! not interested in attns
         INVALIDATE(CUR NR)
         -> WAIT
      FINISH 
      IF  DEVTYPE = 0 THEN  -> INVAL
                                        ! CHECK FOR 7905 (IN WHICH WE ARE NOT INTERESTED)
                                        ! IT RETURNS RESP0 = 00408001
      IF  RESP0 & X'1000' = 0 THEN  -> INVAL
      -> OUT
INVAL:
      INVALIDATE(CUR NR)
OUT:
END ; ! DO
!
ROUTINE  NEW SLOT(INTEGER  DEVTYPE,GPTSM,PROPS0,PROPS1,AUTO)
OWNINTEGERARRAY  MNEMONIC(1:15)=  C 
   M'PT0', M'PR0', M'CP0', M'CR0', M'M00',
   M'LP0', M'GP0', M'OP0', M'GU0', M'DR0',
   M'ZX0', M'CT0', M'SU0', M'FE0', M'LK0'
INTEGER  MD
      G == RECORD(ADDR(TABLE(TABLE(0) + 1)))
      CHECKLIM(8)
      G_DEVTYPE=DEVTYPE
      G_GPTSM=GPTSM
      IF  DEVTYPE=OP THEN  G_MECHINDEX<-PROPS0
                                        ! mnemonic for a MT is bottom 7 bits of
                                        ! byte 1 of props as 2 iso chars
      IF  DEVTYPE = MT START 
         MD = M'M00' + PROPS0 << 9 >> 29 << 8 + PROPS0 << 12 >> 28
      FINISH  ELSE  START 
         IF  DEVTYPE = FE START 
            MD=PROPS0<<8>>24
            FEP MAP=FEP MAP!1<<MD
            MD=MD+M'FE0'
         FINISH  ELSE  IF  DEVTYPE=SU START 
            MD=MNEMONIC(DEVTYPE)!PROPS0<<8>>24
         FINISH  ELSE  START 
            MD = MNEMONIC(DEVTYPE)
            IF  MD & 255 = '9' C 
            THEN  J = MD - '9' + 'A' C 
            ELSE  J = MD + 1
            MNEMONIC(DEVTYPE) = J
         FINISH 
      FINISH 
      G_MNEMONIC=MD
      G_PROPS0 = PROPS0
      G_PROPS1 = PROPS1
      G_PROPS03<-AUTO; ! this is byte 3 of props, except for LP, when
                       ! it's 1st tertiary status byte (containing auto bit)
      TABLE(2)=TABLE(2)+1; ! increment 'lastslot'
END ; ! NEW SLOT
!
INTEGERFN  GPC REINIT(INTEGER  OLD CA,NEW CA,PT)
!
! RESULT=0     OK
!        2<<24 ! CRESP0     initialise failed
!
RECORDFORMAT  INIF(INTEGER  PST S,PST A,CAA,SOE)
RECORD (INIF) INI
RECORDFORMAT  CA0F(INTEGER  MARK,PAW,PIW0,PIW1,CSAW0,CSAW1,  C 
      CRESP0,CRESP1); ! length X20 bytes
RECORD (CA0F)NAME  CA0
RECORD (CA0F)NAME  CA
!
      CA0==RECORD(OLD CA)
                                        ! clear and obtain control of comms area
      CA0=0
      CA0_PAW=DO CONTROLLER REQUEST
      CA0_CSAW0=INIT CONTROLLER
      CA0_CSAW1=ADDR(INI)
      GET PSTB(INI_PST S,INI_PST A)
      INI_CAA=NEW CA
      INI_SOE=0
                                        ! initialise the new comms area
      CA==RECORD(NEW CA)
      CA=0
      CA_MARK=-1
      CA0_MARK=-1; ! free coms area, and let controller do the job
      SEND CH FLAG(PT)
      COUNT=0
      COUNT=COUNT+1 UNTIL  CA_CRESP0#0 OR  COUNT>200000 
      IF  GET CA(NEW CA) > 0 THEN  RESULT  = 2<<24
      IF  CA_CRESP0<<8 >=0 START 
         RESULT =(2<<24) ! CA_CRESP0; ! initialise failed
         FINISH 
      CA_CRESP0=0
      CA_MARK=-1
      RESULT =0; ! success
      END ; ! GPC REINIT
!
ROUTINE  FORM TABLES
INTEGER  NGPCS,J,N,LOPTS,HIPTS,TAD,TEND,SLOTNO
INTEGER  PTS,GPCNO
INTEGER  LASTSTREAM, THIS STREAM, MECH BASE
BYTEINTEGERARRAYNAME  PTS TO SLOT
BYTEINTEGERARRAYNAME  PT TO GPC
BYTEINTEGERARRAYNAME  MECHSLOTS
      NGPCS=TABLE(3)
                                        ! strm semaphores 16 words/GPC
      N = TABLE(0)
      TABLE(40) = N + 1
      FOR  J=1,1,NGPCS<<4 CYCLE 
         CHECKLIM(1)
         TABLE(N+J) = -1
      REPEAT 
                                        ! strmq - need 4 words per GPC
      N=TABLE(0)
      TABLE(4)=N+1; ! start entry in table of strmq array
      FOR  J=1,1,NGPCS<<2 CYCLE 
         CHECKLIM(1)
         TABLE(N+J)=X'FFFFFFFF'
      REPEAT 
                                        ! pts to slot
      N=TABLE(0)+1
      LOPTS=TABLE(16)<<4; ! eg. X'150' for port 1 trunk 5
      HIPTS=(TABLE(16+ NGPCS-1)<<4) + 15; ! eg. X'16F' if top port/trunk is 16
      TAD=ADDR(TABLE(N)); ! address of start of pts to slot array
      TABLE(5)=N; ! start entry in table of ditto
      TEND=TAD + HIPTS - LOPTS; ! address of last byte of pts array
      J = (HIPTS - LOPTS + 1) >> 2
      CHECKLIM(J)
      FOR  J=TAD,1,TEND CYCLE 
         BYTEINTEGER(J)=255; ! set=unused
      REPEAT 
      PTS TO SLOT==ARRAY(TAD,BIFT)
                                        ! pt to GPC
      N=TABLE(0)+1
      TABLE(6)=N
      PT TO GPC==ARRAY(ADDR(TABLE(N)),BIFT)
                                        ! one byte per pt, rounded to n words
      J=(HIPTS-LOPTS+X'31')>>6
      CHECKLIM(J)
      FOR  J=0,1,NGPCS-1 CYCLE 
         PT TO GPC(TABLE(16+J)-LOPTS>>4)=J
         ! holes in this array will be left unassigned
      REPEAT 
      TABLE(7) = TABLE(0) + 1;   !   start of mechslots array
      IF  LAST SLOT < 0 THEN  RETURN 
      MECHBASE = -8
      LAST STREAM = LOGICAL STREAM
      FOR  SLOTNO=0,1,LASTSLOT CYCLE 
         G==RECORD(GPCT BASE + SLOTNO*SLOTSI)
         GPC NO = (G_GPTSM >> 16) & 15
         IF  G_DEVTYPE=MT START 
            THIS STREAM = G_GPTSM & LOGICAL STREAM
            UNLESS  THIS STREAM = LAST STREAM START 
               ! a new stream
               J = TABLE(0) + 1
               CHECKLIM(2);   !   2 words
               TABLE(J) = X'FFFFFFFF';   !   initialise
               TABLE(J+1) = X'FFFFFFFF'
               MECHSLOTS == ARRAY(ADDR(TABLE(J)), BIFT)
               LAST STREAM = THIS STREAM
               MECH BASE = MECH BASE + 8
            FINISH 
            MECHSLOTS(G_GPTSM & 15) = SLOTNO
            G_MECHINDEX = MECHBASE
            MAGSLOTS = MAGSLOTS + 1
         FINISH 
         PTS = (G_GPTSM >> 4) & X'FFF'
         IF  PTS TO SLOT(PTS - LOPTS) = 255 START 
            PTS TO SLOT(PTS - LOPTS) <- SLOTNO UNLESS  G_DEVTYPE=ZX; !except spare slot
         FINISH 
                                        ! allocate space for each device plus
                                        ! work areas for device adaptors
         TABLE(24 + GPC NO) = (TABLE(24+GPC NO)+7) & (¬7) + C 
            DEV ENTRY BASIC << 2 + C 
            ADAPTOR BYTES(G_DEVTYPE)
      REPEAT 
                                        ! allocate space for operlog
                                        ! in first comms area
         J = TABLE(24)
         J = (J+3) & (-4); ! round up to a word boundary
         TABLE(41) = J; ! operlog
         TABLE(24) = J + 1976; ! = 8 + 48*41
END ; ! FORM TABLES
!
ROUTINE  INIT RES PIC(INTEGER  A, L)
CONSTBYTEINTEGERARRAY  BL(0:40) = 64(40), 21; ! a blank line
      INTEGER(A) = L
      INTEGER(A+4) = -1
      MOVE(41,ADDR(BL(0)),A+8)
      MOVE(L-41,A+8,A+8+41)
END ; ! OF INIT RES PIC
!
                                        ! called by part 3 of grope once for each GPC
                                        ! number of current GPC is in global variable GPC count
ROUTINE  FORMAT COMMS AREA(INTEGER  CAA)
INTEGER  J,SLOTNO, DEV OFFSET,CART,DEV ENT BASE
INTEGER  REPAD,REPLEN,IX,CH,GNO
BYTEINTEGERARRAYNAME  REP,TRTAB
RECORDFORMAT  GPCS RCB LB ALF(RECORD (RCBF) RCB,  C 
   INTEGER  LBE0, LBE1, ALE0 BYTES, ALE0 ADDR, ALE1 BYTES, ALE1 ADDR)
RECORD (GPCS RCB LB ALF)NAME  GPCS RCB
      IF  LAST SLOT < 0 THEN  RETURN 
      DEV OFFSET=X'120'
      FOR  SLOTNO=0,1,LASTSLOT CYCLE 
         G==RECORD(GPCT BASE + SLOTNO*SLOTSI)
         GNO=(G_GPTSM>>16) & 15
         IF  GNO=GPC COUNT START 
            ! if the slot relates to this GPC (ie. this comms area) then
            ! format the device entry.
            DEVTYPE=G_DEVTYPE
            DEV ENT BASE=CAA + DEV OFFSET
            D==RECORD(DEV ENT BASE)
            G_DEV ENT BASE=DEV ENT BASE
            D_GPTSM=G_GPTSM
            D_PROPS0=G_PROPS0
            D_PROPS1=G_PROPS1
            D_PROPADDR=ADDR(D_PROPS0)
            D_CAA=CAA
            D_GRCB AD=DEV ENT BASE + ENT FORM BYTES
            D_MNEMONIC=G_MNEMONIC
            D_LOGMASK=LOGMASK(DEVTYPE)
            D_SENSDAT AD=ADDR(D_SENSE1)
            D_TIMEOUT=TIMEOUT SECONDS(DEVTYPE)
            GPCS RCB==RECORD(D_GRCB AD)
            GPCS RCB=0
            GPCS RCB_RCB_LIMFLAGS=X'4000'; ! trusted chain
            GPCS RCB_RCB_LB BYTES=8
            GPCS RCB_RCB_LBA=ADDR(GPCS RCB_LBE0)
            GPCS RCB_RCB_AL BYTES=8
            GPCS RCB_RCB_ALA=ADDR(GPCS RCB_ALE0 BYTES)
            GPCS RCB_LBE0=GPCS LOGIC BLOCK(0)
            GPCS RCB_LBE1=GPCS LOGIC BLOCK(1)
            GPCS RCB_ALE0 BYTES=16
            GPCS RCB_ALE0 ADDR=ADDR(D_SENSE1)
            D_UA AD=DEV ENT BASE + DEV ENTRY BASIC<<2
            D_UA SIZE=ADAPTOR BYTES(DEVTYPE)
            D_ENT SIZE=DEV ENTRY BASIC<<2 + D_UA SIZE
            IF  DEVTYPE=LP START 
               CART=(G_PROPS1>>16)&15
               D_UA SIZE=D_UA SIZE - 256; ! take off size of translate table
               D_TRTAB AD=D_UA AD + D_UA SIZE
               ! create the translate table, based on the repertoire
               REPAD=REPERTOIRE ADDR(CART)
               REP==ARRAY(REPAD,BIFT)
               REPLEN=REPERTOIRE LEN(CART)
               TRTAB==ARRAY(D_TRTAB AD,BIFT)
               IF  CART=0 START 
                  FOR  IX=0,1,255 CYCLE ; TRTAB(IX)=IX; REPEAT 
               FINISH  ELSE  START 
                  FOR  IX=0,1,255 CYCLE 
                     CH=LP ILLCHAR; ! del (07) for ERCC, UKC may use back '?'
                     J=0
                     WHILE  J<REPLEN CYCLE 
                        IF  IX=REP(J) THEN  CH=IX AND  EXIT 
                        J=J+1
                     REPEAT 
                     ! insert 'format effectors' at own values
                     ! and also turn  lf (x'25') into newline (x'15')
                     IF  IX=X'15' THEN  CH=X'15'
                     IF  IX=X'25' THEN  CH=X'15'
                     IF  IX=X'0C' THEN  CH=X'0C'; ! NEWLINE
                     IF  IX=X'0D' THEN  CH=X'0D'
                     IF  IX=X'40' THEN  CH=X'40'; ! SPACE
                     ! If value IX was not found in repertoire (CH still LP ILLCHAR),
                     ! was it a lower case letter? If so, change it to upper case.
                     ! (We do not search to see if the upper case letter is in the
                     ! repertoire (surely it is)).
                     IF  CH=LP ILLCHAR AND   C 
                     (X'81'<=IX<=X'89' OR  X'91'<=IX<=X'99' OR   C 
                        X'A2'<=IX<=X'A9') THEN  CH=IX ! X'40'
                     TRTAB(IX)=CH
                  REPEAT 
               FINISH ; ! cartridge setting non-zero
            FINISH ; ! LP DEVICE
            IF  DEVTYPE = OP START 
               UNLESS  OPSLOTS < 7 START 
                  *IDLE_X'FF04'; !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++FF04
               FINISH 
               G_MECHINDEX = G_MECHINDEX ! (OPSLOTS << 4)
               TABLE(32 + OPSLOTS) = C 
                  D_UA AD << 16 + D_UA SIZE
               OPSLOTS = OPSLOTS + 1
            FINISH 
            DEV OFFSET=DEV OFFSET + D_ENT SIZE
         FINISH ; ! slot belongs to this GPC
      REPEAT 
END ; ! FORMAT COMMS AREA
!
EXTERNALROUTINE  GPC GROPE(RECORD (PARMF)NAME  P)
INTEGER  LPINITWORD,CART
INTEGER  MPROP,AUTO,NEW CAA,FORM STYLE,FORM LEN
INTEGER  I,J,K,L
INTEGER  A,S
INTEGER  DACT
INTEGER  CURNR
!
RECORDFORMAT  ALEF(INTEGER  BYTES,ADDR)
RECORD (ALEF)ARRAYFORMAT  ALEFF(0:3)
RECORD (ALEF)ARRAYNAME  ALE
RECORDFORMAT  RF(INTEGER  GPTSM,RESP0,RESP1,PROPS0,PROPS1,  C 
      SENS0,SENS1,SENS2)
RECORD (RF)NAME  R
!
SWITCH  GROPE(1:3)
      DACT=P_DEST&X'FFFF'
      UNLESS  0<DACT<=3 THEN  RETURN 
      -> GROPE(DACT)
                                        ! initialise GPC and grope
                                        ! called for each GPC, pt in ascending order
                                        ! on first call, various initialisations done
                                        !   P1 = pt
                                        !   P2 = addr of table
                                        !   P3 = CAA
                                        !   P4 = size of table
GROPE(1):
      IF  SETUP = 0 START 
         SETUP = 1
         RESPONSES == ARRAY(X'81002080', IFT)
         FOR  J=0,1,MAX RESPONSE WORDS-1 CYCLE 
            RESPONSES(J) = X'88888888'
         REPEAT 
         FOR  J=0,1,15 CYCLE 
            REPERTOIRE ADDR(J) = ADDR(LP96REP(0))
            REPERTOIRE  LEN(J) = 96
         REPEAT 
         REPERTOIRE ADDR(3) = ADDR(LP384REP(0))
         REPERTOIRE  LEN(2) = 48
         REPERTOIRE  LEN(3) = 384
         REPERTOIRE  LEN(4) = 64
         TABLE == ARRAY(P_P2, IFT)
         TOP TABLE ENTRY = P_P4
         TABLE(0) = 47;   ! last word 'used'
         TABLE(1) = 48;   ! start of slots
         TABLE(2) = -1;   ! last slot
         TABLE(3) = 0;   ! no of GPCS
         GPCT BASE = ADDR(TABLE(TABLE(1)))
         STRING(ADDR(TABLE(44))-1)=VSN
         MSG(VSN)
      FINISH 
      PT = P_P1
      CAA = P_P3
      GPC NO = TABLE(3)
      IF  GPC NO > MAX GPC NO START 
         *IDLE_X'FF00';   !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++FF00
      FINISH 
      TABLE(3) = GPC NO + 1
      TABLE(16 + GPC NO) = PT
      TABLE(24 + GPC NO) = X'120'
      J = GPC INIT(CAA, PT, 1)
      UNLESS  J = 0 START 
         MSG("GPC ".HTOS(PT,2)." init res=".HTOS(J,8))
         RETURN 
      FINISH 
      RCBA=CAA + ONE RCB OFFSET
      RCB==RECORD(RCBA)
      CA==RECORD(CAA)
      RCB_LIMFLAGS = X'4000' ! (TOPLSEG << 18); ! trusted
      RCB_LSTBA=LST RA
      RCB_LB BYTES=AL OFFSET - LB OFFSET
      RCB_LBA=RCBA + LB OFFSET
      RCB_AL BYTES=PROPDAT OFFSET - AL OFFSET
      RCB_ALA=RCBA+AL OFFSET
      PROPDATADDR=RCBA + PROPDAT OFFSET
      SENSDATADDR=RCBA + SENSDAT OFFSET
      ALE==ARRAY(RCBA + AL OFFSET,ALEFF)
                                        ! properties data
      ALE(0)_BYTES=8
      ALE(0)_ADDR=PROPDATADDR
                                        ! sense data
      ALE(1)_BYTES=12
      ALE(1)_ADDR=SENSDATADDR
                                        ! load rep data
      ALE(2)_BYTES=384
      ALE(2)_ADDR=CAA + LPREP OFFSET
                                        ! LP init data
      ALE(3)_BYTES=4
      ALE(3)_ADDR=ADDR(LPINITWORD)
      STRM=0
      PAWSAWFAILS=0
      SPARE SLOT=0;                     ! set up spare slot (if possible)
      UNTIL  STRM>=15 OR  PAWSAWFAILS>=2 CYCLE 
         SENT == CA_S ENTRY(STRM)
         CURNR=NO OF RESPONSES
         J=CURNR;                       ! save for possible connect repeat
                                        ! 'DO' computes DEVTYPE, PROPS & PROPS1
         DO(CONNECT)
                                        ! if 'DO' fails, DEVTYPE is set to zero
         IF  DEVTYPE=0 START ;          ! 1st connect always fails for EMLAN fep !!
            CURNR=J
            NO OF RESPONSES=J
            WAIT(10);                   ! (also needs a wait)
            DO(CONNECT);                ! so try again
         FINISH 
         IF  DEVTYPE = MT START 
            INVALIDATE(CURNR)
            DO(SENDPROP)
         FINISH  ELSE  START 
            IF  DEVTYPE = LP START 
                                        ! PROPS  has bytes 0-3 of LP properties
                                        ! PROPS1 has bytes 4-5
                                        ! bottom 4 bits of byte 5 has cartridge number set on front of LP.
                                        ! if cartridge number is set zero, we don't load any rep if
                                        ! there's one already loaded, else we load the 64-char rep
                                        ! (being the first 64 chars of the 96-char rep above).
                                        ! if the cartridge number is :
                                        !     2   we load the 48-char rep for the BT DPE CRAIGLOCKHART 2970
                                        !     3   we load the 384-char rep for the BUSH ESTATE 2980
                                        !     4   we load the 64-char rep for the BT DPE BARBICAN 2970
                                        !     5   we load the 96-char rep for the ERCC-KB 2972s
               FORM STYLE=PROPS&255
               FORM LEN=(FORM STYLE>>4)*10 + FORM STYLE&15
               FORM LEN=66 IF  FORM LEN=0
               LBE(WRITECONTROL)= C 
                  (LBE(WRITECONTROL)&(¬255))!(FORM LEN - 1)
               CART=(PROPS1>>16)&15
               A = REPERTOIRE ADDR(CART)
               S = REPERTOIRE LEN(CART)
               I=0
               WHILE  I<384 CYCLE ; ! repertoire buffer must be filled with 384 bytes
                  J=A; ! to start of relevant array
                  WHILE  J<A+S CYCLE 
                     INTEGER(CAA+LPREP OFFSET+I)=INTEGER(J)
                     I=I+4; J=J+4
                  REPEAT 
               REPEAT 
         ! what we are doing here is - we want the props & sense info in one entry. the first
         ! chain (sendprop) fails short block until LP has had
         ! initialise. so when we've done that we invalidate the first
         ! entry and do another sendprop+sense, and "NEW ENTRY" uses
         ! that one. this way we can pick up the auto bit in tertiary status to
         ! pass to GPC (we want to allocate M'LP' to be the first LP in auto if
         ! more than one available).
               INVALIDATE(CURNR)
               LPINITWORD=X'00000010'; ! back-question for illegal, auto-throw not set
               DO(INITIALISE)
               UNLESS  CART = 0 AND  C 
                  (PROPS1 & X'100000') = 0 C 
               THEN  DO(LOADREP)
               LPINITWORD=X'0000FC10'
               DO(INITIALISE)
               DO(WRITECONTROL) UNLESS  FORM STYLE=X'99'; ! value for testing omitting write control
               DO(SENDPROP)
            FINISH  ELSE  START 
               IF  DEVTYPE > 15 THEN  INVALIDATE(CURNR)
            FINISH 
         FINISH 
         STRM=STRM+1
      REPEAT 
                                        ! build slots for this GPC
      FOR  J=0,8,NO OF RESPONSES-8 CYCLE 
         R==RECORD(ADDR(RESPONSES(J)))
         GPTSM=R_GPTSM
         IF  GPTSM>>30 = 0 START 
            MPROP=R_PROPS0
            DEVTYPE=MPROP>>24
                                        ! for LP, pass first byte of tertiary status to go into mechindex field
                                        ! (there is one secondary followed by 7? tertiary status bytes).
                                        ! in GPC table (contains manual/auto bit)
            IF  DEVTYPE = MT START 
               MPROP = MPROP & X'FFF0FFFF'
            FINISH 
            AUTO=MPROP
            IF  DEVTYPE=LP THEN  AUTO=R_SENS0>>16
            NEW SLOT(DEVTYPE,GPTSM,MPROP,R_PROPS1,AUTO)
                                        ! for mag tape streams, add slots up to 4 (for MT4) or 8 (for MT6), with
                                        ! increasing mechanism numbers (start at 0).
            IF  DEVTYPE=MT START 
               K=3; ! 3 more for MT4
               IF  R_PROPS0 & MT6PROP#0 THEN  K=7; ! 7 more for MT6
               FOR  L=1,1,K CYCLE 
                  GPTSM=GPTSM+1; ! add one into mech field
                  MPROP=MPROP + X'00010000'; ! & 1 into handler no.
                  NEW SLOT(DEVTYPE,GPTSM,MPROP,R_PROPS1,MPROP)
               REPEAT 
            FINISH 
                                        ! invalidate so not picked up when grope called again
            R_GPTSM=GPTSM ! X'40000000'
         FINISH 
      REPEAT 
      P_P1=0
      RETURN 
!
                                        ! part 2   all GPCs have now been groped, form tables
!
GROPE(3):
      LAST SLOT = TABLE(2)
      FORM TABLES
                                        ! at this point, TABLE(24+n) must have been set
                                        ! up so that sup can supply suitably sized segments
      RETURN 
                                        ! part 3   re-initialise the GPCs to use virtual addrs
                                        !          and format the communications areas
!
GROPE(2):
      ! P_P1 is port+trunk
      ! P_P2 is table address
      ! P_P3 is virtual address of old CA segment
      ! P_P4 is virtual address of new CA segment
      IF  LASTSLOT<0 THEN  RETURN 
      NEW CAA=P_P4
      TABLE(GPC COUNT + 8)=NEW CAA
      IF  GPC COUNT = 0 START 
                                        ! earliest possible time to init res pics
         TABLE(41) = TABLE(41) + NEW CAA
         INIT RES PIC(TABLE(41), 48*41)
      FINISH 
      P_P1=GPC REINIT(P_P3,NEW CAA,P_P1)
      FORMAT COMMS AREA(NEW CAA)
      GPC COUNT=GPC COUNT + 1
      RETURN 
END ; ! GPC GROPE
EXTERNALROUTINE  DISCGROPE(RECORD (PARMF)NAME  P)
!***********************************************************************
!*    TRIES TO READ PROPERTY CODES OF ALL 15 STREAMS ON A FPC2         *
!***********************************************************************
INTEGERFNSPEC  PROPCODES(INTEGER  STRM)
RECORDFORMAT  CCAFORM(INTEGER  MARK,PAW,PIW1,PIW2,CSAW1,CSAW2,C 
      CRESP1,CRESP2,LONGLONGREALARRAY  STRMS(0:15))
RECORDFORMAT  RQBFORM(INTEGER  LSEGPROP, LSEGADDR, LBPROP, LBADDR,  C 
         ALPROP, ALADDR, W6, W7, W8)
RECORDFORMAT  DDTFORM(INTEGER  SER, PTS, PROPADDR, STICK, CCA, RQA,  C 
         LBA, ALA, STATE, IW1, CONCOUNT, SENSE1, SENSE2, SENSE3,  C 
         SENSE4, REPSNO, BASE, ID, DLVN, MNEMONIC,  C 
         STRING  (6) LAB, BYTEINTEGER  MECH, C 
         INTEGER  PROPS,STATS1,STATS2, C 
         BYTEINTEGER  QSTATE,PRIO,SP1,SP2, C 
         INTEGER  LQLINK,UQLINK,CURCYL,SEMA,TRLINK,CHFISA)
RECORDFORMAT  PROPFORM(INTEGER  TRACKS,CYLS,PPERTRK,BLKSIZE,TOTPAGES,C 
      RQBLKSIZE,LBLKSIZE,ALISTSIZE,KEYLEN,SECTINDX)
RECORDFORMAT  INITFORM(INTEGER  W0, W1, W2, W3, W4)
RECORD (INITFORM) INIT
EXTERNALINTEGERSPEC  HI STRM
CONSTINTEGER  DDTSIZE=128
CONSTINTEGER  TEMP CA=X'80000000'!10<<18
CONSTINTEGER  TIMEOUT=200000
CONSTINTEGER  SD=X'58000000', LST RA=X'8080'
CONSTINTEGER  READ9388=X'93880E80';     ! TO READ DFC WORD FOR EXTENDED OPTION FLAG
CONSTINTEGER  EXFLAG=X'08000000';       ! FLAG
CONSTINTEGER  AFA=X'100',RFB=X'400'
RECORD (PROPFORM)NAME  PROP
RECORD (CCAFORM)NAME  ICA,CCA
RECORD (RQBFORM)NAME  RQB
RECORD (DDTFORM)NAME  DDT
INTEGER  PT, ISA, STRM, AD, I,J,K, M, DITADDR, PTR, SIZE, C 
            NCONTROLERS, INF, RESPONSE, FAILCOUNT, MNEM
      FAILCOUNT=0
      IF  P_DEST#0 THEN  ->REINIT
      PT=P_P1;                          ! PORT & TRUNK IN P_P1
      ISA=X'40000800'!PT<<16
                                        ! FIND OUT HOW MANY STREAMS
      *LB_ISA; *LSS_3; *ST_(0+B );      ! 2 SUSPENDS BEFORE DCM
      WAIT(1)
      *LB_ISA; *LSS_(0+B );             ! READ TO CLEAR P4 LOCK
      *LSS_3;  *ST_(0+B ); *LSS_(0+B )
      *ADB_X'500';                      ! TO X'40PT0D00'
      *LSS_X'400'; *ST_(0+B );          ! SET DCM
      *ADB_X'100';                      ! TO X'40PT0E00'
      *STB_I
      *LSS_READ9388;                    ! REQUIRED WORD
      *ST_(0+B );                       ! READ IT
      K=200
AWAIT:                                  ! WAIT FOR RESPONSE
      *LB_I
      *LSS_(0+B )
      *ST_J
      K=K-1
      ->AWAIT UNLESS  K=0 OR  J&RFB#0
      *LSS_AFA;                         ! SEND RESPONSE
      *LB_I
      *ST_(0+B )
      *LSS_X'1E12'; *ST_(0+B );         ! MASTER CLEAR & FBS
      *SBB_X'100';                      ! TO X'40PT0D00'
      *LSS_0; *ST_(0+B );               ! UNSET DCM & MC
      HI STRM=15
      UNLESS  K=0 START 
         IF  J&EXFLAG#0 THEN  HI STRM=7;   ! 8 STREAMS
         PRINTSTRING("DFC ".HTOS(PT,2)." EXOPT reg = ".HTOS(J,8)."
")
      FINISH  ELSE  OPMESS("DFC ".HTOS(PT,2)." EXOPT flag RTO")
      WAIT(100);                        ! SETTLE DOWN
GROPE AGAIN:                            ! AFTER SHIFT CA FROM 0 FAILS
      CCA==RECORD(0)
      CCA_MARK=-1
      INIT=0
      INIT_W0=((INTEGER(PST VA+PST SEG*8)&X'FFFC'+X'80')//8-1)<<18!X'80000000'
      INIT_W1=INTEGER(PST VA+PST SEG*8+4)&X'0FFFFF80'
      INIT_W2=TEMP CA
      CCA_PAW=X'04000000';              ! CONTROLLER REQUEST
      CCA_CSAW1=X'32000014';            ! ** STRAW CLUTCH **JM**!!
!      CCA_CSAW1=X'12000014'
      CCA_CSAW2=REALISE(ADDR(INIT))
! RESPONSE WILL BE NEW COMM AREA. REMAP CCA BEFORE FIRING IO
      CCA==RECORD(TEMP CA)
      CCA=0;  CCA_MARK=-1
      *LB_ISA;  *LSS_1;  *ST_(0+B )
      J=0
      WHILE  CCA_CRESP1=0 OR  CCA_MARK#-1 CYCLE 
         J=J+1
         IF  J>TIMEOUT THEN  START 
            OPMESS("DISCGROPE failed".HTOS(PT,2))
            DUMPTABLE(10,REAL0ADDR,32)
            DUMPTABLE(11,ADDR(CCA),32)
            IF  FAILCOUNT<4 START 
               *LB_ISA; *LSS_2; *ST_(0+B )
               FAILCOUNT=FAILCOUNT+1
               WAIT(100*FAILCOUNT)
               ->GROPE AGAIN;           ! HAVE ANOTHER SHOT
            FINISH 
            RETURN 
         FINISH 
      REPEAT 
      RQB==RECORD(X'120'+TEMP CA)
      RQB_LSEGPROP=128<<18!X'C000'
      RQB_LSEGADDR=LST RA
      RQB_LBPROP=X'18000008'
      RQB_LBADDR=X'200'+TEMP CA
      RQB_ALPROP=X'18000010'
      RQB_ALADDR=X'210'+TEMP CA
      RQB_W6=X'FF00';                   ! STATUS MASK
      RQB_W7=X'02001300'
! SET UP ONE LOGICAL BLOCK ENTRY AND ONE ADDRESSLIST ENTRY TO READ
! PROPERTY CODES. ALL STREAMS WILL USE SAME RQB ETC
! CYCLE THRU ALL POSSIBLE STREAMS
      CYCLE  STRM=0,1,HI STRM
         RESPONSE=PROPCODES(STRM)
!
! FIRST STREAM GIVES ERRONEOUS RESPONSE DUE TO UNKNOWN TIMING
! IF THERE IS NO STREAM 0 THEN WAIT A BIT AND TRY AGAIN
!
         IF  RESPONSE=X'00411001' AND  STRM=0 THEN  C 
            WAIT(500) AND  RESPONSE=PROPCODES(0)
! BUILD THE DISC DEVICE TABLE FROM PROPERTY CODES
         M=J>>16&255
         K=J>>24
         MNEM=M'ED'
         UNLESS  K=X'33' OR  K=X'35' START ;  ! NOT EDS100 OR EDS200
            K=X'33';                    ! FORCE EDS100 PROPS PROTEM
            M=(PT&15)<<4!STRM;          ! TS AS DEVNO
            MNEM=M'ZX';                 ! 'SPARE' MNEMONIC
         FINISH  
         DDT==RECORD(P_P2+NDISCS*DDTSIZE)
         DDT=0
         DDT_SER=X'300010'+NDISCS
         DDT_PTS=PT<<4!STRM
         DDT_PROPADDR=(K-X'33')*20;        !DISPLACEMENT IN TABLE
         DDT_MNEMONIC=MNEM<<16+HEXDS(M>>4)<<8+HEXDS(M&15)
         DDT_MECH=M
         DDT_PROPS=J
         DDT_CHFISA=ISA
         NDISCS=NDISCS+1
!MISS:
DUP:  REPEAT 
      *LB_ISA; *LSS_2; *ST_(0+B );      ! MATERCLEAR AGAIN IN CASE ATTNS
      RETURN 
REINIT:
                                        ! P_P2=ADDR(CONTROLLER LIST)
                                        ! P_P3=DITADDR
                                        ! P_P4=NO OF DISCS
      DITADDR=P_P3
      NCONTROLERS=INTEGER(P_P2)
      CYCLE  I=1,1,NCONTROLERS;     ! DOWN CONTROLLER LIST
         INF=INTEGER(P_P2+4*I)
         CCA==RECORD(X'80000000'!(INF&X'FFFF')<<18)
         CCA=0;                      ! CLEAR COMMUNICATION AREA
         PTR=ADDR(CCA)+(32+16*(INF>>16&15+1));      ! START OF RQBS (INF HAS HI STRM NO.)
         CCA_MARK=-1
!
         CYCLE  J=0,1,NDISCS-1
            DDT==RECORD(INTEGER(DITADDR+4*J))
            PROP==RECORD(DDT_PROPADDR)
            IF  DDT_PTS>>4=INF>>24 START ;  ! ON THE DFC
               RQB==RECORD(PTR)
               PTR=PTR+PROP_RQBLKSIZE
               RQB_LSEGPROP=128<<18!X'C000';! PRIV & ACR=0
               RQB_LSEGADDR=INTEGER(PST VA+PST SEG*8+4)&X'FFFFF80';! REAL ADR OF PST
               SIZE=PROP_LBLKSIZE
               RQB_LBPROP=X'18000000'+SIZE
               RQB_LBADDR=PTR+12
               INTEGER(PTR)=X'04010800';     ! CONNECT STREAM
               INTEGER(PTR+4)=X'04400400';   ! READ PROPCODES
               INTEGER(PTR+8)=X'00410102';   ! SENSE
               PTR=PTR+SIZE+16
               SIZE=PROP_ALISTSIZE
               RQB_ALPROP=X'18000000'+SIZE
               RQB_ALADDR=PTR+16
               RQB_W6=X'FF00';       ! STATUS MASK ALLOW ALL
               INTEGER(PTR)=SD+4;    ! 4 BYTES OF PROPCODES
               INTEGER(PTR+4)=ADDR(DDT_PROPS)
               INTEGER(PTR+8)=X'58000030';! SENSE 48 BYTES(UP TO MECH7)
               INTEGER(PTR+12)=RQB_ALADDR+128
               PTR=PTR+SIZE+16
               DDT_CCA=ADDR(CCA)
               DDT_RQA=ADDR(RQB)
               DDT_LBA=RQB_LBADDR
               DDT_ALA=RQB_ALADDR
               STRM=DDT_PTS&15
               INTEGER(ADDR(CCA_STRMS(STRM))+4)=ADDR(RQB)
            FINISH 
         REPEAT 
!
! HAVE SET UP DDT FOR ALL DEVICES ON THIS CONTROLLER
! SO NOW INITIALISE IT
!
REINIT AGAIN:
         CCA_PAW=X'04000000';        ! DO CONTROLLER REQUEST
         CCA_CSAW1=X'32000014';        ! NO TERMINATION INT
! REAL ADDRESS RREQUIRED. SUBTRACT SEGNO AND ADD GLA SEG BASE
         CCA_CSAW2=REALISE(ADDR(INIT))
         INIT_W0=((INTEGER(PST VA+PST SEG*8)&X'FFFC'+X'80')//8-1)<<18!X'80000000'
         INIT_W1=INTEGER(PST VA+PST SEG*8+4)&X'0FFFFF80'
         INIT_W2=(INF&X'FFFF')<<18!X'80000000'
         ICA==RECORD(REAL0ADDR);   ! REAL ADDR 0
!
!COPY 10 WORDS OC CCA TO REAL ADDRESS 0 AND INITIALISE
!
         CYCLE  J=0,4,36
            INTEGER(ADDR(ICA)+J)=INTEGER(ADDR(CCA)+J)
         REPEAT 
         CCA_PAW=0
         CCA_CSAW1=0
         PT=INF>>24
         ISA=PT<<16!X'40000800'
         *LB_ISA; *LSS_1; *ST_(0+B )
!
! MUST WAIT TILL CONTROLLER HAS FINISHED WITH REAL ADDRESS 0 BEFORE
! TRYING TI INITIALISE THE NEXT CONTROLLER
!
         J=0
         WHILE  CCA_CRESP1=0 OR  CCA_MARK#-1 CYCLE 
            J=J+1
            IF  J>=TIMEOUT START 
               OPMESS("DFC REINIT fails ".HTOS(PT,2))
               DUMPTABLE(10,REAL0ADDR,32)
               DUMPTABLE(11,ADDR(CCA),32)
               IF  FAILCOUNT<4 START 
                  *LB_ISA; *LSS_2; *ST_(0+B )
                  FAILCOUNT=FAILCOUNT+1
                  WAIT(100*FAILCOUNT)
                  ->REINIT AGAIN;           ! HAVE ANOTHER SHOT
               FINISH 
               EXIT 
            FINISH 
         REPEAT 
         CCA_CRESP1=0; CCA_CRESP2=0
      REPEAT 
      RETURN 
INTEGERFN  PROPCODES(INTEGER  STRM)
INTEGER  K
         INTEGER(X'200'+TEMP CA)=X'04010800'
         INTEGER(X'204'+TEMP CA)=X'00000400'
         INTEGER(X'210'+TEMP CA)=SD+X'2C'
         INTEGER(X'214'+TEMP CA)=X'240'+TEMP CA
         AD=ADDR(CCA_STRMS(STRM))
         INTEGER(AD)=X'10000024'
         INTEGER(AD+4)=ADDR(RQB)
         INTEGER(AD+8)=0
         INTEGER(AD+12)=0
         INTEGER(X'240'+TEMP CA)=-1;    ! IN CASE NO PROPERTY CODES
         CCA_MARK=-1
         CCA_PIW1=0;  CCA_PIW2=0
         CCA_PAW=X'01000000'+STRM
         *LB_ISA;  *LSS_1;  *ST_(0+B )
! WAIT FOR RESPONSE
WAIT:    J=10000
         WHILE  INTEGER(AD+8)=0 OR  CCA_MARK#-1 CYCLE 
            J=J-1
            ->MISS IF  J=0
         REPEAT 
         K=INTEGER(AD+8);               ! RESPONSE
         IF  K>>22=0 START ;             ! ATTENTION
            PRINTSTRING("DISC stream ".HTOS(PT<<4!STRM,3). C 
                 " Attention ".STRHEX(K)."
")
            INTEGER(AD+8)=0
            ->WAIT
         FINISH 
         J=INTEGER(TEMPCA+X'240')
         PRINTSTRING("DISC stream ".HTOS(PT<<4!STRM,3)." responds ")
         PRINTSTRING(STRHEX(K)." ".STRHEX(INTEGER(AD+12))C 
            ." PROPS=".STRHEX(J)."
")
      RESULT =K
MISS: RESULT =0
END 
END 
EXTERNALROUTINE  DRUMGROPE(RECORD (PARMF)NAME  P)
RECORDFORMAT  STRF(INTEGER  SAW0,SAW1,RESP0,RESP1);   ! WITHIN COMM AREA
RECORDFORMAT  ESCBF(INTEGER  HQ,LQ,SAW0,PAWBS, ADDSTRS)
RECORDFORMAT  DTENTF(INTEGER  NSECS,CONTI,SPTRK,NEXT,STATE, C 
                     INTEGERNAME  MARK,PAW,PIW, C 
                     RECORD (ESCBF)ARRAY  ESCBS(0:31))
                       ! IN FACT ONLY NECESSARY ESCBS ARE PRESENT.
RECORD (DTENTF)NAME  DTAB0;    ! MAPS ONTO FIRST ENTRY IN TABLE
RECORDFORMAT  CONTABF(INTEGER  ISCR,BATCH,INTEGERNAME  MARK,CRESP0)
RECORD (CONTABF)ARRAYFORMAT  CONTABAF(1:8)
OWNRECORD (CONTABF)ARRAY  TCONTAB(1:8);  ! TEMPORARAY (PHASE 1) CONTROLLER TABLE
RECORD (CONTABF)ARRAYNAME  CONTAB;   ! MAPPED TO FINAL POS IN DTAB
RECORDFORMAT  COMAF(INTEGER  MARK, PAW, COUNTS, DRUMRQ, CAW0,  C 
         CAW1, CRESP0, CRESP1, INTEGERARRAY  PAWS, PIWS(0:7), C 
        RECORD (STRF)ARRAY  STRS(0:127))
                                        ! NOW OWNS WHICH STORE PRINCIPAL
                                        ! PARAMETERS
OWNINTEGER  DNEXTD,DCURRD;        ! DISPLACEMENTS OF NEXT AND CURRENT DRUM TAB ENTRIES RESP.
OWNINTEGER  TOTDSP;                     ! THE NUMBER OF KBYTES OF DRUM STORE AVAILABLE.
OWNINTEGER  CONT1,CONT2;               ! CONTAB INDICES FOR USE IN PHASE1 AND 2 RESP.
INTEGERNAME  TSIZE;                     ! ==INTEGER(AREA), HOLDS AREA SIZE IN BYTES.
INTEGER  NESECQS;                    ! ON A DRUM
INTEGER  UTILISATION;                ! OF DRUM SPACE ON SFC
INTEGER  PT, MN;                        ! PORT/TRUNK, MECHANISM NUMBER
INTEGER  SECINC,PROPCODE
INTEGER  EPN;                       ! = EPAGESIZE
INTEGER  TCAD;                          ! TEMPORARY COMMUNICATION AREA ADRRESS.
INTEGER  MAXMN;                         ! MAX ON GIVEN SFC => SIZE OF COMM AREA.
INTEGER  CAD;                           ! FINAL COMM AREA ADDRESS.
INTEGER  CASIZE
INTEGER  STRI,ESEC;                    ! INDICES INTO COMM AREA, AND DTENT
INTEGER  PAWBS,SAW0;                   ! ESCB VALUES
INTEGER  SLINK;                         ! LINK SAVE
STRING  (30) REPORT;                    ! MESSAGE FOR OPER SCREEN
RECORD (COMAF)NAME  CA
RECORD (DTENTF)NAME  DTENT
RECORD (DTENTF)NAME  DTENT2,DTENT3;     ! FOR TABLE TIDY
RECORD (CONTABF)NAME  CTENT
RECORD (CONTABF)NAME  TCTENT
RECORD (ESCBF)NAME  ESCB
CONSTINTEGER  CONTROL=X'800';           ! TRUNK IMAGE STORE ADDRESS
                                        ! CONSTANTS WHICH DEFINE COMM AREA PATTERNS
CONSTINTEGER  PWFCR=X'04000000'
CONSTINTEGER  CRFINIT=X'32000004'
CONSTINTEGER  CRFDRUMRQ=X'3A000004'
CONSTINTEGER  CRFRSTATUS=X'31000014'
CONSTINTEGER  DRFRPC=0;                 ! FOR COMPLETENESS!!
CONSTINTEGER  DRFWFMT=X'01000000'
CONSTINTEGER  DRFCONN=X'05000000'
CONSTINTEGER  DRFERRC=X'0700000F';      ! TO MAX OF 15
                                        ! NOW REPLY BITS
CONSTINTEGER  NT=X'00800000';           ! NORMAL TERMINATION
CONSTINTEGER  AUTO=X'80',AVAIL=8;     ! IN SAME PLACE.
CONSTINTEGER  SFLAGS= X'A2000000';           ! SAW FLAGS FOR COMM AREA STREAMS
                                        ! PROPERTIES OF VARIOUS DRUM TYPES
CONSTBYTEINTEGERARRAY  SECSPTRK(1:4)=16,24,24,11;   ! SECTORS PER TRACK
CONSTINTEGERARRAY  TRKSPDRUM(1:4)=128,256,256,512;    ! TRACKS PER DRUM
CONSTINTEGERARRAY  SECSPDRUM(1:4)=X'800',X'1800',X'1800',X'1600';  ! SECTORS PER DRUM
CONSTINTEGER  CABASIC=96             ;  ! SIZE OF COMM AREA(BYTES)
                                        ! BASIC IE WITHOUT ANY DRUMS
CONSTINTEGERARRAY  BPMECH(1:4)=512(3),256;! BYTES COMM PER MECHNSM
                                        ! NOW NECESSARY ROUTINE SPECS
ROUTINESPEC  LOADUPROG(INTEGER  PT);    ! LOADS MICROPROGRAM TO PORT
                                        ! AND TRUNK GIVEN, PLUS
                                        ! SETS IN INITIAL ADDRESSING MODE.
ROUTINESPEC  FEEL FOR(INTEGER  PT, MN, CAD)
                                        ! ESTABLISHES PRESENCE OR NOT
                                        ! OF EACH  MECHANISM.
ROUTINESPEC  MOVE(INTEGER  PT, OLDCA, NEWCA)
                                        ! MOVES COMMUNICATION AREA
ROUTINESPEC  DO IT(INTEGER  TIME, PT, RECORD (COMAF)NAME  CA)
                                        ! DO A CONTROLLER REQUEST
                                        ! ON PORT, TRUNK VIA COMM AREA.
SWITCH  PHASE(1:3);                       ! POFF SWITCH
      PT=P_P1<<16!X'40000000';          ! NOW IMAGE STORE ADDRESS
      TSIZE==INTEGER(P_P2)
      DTAB0==RECORD(P_P2+4)
      TCAD=P_P3
      EPN=EPAGESIZE
      ->PHASE(P_DEST)
                                       !
PHASE(1):                               ! P1=PT, P2=AREA, P3=TCAD
                                        ! LOAD MICROPROGRAM AND GET INTO INITIAL ADDRESSING
                                        ! MODE.
      LOAD UPROG(PT)
      INTEGER(REAL0ADDR)=-1;          ! SET MARK
      WAIT(1);                          ! GUARANTEE WRITTEN THROUGH
      MOVE(PT,REAL0ADDR,TCAD);          ! MOVE COMM AREA FROM INIT TO TCAD
      REPORT=""
      MAXMN=-1;                         ! FEEL FOR DRUMS ON THIS SFC
      CYCLE  MN=0,1,3
         FEEL FOR(PT,MN,TCAD)
         REPORT=REPORT.STRINT(MN)."," IF  MAXMN=MN;  ! FOUND THIS ONE
      REPEAT 
      IF  MAXMN>=0 START 
         CA==RECORD(TCAD)
         CA_DRUMRQ=DRFERRC
         CA_CAW0=CRFDRUMRQ
         DO IT(5,PT,CA)
                                        ! THAT SETS ERROR COUNT ON THIS SFC
         CASIZE=CABASIC+(MAXMN+1)*BPMECH(PROPCODE)
         CONT1=CONT1+1;            ! MAKE A NEW CONTAB ENTRY
         TCONTAB(CONT1)_ISCR=PT+CONTROL
         TCONTAB(CONT1)_BATCH=0;      ! REST FILLED AND MOVED IN PHASE 2
                                        ! FORM REPORT FOR THIS SFC
         LENGTH(REPORT)=LENGTH(REPORT)-1;  ! DELETE TRAILING COMMA
         REPORT=REPORT." ".STRINT(UTILISATION)."%"
      FINISH  ELSE  START 
         REPORT="  none"
         CASIZE=0
      FINISH 
      REPORT="SFC PT".HTOS(PT>>16,2)." DRUMS ".REPORT
      OPMESS(REPORT)
                                        ! SET UP REPLY
      P_P5=TOTDSP
      P_P6=CASIZE
      RETURN 
                                        ! ON RETURN FROM ACTIVITY 1 :-
                                        ! P5= NUMBER OF DRUM PAGES SO FAR
                                        ! P6= FINAL SIZE OF THIS SFC COMM AREA
                                        !
PHASE(2):                               ! P1=PT, P2=AREA,  P3=TCAD, P4=CAD
!
! SET UP REMAINING DTAB AND CONTAB ENTRIES  FOR THIS SFC
! CONT2 DETERMINES WHICH CONTAB ENTRY I.E. ASSUMES ORDER IN
! PHASE1 AND PHASE 2 ARE THE SAME.
!
  CAD=P_P4
  MOVE(PT,TCAD,CAD);        ! TO FINAL POSITION
  CA==RECORD(CAD)
  CONTAB==ARRAY(P_P2 + DNEXTD,CONTABAF);  ! I.E. IMMEDIATELY FOLLOWING LAST DRUM ENTRY
  CONT2=CONT2+1;             ! THE CURRENT ENTRY
  CTENT==CONTAB(CONT2)
  TCTENT==TCONTAB(CONT2)
  TCTENT_MARK==CA_MARK;      ! FORM COMPLETE CONTAB ENTRY
  TCTENT_CRESP0==CA_CRESP0;    ! IN TEMP CONTAB
  CTENT=TCTENT;               ! AND COPY TO FINAL POSITION
                             !
                             ! FIND EACH DRUM ON THIS SFC AND FILL ENTRY
  DTENT==DTAB0
  DTENT==RECORD(P_P2+DTENT_NEXT) WHILE  DTENT_CONTI#CONT2
                             ! NOW FOUND FIRST SUCH DRUM, REST FOLLOW
  CYCLE 
    ESCB==DTENT_ESCBS(0)
    MN=ESCB_HQ;               ! FROM PHASE1
    NESECQS=ESCB_LQ
    PROPCODE=ESCB_SAW0
                               ! SET UP REFERENCES
    DTENT_MARK==CA_MARK
    DTENT_PAW==CA_PAWS(MN)
    DTENT_PIW==CA_PIWS(MN)
                              ! THEN ESCBS
    IF  PROPCODE#4 THEN  STRI=MN<<5 ELSE  STRI=MN<<4;! 32 0R 16
    SAW0=SFLAGS!MN<<21
    PAWBS=(-1)<<(32-EPN);     ! EPN BITS RIGHT JUSTIFIED IN A WORD
    SECINC=EPN<<16
    CYCLE  ESEC=0,1,NESECQS-1
      ESCB==DTENT_ESCBS(ESEC)
      ESCB_HQ=0
      ESCB_LQ=0
      ESCB_SAW0=SAW0
      ESCB_PAWBS=PAWBS
      ESCB_ADDSTRS=ADDR(CA_STRS(STRI))
      SAW0=SAW0+SECINC
      PAWBS=PAWBS>>EPN
      STRI=STRI+EPN
    REPEAT 
                              ! RECORD INFO FOR DRUM
    ESCB==DTAB0_ESCBS(0)
    ESCB_HQ=DNEXTD;           ! THE DISPLACEMENT OF CONTAB
    ESCB_LQ=CONT2;            ! THE HIGHEST INDEX (SO FAR)
                              !
  EXITIF  DTENT_NEXT=0
    DTENT==RECORD(P_P2 +DTENT_NEXT)
  EXITIF  DTENT_CONTI # CONT2
  REPEAT 
                             ! NOW TIDY UP
  TSIZE=ADDR(CONTAB(CONT1+1))-ADDR(TSIZE)-4;    ! N.B. BYTES!!!!!!!!!!
  RETURN 
!
PHASE(3):                               ! TIDY TABLE (TO SPREAD LOAD ACCROSS SFCS)
      DTENT==DTAB0
      UNLESS  DTENT_NEXT=0 START ;      ! CRUDE VERSION PROTEM
         DTENT2==RECORD(P_P2+DTENT_NEXT)
         UNLESS  DTENT2_NEXT=0 OR  DTENT_CONTI#DTENT2_CONTI START 
            DTENT3==RECORD(P_P2+DTENT2_NEXT)
            SLINK=DTENT_NEXT
            DTENT_NEXT=DTENT2_NEXT
            DTENT2_NEXT=DTENT3_NEXT
            DTENT3_NEXT=SLINK
         FINISH 
      FINISH 
      RETURN 
                                 !
                                 !
ROUTINE  FEEL FOR(INTEGER  PT, MN, CAD);! IS THERE A DEVICE OUT THERE??
RECORD (DTENTF)NAME  DTENT
RECORD (COMAF)NAME  CA
RECORD (ESCBF)NAME  ESCB
INTEGERARRAYNAME  STATE;                ! MAPPED ONTO CA_PAWS, NON-SLAVED
                                        ! DESTINATION FOR STATUS INFO - 5 WORDS.
INTEGERNAME  PC;                        ! SIMILAR DESTINATION FOR PROPERTY CODE
                                        ! MAPPED TO STATE(5).
INTEGER  SPTRK;                         ! SECTORS PER TRACK.
                                        ! IF IT FINDS A DEVICE THIS ROUTINE
                                        ! FILLS IN THE DEVICE TABLE ENTRY (DTENT)
                                        ! AND UPDATES LDEVMAX AND ADDLIM ACCORDINGLY.
      MN=MN<<21;                        ! POS FOR USE IN COMM AREA.
      CA==RECORD(CAD)
      STATE==CA_PAWS
      PC==STATE(5);                     ! I.E. AFTER STATUS INFORMATION
                                        ! FIRST READ DEVICE STATUS
      STATE(1)=0;                       ! I.E. NOT AVAILABLE
      CA_CAW0=CRFRSTATUS!MN
      CA_CAW1=REALISE(ADDR(STATE(0)))
      DO IT(5,PT,CA)
                                        ! EXAMINE STATUS READ TO DETERMINE
                                        ! DEVICE TYPE AND CONDITION
      PRINTSTRING("DRUM".HTOS(PT<<4!MN>>21,3)." reports ")
      PRHEX(STATE(0)); PRHEX(STATE(1)); NEWLINE
      RETURNIF  STATE(1) & AUTO=0
      IF  STATE(1)&AVAIL=0 THEN  C 
         OPMESS("DRUM".HTOS(PT<<4!MN>>21,3)." has warning bits")
                                        ! THERE IS ONE OUT THERE AND IT'S GOING
                                        ! NOW READ PROPERTY CODE (PC)
         PC=0;                          ! IN CASE OF ANY FAILURE
         CA_DRUMRQ=DRFRPC!MN
         CA_CAW0=CRFDRUMRQ!MN
         CA_CAW1=REALISE(ADDR(PC))
         DO IT(5,PT,CA)
         PC=PC>>24;                     ! 1,2,3 INDEXES PROPERTY TABLES
         UNLESS  1<=PC<=4 START 
            OPMESS("Invalid PROP. CODE =".HTOS(PC,2))
            RETURN 
         FINISH 
         PROPCODE=PC
         CA_DRUMRQ=DRFCONN!MN;          ! NOW CONNECT THIS DEVICE
         CA_CAW0=CRFDRUMRQ!MN
         DO IT(5,PT,CA)
                                        ! AND FORMAT THE LOT
         CA_DRUMRQ=DRFWFMT!MN
         CA_CAW0=CRFDRUMRQ!MN-4+SECSPDRUM(PC); ! ALL SECTORS ON DRUM
         DO IT(4000,PT,CA)
                                        ! ESTABLISH THIS DRUM 
  NESECQS=SECSPTRK(PC)//EPN
  IF  DCURRD#0 START ;                  ! LINK NEW INTO PREVIOUS
    DTENT==RECORD(P_P2 + DCURRD)
    DTENT_NEXT=DNEXTD
  FINISHELSESTART 
    DNEXTD=4
  FINISH 
  DCURRD=DNEXTD
  DTENT==RECORD(P_P2 +DCURRD)
  DNEXTD=ADDR(DTENT_ESCBS(NESECQS)) - P_P2
                                        ! FILL IN BASIC SCALARS
  DTENT_NEXT=0;                         ! MAY BE LAST DRUM
  SPTRK=NESECQS*EPN
  DTENT_SPTRK=SPTRK
  DTENT_NSECS=SPTRK*TRKSPDRUM(PC)
  DTENT_STATE=0
  DTENT_CONTI=CONT1+1;                 !  PHASE1 INDEX
                                      ! REFERENCES ARE FILLED IN PHASE 2
                                        ! REMAINS TO SET GLOBAL PARAMETERS
  MN=MN>>21;                         ! CONVENTIONAL POSITION
  MAXMN=MN
  TOTDSP=TOTDSP+DTENT_NSECS
  UTILISATION= DTENT_NSECS*100// SECSPDRUM(PC)
                                       ! AND TO RECORD FOR PHASE2:-
  ESCB==DTENT_ESCBS(0)
  ESCB_HQ=MN
  ESCB_LQ=NESECQS
  ESCB_SAW0=PROPCODE
END ;                                   ! OF FEEL FOR
                          !
                          !
ROUTINE  MOVE(INTEGER  PT, OLDCA, NEWCA);    ! MOVES MARK (COMM AREA)
RECORDFORMAT  SHORTCAF(INTEGER  MARK,PAW,CNTS,DRQ,CAW0,CAW1, C 
                           CRESP0,CRESP1,INTEGERARRAY  PAWS,PIWS(0:7))
RECORD (SHORTCAF)NAME  CA
INTEGER  MARKAD
      CA==RECORD(OLDCA)
      CA_PAW=PWFCR
      CA_CAW0=CRFINIT
      CA_CAW1=REALISE(NEWCA)
      CA_CRESP0=0
                                        ! ENSURE SLAVE INTERLOCK
      MARKAD=ADDR(CA_MARK)
      *LXN_MARKAD
      *INCT_(XNB );  *TDEC_(XNB )
                                        ! PREPARE NEW SITE
      CA==RECORD(NEWCA)
      CA_MARK=-1
      WAIT(1);                          ! FOR WRITE THROUGH.
      DO IT(5,PT,CA)
                                        ! NOW CLEAR PAWS & PIWS
      CA=0;                             ! WILL CLEAR EVERYTHING, ESP PAW,PAWS & PIWS
      CA_MARK=-1
END ;                                   ! OF MOVE
ROUTINE  DO IT(INTEGER  TIMESLOTS, PT, RECORD (COMAF)NAME  CA)
                                        ! DOES A CONTROLLER REQUEST
                                        ! ON THIS SFC (PT)
INTEGER  MARKADD, ISA, CRESP0
                                        ! TIMESLOTS ARE 10MS PERIODS.
                                        ! HEAVY USE OF M/C IN ORDER TO ENSURE
                                        ! THE ABOLITION OF SLAVERY.
      MARKADD=ADDR(CA_MARK)
      ISA=PT+CONTROL
                                        ! CLAIM SEMA
      *LXN_MARKADD;                     ! SHOULD INVARIABLY BE FREE, BUT MUST
LAB1: *INCT_(XNB );                     ! ENSURE SLAVES CLEARED THROUGH.
      *JCC_7, <LAB1>;                   ! LOOP UNLESS CC=0=MARK.
      CA_PAW=PWFCR
      CA_CRESP0=0;                      ! CLEAR FOR REPLY
                                        ! SEND FLAG BEFORE RELEASING SEMA, ENSURES
      *LB_ISA;                          ! WRITES THROUGH BEFORE ACCESS BY SFC.
      *LSS_1
      *ST_(0+B )
                                        ! RELEASE SEMA
      *LXN_MARKADD
      *TDEC_(XNB );                     ! SFC CLAIMS BY READ AND CLEAR, HENCE TDEC
                                        ! GUARANTEED TO RELEASE.
      UNTIL  CRESP0#0 OR  TIMESLOTS=0 CYCLE 
         WAIT(2)
         TIMESLOTS=TIMESLOTS-1
                                        ! ENSURE CRESP0 READ FROM REAL STORE
         *LXN_MARKADD
LAB2:    *INCT_(XNB )
         *JCC_7, <LAB2>
         CRESP0=CA_CRESP0
         *LXN_MARKADD
         *TDEC_(XNB )
      REPEAT 
      IF  CRESP0#0 START ;              ! IF GENUINE RESPONSE
         IF  CRESP0#NT START 
            OPMESS("SFC request fails")
            OPMESS(STRHEX(CA_CRESP0)." ".STRHEX(CA_CRESP1))
         FINISH 
         CA_CRESP0=0;                   ! LET NORMAL WRITE THROUGH APPLY
      FINISH  ELSE  START 
         OPMESS("SFC Time out ")
         OPMESS(STRHEX(CA_PAW)." ".STRHEX(CA_CAW0))
      FINISH 
END ;                                   ! OF DO IT
ROUTINE  LOAD UPROG(INTEGER  PT)
ROUTINESPEC  WAITAFB(INTEGER  ISDIAG);  ! WAIT FOR ACKNOWLEDGE FROM B
! SFC MICROPROGRAM VERSION 941 DATED 29NOV78
!
! THIS VERSION FIRST USED IN CHOPSUPE 18E
! PREVIOUSLY VSN 940 USED FROM 15JAN78
ENDOFLIST 
CONSTINTEGERARRAY  UPA(0:X'200')=C 
X'3006E841',X'0C829041',X'00018782',X'00032C22',
X'00014003',X'00031874',X'22601141',X'0001D041',X'86803951',
X'86858041',X'22601141',X'A0103941',X'00029041',X'0001004C',
X'86803901',X'0881E841',X'A0136841',X'0F00E8C1',X'22605041',
X'0002DF62',X'00051844',X'00000044',X'0000F4A3',X'00028042',
X'8004F462',X'80801157',X'2260417A',X'86803941',X'30003906',
X'00008841',X'0000907E',X'A00B3840',X'0000A879',X'0000115E',
X'0810E87B',X'0000A876',X'00010079',X'0002E876',X'0000A873',
X'0000A872',X'0002780B',X'0001D07D',X'00050873',X'0000F072',
X'0000F871',X'0000A86C',X'0000A86B',X'0000A86A',X'50003941',
X'0002C041',X'00001940',X'00031846',X'A0705815',X'0000EA7D',
X'00028003',X'00031F42',X'000284E7',X'000004E7',X'0DE00034',
X'2260212C',X'0C81C833',X'0001D82F',X'0001B823',X'81040041',
X'0E024041',X'00012041',X'000209C1',X'84040041',X'0001E9E8',
X'00000040',X'0001B045',X'0001E045',X'000251C5',X'0001F9C6',
X'000201C6',X'86803961',X'64103960',X'8406D041',X'6390395E',
X'8400395D',X'84003941',X'00032AC1',X'0002F84A',X'8400393A',
X'000000C1',X'80000041',X'00000402',X'0000F83E',X'2260111A',
X'0DE00041',X'00008042',X'0000E87C',X'0000F056',X'00000482',
X'00000071',X'0002C041',X'00000040',X'0001D045',X'0000116F',
X'00004171',X'0002E87C',X'80802174',X'06808841',X'00000764',
X'00027041',X'20E00041',X'00091841',X'0000800F',X'0002E483',
X'0002F841',X'00032861',X'00026841',X'0002C02B',X'80048036',
X'22604149',X'00036040',X'22601136',X'86050852',X'000249CF',
X'3007003B',X'2260113B',X'0001003F',X'0001084D',X'0002F0C1',
X'80000041',X'0001EC02',X'0000020C',X'80802141',X'000000C1',
X'00000442',X'0002C00F',X'00000204',X'00011812',X'84050841',
X'0001E9C1',X'22601141',X'0001D041',X'86803941',X'A0103941',
X'00023041',X'00023841',X'0000A041',X'20E0113B',X'00008841',
X'0DE00036',X'00026041',X'80003941',X'00026839',X'000080C1',
X'80026841',X'0F80003A',X'0000115A',X'0000803C',X'0000A8E5',
X'0000FAC1',X'00031041',X'0003302A',X'0000A02B',X'0000F82C',
X'0D900007',X'00044141',X'820CE841',X'090890C6',X'2262E483',
X'81840041',X'00011816',X'00044141',X'000C00C1',X'8006C041',
X'0C826840',X'00015007',X'00016008',X'81857782',X'00011815',
X'00014041',X'0001200C',X'000518BF',X'00014841',X'0002E484',
X'00000024',X'0002C041',X'00003940',X'84826802',X'00015805',
X'00016806',X'0000EF82',X'00000022',X'0001400A',X'00048838',
X'00051820',X'20E33442',X'00002143',X'80800241',X'00002128',
X'000D98C1',X'80040041',X'80801141',X'50003941',X'0F03852D',
X'00005AC1',X'00033840',X'0901004D',X'0900F04C',X'0900F04B',
X'0900F84A',X'0900F849',X'0900E848',X'0900E847',X'09822038',
X'84003941',X'0002383A',X'30040034',X'00007041',X'0000833B',
X'8106F841',X'0E024041',X'8186C041',X'0E021040',X'00000020',
X'0000833D',X'A0600908',X'00026841',X'A060110D',X'0003304C',
X'80808040',X'00002152',X'00002146',X'20E10027',X'00000001',
X'20E10829',X'0000A062',X'00006288',X'20E2E8C1',X'0000CC42',
X'00002142',X'0000580B',X'20E128C1',X'0002AE87',X'0000B662',
X'20E0213F',X'80801141',X'50003941',X'20E00035',X'000231FA',
X'0000EF67',X'00042141',X'000D4841',X'848490C1',X'80022041',
X'00000405',X'0000F044',X'80801141',X'50003941',X'00001916',
X'60100041',X'0C880402',X'00028841',X'00015786',X'0002C041',
X'0000B040',X'0DE13014',X'00031841',X'0000A5AE',X'22614012',
X'20E23041',X'00001141',X'84003941',X'0002380E',X'0D9220C1',
X'C1E01141',X'8206F041',X'30003941',X'21E01141',X'0001C041',
X'86803941',X'30003941',X'8504C041',X'20E04141',X'00014841',
X'44B2F041',X'0C840402',X'850400C2',X'00029001',X'20E01141',
X'00015742',X'00028041',X'60303919',X'00000545',X'00000443',
X'000231CC',X'00000545',X'80802149',X'00000443',X'80801141',
X'50003904',X'0000002A',X'00000041'(3),X'0000A54F',X'000231C1',
X'8402B765',X'5D09E841',X'0E857CE3',X'8500B841',X'000404C2',
X'0002380A',X'22200041',X'209890C1',X'0006FC42',X'00004147',
X'0000A4E3',X'80802141',X'A0100084',X'20E230C1',X'00001141',
X'8400391D',X'22100041',X'0004E0C1',X'09002141',X'80040041',
X'00011CF3',X'0E021D22',X'0F0080C2',X'30048038',X'8004F6E2',
X'0DE22039',X'0880A041',X'85055D66',X'44B00583',X'00022841',
X'0000E843',X'0C89A041',X'00016041',X'20E23041',X'00001141',
X'84003941',X'0000D041',X'80801141',X'50003944',X'818220C1',
X'00001141',X'8000395F',X'A0636841',X'00019841',X'0002D841',
X'000197A8',X'8106C041',X'00017840',X'00001943',X'A0500941',
X'00007041',X'8504BCD8',X'8080EAC2',X'81057805',X'00031041',
X'00033341',X'00002141',X'0001E841',X'000000C1',X'00016C47',
X'8584A5B4',X'44B08041',X'5C895DF0',X'81040041',X'0E90062F',
X'81801142',X'00002108',X'8584D841',X'61603941',X'64B17041',
X'0C86C041',X'000160C0',X'0000A079',X'800402C1',X'08800070',
X'2220F041',X'20980051',X'0904A032',X'0E840041',X'C1E01151',
X'81801141',X'00008615',X'64B005C7',X'0C840041',X'C2E17041',
X'669AC041',X'61183940',X'0000A06A',X'0000000F',X'C162C041',
X'66983940',X'0000A066',X'00000013',X'808802C1',X'00031041',
X'0003306D',X'30003941',X'21E01141',X'0001C041',X'86803941',
X'0001F041',X'80040046',X'0002C041',X'81003940',X'00000007',
X'82001141',X'80003909',X'0880C1C1',X'84003941',X'85854841',
X'20E04141',X'44B17041',X'0C855C02',X'858550C2',X'00029001',
X'20E01141',X'60303941',X'44B00742',X'00028041',X'0C86C041',
X'000162C0',X'00000047',X'82000763',X'00000642',X'0002F82F',
X'00001141',X'81003941',X'00022041',X'0000A041',X'20E23041',
X'00001141',X'84003941',X'80801141',X'50003941',X'00001941',
X'00031841',X'00023CE7',X'00009442',X'A0104144',X'808004E4',
X'00002141',X'00000084',X'221000C3',X'00031B41',X'0000ED65',
X'00051B41',X'00002141',X'00000041',X'80056CE6',X'0E021D28',
X'0F01E8C1',X'00031EE2',X'300485CC',X'00000569',X'8201E8C1',
X'00001141',X'80003956',X'3005E841',X'0000E0C1',X'0DE22041',
X'0E908041',X'C2601141',X'30003941',X'22601141',X'0002F041',
X'0001C041',X'86803941',X'30003941',X'8504C041',X'20E04141',
X'00014841',X'44B00041',X'0C856841',X'850550C1',X'20E01141',
X'60303941',X'0002F742',X'00028041',X'00031AC1',X'0000ED69',
X'00000000'(28),X'00D20941',X'84640616',X'84640716',X'F2B24B72'
LIST 
INTEGER  I, ISA, DATA, COMM, DCM FAIL
INTEGER  MSH, LSH;                      ! WILL LOAD THE SFC MICROPROGRAM
                                        ! IN UPA INTO SFC ON GIVEN
                                        ! PORT AND TRUNK
!  FIRST DEFINE IMAGE STORE ADDRESSES
CONSTINTEGER  CONTROL=X'800'
CONSTINTEGER  DIAGSTAT=X'D00'
CONSTINTEGER  ISDIAG=X'E00';            ! THESE ARE THE 3 NECESSARY AND SUFFICIENT
                                        ! REGISTERS
! NOW SOME VALUES WHICH ARE SENT TO ABOVE
CONSTINTEGER  MCLEAR=2
CONSTINTEGER  DCMBIT=X'400';            ! DCM BIT IN DIAG STAT
CONSTINTEGER  NOTDCM=X'FFFFFBFF';       ! ¬DCMBIT!!
CONSTINTEGER  AFB=X'800';               ! WAIT FOR THIS AFTER SENDING
CONSTINTEGER  CLEARTOSEND=X'E80';       ! ISDIAG:- CLEAR FB'S SEND RFA
CONSTINTEGER  CLEAR FOR NEXT=X'E00';    ! ISDIAG:- CLEAR FB'S
                                        ! NOW SOME USEFUL MASKS
CONSTINTEGER  UH=X'FFFF0000'
                                        ! THE ONLY DIRECT MODE COMMAND NEEDED
CONSTINTEGER  WIDCOM=X'A200'
                                        ! FIRST MASTER CLEAR SFC
      ISA=PT+CONTROL
      *LB_ISA;  *LSS_MCLEAR;  *ST_(0+B )
                                        ! NOW GET INTO DIRECT CONTROL MODE
      ISA=PT+DIAGSTAT
      *LB_ISA
      *LSS_(0+B );  *OR_DCMBIT;  *ST_(0+B ); ! OR IN DCM BIT
                                        ! NOW WRITE MICROPROGRAM
      ISA=PT+ISDIAG
      DCM FAIL=0
      CYCLE  I=0,1,511
         DATA=UPA(I)
         MSH=DATA&UH!CLEAR TO SEND
         LSH=DATA<<16!CLEAR TO SEND
         COMM=(WIDCOM+I)<<16!CLEAR TO SEND
                                        ! DATA PREPARED NOW WRITE
         *LB_ISA;  *LSS_COMM
         *ST_(0+B );  WAITAFB(ISA)
         *LB_ISA;  *LSS_MSH
         *ST_(0+B );  WAITAFB(ISA)
         *LB_ISA;  *LSS_LSH  
         *ST_(0+B );  WAITAFB(ISA)
      REPEAT 
                                        ! NOW SET THE MICROPROGRAM LOADED
                                        ! INDICATOR
      COMM=(WIDCOM+X'200')<<16!CLEAR TO SEND
      *LB_ISA;  *LSS_COMM  
      *ST_(0+B );  WAITAFB(ISA)
      *LB_ISA;  *LSS_CLEARTOSEND  
      *ST_(0+B );  WAITAFB(ISA)
      *LB_ISA;  *LSS_CLEARTOSEND
      *ST_(0+B );  WAITAFB(ISA)
      IF  DCM FAIL#0 THEN  PRINTSTRING("
SFC MP FLAGS=".HTOS(DCM FAIL,4)."
")
                                        ! RETURN FROM DIRECT CONTROL MODE
                                        ! FIRST CLEAR DOWN FB'S IN ISDIAG
      *LB_ISA;  *LSS_CLEAR FOR NEXT;  *ST_(0+B )
      ISA=PT+DIAGSTAT;                  ! CLEAR DCMBIT IN DIAG STAT
      *LB_ISA
      *LSS_(0+B );  *AND_NOTDCM;  *ST_(0+B );! CLEAR DCM BIT
                                        ! THE ONLY SAFE WAY
                                        ! MASTER CLEAR AGAIN TO ENSURE
                                        ! SFC IN INT ADDRESSING MODE
ROUTINE  WAITAFB(INTEGER  ISDIAG)
!***********************************************************************
!*    WAIT FOR ACKNOWLEGE FROM B (B IS SFC!) AFTER DIRECT WRITE        *
!*    PARAMETER IS APPROPRIATE TRUNK REGISTER                         *
!***********************************************************************
INTEGER  I
AGAIN:                                  ! INCLUDE RELOADING B AS DELAY
      *LB_ISDIAG
      *LSS_(0+B )
      *ST_I
      *AND_AFB;                         ! ??????? AFB ACCESSIBLE ????????
      *JAT_4,<AGAIN>
      DCM FAIL=DCM FAIL!(I&X'1FF');     ! ALL FFBS AND PARITY FAILS
END ;                                   ! OF WAITAFB
END ;                                   ! OF LOAD UPROG
END ;                                   ! OF DRGROPE
FINISH 
ENDOFFILE