!                 VERSION OF 13/06/80
!        DEC 79: INCLUDE AN OPER ADAPTOR
!                HAVE ONLY ONE SERVICE NUMBER, X'37'
!        OTHER POSSIBLE CHANGES
!           MAKE INITIALISATION CALL OF CC INITIALISE ADAPTORS AS WELL
!           MAKE IT POSSIBLE TO DEALLOCATE/ALLOCATE ALL DEVICES
!* COMMUNICATIONS RECORD FORMAT - EXTANT FROM CHOPSUPE 20A ONWARDS *
%RECORDFORMAT COMF(%INTEGER OCPTYPE,IPLDEV,SBLKS,SEPGS,NDISCS,  %C
         DDTADDR,GPCTABSIZE,GPCA,SFCTABSIZE,SFCA,SFCK,DIRSITE,  %C
         DCODEDA,SUPLVN,WASKLOKCORRECT,DATE0,DATE1,DATE2,  %C
         TIME0,TIME1,TIME2,EPAGESIZE,USERS,CATTAD,DQADDR,  %C
         %BYTEINTEGER NSACS,RESV1,SACPORT1,SACPORT0, %C
         NOCPS,RESV2,OCPPORT1,OCPPORT0, %C
         %INTEGER ITINT,CONTYPEA,GPCCONFA,FPCCONFA,SFCCONFA, %C
         BLKADDR,DPTADDR,SMACS,TRANS,%LONGINTEGER KMON,  %C
         %INTEGER DITADDR,SMACPOS,SUPVSN,PSTVA,SECSFRMN,SECSTOCD, %C
         SYNC1DEST,SYNC2DEST,ASYNCDEST,MAXPROCS,INSPERSEC,ELAPHEAD, %C
         COMMSRECA,STOREAAD,PROCAAD,SFCCTAD,DRUMTAD,TSLICE,SP0,SP1, %C
         SP2,SP3,SP4,SP5,SP6,SP7,SP8, %C
         LSTL,LSTB,PSTL,PSTB,HKEYS,HOOT,SIM,CLKX,CLKY,CLKZ,  %C
         HBIT,SLAVEOFF,INHSSR,SDR1,SDR2,SDR3,  %C
         SDR4,SESR,HOFFBIT,S2,S3,S4,END)
%RECORDFORMAT PE(%INTEGER DSERV, SSERV, P1, P2, P3, P4, P5, P6)
%RECORDFORMAT PARMF(%INTEGER DSERV, SSERV, P1, P2, P3, P4, P5, P6, LINK)
!*
!***********************************************************************
!*  SERVICE NUMBERS AND ACTIVITY NUMBERS                               *
!***********************************************************************
!*
!*  GLOBAL CONSTANTS
!*
%CONSTINTEGER VIRTUAL = X'81000000'
%CONSTINTEGER NOT ALLOCATED = X'F0F0F0F0'
%CONSTINTEGER EMPTY = X'F0F0'
%CONSTINTEGER ATTENTION = 1
%CONSTINTEGER NORMAL TERMINATION = 8
%CONSTINTEGER AUTO = X'00008400'
%CONSTINTEGER EBC NL = 21
%CONSTINTEGER EBC LF = 37
%CONSTINTEGER EBC FF = 12
%CONSTINTEGER EBC CR = 13
%CONSTINTEGER EBC VP = 34
%IF SSERIES = NO %START
%CONSTINTEGER EBC MS = 32
%FINISH
%CONSTINTEGER EBC SP = 64
%CONSTSTRING(1) SNL = "
"
!*
!*
!* COMMS CONTROLLER ACTIVITIES
!*
%CONSTINTEGER INIT = 0
%CONSTINTEGER CONNECT = 1
%CONSTINTEGER CONNECT REPLY = 13
%CONSTINTEGER ENABLE = 2
%CONSTINTEGER CLAIM AMT INDEX REPLY = 14
%CONSTINTEGER ENABLE REPLY = 15
%CONSTINTEGER DISABLE = 4
%CONSTINTEGER DISABLE REPLY = 16
%CONSTINTEGER DISCONNECT = 5
%CONSTINTEGER DISCONNECT REPLY = 17
%CONSTINTEGER CONTROL MSG6 = 6
%CONSTINTEGER CONTROL MSG6 REPLY = 18
%CONSTINTEGER CONTROL MSG7 = 7
%CONSTINTEGER CONTROL MSG7 REPLY = 19
%CONSTINTEGER TRANSFER REQUESTED = 10
%CONSTINTEGER PAGE HERE = 11
%CONSTINTEGER TRANSFER COMPLETED = 12
!*
!* COMMS CONTROLLER REQUESTS AND REPLY SERVICE NUMBERS
!*
%CONSTINTEGER COMMS COMMAND = X'00370000'
%CONSTINTEGER CLAIM BLOCK = X'00080001'
%CONSTINTEGER BLOCK CLAIMED = COMMS COMMAND!CLAIM AMT INDEX REPLY
%CONSTINTEGER FREE BLOCK = X'00080002'
%CONSTINTEGER CLAIM PAGE = X'00040001'
%CONSTINTEGER PAGE CLAIMED = COMMS COMMAND!PAGE HERE
%CONSTINTEGER FREE PAGE = X'00040002'
%CONSTINTEGER REQUEST TRANSFER = COMMS COMMAND!TRANSFER REQUESTED
%CONSTINTEGER TRANSFER COMPLETE = COMMS COMMAND!TRANSFER COMPLETED
!*
!* ADAPTOR ACTIVITIES
!*
%CONSTINTEGER DEALLOCATED = X'00000001'
%CONSTINTEGER ALLOCATED = X'00000002'
%CONSTINTEGER EXECUTE FAILS = X'00000003'
%CONSTINTEGER INITIALISE = X'00000004'
%CONSTINTEGER INTERRUPT = X'00000005'
%CONSTINTEGER GO AHEAD = X'00000006'
%CONSTINTEGER SEND CONTROL = X'00000007'
%CONSTINTEGER CLOCK TICK = X'00000008'
%CONSTINTEGER ALLOCATE REQUEST = X'00000009'
!*
!* ADAPTOR SERVICE NUMBERS
!*
%CONSTINTEGER OP SERVICE = X'32'
%CONSTINTEGER LP SERVICE = X'33'
%CONSTINTEGER CR SERVICE = X'34'
%IF CP FITTED = YES %START
%CONSTINTEGER CP SERVICE = X'35'
%FINISH
%CONSTINTEGER MK1 FE SERVICE = X'39'
%CONSTINTEGER OP COMMAND = OP SERVICE<<16
%CONSTINTEGER LP COMMAND = LP SERVICE<<16
%CONSTINTEGER CR COMMAND = CR SERVICE<<16
%IF CP FITTED = YES %START
   %CONSTINTEGER CP COMMAND = CP SERVICE<<16
%FINISH %ELSE %START
   %CONSTINTEGER CP COMMAND = NOT ALLOCATED
%FINISH
%CONSTINTEGER MK1 FE COMMAND = MK1 FE SERVICE<<16
!*
!* ADAPTOR REQUESTS AND REPLIES SERVICE NUMBERS
!*
%CONSTINTEGER GPC COMMAND = X'00300000'
%CONSTINTEGER ALLOCATE DEVICE = GPC COMMAND!X'0000000B'
%CONSTINTEGER LP ALLOCATED = LP COMMAND!ALLOCATED
%CONSTINTEGER CR ALLOCATED = CR COMMAND!ALLOCATED
%IF CP FITTED = YES %START
   %CONSTINTEGER CP ALLOCATED = CP COMMAND!ALLOCATED
%FINISH
%CONSTINTEGER MK1 FE ALLOCATED = MK1 FE COMMAND!ALLOCATED
%CONSTINTEGER DEALLOCATE DEVICE = GPC COMMAND!X'00000005'
%CONSTINTEGER LP DEALLOCATED = LP COMMAND!DEALLOCATED
%CONSTINTEGER CR DEALLOCATED = CR COMMAND!DEALLOCATED
%IF CP FITTED = YES %START
   %CONSTINTEGER CP DEALLOCATED = CP COMMAND!DEALLOCATED
%FINISH
%CONSTINTEGER MK1 FE DEALLOCATED = MK1 FE COMMAND!DEALLOCATED
%CONSTINTEGER EXECUTE CHAIN = GPC COMMAND!X'0000000C'
%CONSTINTEGER LP EXECUTED = LP COMMAND!EXECUTE FAILS
%CONSTINTEGER CR EXECUTED = CR COMMAND!EXECUTE FAILS
%IF CP FITTED = YES %START
   %CONSTINTEGER CP EXECUTED = CP COMMAND!EXECUTE FAILS
%FINISH
%CONSTINTEGER MK1 FE EXECUTED = MK1 FE COMMAND!EXECUTE FAILS
%CONSTINTEGER LP INTERRUPT = LP COMMAND!INTERRUPT
%CONSTINTEGER CR INTERRUPT = CR COMMAND!INTERRUPT
%IF CP FITTED = YES %START
   %CONSTINTEGER CP INTERRUPT = CP COMMAND!INTERRUPT
%FINISH
%CONSTINTEGER MK1 FE INTERRUPT = MK1 FE COMMAND!INTERRUPT
%CONSTINTEGER ELAPSED INT COMMAND = X'000A0001';   !TICK EVERY N SECS
%CONSTINTEGER MK1 FE CLOCK TICK=MK1 FE COMMAND!CLOCK TICK
%CONSTINTEGER SECS PER TICK = 15
!*
!*
%IF MON LEVEL&2 # 0 %START
        %EXTRINSICLONGINTEGER KMON;           !BIT 2**SNO IS SET WHEN SERVICE SNO IS TO BE MONITORED
%FINISH
!*
%EXTERNALROUTINESPEC DUMP TABLE(%INTEGER TABLE, ADDRESS, LENGTH)
%EXTERNALROUTINESPEC DPON(%RECORDNAME MESS, %INTEGER SECS)
%EXTERNALROUTINESPEC PON(%RECORDNAME MESS)
%EXTERNALROUTINESPEC GPC(%RECORDNAME MESS)
%EXTERNALROUTINESPEC PKMON REC(%STRING (20) TEXT, %RECORDNAME MESS)
%EXTERNALROUTINESPEC OP MESS(%STRING (63) S)
%EXTERNALROUTINESPEC MONITOR(%STRING (63) S)
%SYSTEMROUTINESPEC MOVE(%INTEGER LENGTH, FROM, TO)
%SYSTEMROUTINESPEC ETOI(%INTEGER ADDRESS, LENGTH)
%SYSTEMROUTINESPEC I TO E(%INTEGER ADDRESS, LENGTH)
%EXTERNALINTEGERFNSPEC REALISE(%INTEGER VIRTUAL ADDRESS)
%EXTERNALINTEGERFNSPEC NEW PP CELL
%EXTERNALROUTINESPEC ELAPSED INT(%RECORDNAME P)
%EXTERNALSTRINGFNSPEC STRINT(%INTEGER I)
!*
!*
%IF MULTI OCP = NO %START
%ROUTINESPEC COMMS CONTROL(%RECORDNAME MESS)
%ROUTINESPEC MK1 FE ADAPTOR(%RECORDNAME MESS)
%FINISH %ELSE %START
%EXTERNALROUTINESPEC RESERVE LOG
%EXTERNALROUTINESPEC RELEASE LOG
%FINISH
%EXTERNALROUTINESPEC RETURN PP CELL(%INTEGER CELL)
!*
!*
%IF MON LEVEL&256 # 0 %START;                 !include harvesting?
%EXTERNALROUTINESPEC HARVEST(%INTEGER EVENT, PROCESS, LEN, A, B, C, D, E)
%EXTRINSICINTEGER TRACE EVENTS
%EXTRINSICINTEGER TRACE PROCESS
%EXTRINSICINTEGER TRACE
%FINISH
!*
!*
!***********************************************************************
!*                                                                     *
!*  STREAM TYPES.                                                      *
!*                                                                     *
!*  EVEN STREAMS: INPUT STREAMS                                        *
!*  ODD  STREAMS: OUTPUT STREAMS                                       *
!*                                                                     *
!*  STREAM STATES.                                                     *
!*                                                                     *
!*  FROM STATE      TITLE         TO STATE                             *
!*         1->   0: UNUSED        ->2                                  *
!*         3->   1: DISCONNECTING ->0                                  *
!*         0->   2: CONNECTING    ->3                                  *
!*     2,4,5->   3: CONNECTED     ->1,6                                *
!*      8-11->   4: SUSPENDING    ->3                                  *
!*      8-11->   5: ABORTING      ->3                                  *
!*         3->   6: CLAIMING      -> 7                                  *
!*         6->   7: ENABLING      ->8                                  *
!*         7->   8: ENABLED       ->4,5,9-11                           *
!*         8->   9: QUEUED        ->4,5,10                             *
!*       8,9->  10: PAGING IN     ->4,5,11                             *
!*      8,10->  11: ACTIVE        ->4,5,8,10                           *
!*                                                                     *
!*                                                                     *
!*  STREAM BUFFER MODES.                                               *
!*                                                                     *
!*    BITS 2**0 TO 2**3                                                *
!*               0: SEQUENTIAL   2: SEQUENTIAL CONTINUATION            *
!*               1: CIRCULAR                                           *
!*                                                                     *
!*    BITS 2**4 TO 2**7                                                *
!*               0: ISO          2: BINARY                             *
!*               1: EBCIDIC      3: CONTROL                            *
!*                                                                     *
!*  ADAPTOR TYPES.                                                     *
!*                                                                     *
!*  DEVICE TYPE        DEVICE MNEMONIC     ADAPTOR NAME                *
!*        1                  PT            NOT AVAILABLE               *
!*        2                  PR            NOT AVAILABLE               *
!*        3                  CP            CP ADAPTOR                  *
!*        4                  CR            CR ADAPTOR                  *
!*        5                  M0            NOT AVAILABLE               *
!*        6                  LP            LP ADAPTOR                  *
!*        7                  GP            NOT AVAILABLE               *
!*        8                  OP            OPER ADAPTOR                *
!*        9                  GU            NOT AVAILABLE               *
!*       10                  DR            NOT AVAILABLE               *
!*       11                  NA            NOT AVAILABLE               *
!*       12                  CT            NOT AVAILABLE               *
!*       13                  SU            NOT AVAILABLE               *
!*       14                  FE            MK1 FE ADAPTOR              *
!*       15                  LK            NOT AVAILABLE               *
!*                                                                     *
!*  DEVICE NO.                                                         *
!*                                                                     *
!*    CAN CURRENTLY BE IN THE RANGE 0 - 9.                             *
!*    WHEN COMBINED WITH AN ADAPTOR TYPE A DEVICE MNEMONIC IS PRODUCED *
!*    I.E. LP0 TO LP9  OR  FE0 TO FE9.                                 *
!*                                                                     *
!***********************************************************************
!*
!*
%RECORDFORMAT SR(%HALFINTEGER STREAM NO, EXTERNAL STREAM NO, %C
   %BYTEINTEGER STATE, MODE, ADAPTOR NO, DEVICE NO, %C
   %INTEGER LENGTH, OWNER, CALLER, AMT INDEX, START, CURSOR, LINK)
%RECORDFORMAT BR(%INTEGER STREAM NO, EXTERNAL STREAM NO, AMT INDEX, OFFSET, LENGTH,  %C
   REAL ADDRESS, P5, P6, LINK)
%RECORDFORMAT COMMS INFF( %C
   %INTEGER INDEX ADDR, NEXT FREE BUFFER, QUEUED STREAM HEAD, QUEUED STREAM TAIL)
!*
%CONSTINTEGER MAX STREAM = MAXPROCS*3-1
%OWNINTEGERARRAY STREAM INDEX(0 : MAX STREAM) = %C
EMPTY(MAX STREAM+1)
%OWNRECORDARRAYFORMAT SARF(0 : 4095)(SR);     !MAPPED ONTO PARM TABLE
%OWNRECORDARRAYNAME STREAM TAB(SR);           !STREAM TABLE ARRAY
%OWNRECORDARRAYFORMAT BARF(0 : 4095)(BR);     !MAPPED ONTO THE PARM TABLE
%OWNRECORDARRAYNAME BUFFER TAB(BR);           !BUFFER TABLE ARRAY
%OWNRECORDARRAYFORMAT PARF(0 : 4095)(PARMF);  !MAPPED ONTO PARM TABLE
%OWNRECORDARRAYNAME PARM TAB(PARMF);          !PARM TABLE ARRAY
%OWNRECORDNAME COM AREA(COMF)
%OWNRECORD COMMS(COMMS INFF)
!*
!*
%IF SFC FITTED = YES %START
%CONSTINTEGER DRUM UPDATE = 5;                !DRUM UPDATE AND RECAPTURE
%FINISH %ELSE %START
%CONSTINTEGER DRUM UPDATE = 1;                !ONLY RECAPTURE
%FINISH
%CONSTINTEGER NO DRUM UPDATE = 0;             !NO DRUM UPDATE AND NO RECAPTURE
!*
%CONSTINTEGER BYTE MASK = X'FF'
%CONSTINTEGER SHORT MASK = X'FFFF'
%CONSTINTEGER TOP SHORT MASK = X'FFFF0000'
!*
!*  STREAM STATES
!*
%CONSTINTEGER DISCONNECTING = 1;              !WAITING ON ADAPTOR ACTION
%CONSTINTEGER CONNECTING = 2;                 !WAITING ON ADAPTOR ACTION
%CONSTINTEGER CONNECTED = 3;                  !WAITING ON USER ACTION
%CONSTINTEGER SUSPENDING = 4;                 !WAITING ON ADAPTOR ACTION
%CONSTINTEGER ABORTING = 5;                   !WAITING ON ADAPTOR ACTION
%CONSTINTEGER CLAIMING = 6;                   !WAITING ON GLOBAL CONTROLLER ACTION
%CONSTINTEGER ENABLING = 7;                   !WAITING ON ADAPTOR ACTION
%CONSTINTEGER ENABLED = 8;                    !WAITING ON USER ACTION
%CONSTINTEGER QUEUED = 9;                     !WAITING ON A FREE EPAGE (BUFFER)
%CONSTINTEGER PAGING IN = 10;                 !WAITING ON GLOBAL CONTROLLER ACTION
%CONSTINTEGER ACTIVE = 11;                    !WAITING ON ADAPTOR ACTION
!*
!*  STREAM BUFFER MODES
!*
%CONSTINTEGER SEQUENTIAL = 0;                 !FIRST ENABLE ON A STREAM POSSIBLE SPECIAL ADAPTOR ACTION
%CONSTINTEGER CIRCULAR = 1
%CONSTINTEGER SEQUENTIAL CONTINUATION = 2;    !A SUBSEQUENT SEQUENTIAL ENABLE NO SPECIAL ACTION
!*
%CONSTINTEGER ISO = 0
%CONSTINTEGER EBCIDIC = 1
%CONSTINTEGER BINARY = 2
!*            CONTROL = 3
!*
!*  ADAPTOR TYPES
!*
%CONSTINTEGER CP = 3
%CONSTINTEGER CR = 4
%CONSTINTEGER LP = 6
%CONSTINTEGER OP = 8
%CONSTINTEGER FE = 14
!*
!***********************************************************************
!*  THE NEXT ARRAY IS A MAPPING FROM ADAPTOR TYPES TO SERVICE NUMBERS  *
!***********************************************************************
!*
%CONSTINTEGERARRAY ADAPT(0 : 15) =       %C
NOT ALLOCATED(3),CP COMMAND,CR COMMAND,NOT ALLOCATED,LP COMMAND,
NOT ALLOCATED,OP COMMAND,
NOT ALLOCATED(5),MK1 FE COMMAND,NOT ALLOCATED
!*
!***********************************************************************
!*  THE NEXT ARRAY IS A MAPPING FROM STATES TO REPLY ACTIVITIES        *
!***********************************************************************
!*
%CONSTINTEGERARRAY SUB IDENT(0 : ACTIVE) =     %C
-1,DISCONNECT REPLY<<16,CONNECT REPLY<<16,-1,DISABLE REPLY<<16(2),
-1,ENABLE REPLY<<16,-1(4)
!*
!*

%EXTERNALROUTINE COMMS CONTROL(%RECORDNAME MESS)
%RECORDSPEC MESS(PE)
!*
!***********************************************************************
!*                                                                     *
!*    AFTER INITIALIZATION, ALL FREE BUFFER RECORDS ARE HELD ON A      *
!*  LINKED LIST, THE FIRST ELEMENT OF WHICH IS POINTED AT BY "NEXT     *
!*  FREE BUFFER". THE MAXIMUM BUFFER (E PAGE) ALLOCATION IS            *
!*  GIVEN BY THE GLOBAL CONTROLLER AT INITIALISATION. THE COMMS        *
!*  CONTROLLER CAN THEN OWN UP TO THIS ALLOCATION OF EPAGES.           *
!*    IF THERE ARE NO FREE BUFFERS, STREAMS REQUIRING A BUFFER ARE     *
!*  HELD ON A LINKED LIST, THE FIRST ELEMENT OF WHICH IS POINTED AT    *
!*  BY "QUEUED STREAM HEAD" AND THE LAST ELEMENT OF WHICH IS POINTED   *
!*  AT BY "QUEUED STREAM TAIL".                                        *
!*    IF THE LIST IS EMPTY, BOTH THE HEAD AND TAIL POINTERS WILL       *
!*  BE SET TO "EMPTY" (X'F0F0').                                       *
!*    SPACE FOR STREAM DESCRIPTORS AND BUFFER DESCRIPTORS IS ALLOCATED *
!*  DYNAMICALLY BY CLAIMING PARAM CELLS FROM THE DYNAMICALLY EXTENDABLE*
!*  PARAM TABLE VIA A CALL ON "NEW PP CELL". STREAM DESCRIPTORS ARE    *
!*  ACCESSED INDIRECTLY THROUGH THE ARRAY "STREAM INDEX" BUT BUFFER    *
!*  DESCRIPTORS ARE ACCESSED DIRECTLY. WHEN DESCRIPTORS ARE NO LONGER  *
!*  REQUIRED THE SPACE IS RETURNED TO THE PARAM TABLE VIA A CALL ON    *
!*  "RETURN PP CELL".                                                  *
!*    NOTE: ALL PAGE SHARING AND PAGE RECAPTURE IS HANDLED BY THE      *
!*  GLOBAL CONTROLLER. THE COMMS CONTROLLER ONLY REQUESTS "PAGE INS"   *
!*  AND "PAGE OUTS". IT IS UP TO THE GLOBAL CONTROLLER TO SUPPLY THE   *
!*  LATEST COPY OF THE PAGE AND TO UPDATE THE RELEVANT COPIES.         *
!*                                                                     *
!***********************************************************************
!*
!*
!   %IF MON LEVEL&256 # 0 %START;              !include harvesting?
   %INTEGER PROC NO
!   %FINISH
!*
!*
%INTEGER STATUS
%INTEGER CALL PROC
%CONSTINTEGER SUCCESSFUL = 0, UNSUCCESSFUL = 1
!*
%RECORDNAME STREAM(SR);  %INTEGER STREAM NO
%RECORDNAME BUFFER(BR);  %INTEGER BUFFER NO
!*
%INTEGER TEMP, DSERV, DACT, OLD STATE, LINK
!*
%SWITCH COM(INIT : CONTROL MSG7 REPLY)
!*
!*
%ROUTINESPEC RELEASE PAGE(%INTEGER DRUM UP)
%ROUTINESPEC RELEASE BUFFER(%INTEGER DRUM UP)
%INTEGERFNSPEC UNLINK STREAM(%INTEGER STREAM NO)
%ROUTINESPEC GET PAGE(%INTEGER STREAM NO)
%ROUTINESPEC RELEASE BLOCK
!*
!*
!*
!*
!***********************************************************************
!*                        MAIN PROGRAM                                 *
!***********************************************************************
!*
!** MONITOR
!*
   %IF MON LEVEL&2 # 0 %AND (KMON>>(COMMS COMMAND>>16))&1 # 0 %C
      %THEN PKMON REC("Comms Control: ",MESS)
!*
!*
   DSERV = MESS_DSERV
   DACT = DSERV&SHORT MASK
   -> COM(DACT) %IF DACT <= CONNECT;          !CONNECT OR INIT?
   STREAM NO = MESS_P1;                       !PULL OUT STREAM NUMBER FIRST ALWAYS IN P1
   %IF 0 < STREAM NO <= MAX STREAM %START;    !WITHIN RANGE?
      TEMP = STREAM INDEX(STREAM NO)
      %IF TEMP # EMPTY %START;                !ALLOCATED?
         STREAM == STREAM TAB(TEMP)
         PROC NO = ((STREAM_OWNER>>16)-64)&(MAX PROCS-1)
         %IF DACT < TRANSFER REQUESTED %START
            CALL PROC = ((MESS_SSERV>>16)-64)&(MAX PROCS-1)
            %IF 2 < PROC NO # CALL PROC %START
               OP MESS("Comms: illegal use of stream ".STRINT(STREAM NO))
               OP MESS("Comms: caller ".STRINT(CALL PROC).", owner ".STRINT(PROC NO))
               PRINTSTRING("Comms control: offending POFF -".SNL)
               PKMON REC("Comms control: ",MESS)
               %RETURN
            %FINISH %ELSE STREAM_CALLER = MESS_SSERV
         %FINISH %ELSE STATUS = MESS_P2
         -> COM(DACT)
      %FINISH
   %FINISH
NO STREAM:

   PRINT STRING("Comms Control: Stream not allocated ".SNL)
   PKMON REC("Comms Control: ",MESS)
   %RETURN %UNLESS DACT < TRANSFER REQUESTED
   MESS_DSERV = MESS_SSERV;                   !REPLY TO CALLER 
   MESS_SSERV = DSERV
   MESS_P2 = UNSUCCESSFUL
   PON(MESS)
   %RETURN
!*
!*
!*
!***********************************************************************
!*                                                                     *
!*  THIS SECTION HANDLES:                                              *
!*  1). COMMUNICATIONS CONTROLLER INITIALISATION                       *
!*                                                                     *
!***********************************************************************
!*
!*
COM(INIT):!** COMMAND: INITIALISE
   STREAM TAB == ARRAY(MESS_P3,SARF);         !ADDRESS SUPPLIED BY GLOBAL CONTROLLER
   BUFFER TAB == ARRAY(MESS_P3,BARF);         !ADDRESS OF PARM ARRAY SUPPLIED BY GLOBAL CONTROLLER
   PARM TAB == ARRAY(MESS_P3,PARF);           !PARM TABLE ARRAY
   COM AREA == RECORD(X'80000000'+48<<18)
   COMMS_INDEX ADDR = ADDR(STREAM INDEX(0))
   COMMS_QUEUED STREAM HEAD = EMPTY
   COMMS_QUEUED STREAM TAIL = EMPTY
   COM AREA_COMMSRECA = ADDR(COMMS)
   LINK = NEW PP CELL;                        !GET A BUFFER ENTRY FROM PARAM TABLE
   COMMS_NEXT FREE BUFFER = LINK;             !HEAD OF LIST
   %CYCLE TEMP = 1,1,MESS_P2;                 !GET OTHER BUFFERS
      BUFFER == BUFFER TAB(LINK);             !MAP BUFFER
      BUFFER = 0
      BUFFER_STREAM NO = EMPTY
      %IF TEMP = MESS_P2 %THEN LINK = EMPTY %ELSE LINK = NEW PP CELL
!LAST BUFFER?
      BUFFER_LINK = LINK;                     !LINK BUFFERS TOGETHER
   %REPEAT
   %RETURN
!*
!*
!*
!***********************************************************************
!*                                                                     *
!*  THIS SECTION HANDLES:                                              *
!*  1). COMMANDS FROM COMMUNICATIONS USERS.                            *
!*  2). REPLIES FROM THE GLOBAL CONTROLLER IN RESPONSE                 *
!*         TO CALLS GENERATED BY COMMANDS.                             *
!*  3). REPLIES FROM AN ADAPTOR IN RESPONSE                            *
!*         TO CALLS GENERATED BY A COMMAND.                            *
!*                                                                     *
!***********************************************************************
!*
!*
COM(CONNECT):!** COMMAND: CONNECT STREAM TO COMMUNICATIONS USER
   STREAM NO = MESS_P1&1;                     !MAKE EVEN OR ODD I.E. INPUT OR OUTPUT
   %CYCLE STREAM NO = STREAM NO+2,2,MAX STREAM-(1-STREAM NO)
      %IF STREAM INDEX(STREAM NO) = EMPTY %START;  !FOUND A FREE STREAM
         STREAM INDEX(STREAM NO) = NEW PP CELL;    !GET A STREAM TABLE ENTRY
         STREAM == STREAM TAB(STREAM INDEX(STREAM NO))
         STREAM = 0
         STREAM_STREAM NO = STREAM NO
         STREAM_CALLER = MESS_SSERV
         STREAM_OWNER = MESS_P2;              !HIGH LEVEL CONTROL MESSAGES SENT ON THIS SERVICE
         %IF MON LEVEL&256 # 0 %START;        !include harvesting?
            PROC NO = ((STREAM_OWNER>>16)-64)&(MAX PROCS-1)
            HARVEST(14,PROC NO,8,STREAM NO,MESS_P3,0,0,0) %C
               %IF TRACE = YES %AND TRACE EVENTS&(1<<14) # 0 %C
               %AND (TRACE PROCESS = -1 %OR TRACE PROCESS = PROC NO)
         %FINISH
         STREAM_LENGTH = STREAM NO;           !SET FOR REPLIES FROM FRONT END
         STREAM_CURSOR = MESS_P5;             !SET FOR REPLY TO CONNECT ONLY
         STREAM_ADAPTOR NO = (MESS_P3>>24)&15
         %IF ADAPT(STREAM_ADAPTOR NO) = NOT ALLOCATED %START
            STATUS = UNSUCCESSFUL
            -> RETURN CELL
         %FINISH
         STREAM_DEVICE NO = (MESS_P3>>16)&BYTE MASK
         STREAM_EXTERNAL STREAM NO = MESS_P3&SHORT MASK
         STREAM_AMT INDEX = NOT ALLOCATED
         STREAM_LINK = EMPTY
         STREAM_STATE = CONNECTING
         -> UPDATE ADAPTOR
      %FINISH
   %REPEAT
   -> NO STREAM
!*
!*
COM(CONNECT REPLY):!** ADAPTOR REPLY: STREAM CONNECTED
   %IF MON LEVEL&256 # 0 %START;              !include harvesting?
      HARVEST(15,PROC NO,4,STREAM NO,0,0,0,0) %C
         %IF TRACE = YES %AND TRACE EVENTS&(1<<15) # 0 %AND (TRACE PROCESS = -1 %C
         %OR TRACE PROCESS = PROC NO)
   %FINISH
   %IF STATUS = SUCCESSFUL %START
      STREAM_STATE = CONNECTED
      -> REPLY TO CALLER
   %FINISH %ELSE -> RETURN CELL
!*
!*
COM(ENABLE):!** COMMAND: ENABLE STREAM
   %IF MON LEVEL&256 # 0 %START;              !include harvesting?
      HARVEST(16,PROC NO,12,STREAM NO,MESS_P4&BYTE MASK,MESS_P6,0,0) %C
         %IF TRACE = YES %AND TRACE EVENTS&(1<<16) # 0 %AND (TRACE PROCESS = -1 %C
         %OR TRACE PROCESS = PROC NO)
   %FINISH
   %IF STREAM_STATE = CONNECTED %START
      STREAM_STATE = CLAIMING
      STREAM_MODE = MESS_P4&BYTEMASK
      STREAM_LENGTH = MESS_P6-1
      %IF STREAM_MODE&15 = CIRCULAR %START
         STREAM_START = MESS_P5&SHORT MASK
         STREAM_CURSOR = MESS_P5>>16
      %FINISH %ELSE %START
         STREAM_START = MESS_P5
         STREAM_CURSOR = 0
      %FINISH
!** CLAIM BLOCK FROM THE GLOBAL CONTROLLER
      MESS_DSERV = CLAIM BLOCK
      MESS_SSERV = BLOCK CLAIMED
!** P1=STREAM NO, P2=DISC ADDRESS, P3=FLAGS/EPAGES. USER SUPPLIED CANNOT CHECK
      PON(MESS)
      %RETURN
   %FINISH %ELSE STATUS = UNSUCCESSFUL
   -> REPLY TO CALLER
!*
!*
COM(CLAIM AMT INDEX REPLY):!** GLOBAL CONTROLLER REPLY: BLOCK CLAIMED
   %IF MON LEVEL&256 # 0 %START;              !include harvesting?
      HARVEST(17,PROC NO,8,STREAM NO,STATUS,0,0,0) %C
         %IF TRACE = YES %AND TRACE EVENTS&(1<<17) # 0 %AND (TRACE PROCESS = -1 %C
         %OR TRACE PROCESS = PROC NO)
   %FINISH
   %IF STATUS <= 0 %START;                    !BLOCK UNSUCCESSFUL
!*  0 CHANGE BLOCK SIZE IN SITU
!* -1 NO AMT CELLS
!* -2 NOT ENOUGH GARBAGE
!* -3 CHANGE BLOCK SIZE WHILE STILL IN USE
      STREAM_STATE = CONNECTED
      STATUS = STATUS-1;                      !ZERO NORMALLY SUCCESSFUL
      -> REPLY TO CALLER
   %FINISH
   STREAM_AMT INDEX = STATUS<<16
   STREAM_STATE = ENABLING
   -> UPDATE ADAPTOR
!*
!*
COM(ENABLE REPLY):!** ADAPTOR REPLY: STREAM ENABLED
   %IF MON LEVEL&256 # 0 %START;              !include harvesting?
      HARVEST(18,PROC NO,4,STREAM NO,0,0,0,0) %C
         %IF TRACE = YES %AND TRACE EVENTS&(1<<18) # 0 %AND (TRACE PROCESS = -1 %C
         %OR TRACE PROCESS = PROC NO)
   %FINISH
   %IF STATUS = SUCCESSFUL %START
      STREAM_STATE = ENABLED
      %RETURN %IF STREAM_MODE&15 # CIRCULAR
   %FINISH %ELSE %START
      STREAM_STATE = CONNECTED
      RELEASE BLOCK
   %FINISH
   -> REPLY TO CALLER
!*
!*
COM(DISABLE):!** COMMAND: DISABLE STREAM (SUSPEND OR ABORT)
   %IF MON LEVEL&256 # 0 %START;              !include harvesting?
      HARVEST(19,PROC NO,12,STREAM NO,MESS_P2,STREAM_STATE,0,0) %C
         %IF TRACE = YES %AND TRACE EVENTS&(1<<19) # 0 %AND (TRACE PROCESS = -1 %C
         %OR TRACE PROCESS = PROC NO)
   %FINISH
   %IF STREAM_STATE >= ENABLED %AND SUSPENDING <= MESS_P2 <= ABORTING %START
      OLD STATE = STREAM_STATE;               !REMEMBER STATE
      STREAM_STATE = MESS_P2;                 !SET TO SUSPENDING OR ABORTING
      %RETURN %IF OLD STATE >= PAGING IN;     !WAIT FOR COMPLETION OF PAGE IN OR TRANSFER
      STREAM NO = UNLINK STREAM(STREAM NO) %IF OLD STATE = QUEUED
      RELEASE BLOCK
      -> UPDATE ADAPTOR
   %FINISH %ELSE STATUS = UNSUCCESSFUL
   -> REPLY TO CALLER
!*
!*
COM(DISABLE REPLY):!** ADAPTOR REPLY: DISABLED
   %IF MON LEVEL&256 # 0 %START;              !include harvesting?
      HARVEST(20,PROC NO,4,STREAM NO,0,0,0,0) %C
         %IF TRACE = YES %AND TRACE EVENTS&(1<<20) # 0 %AND (TRACE PROCESS = -1 %C
         %OR TRACE PROCESS = PROC NO)
   %FINISH
   STREAM_STATE = CONNECTED
   STATUS = SUCCESSFUL;                       !ALWAYS SUCCESSFUL
   -> REPLY TO CALLER
!*
!*
COM(DISCONNECT):!** COMMAND: DISCONNECT STREAM
   %IF MON LEVEL&256 # 0 %START;              !include harvesting?
      HARVEST(21,PROC NO,4,STREAM NO,0,0,0,0) %C
         %IF TRACE = YES %AND TRACE EVENTS&(1<<21) # 0 %AND (TRACE PROCESS = -1 %C
         %OR TRACE PROCESS = PROC NO)
   %FINISH
   %IF STREAM_STATE = CONNECTED %START
      STREAM_STATE = DISCONNECTING
      -> UPDATE ADAPTOR
   %FINISH %ELSE STATUS = UNSUCCESSFUL
   -> REPLY TO CALLER
!*
!*
COM(DISCONNECT REPLY):!** ADAPTOR REPLY: STREAM DISCONNECTED
   %IF MON LEVEL&256 # 0 %START;              !include harvesting?
      HARVEST(22,PROC NO,4,STREAM NO,0,0,0,0) %C
         %IF TRACE = YES %AND TRACE EVENTS&(1<<22) # 0 %AND (TRACE PROCESS = -1 %C
         %OR TRACE PROCESS = PROC NO)
   %FINISH
   STATUS = SUCCESSFUL;                       !ALWAYS SUCCESSFUL
   -> RETURN CELL
!*
!*
COM(CONTROL MSG6):!** COMMAND: SEND HIGH LEVEL CONTROL MESSAGE WHICH GENERATES NO REPLY
   %IF MON LEVEL&256 # 0 %START;              !include harvesting?
      HARVEST(23,PROC NO,12,STREAM NO,DACT,STREAM_STATE,0,0) %C
         %IF TRACE = YES %AND TRACE EVENTS&(1<<23) # 0 %AND (TRACE PROCESS = -1 %C
         %OR TRACE PROCESS = PROC NO)
   %FINISH
   MESS_SSERV = COMMS COMMAND!CONTROL MSG6 REPLY
   -> PON ADAPTOR
!*
!*
COM(CONTROL MSG7):!** COMMAND: SEND HIGH LEVEL CONTROL MESSAGE WHICH GENERATES A REPLY
   %IF MON LEVEL&256 # 0 %START;              !include harvesting?
      HARVEST(23,PROC NO,12,STREAM NO,DACT,STREAM_STATE,0,0) %C
         %IF TRACE = YES %AND TRACE EVENTS&(1<<23) # 0 %AND (TRACE PROCESS = -1 %C
         %OR TRACE PROCESS = PROC NO)
   %FINISH
   MESS_SSERV = COMMS COMMAND!CONTROL MSG7 REPLY
   -> PON ADAPTOR
!*
!*
!*
!*
!*
!***********************************************************************
!*                                                                     *
!*  THIS SECTION HANDLES:                                              *
!*  1). TRANSFER REQUESTS FROM AN ADAPTOR.                             *
!*  2). REPLIES FROM THE GLOBAL CONTROLLER IN RESPONSE                 *
!*         TO CALLS GENERATED BY TRANSFER REQUESTS.                    *
!*  3). TRANSFER COMPLETE REPLIES FROM AN ADAPTOR.                     *
!*                                                                     *
!***********************************************************************
!*
!*
COM(TRANSFER REQUESTED):!** ADAPTOR REQUEST: TRANSFER REQUEST
   %IF STREAM_STATE = ENABLED %START;         !IGNORE IF SUSPENDING OR ABORTING
      STREAM_CURSOR = STATUS&SHORT MASK %C
         %IF STATUS # 0 %AND STREAM_MODE&15 = CIRCULAR %C
         %AND 0 <= STATUS&SHORT MASK <= STREAM_LENGTH
      %IF COMMS_NEXT FREE BUFFER = EMPTY %START;   !NO BUFFER AVAILABLE
         %IF MON LEVEL&256 # 0 %START;        !include harvesting?
            HARVEST(27,PROC NO,4,STREAM NO,0,0,0,0) %C
               %IF TRACE = YES %AND TRACE EVENTS&(1<<27) # 0 %C
               %AND (TRACE PROCESS = -1 %OR TRACE PROCESS = PROC NO)
         %FINISH
         STREAM_STATE = QUEUED;               !SO WAIT.
         STREAM_LINK = EMPTY
         %IF COMMS_QUEUED STREAM HEAD = EMPTY %C
            %THEN COMMS_QUEUED STREAM HEAD = STREAM NO %C
            %ELSE STREAM TAB(STREAM INDEX(COMMS_QUEUED STREAM TAIL))_LINK = STREAM NO
         COMMS_QUEUED STREAM TAIL = STREAM NO
      %FINISH %ELSE %START;                   !BUFFER AVAILABLE
         BUFFER NO = COMMS_NEXT FREE BUFFER
         BUFFER == BUFFER TAB(BUFFER NO)
         COMMS_NEXT FREE BUFFER = BUFFER_LINK
         BUFFER = 0
         BUFFER_LINK = EMPTY
         STREAM_LINK = BUFFER NO
         GET PAGE(STREAM NO)
      %FINISH
   %FINISH
   %RETURN
!*
!*
COM(PAGE HERE):!** GLOBAL CONTROLLER REPLY: CURRENT OWNER'S PAGE HERE
%IF STREAM_STATE = PAGING IN %START;!CHECK BUFFER NOT DISABLED (SUSPEND OR ABORT)
   %IF MON LEVEL&256 # 0 %START;!include harvesting?
      HARVEST(30, PROC NO, 8, STREAM NO, %C
 (MESS_SSERV&255)<<24!(MESS_P5&255)<<16!(MESS_P6&SHORT MASK),0 ,0, 0) %IF %C
   TRACE = YES %AND TRACE EVENTS&(1<<30) # 0 %AND (TRACE PROCESS = -1 %C
      %OR TRACE PROCESS = PROC NO)
%FINISH
OP MESS("Comms page in fails") %IF MESS_P3 # 0
!* SHOULD DEAL WITH TRANSFER FAILURES HERE?
!* P_P3 = 0 OK
!* P_P3 = 1 PARITY ERROR
!* P_P3 > 1 ZERO PAGE
MONITOR("COMMS DEADLOCK PAGE?") %IF STATUS = -1
BUFFER TAB(STREAM_LINK)_REAL ADDRESS = STATUS;!REAL PAGE ADDRESS
STREAM_STATE = ACTIVE
MESS_SSERV = TRANSFER COMPLETE
MESS_DSERV = GO AHEAD!ADAPT(STREAM_ADAPTOR NO)
MESS_P2 = STREAM_LINK
MESS_P5 = STREAM_DEVICE NO;                   ! for use by OPER
%IF MULTI OCP = NO %AND STREAM_ADAPTOR NO = FE %THEN MK1 FE ADAPTOR(MESS) %C
   %ELSE PON(MESS)
%RETURN
%FINISH %ELSE %START;                         !BUFFER DISABLED
   BUFFER NO = STREAM_LINK
   BUFFER == BUFFER TAB(BUFFER NO)
   RELEASE BUFFER(NO DRUM UPDATE)
   RELEASE BLOCK
   -> UPDATE ADAPTOR
%FINISH
!*
!*
COM(TRANSFER COMPLETED):!** ADAPTOR REPLY: TRANSFER COMPLETE
%IF MON LEVEL&256 # 0 %START;                 !include harvesting?
   HARVEST(31,PROC NO,12,STREAM NO,MESS_P2,MESS_P3,0,0) %C
      %IF TRACE = YES %AND TRACE EVENTS&(1<<31) # 0 %AND (TRACE PROCESS = -1 %C
      %OR TRACE PROCESS = PROC NO)
%FINISH
!*
!* P2 HAS BITS SET WITH SIGNIFICANCE 2**0 NEXT PAGE REQUIRED
!*                                   2**1 PAGE NOT ELIGIBLE FOR RECAPTURE
!*                                   2**2 UPDATE USERS CURSOR
!* P3 HAS NUMBER OF BYTES TRANSFERED
!*
BUFFER NO = STREAM_LINK
BUFFER == BUFFER TAB(BUFFER NO)
STREAM_CURSOR = STREAM_CURSOR+MESS_P3;        !ADD IN LENGTH TRANSFERED
%IF MESS_P3 = BUFFER_LENGTH %OR STREAM_STATE # ACTIVE %START
                                              !END OF BUFFER OR BUFFER DISABLED
   %IF STREAM_CURSOR = (STREAM_LENGTH+1) %START;   !END OF SECTION (WRAP ROUND OR SUSPEND)
      %IF STREAM_MODE&15 = CIRCULAR %THEN STREAM_CURSOR = 0 %C
         %ELSE STREAM_STATE = SUSPENDING
   %FINISH
   %IF STREAM_STATE = ACTIVE %START;          !CHECK NOT DISABLED (SUSPEND OR ABORT)
      %IF STATUS&1 # 0 %START;                !GET THE NEXT PAGE.
         RELEASE PAGE(NO DRUM UPDATE);        !RELEASE PAGE TO GET ANOTHER
         GET PAGE(STREAM NO)
         %RETURN
      %FINISH
   %FINISH %ELSE %START;                      !DISABLED (SUSPEND OR ABORT)
      RELEASE BUFFER(NO DRUM UPDATE)
      RELEASE BLOCK
      -> UPDATE ADAPTOR
   %FINISH
%FINISH
%IF STATUS&2 = 0 %THEN TEMP = DRUM UPDATE %ELSE TEMP = NO DRUM UPDATE
RELEASE BUFFER(TEMP)
STREAM_STATE = ENABLED
%IF STATUS&4 # 0 %START;                      !UPDATE USERS CURSOR
   MESS_DSERV = STREAM_OWNER
   MESS_SSERV = DSERV
   MESS_P2 = STREAM_CURSOR
   MESS_P3 = 0;                               !MUST BE ZERO OTHERWISE SIGNIFIES AN ABORT
   PON(MESS)
%FINISH
%RETURN
!*
!*
UPDATE ADAPTOR:

                                              !SEND LOW LEVEL CONTROL INFO TO THE ADAPTOR
MESS = 0
MESS_P1 = SUB IDENT(STREAM_STATE)!STREAM NO
MESS_P2 = STREAM_STATE<<24!STREAM_MODE<<16!(STREAM_LENGTH&X'FFFF')
MESS_P3 = STREAM_CURSOR
MESS_P4 = STREAM INDEX(STREAM NO);            ! when connecting, set to cell
MESS_P5 = STREAM_DEVICE NO;                   ! for OPER
MESS_P6 = STREAM_LENGTH
MESS_SSERV = DSERV
PON ADAPTOR:

MESS_DSERV = SEND CONTROL!ADAPT(STREAM_ADAPTOR NO)
%IF MULTI OCP = NO %AND STREAM_ADAPTOR NO = FE %THEN MK1 FE ADAPTOR(MESS) %C
   %ELSE PON(MESS)
%RETURN
!*
!*
COM(CONTROL MSG6 REPLY):!** ADAPTOR REPLY: CONTROL MESSAGE 6 REPLY
%IF MON LEVEL&256 # 0 %START;                 !include harvesting?
   HARVEST(24,PROC NO,12,STREAM NO,DACT-12,STREAM_STATE,0,0) %C
      %IF TRACE = YES %AND TRACE EVENTS&(1<<24) # 0 %AND (TRACE PROCESS = -1 %C
      %OR TRACE PROCESS = PROC NO)
%FINISH
%IF STATUS = SUCCESSFUL %THEN %RETURN %ELSE -> REPLY TO CALLER;   !??????
!*
!*
COM(CONTROL MSG7 REPLY):!** ADAPTOR REPLY: CONTROL MESSAGE 7 REPLY
%IF MON LEVEL&256 # 0 %START;                 !include harvesting?
   HARVEST(24,PROC NO,12,STREAM NO,DACT-12,STREAM_STATE,0,0) %C
      %IF TRACE = YES %AND TRACE EVENTS&(1<<24) # 0 %AND (TRACE PROCESS = -1 %C
      %OR TRACE PROCESS = PROC NO)
%FINISH
!*
!*
!*
REPLY TO CALLER:

STREAM NO = -1
RETURN CELL:

MESS_SSERV = DSERV
MESS_DSERV = STREAM_CALLER
MESS_P2 = STATUS
MESS_P3 = STREAM_OWNER
MESS_P4 = STREAM_STATE
MESS_P5 = STREAM_CURSOR
PON(MESS)
%UNLESS STREAM NO < 0 %START
   RETURN PP CELL(STREAM INDEX(STREAM NO))
   STREAM INDEX(STREAM NO) = EMPTY
%FINISH
%RETURN
!*
!*

%ROUTINE RELEASE PAGE(%INTEGER DRUM UP)
!***********************************************************************
!*                                                                     *
!*  SEND A PAGE OUT REQUEST TO THE GLOBAL CONTROLLER. SPECIFYING       *
!*  WHETHER THE DRUM COPY IS TO BE UPDATED, WHETHER THE PAGE HAS BEEN  *
!*  WRITTEN TO AND WHETHER THE PAGE IS ELIGIBLE FOR RECAPTURE.         *
!*  NO REPLY IS GIVEN BY THE GLOBAL CONTROLLER                         *
!*  BIT SIGNIFICANCE:                                                  *
!*       2**0 - RECAPTURABLE                                           *
!*       2**1 - MAKE NEW                                               *
!*       2**2 - DRUM UPDATE                                            *
!*       2**3 - WRITTEN TO                                             *
!*                                                                     *
!***********************************************************************
%RECORD MESS(PE)
   MESS = 0
   MESS_DSERV = FREE PAGE
   MESS_SSERV = DSERV
   MESS_P1 = BUFFER_AMT INDEX;                !INDEX<<16!EPAGE
   MESS_P2 = (1-(STREAM NO&1))<<3!DRUM UP
   %IF MON LEVEL&256 # 0 %START;              !include harvesting?
      HARVEST(25,PROC NO,12,STREAM NO,MESS_P1,MESS_P2,0,0) %C
         %IF TRACE = YES %AND TRACE EVENTS&(1<<25) # 0 %AND (TRACE PROCESS = -1 %C
         %OR TRACE PROCESS = PROC NO)
   %FINISH
   PON(MESS)
%END;                                         !OF ROUTINE RELEASE PAGE
!*
!*

%ROUTINE RELEASE BUFFER(%INTEGER DRUM UP)
!***********************************************************************
!*                                                                     *
!*  RELEASE A COMMS PAGE FROM ITS ALLOCATION. IF A TRANSFER REQUEST IS *
!*  QUEUED REQUEST ANOTHER PAGE.                                       *
!*                                                                     *
!***********************************************************************
%INTEGER TEMPST NO
   RELEASE PAGE(DRUM UP)
   BUFFER_STREAM NO = EMPTY
   STREAM_LINK = EMPTY
   %IF COMMS_QUEUED STREAM HEAD # EMPTY %START;    !PAGE FRAME REQUIRED
      TEMPST NO = UNLINK STREAM(COMMS_QUEUED STREAM HEAD)
      STREAM TAB(STREAM INDEX(TEMPST NO))_LINK = BUFFER NO
      GET PAGE(TEMPST NO)
   %FINISH %ELSE %START;                      !RETURN BUFFER TO FREE LIST
      BUFFER_LINK = COMMS_NEXT FREE BUFFER
      COMMS_NEXT FREE BUFFER = BUFFER NO
   %FINISH
%END;                                         !OF ROUTINE RELEASE BUFFER
!*
!*

%INTEGERFN UNLINK STREAM(%INTEGER TEMPST NO)
!***********************************************************************
!*                                                                     *
!*  REMOVE A QUEUED TRANSFER FROM THE LINKED LIST                      *
!*                                                                     *
!***********************************************************************
%INTEGER STREAM ID, STREAM LINK, LINK
%RECORDNAME TEMPST(SR)
   %IF MON LEVEL&256 # 0 %START;              !include harvesting?
      HARVEST(28,PROC NO,4,TEMPST NO,0,0,0,0) %C
         %IF TRACE = YES %AND TRACE EVENTS&(1<<28) # 0 %AND (TRACE PROCESS = -1 %C
         %OR TRACE PROCESS = PROC NO)
   %FINISH
   LINK = STREAM TAB(STREAM INDEX(TEMPST NO))_LINK
   STREAM TAB(STREAM INDEX(TEMPST NO))_LINK = EMPTY
   %IF COMMS_QUEUED STREAM HEAD = TEMPST NO %START
      COMMS_QUEUED STREAM HEAD = LINK
      COMMS_QUEUED STREAM TAIL = EMPTY %IF COMMS_QUEUED STREAM HEAD = EMPTY
   %FINISH %ELSE %START
      STREAM LINK = COMMS_QUEUED STREAM HEAD
      %UNTIL STREAM LINK = TEMPST NO %CYCLE
         STREAM ID = STREAM LINK
         TEMPST == STREAM TAB(STREAM INDEX(STREAM ID))
         STREAM LINK = TEMPST_LINK
      %REPEAT
      TEMPST_LINK = LINK
      COMMS_QUEUED STREAM TAIL = STREAM ID %IF COMMS_QUEUED STREAM TAIL = TEMPST NO
   %FINISH
   %RESULT = TEMPST NO
%END;                                         !OF INTEGERFN UNLINK STREAM
!*
!*

%ROUTINE GET PAGE(%INTEGER TEMPST NO)
!***********************************************************************
!*                                                                     *
!*  CALCULATE THE NEXT PAGE REQUIRED AND REQUEST IT FROM THE GLOBAL    *
!*  CONTROLLER.                                                        *
!*                                                                     *
!***********************************************************************
%RECORD MESS(PE)
%RECORDNAME TEMPST(SR)
%INTEGER EPAGE, LENGTH, OFFSET, ESIZE
   TEMPST == STREAM TAB(STREAM INDEX(TEMPST NO))
   TEMPST_STATE = PAGING IN
   ESIZE = COM AREA_EPAGESIZE<<10;            !CALCULATE THE EPAGE SIZE IN BYTES
   OFFSET = TEMPST_CURSOR+TEMPST_START;       !OFFSET IN THE FILE SECTION
   EPAGE = OFFSET//ESIZE;                     !CALCULATE IN WHICH PAGE
   OFFSET = OFFSET-EPAGE*ESIZE;               !OFFSET IN PAGE
   LENGTH = (TEMPST_LENGTH+1)-TEMPST_CURSOR;  !CALCULATE LENGTH
   LENGTH = ESIZE-OFFSET %IF OFFSET+LENGTH > ESIZE
                                              !LENGTH WITHIN PAGE
   BUFFER_STREAM NO = TEMPST NO
   BUFFER_EXTERNAL STREAM NO = TEMPST_EXTERNAL STREAM NO
   BUFFER_AMT INDEX = TEMPST_AMT INDEX!EPAGE
   BUFFER_OFFSET = OFFSET
   BUFFER_LENGTH = LENGTH
   MESS = 0
   MESS_DSERV = CLAIM PAGE
   MESS_SSERV = PAGE CLAIMED
   MESS_P1 = BUFFER_AMT INDEX
   MESS_P2 = TEMPST NO
   %IF MON LEVEL&256 # 0 %START;              !include harvesting?
      HARVEST(29,PROC NO,12,STREAM NO,MESS_P1,BUFFER_LENGTH,0,0) %C
         %IF TRACE = YES %AND TRACE EVENTS&(1<<29) # 0 %AND (TRACE PROCESS = -1 %C
         %OR TRACE PROCESS = PROC NO)
   %FINISH
   PON(MESS)
%END;                                         !OF ROUTINE GET PAGE
!*
!*

%ROUTINE RELEASE BLOCK
!***********************************************************************
!*                                                                     *
!*  RELEASE THE PAGE AND BUFFER IF THE STREAM HAS ONE AND THEN RELEASE *
!*  THE FILE SECTION FROM THE ACTIVE MEMORY TABLE                      *
!*                                                                     *
!***********************************************************************
%RECORD MESS(PE)
   MESS = 0
   MESS_DSERV = FREE BLOCK
   MESS_SSERV = DSERV
   MESS_P1 = STREAM NO
   MESS_P2 = STREAM_AMT INDEX>>16
   %IF MON LEVEL&256 # 0 %START;              !include harvesting?
      HARVEST(26,PROC NO,8,STREAM NO,MESS_P2,0,0,0) %C
         %IF TRACE = YES %AND TRACE EVENTS&(1<<26) # 0 %AND (TRACE PROCESS = -1 %C
         %OR TRACE PROCESS = PROC NO)
   %FINISH
   PON(MESS)
   STREAM_AMT INDEX = NOT ALLOCATED
%END;                                         !OF ROUTINE RELEASE BLOCK
!*
!*
%END;                                         !OF ROUTINE COMMS CONTROL
!*
!*
!*
!*
!*
!*
!*
!*

%EXTERNALROUTINE MK1 FE ADAPTOR(%RECORDNAME MESS)
!***********************************************************************
!*    SERVICE 57 (X39)                                                 *
!*    DRIVES THE MK1 FRONT END TO COMMS CONTROLLER SPEC                *
!*    CAN MANAGE UP TO 10 FRONT ENDS (FE0-FE9) USING A 512 BYTE AREA   *
!*    FOR EACH WHICH IS PROVIDED ON ALLOCATION                         *
!***********************************************************************
%RECORDSPEC MESS(PE)
   %IF SSERIES = NO %START
   %RECORDFORMAT DAF(%INTEGER LST0, LST1, RCB0, RCB1, RCB2,  %C
         RCB3, RCB4, RCB5, RCB6, RCB7, LB0, LB1, AL00, AL01,  %C
         AL10, AL11, STREAM, NAME, COB START, COB INDEX,  %C
         HEAD, TAIL, IDENT, CO HEAD, CO TAIL, MAX QD TRANSFERS,  %C
         MAX QD CONTROL MSGS, TOTAL QD TRANSFERS, TOTAL QD CONTROL MSGS,  %C
         BYTES OF CONTROL INPUT, BYTES OF CONTROL OUTPUT, BYTES OF DATA INPUT,  %C
         BYTES OF DATA OUTPUT, CONTROL INPUT TRANSFERS,  %C
         CONTROL OUTPUT TRANSFERS, DATA INPUT TRANSFERS,  %C
         DATA OUTPUT TRANSFERS, OUTPUT UPDATES, UPDATE OVERWRITES,  %C
         CONTROL INPUT XBIT SET, CONTROL OUTPUT XBIT SET, %C
         DATA INPUT XBIT SET, DATA OUTPUT XBIT SET, %C
         %BYTEINTEGER ATTN SET, SPARE, DEVICE NO, FAILURES, %C
         %BYTEINTEGERARRAY CIB, COB(0 : 167))
   %FINISH %ELSE %START
   %RECORDFORMAT DAF(%INTEGER TCB0 COMMAND, TCB0 STE,  %C
         TCB0 LENGTH, TCB0 ADDRESS, TCB0 NEXT TCB,  %C
         TCB0 RESPONSE, %INTEGERARRAY TCB0 PREAMBLE,  %C
         TCB0 POSTAMBLE(0 : 3),  %C
         %INTEGER TCB1 COMMAND, TCB1 STE, TCB1 LENGTH,  %C
         TCB1 ADDRESS, TCB1 NEXT TCB, TCB1 RESPONSE,  %C
         %INTEGERARRAY TCB1 PREAMBLE, TCB1 POSTAMBLE(0 : 3),  %C
         %INTEGER STREAM, NAME, COB START, COB INDEX, HEAD,  %C
         TAIL, QUEUED TRANSFERS, IDENT, CO HEAD, CO TAIL,  %C
         %BYTEINTEGER ATTN SET, SPARE, DEVICE NO, FAILURES,  %C
         %BYTEINTEGERARRAY CIB, COB(0 : 167))
   %FINISH
%RECORDFORMAT GPCF(%INTEGER SER, GPTSM, PROPADDR, TICKS, CAA,  %C
      GRCB AD, LBA, ALA, STATE, RESP0, RESP1, SENSE1, SENSE2,  %C
      SENSE3, SENSE4, REPSNO, BASE, ID, DLVN, MNEMONIC,  %C
      ENTSIZE, PAW, USAW0, URCB AD, SENSE AD, LOGMASK, TRT AD %C
      , UA SIZE, UA AD, TIMEOUT, PROPS0, PROPS1)
   %RECORDFORMAT CR(%INTEGER MESSAGE IDENT, P2, P3, P4, P5, P6)
   %OWNINTEGERARRAY DEVICE TO DA AD(0 : 9) =        %C
      NOT ALLOCATED(10)
   %OWNINTEGER TIMER STARTED
   %CONSTINTEGER CIB LENGTH = 168;            !7 CONTROL MESSAGES
   %CONSTINTEGER COB LENGTH = 168
   %CONSTINTEGER MAX FAILURES = 5
%CONSTBYTEINTEGERARRAY RETRY DELAY(0 : MAX FAILURES) = %C
      0,1,0,2,0,3
   %CONSTINTEGER CONTROL MSG LENGTH = 24
%CONSTINTEGER CONTROL MSG DESCRIPTOR = CONTROL MSG LENGTH!X'18000000'
%CONSTINTEGER CONTROL IN MSG DESCRIPTOR = CONTROL MSG DESCRIPTOR - 4
   %CONSTINTEGER IDLE = X'F0F0F0F0'
%CONSTINTEGER CONTROL IN=-2
%CONSTINTEGER CONTROL OUT=-1
   %CONSTINTEGER DATA STREAM = 0
      %IF SSERIES = NO %START
      %CONSTINTEGER LONG BLOCK MASK = X'0400'
      %CONSTINTEGER X MASK = X'0200'
      %CONSTINTEGER Y MASK = X'0100'
      %FINISH %ELSE %START
      %CONSTINTEGER LONG BLOCK MASK = X'04000000'
      %CONSTINTEGER X MASK = X'02000000'
      %CONSTINTEGER Y MASK = X'01000000'
      %FINISH
   %INTEGER STREAM NO, BUFFER NO, DEVICE NO
   %INTEGER TEMP, LENGTH, MESSAGE IDENT, S IDENT, SEARCH INDEX, CIB INDEX, DACT,  %C
         CONTROL ADDR, L, F, T, X, INTERRUPT ANALYSIS FLAGS, XBIT SET
   %RECORD GPC REQ(PE)
   %RECORD CMES(PE)
   %RECORDNAME GPC ENTRY(GPCF)
   %RECORDNAME DEVICE AREA(DAF)
   %RECORDNAME STREAM(SR)
   %RECORDNAME BUFFER(BR)
   %RECORDNAME CONTROL(CR)
   %RECORDNAME PARM CELL(PARMF)
%SWITCH ACT(0 : ALLOCATE REQUEST)
!*
!*

      %ROUTINE START DEVICE(%INTEGER STREAM NO, EXT STREAM NO, REAL ADDRESS, START,  %C
         LENGTH)
         %IF SSERIES = NO %START
         %CONSTINTEGERARRAY LB MASK(0 : 1) =      %C
        X'00F00202', X'80F00302'
         %FINISH %ELSE %START
         %CONSTINTEGERARRAY LB MASK(0 : 1) =        %C
        X'2F004002', X'2F004083'
         %FINISH
         %IF SSERIES = NO %START
            DEVICE AREA_LST1 = X'80000001'!REAL ADDRESS
            DEVICE AREA_LB1 = LB MASK(STREAM NO&1)
            DEVICE AREA_AL10 = LENGTH
            DEVICE AREA_AL11 = START
         %FINISH %ELSE %START
            DEVICE AREA_TCB0 RESPONSE = 0
            DEVICE AREA_TCB1 COMMAND = LB MASK(STREAM NO&1)
            %IF REAL ADDRESS = 0 %THEN DEVICE AREA_TCB1 STE = DEVICE AREA_ %C
               TCB0 STE %ELSE DEVICE AREA_TCB1 STE = REAL ADDRESS!1
            DEVICE AREA_TCB1 LENGTH = LENGTH
            DEVICE AREA_TCB1 ADDRESS = START
            DEVICE AREA_TCB1 RESPONSE = 0
         %FINISH
         DEVICE AREA_STREAM = STREAM NO
         DEVICE AREA_NAME = EXT STREAM NO<<16!LENGTH
         GPC REQ = 0
         GPC REQ_DSERV = EXECUTE CHAIN
         GPC REQ_SSERV = MK1 FE EXECUTED
         %IF SSERIES = NO %START
            GPC REQ_P1 = ADDR(DEVICE AREA_RCB0)
         %FINISH %ELSE %START
            GPC REQ_P1 = ADDR(DEVICE AREA_TCB0 COMMAND)
         %FINISH
         GPC REQ_P2 = DEVICE AREA_IDENT
         %IF SSERIES = NO %START
            GPC REQ_P3 = X'11';               !PAW FUNCTION<<4!SAW FLAGS
         %FINISH
         %IF MULTI OCP = NO %THEN GPC(GPC REQ) %ELSE PON(GPC REQ)
      %END;                                   !OF ROUTINE START DEVICE
!*
!*

      %ROUTINE QUEUE
!* QUEUE A HIGH OR LOW LEVEL CONTROL MESSAGE FOR OUTPUT AS THE COB IS FULL
      %INTEGER TEMP
         TEMP = NEW PP CELL
         PARM CELL == PARM TAB(TEMP)
         CONTROL ADDR = ADDR(PARM CELL_P1)
         %IF DEVICE AREA_CO HEAD = EMPTY %THEN DEVICE AREA_CO HEAD = TEMP %C
            %ELSE PARM TAB(DEVICE AREA_CO TAIL)_LINK = TEMP
         PARM CELL_LINK = EMPTY
         DEVICE AREA_CO TAIL = TEMP
         %IF MON LEVEL&4 # 0 %START
            DEVICE AREA_TOTAL QD CONTROL MSGS = DEVICE AREA_TOTAL QD CONTROL MSGS+1
            L = DEVICE AREA_CO HEAD
            F = 1
            %WHILE L # DEVICE AREA_CO TAIL %CYCLE
               F = F+1
               L = PARM TAB(L)_LINK
            %REPEAT
            %IF F > DEVICE AREA_MAX QD CONTROL MSGS %C
               %THEN DEVICE AREA_MAX QD CONTROL MSGS = F
         %FINISH
      %END;                                   !OF ROUTINE QUEUE
!*
!*
!*
!*
!***********************************************************************
!*                            MAIN PROGRAM                             *
!***********************************************************************
      %IF MON LEVEL&2 # 0 %AND (KMON>>MK1 FE SERVICE)&1 # 0 %C
         %THEN PKMON REC("Mk1 FE Adaptor: ",MESS)
      DACT = MESS_DSERV&SHORT MASK
      %IF DACT = INTERRUPT %START;            !FROM GPC MESS_P3=GPC AREA ADRRESS
         INTERRUPT ANALYSIS FLAGS = MESS_P1>>20&15
         GPC ENTRY == RECORD(MESS_P3);        !MAP DEVICE AREA
         DEVICE AREA == RECORD(GPC ENTRY_UA AD);   !MAP PRIVATE DEVICE AREA
         DEVICE NO = DEVICE AREA_DEVICE NO
         STREAM NO = DEVICE AREA_STREAM
      %FINISH %ELSE %START
         %IF GO AHEAD <= DACT <= SEND CONTROL %START;   !FROM COMMS MESS_P1=STRM&SHORT MASK
            STREAM NO = MESS_P1&SHORT MASK
            STREAM == STREAM TAB(STREAM INDEX(STREAM NO))
            DEVICE NO = STREAM_DEVICE NO
            %IF DEVICE TO DA AD(DEVICE NO) = NOT ALLOCATED %START
                                              !SEND FAILURE REPLIES
               CMES = 0
               %IF MESS_P1&TOP SHORT MASK # 0 %C
                  %THEN CMES_DSERV = COMMS COMMAND!MESS_P1>>16 %C
                  %ELSE CMES_DSERV = MESS_SSERV
               CMES_SSERV = MESS_DSERV
               CMES_P1 = STREAM NO
               CMES_P2 = 1
               PON(CMES)
               %RETURN
            %FINISH %ELSE DEVICE AREA == RECORD(DEVICE TO DA AD(DEVICE NO))
         %FINISH
      %FINISH
      -> ACT(DACT)
!*
!*
ACT(0):!** PRINT MONITORING IF COLLECTED
      %IF MON LEVEL&4 # 0 %START
         %IF MULTI OCP = YES %THEN RESERVE LOG
         %CYCLE DEVICE NO = 0,1,9
            %IF DEVICE TO DA AD(DEVICE NO) # NOT ALLOCATED %START
               DEVICE AREA == RECORD(DEVICE TO DA AD(DEVICE NO))
               NEWLINES(2)
               PRINT STRING("FE".TO STRING(DEVICE NO+'0'). %C
                  " Log              Bytes  Transfers   XBIT Set   Bytes/Transfer")
               NEWLINE
               PRINT STRING("Data Input     ")
               F = DEVICE AREA_BYTES OF DATA INPUT
               WRITE(DEVICE AREA_BYTES OF DATA INPUT,10)
               T = DEVICE AREA_DATA INPUT TRANSFERS
               WRITE(DEVICE AREA_DATA INPUT TRANSFERS,10)
               X = DEVICE AREA_DATA INPUT XBIT SET
               WRITE(DEVICE AREA_DATA INPUT XBIT SET,10)
               WRITE(DEVICE AREA_BYTES OF DATA INPUT//DEVICE AREA_ %C
                  DATA INPUT TRANSFERS,10) %IF DEVICE AREA_DATA INPUT TRANSFERS # 0
               NEWLINE
               PRINT STRING("Data Output    ")
               F = F+DEVICE AREA_BYTES OF DATA OUTPUT
               WRITE(DEVICE AREA_BYTES OF DATA OUTPUT,10)
               T = T+DEVICE AREA_DATA OUTPUT TRANSFERS
               WRITE(DEVICE AREA_DATA OUTPUT TRANSFERS,10)
               X = X+DEVICE AREA_DATA OUTPUT XBIT SET
               WRITE(DEVICE AREA_DATA OUTPUT XBIT SET,10)
               WRITE(DEVICE AREA_BYTES OF DATA OUTPUT//DEVICE AREA_ %C
                  DATA OUTPUT TRANSFERS,10) %IF DEVICE AREA_DATA OUTPUT TRANSFERS # 0
               NEWLINE
               PRINT STRING("Control Input  ")
               F = F+DEVICE AREA_BYTES OF CONTROL INPUT
               WRITE(DEVICE AREA_BYTES OF CONTROL INPUT,10)
               T = T+DEVICE AREA_CONTROL INPUT TRANSFERS
               WRITE(DEVICE AREA_CONTROL INPUT TRANSFERS,10)
               X = X+DEVICE AREA_CONTROL INPUT XBIT SET
               WRITE(DEVICE AREA_CONTROL INPUT XBIT SET,10)
               WRITE(DEVICE AREA_BYTES OF CONTROL INPUT//DEVICE AREA_ %C
                  CONTROL INPUT TRANSFERS,10) %C
                  %IF DEVICE AREA_CONTROL INPUT TRANSFERS # 0
               NEWLINE
               PRINT STRING("Control Output ")
               F = F+DEVICE AREA_BYTES OF CONTROL OUTPUT
               WRITE(DEVICE AREA_BYTES OF CONTROL OUTPUT,10)
               T = T+DEVICE AREA_CONTROL OUTPUT TRANSFERS
               WRITE(DEVICE AREA_CONTROL OUTPUT TRANSFERS,10)
               X = X+DEVICE AREA_CONTROL OUTPUT XBIT SET
               WRITE(DEVICE AREA_CONTROL OUTPUT XBIT SET,10)
               WRITE(DEVICE AREA_BYTES OF CONTROL OUTPUT//DEVICE AREA_ %C
                  CONTROL OUTPUT TRANSFERS,10) %C
                  %IF DEVICE AREA_CONTROL OUTPUT TRANSFERS # 0
               NEWLINE
               PRINT STRING("Totals         ")
               WRITE(F,10)
               WRITE(T,10)
               WRITE(X,10)
               WRITE(F//T,10) %IF T # 0
               NEWLINE
               PRINT STRING("Output Updates ")
               WRITE(DEVICE AREA_OUTPUT UPDATES,10)
               NEWLINE
               PRINT STRING("Update Overwrites")
               WRITE(DEVICE AREA_UPDATE OVERWRITES,8)
               NEWLINE
               PRINT STRING("Overwrite Ratio")
               %IF DEVICE AREA_UPDATE OVERWRITES > 0 %C
                  %THEN WRITE(DEVICE AREA_OUTPUT UPDATES//DEVICE AREA_ %C
                  UPDATE OVERWRITES,10) %ELSE WRITE(0,10)
               PRINT STRING(":1")
               NEWLINE
               PRINT STRING("Queued Transfers")
               WRITE(DEVICE AREA_TOTAL QD TRANSFERS,9)
               NEWLINE
               PRINT STRING("Queued Control ")
               WRITE(DEVICE AREA_TOTAL QD CONTROL MSGS,10)
               NEWLINE
               PRINT STRING("Max Queued Trans")
               WRITE(DEVICE AREA_MAX QD TRANSFERS,9)
               NEWLINE
               PRINT STRING("Max Queued Control")
               WRITE(DEVICE AREA_MAX QD CONTROL MSGS,7)
               NEWLINES(2)
               DEVICE AREA_MAX QD TRANSFERS = 0
               DEVICE AREA_MAX QD CONTROL MSGS = 0
               DEVICE AREA_TOTAL QD TRANSFERS = 0
               DEVICE AREA_TOTAL QD CONTROL MSGS = 0
               DEVICE AREA_BYTES OF CONTROL INPUT = 0
               DEVICE AREA_BYTES OF CONTROL OUTPUT = 0
               DEVICE AREA_BYTES OF DATA INPUT = 0
               DEVICE AREA_BYTES OF DATA OUTPUT = 0
               DEVICE AREA_CONTROL INPUT TRANSFERS = 0
               DEVICE AREA_CONTROL OUTPUT TRANSFERS = 0
               DEVICE AREA_DATA INPUT TRANSFERS = 0
               DEVICE AREA_DATA OUTPUT TRANSFERS = 0
               DEVICE AREA_OUTPUT UPDATES = 0
               DEVICE AREA_UPDATE OVERWRITES = 0
               DEVICE AREA_CONTROL INPUT XBIT SET = 0
               DEVICE AREA_CONTROL OUTPUT XBIT SET = 0
               DEVICE AREA_DATA INPUT XBIT SET = 0
               DEVICE AREA_DATA OUTPUT XBIT SET = 0
            %FINISH
         %REPEAT
         %IF MULTI OCP = YES %THEN RELEASE LOG
      %FINISH
      %RETURN
!*
!*
ACT(INTERRUPT):!** DEVICE INTERRUPT (NORMAL, ABNORMAL OR ATTENTION)
      -> ATTEN %IF INTERRUPT ANALYSIS FLAGS = ATTENTION
      -> ABNORMAL TERMINATION %UNLESS INTERRUPT ANALYSIS FLAGS&NORMAL TERMINATION =  %C
         NORMAL TERMINATION
!** NORMAL TERMINATION INTERRUPT
      %IF DEVICE AREA_FAILURES # 0 %START
         OP MESS("FE".TO STRING(DEVICE NO+'0')." Transfer Recovered")
         DEVICE AREA_FAILURES = 0
      %FINISH
!*
!*
      %IF SSERIES = NO %START
         LENGTH = DEVICE AREA_AL10-GPC ENTRY_RESP1&SHORT MASK
                                              !NUMBER OF BYTES TRANSFERED
         XBIT SET = GPC ENTRY_RESP0&X MASK
      %FINISH %ELSE %START
         LENGTH = DEVICE AREA_TCB1 LENGTH-DEVICE AREA_TCB1 RESPONSE&SHORT MASK
!NUMBER OF BYTES TRANSFERED
         XBIT SET = DEVICE AREA_TCB1 RESPONSE&X MASK
      %FINISH
!*
!*
      %IF STREAM NO = CONTROL OUT %START;     !CONTROL OUTPUT TERMINATION
        %IF LENGTH # (LENGTH//CONTROL MSG LENGTH)*CONTROL MSG LENGTH %START
                %IF LENGTH#1 %START;!DUE TO A QUIRK OF THE GPC 1 IS NOT AN ERROR
                OP MESS("FE".TOSTRING(DEVICE NO+'0'). %C
                " PARTIAL CONTROL MSG OUTPUT".TOSTRING(17))
                DUMP TABLE(80,MESS_P3,GPC ENTRY_ENTSIZE)
                %FINISH
                LENGTH = (LENGTH//CONTROL MSG LENGTH)*CONTROL MSG LENGTH
        %FINISH
         %IF MON LEVEL&4 # 0 %START
            DEVICE AREA_BYTES OF CONTROL OUTPUT = DEVICE AREA_ %C
               BYTES OF CONTROL OUTPUT+LENGTH
            DEVICE AREA_CONTROL OUTPUT TRANSFERS = DEVICE AREA_ %C
               CONTROL OUTPUT TRANSFERS+1
            DEVICE AREA_CONTROL OUTPUT XBIT SET = DEVICE AREA_ %C
               CONTROL OUTPUT XBIT SET+1 %IF XBIT SET # 0
         %FINISH
         DEVICE AREA_COB INDEX = DEVICE AREA_COB INDEX-LENGTH
!*MC      MOVE(DEVICE AREA_COB INDEX,ADDR(DEVICE AREA_COB(LENGTH)), %C
            ADDR(DEVICE AREA_COB(0))) %IF DEVICE AREA_COB INDEX > 0
                                              !SHUFFLE DOWN?
         %IF DEVICE AREA_COB INDEX > 0 %START
            L = DEVICE AREA_COB INDEX
            F = ADDR(DEVICE AREA_COB(LENGTH))
            T = ADDR(DEVICE AREA_COB(0))
            *LDTB_X'18000000'
            *LDB_L 
            *LDA_F
            *CYD_0  
            *LDA_T
            *MV_%L=%DR
         %FINISH
         DEVICE AREA_COB START = 0
         %WHILE DEVICE AREA_CO HEAD # EMPTY %C
            %AND DEVICE AREA_COB INDEX < COB LENGTH %CYCLE
            PARM CELL == PARM TAB(DEVICE AREA_CO HEAD)
!*MC         MOVE(CONTROL MSG LENGTH,ADDR(PARM CELL_P1),ADDR( %C
            DEVICE AREA_COB(DEVICE AREA_COB INDEX)))
            T = ADDR(DEVICE AREA_COB(DEVICE AREA_COB INDEX))
            *LSD_PARM CELL
            *IAD_8
            *LDTB_CONTROL MSG DESCRIPTOR
            *LDA_T
            *MV_%L=%DR
            DEVICE AREA_COB INDEX = DEVICE AREA_COB INDEX+CONTROL MSG LENGTH
            TEMP = PARM CELL_LINK
            RETURN PP CELL(DEVICE AREA_CO HEAD)
            DEVICE AREA_CO HEAD = TEMP
            DEVICE AREA_CO TAIL = EMPTY %IF TEMP = EMPTY
         %REPEAT
         -> MAKE IDLE
      %FINISH
!*
!*
      %IF STREAM NO = CONTROL IN %START;      !CONTROL INPUT TERMINATION
        %IF LENGTH # (LENGTH//CONTROL MSG LENGTH)*CONTROL MSG LENGTH %START
                %IF LENGTH#1 %START;!DUE TO A QUIRK OF THE GPC NOT AN ERROR IF 1
                OP MESS("FE".TOSTRING(DEVICE NO+'0'). %C
                " PARTIAL CONTROL MSG INPUT".TOSTRING(17))
                DUMP TABLE(80,MESS_P3,GPC ENTRY_ENTSIZE)
                %FINISH
                LENGTH = (LENGTH//CONTROL MSG LENGTH)*CONTROL MSG LENGTH
        %FINISH
         %IF MON LEVEL&4 # 0 %START
            DEVICE AREA_BYTES OF CONTROL INPUT = DEVICE AREA_ %C
               BYTES OF CONTROL INPUT+LENGTH
            DEVICE AREA_CONTROL INPUT TRANSFERS = DEVICE AREA_ %C
               CONTROL INPUT TRANSFERS+1
            DEVICE AREA_CONTROL INPUT XBIT SET = DEVICE AREA_ %C
               CONTROL INPUT XBIT SET+1 %IF XBIT SET # 0
         %FINISH
         CIB INDEX = 0
         CMES_SSERV = MESS_DSERV
         %WHILE CIB INDEX < LENGTH %CYCLE
            CONTROL == RECORD(ADDR(DEVICE AREA_CIB(CIB INDEX)))
            TEMP = CONTROL_MESSAGE IDENT
            S IDENT = TEMP&SHORT MASK
            STREAM NO = TEMP>>16
            %IF 0 <= STREAM NO <= MAX STREAM %C
               %AND STREAM INDEX(STREAM NO) # EMPTY %START
                                              !VALID STREAM?
               CMES_P1 = STREAM NO
!*MC            MOVE(CONTROL MSG LENGTH-4,ADDR(CONTROL_P2),ADDR(CMES_P2))
               T = ADDR(CMES_P2)
               *LSD_CONTROL
               *IAD_4
               *LDTB_CONTROL IN MSG DESCRIPTOR
               *LDA_T
               *MV_%L=%DR
               %IF S IDENT = 0 %THEN CMES_DSERV = STREAM TAB(STREAM INDEX( %C
                  STREAM NO))_OWNER %ELSE CMES_DSERV = COMMS COMMAND!S IDENT
               %IF CMES_P2 = -1 %AND BYTEINTEGER(ADDR(CMES_P3))>15 %THEN BYTEINTEGER(ADDR(CMES_P3))= 15
               PON(CMES)
            %FINISH %ELSE %START
               PRINT STRING("Mk1 FE Adaptor: FE".TO STRING(DEVICE NO+'0'). %C
                  " Bad stream number ".STRINT(STREAM NO).SNL)
            %FINISH
            CIB INDEX = CIB INDEX+CONTROL MSG LENGTH
         %REPEAT
         -> MAKE IDLE
      %FINISH
!*
!*
      %IF MON LEVEL&4 # 0 %START
         %IF STREAM NO&1 = 1 %START;          !OUTPUT
            DEVICE AREA_BYTES OF DATA OUTPUT = DEVICE AREA_BYTES OF DATA OUTPUT+ %C
               LENGTH
            DEVICE AREA_DATA OUTPUT TRANSFERS = DEVICE AREA_DATA OUTPUT TRANSFERS+1
            DEVICE AREA_DATA OUTPUT XBIT SET = DEVICE AREA_DATA OUTPUT XBIT SET+1 %C
               %IF XBIT SET # 0
         %FINISH %ELSE %START
            DEVICE AREA_BYTES OF DATA INPUT = DEVICE AREA_BYTES OF DATA INPUT+LENGTH
            DEVICE AREA_DATA INPUT TRANSFERS = DEVICE AREA_DATA INPUT TRANSFERS+1
            DEVICE AREA_DATA INPUT XBIT SET = DEVICE AREA_DATA INPUT XBIT SET+1 %C
               %IF XBIT SET # 0
         %FINISH
      %FINISH
      CMES = 0
      CMES_SSERV = MESS_DSERV
      CMES_DSERV = TRANSFER COMPLETE
      CMES_P1 = STREAM NO
      %IF SSERIES = NO %START
         CMES_P2 = 3 %IF GPC ENTRY_RESP0&LONG BLOCK MASK # 0
                                              !NEXT PAGE REQUIRED AND DONT RECAPTURE
      %FINISH %ELSE %START
         CMES_P2 = 3 %IF DEVICE AREA_TCB1 RESPONSE&LONG BLOCK MASK # 0
!NEXT PAGE REQUIRED AND DONT RECAPTURE
      %FINISH
      CMES_P3 = LENGTH
      %IF SSERIES = NO %START
         TEMP = GPC ENTRY_RESP0&Y MASK;       !SEND HIGH LEVEL CONTROL MESSAGE TO SIGNAL END OF MESSAGE
      %FINISH %ELSE %START
         TEMP = DEVICE AREA_TCB1 RESPONSE&Y MASK;  !SEND HIGH LEVEL CONTROL MESSAGE TO SIGNAL END OF MESSAGE
      %FINISH
      CMES_P2 = CMES_P2!4 %IF TEMP # 0
      PON(CMES)
      -> MAKE IDLE
!*
!*
ABNORMAL TERMINATION:!** ABNORMAL TERMINATION INTERRUPT
      PRINT STRING("Mk1 FE Adaptor: Abterm FE".TO STRING(DEVICE NO+'0')." ".STRINT( %C
         DEVICE AREA_FAILURES).SNL)
      PKMON REC("Mk1 FE Adaptor: ",MESS)
      %IF DEVICE AREA_FAILURES <= MAX FAILURES %START
                                              !TRY AGAIN?
         GPC REQ = 0
         GPC REQ_DSERV = EXECUTE CHAIN
         GPC REQ_SSERV = MK1 FE EXECUTED
         %IF SSERIES = NO %START
            GPC REQ_P1 = ADDR(DEVICE AREA_RCB0)
         %FINISH %ELSE %START
            GPC REQ_P1 = ADDR(DEVICE AREA_TCB0 COMMAND)
         %FINISH
         GPC REQ_P2 = DEVICE AREA_IDENT
         %IF SSERIES = NO %START
            GPC REQ_P3 = X'11';               !PAW FUNCTION<<4!SAW FLAGS
         %FINISH
         DPON(GPC REQ,RETRY DELAY(DEVICE AREA_FAILURES))
                                              !TRY AGAIN IN ? SECONDS
         DEVICE AREA_FAILURES = DEVICE AREA_FAILURES+1
      %FINISH %ELSE %START
DOWN:
         OP MESS("FE".TO STRING('0'+DEVICE NO)." DOWN".TO STRING(17))
                                              !FLASH
         %IF STREAM NO >= DATA STREAM %START; !FAIL TRANSFER IN PROGRESS
            CMES = 0
            CMES_DSERV = TRANSFER COMPLETE
            CMES_SSERV = MESS_DSERV
            CMES_P1 = STREAM NO
            CMES_P2 = 2;                      !DONT RECAPTURE
            PON(CMES)
         %FINISH
         %WHILE DEVICE AREA_HEAD # EMPTY %CYCLE;   !FAIL ALL QUEUED TRANSFERS
            BUFFER == BUFFER TAB(DEVICE AREA_HEAD)
            DEVICE AREA_HEAD = BUFFER_LINK
            DEVICE AREA_TAIL = EMPTY %IF DEVICE AREA_HEAD = EMPTY
            CMES = 0
            CMES_DSERV = TRANSFER COMPLETE
            CMES_SSERV = MESS_DSERV
            CMES_P1 = BUFFER_STREAM NO
            CMES_P2 = 2;                      !DONT RECAPTURE
            PON(CMES)
         %REPEAT
         %CYCLE STREAM NO = 0,1,MAX STREAM
            %IF STREAM INDEX(STREAM NO) # EMPTY %START
               STREAM == STREAM TAB(STREAM INDEX(STREAM NO))
               %IF STREAM_ADAPTOR NO = FE %AND STREAM_DEVICE NO = DEVICE NO %START
!CONNECTED THRU THIS FRONT END
                  CMES = 0
                  CMES_SSERV = MESS_DSERV
                  CMES_P1 = STREAM NO
                  %IF STREAM NO&1 = 0 %AND SUB IDENT(STREAM_STATE) = -1 %START
!INPUT STREAM NOT WAITING ON A REPLY
                     CMES_DSERV = STREAM_OWNER;    !THEN SEND A HIGH LEVEL CONTROL MSG
                     CMES_P2 = STREAM_CURSOR
                     CMES_P3 = X'01590000';   !CONTROL (E O T) Y
                     PON(CMES)
                  %FINISH %ELSE %START;       !WAITING ON A REPLY
                     %IF SUB IDENT(STREAM_STATE) # -1 %START
                        CMES_DSERV = COMMS COMMAND!SUB IDENT(STREAM_STATE)>>16
!SIMULATE ONE
                        CMES_P2 = 1;          !FAILURE
                        PON(CMES)
                     %FINISH
                  %FINISH
               %FINISH
            %FINISH
         %REPEAT
         DEVICE AREA_STREAM = IDLE
         DEVICE AREA_COB START = 0
         DEVICE AREA_COB INDEX = 0
         DEVICE AREA_FAILURES = 0
         GPC REQ = 0
         GPC REQ_DSERV = DEALLOCATE DEVICE
         GPC REQ_SSERV = MK1 FE DEALLOCATED
         GPC REQ_P1 = M'FE0'!DEVICE NO
         PON(GPC REQ)
         DEVICE TO DA AD(DEVICE NO) = NOT ALLOCATED
      %FINISH
      %RETURN
!*
!*
ATTEN:!** ATTENTION INTERRUPT
      %IF MESS_P1&X'1FF00' = X'08000' %START; !EXPECTED ATTN ?
         %IF STREAM NO = IDLE %START
            %IF DEVICE TO DA AD(DEVICE NO) = NOT ALLOCATED %START
!            MESS_DSERV = X'32000B'
               !FLASH OFF
!            PON(MESS)
               OP MESS("FE".TO STRING(DEVICE NO+'0')." UP")
               DEVICE TO DA AD(DEVICE NO) = ADDR(DEVICE AREA)
            %FINISH
MAKE IDLE:
            DEVICE AREA_STREAM = IDLE
            %IF INTERRUPT ANALYSIS FLAGS = ATTENTION %OR XBIT SET # 0 %C
               %OR DEVICE AREA_ATTN SET # 0 %START
                                              !CONTROL INPUT PENDING
               DEVICE AREA_ATTN SET = 0
               START DEVICE(CONTROL IN,CONTROL IN,0,ADDR(DEVICE AREA_CIB(0)), %C
                  CIB LENGTH)
               %RETURN
            %FINISH
!*
!*
            %IF DEVICE AREA_COB INDEX > 0 %START;  !CONTROL OUTPUT PENDING.
               START DEVICE(CONTROL OUT,CONTROL OUT,0,ADDR(DEVICE AREA_COB(0)), %C
                  DEVICE AREA_COB INDEX)
               DEVICE AREA_COB START = DEVICE AREA_COB INDEX
               %RETURN
            %FINISH
!*
!*
            %IF DEVICE AREA_HEAD # EMPTY %START;   !DATA TRANSFER QUEUED
               BUFFER == BUFFER TAB(DEVICE AREA_HEAD)
               DEVICE AREA_HEAD = BUFFER_LINK
               DEVICE AREA_TAIL = EMPTY %IF DEVICE AREA_HEAD = EMPTY
               START DEVICE(BUFFER_STREAM NO,BUFFER_EXTERNAL STREAM NO,BUFFER_ %C
                  REAL ADDRESS,BUFFER_OFFSET,BUFFER_LENGTH)
               %RETURN
            %FINISH
         %FINISH %ELSE DEVICE AREA_ATTN SET = 1
      %FINISH %ELSE %START
   OP MESS("FE".TO STRING(DEVICE NO +'0').%C 
   " Unexpected attention")
         PKMON REC("Mk1 FE Adaptor: ",MESS)
      %FINISH
      %RETURN
!*
!*
ACT(DEALLOCATED):!** OPERATOR CAN NOW INVOKE REALLOCATION FOLLOWING
                 !   A DISCONNECT/RECONNECT SEQUENCE BY GPC
      %RETURN
!*
!*
ACT(ALLOCATE REQUEST):!** OPERATOR REQUEST TO RECOVER FE
      DEVICE NO = MESS_P1
      %IF DEVICE TO DA AD(DEVICE NO) = NOT ALLOCATED %START
         GPC REQ= 0
         GPC REQ_DSERV = ALLOCATE DEVICE
         GPC REQ_SSERV = MK1 FE ALLOCATED
         GPC REQ_P1 = M'FE0'!DEVICE NO
         GPC REQ_P2 = MK1 FE INTERRUPT
         PON(GPC REQ)
      %FINISH %ELSE OPMESS("FE".TOSTRING(DEVICE NO+'0')." Already allocated")
      %RETURN
!*
!*
ACT(ALLOCATED):!** DEVICE ALLOCATED BY GPC
      %IF MESS_P1 = 0 %START;                 !ALLOCATED
         GPC ENTRY == RECORD(MESS_P3)
         DEVICE AREA == RECORD(GPC ENTRY_UA AD)
         DEVICE NO = GPC ENTRY_MNEMONIC&BYTE MASK-'0'
         DEVICE TO DA AD(DEVICE NO) = GPC ENTRY_UA AD
         %IF SSERIES = NO %START
            DEVICE AREA_LST0 = X'02201000';   !LOCAL SEGMENT TABLE ENTRY R2, W2, MAX LEN 1 EPAGE
            DEVICE AREA_LST1 = X'00000000'
            DEVICE AREA_RCB0 = X'00000002';   !RCB FLAGS ETC
            DEVICE AREA_RCB1 = REALISE(ADDR(DEVICE AREA_LST0))
                                              !SEGMENT TABLE (REAL ADDRESS)
            DEVICE AREA_RCB2 = 8;             !LOGIC BLOCK BYTE COUNT
            DEVICE AREA_RCB3 = ADDR(DEVICE AREA_LB0);   !LOGIC BLOCK ADDRESS (VIRTUAL)
            DEVICE AREA_RCB4 = 16;            !ADDRESS LIST BYTE COUNT
            DEVICE AREA_RCB5 = ADDR(DEVICE AREA_AL00)
                                              !ADDRESS LIST ADDRESS (VIRTUAL)
            DEVICE AREA_RCB6 = 0;             !UNUSED
            DEVICE AREA_RCB7 = 0;             !UNUSED
            DEVICE AREA_LB0 = X'84F00500';    !WRITE CONTROL (EXT STREAM NO)<<16!LENGTH
            DEVICE AREA_LB1 = X'00000000';    !READ OR WRITE DATA
            DEVICE AREA_AL00 = 4;             !LENGTH OF EXT STREAM NO AND LENGTH FIELD
            DEVICE AREA_AL01 = ADDR(DEVICE AREA_NAME)
                                              !ADDRESS OF STREAM FIELD
            DEVICE AREA_AL10 = 0;             !LENGTH OF READ / WRITE AREA
            DEVICE AREA_AL11 = 0;             !ADDRESS OF READ / WRITE AREA
         %FINISH %ELSE %START
            COM AREA == RECORD(X'80000000'!48<<18)
            DEVICE AREA_TCB0 COMMAND = X'2F404085';!WRITE CONTROL CHAINED
            DEVICE AREA_TCB0 STE = INTEGER(ADDR(DEVICE AREA)<<1>>19<<3+COM AREA_ %C
               PSTVA+4)!1
            DEVICE AREA_TCB0 LENGTH = 4
            DEVICE AREA_TCB0 ADDRESS = ADDR(DEVICE AREA_NAME)
            DEVICE AREA_TCB0 NEXT TCB = ADDR(DEVICE AREA_TCB1 COMMAND)
            DEVICE AREA_TCB0 RESPONSE = 0
            DEVICE AREA_TCB1 COMMAND = 0
            DEVICE AREA_TCB1 STE = 0
            DEVICE AREA_TCB1 LENGTH = 0
            DEVICE AREA_TCB1 ADDRESS = 0
            DEVICE AREA_TCB1 NEXT TCB = 0
            DEVICE AREA_TCB1 RESPONSE = 0
         %FINISH
         DEVICE AREA_IDENT = MESS_P2
         DEVICE AREA_NAME = 0;                !CONTROL INFORMATION SENT WRITTEN HERE
         DEVICE AREA_STREAM = IDLE
         DEVICE AREA_COB START = 0
         DEVICE AREA_COB INDEX = 0
         DEVICE AREA_HEAD = EMPTY
         DEVICE AREA_TAIL = EMPTY
         %IF MON LEVEL&4 # 0 %START
            DEVICE AREA_MAX QD TRANSFERS = 0
            DEVICE AREA_MAX QD CONTROL MSGS = 0
            DEVICE AREA_TOTAL QD TRANSFERS = 0
            DEVICE AREA_TOTAL QD CONTROL MSGS = 0
            DEVICE AREA_BYTES OF CONTROL INPUT = 0
            DEVICE AREA_BYTES OF CONTROL OUTPUT = 0
            DEVICE AREA_BYTES OF DATA INPUT = 0
            DEVICE AREA_BYTES OF DATA OUTPUT = 0
            DEVICE AREA_CONTROL INPUT TRANSFERS = 0
            DEVICE AREA_CONTROL OUTPUT TRANSFERS = 0
            DEVICE AREA_DATA INPUT TRANSFERS = 0
            DEVICE AREA_DATA OUTPUT TRANSFERS = 0
            DEVICE AREA_OUTPUT UPDATES = 0
            DEVICE AREA_UPDATE OVERWRITES = 0
            DEVICE AREA_CONTROL INPUT XBIT SET = 0
            DEVICE AREA_CONTROL OUTPUT XBIT SET = 0
            DEVICE AREA_DATA INPUT XBIT SET = 0
            DEVICE AREA_DATA OUTPUT XBIT SET = 0
         %FINISH
         DEVICE AREA_CO HEAD = EMPTY
         DEVICE AREA_CO TAIL = EMPTY
         DEVICE AREA_ATTN SET = 0
         DEVICE AREA_DEVICE NO = DEVICE NO
         DEVICE AREA_FAILURES = 0
      %FINISH
      %IF TIMER STARTED = NO %START
         MESS = 0
         MESS_DSERV = ELAPSED INT COMMAND;    !TICK EVERY N SECS
         MESS_SSERV = MK1 FE CLOCK TICK
         MESS_P1 = MK1 FE CLOCK TICK
         MESS_P2 = SECS PER TICK
         %IF MULTI OCP = NO %THEN ELAPSED INT(MESS) %ELSE PON(MESS)
         TIMER STARTED = YES
      %FINISH
      %RETURN
!*
!*
ACT(EXECUTE FAILS):!** EXECUTE CHAIN FAILS
      GPC ENTRY == RECORD(MESS_P3)
      DUMP TABLE(80,MESS_P3,GPC ENTRY_ENT SIZE)
      DEVICE AREA == RECORD(GPC ENTRY_UA AD)
      DEVICE NO = DEVICE AREA_DEVICE NO
      STREAM NO = DEVICE AREA_STREAM
      ->DOWN
!*
!*
!* ABOVE ENTRIES ARE CALLED BY GPC
!*
!* BELOW ENTRIES ARE CALLED BY COMMS CONTROL
!*
!*
ACT(GO AHEAD):!** TRANSFER GO AHEAD
      BUFFER NO = MESS_P2
      BUFFER == BUFFER TAB(BUFFER NO)
      %IF DEVICE AREA_STREAM # IDLE %START;   !QUEUE A TRANSFER
         BUFFER_LINK = EMPTY
         %IF DEVICE AREA_HEAD = EMPTY %THEN DEVICE AREA_HEAD = BUFFER NO %C
            %ELSE BUFFER TAB(DEVICE AREA_TAIL)_LINK = BUFFER NO
         DEVICE AREA_TAIL = BUFFER NO
         %IF MON LEVEL&4 # 0 %START
            DEVICE AREA_TOTAL QD TRANSFERS = DEVICE AREA_TOTAL QD TRANSFERS+1
            L = DEVICE AREA_HEAD
            F = 1
            %WHILE L # DEVICE AREA_TAIL %CYCLE
               F = F+1
               L = BUFFER TAB(L)_LINK
            %REPEAT
            %IF F > DEVICE AREA_MAX QD TRANSFERS %C
               %THEN DEVICE AREA_MAX QD TRANSFERS = F
         %FINISH
      %FINISH %ELSE START DEVICE(STREAM NO,BUFFER_EXTERNAL STREAM NO,BUFFER_ %C
         REAL ADDRESS,BUFFER_OFFSET,BUFFER_LENGTH)
      %RETURN
!*
!*
ACT(SEND CONTROL):!** CONTROL MESSAGE
      MESSAGE IDENT = STREAM_EXTERNAL STREAM NO<<16!MESS_P1>>16
      MESS_P1 = MESSAGE IDENT
      %IF MESSAGE IDENT&SHORT MASK = 0 %C
         %AND MESS_SSERV&SHORT MASK = CONTROL MSG7 REPLY %START
!HIGH LEVEL AND UPDATE MESSAGE
         DEVICE AREA_OUTPUT UPDATES = DEVICE AREA_OUTPUT UPDATES+1 %IF MON LEVEL&4 # 0
         SEARCH INDEX = DEVICE AREA_COB START
         %CYCLE
            CONTROL ADDR = ADDR(DEVICE AREA_COB(0))+SEARCH INDEX
            %IF SEARCH INDEX >= DEVICE AREA_COB INDEX %START
                                              !END OF WHATS IN BUFFER?
               %IF DEVICE AREA_COB INDEX >= COB LENGTH %START
                                              !COB BUFFER FULL
                  TEMP = DEVICE AREA_CO HEAD
                  %WHILE TEMP # EMPTY %CYCLE; !SCAN DOWN EXISTING QUEUE
                     PARM CELL == PARM TAB(TEMP)
                     %IF MESSAGE IDENT = PARM CELL_P1 %START
                                              !UPDATE?
                        DEVICE AREA_UPDATE OVERWRITES = DEVICE AREA_ %C
                           UPDATE OVERWRITES+1 %IF MON LEVEL&4 # 0
                        CONTROL ADDR = ADDR(PARM CELL_P1)
                        -> OUT
                     %FINISH %ELSE TEMP = PARM CELL_LINK
                  %REPEAT
                  QUEUE
OUT:
               %FINISH %ELSE DEVICE AREA_COB INDEX = DEVICE AREA_COB INDEX+ %C
                  CONTROL MSG LENGTH
               %EXIT
            %FINISH
            %IF INTEGER(CONTROL ADDR) = MESSAGE IDENT %START
               DEVICE AREA_UPDATE OVERWRITES = DEVICE AREA_UPDATE OVERWRITES+1 %C
                  %IF MON LEVEL&4 # 0
               %EXIT
            %FINISH
            SEARCH INDEX = SEARCH INDEX+CONTROL MSG LENGTH
         %REPEAT
         CMES = 0
         CMES_DSERV = MESS_SSERV
         CMES_SSERV = MESS_DSERV
         CMES_P1 = STREAM NO
         %IF MULTI OCP = NO %THEN COMMS CONTROL(CMES) %ELSE PON(CMES)
                                              !REPLY MESSAGE UPDATED
      %FINISH %ELSE %START
         %IF DEVICE AREA_COB INDEX < COB LENGTH %START
                                              !COB BUFFER FULL?
            CONTROL ADDR = ADDR(DEVICE AREA_COB(DEVICE AREA_COB INDEX))
            DEVICE AREA_COB INDEX = DEVICE AREA_COB INDEX+CONTROL MSG LENGTH
         %FINISH %ELSE QUEUE
      %FINISH
!*MCMOVE(CONTROL MSG LENGTH,ADDR(MESS_P1),CONTROL ADDR)
      *LSD_MESS
      *IAD_8
      *LDTB_CONTROL MSG DESCRIPTOR
      *LDA_CONTROL ADDR
      *MV_%L=%DR
      START DEVICE(CONTROL OUT,CONTROL OUT,0,ADDR(DEVICE AREA_COB(0)),DEVICE AREA %C
         _COB INDEX) %AND DEVICE AREA_COB START = DEVICE AREA_COB INDEX %C
         %IF DEVICE AREA_STREAM = IDLE
      %RETURN
!*
!*
ACT(CLOCK TICK):  !SEND CONTROL MSG EVERY N SECS TO CONFIRM FES ARE UP
      %CYCLE DEVICE NO = 0,1,9
         %UNLESS DEVICE TO DA AD(DEVICE NO) = NOT ALLOCATED %START
            DEVICE AREA == RECORD(DEVICE TO DA AD(DEVICE NO))
            %IF DEVICE AREA_COB INDEX = 0 %AND DEVICE AREA_STREAM = IDLE %START
               MESS = 0
               MESS_P1 = -1;                  !DUMMY STREAM NO FOR FE TO IGNORE
               CONTROL ADDR = ADDR(DEVICE AREA_COB(0))
               DEVICE AREA_COB INDEX = CONTROL MSG LENGTH
!*MCMOVE(CONTROL MSG LENGTH,ADDR(MESS_P1),CONTROL ADDR)
               *LSD_MESS
               *IAD_8
               *LDTB_CONTROL MSG DESCRIPTOR
               *LDA_CONTROL ADDR
               *MV_%L=%DR
               START DEVICE(CONTROL OUT,CONTROL OUT,0,ADDR(DEVICE AREA_COB(0)), %C
                  CONTROL MSG LENGTH)
               DEVICE AREA_COB START = CONTROL MSG LENGTH
            %FINISH
         %FINISH
      %REPEAT
   %END;                                      !OF ROUTINE MK1 FE ADAPTOR
!*
!*
!*
!*
!*
!*
!*
!*

   %EXTERNALROUTINE LP ADAPTOR(%RECORDNAME MESS)
!***********************************************************************
!*    SERVICE 51 (X33)                                                 *
!*    DRIVES THE LINE PRINTER TO COMMS CONTROLLER SPEC                 *
!*    ACCEPTS THE DATA AS ISO, EBCIDIC OR BINARY. ISO IS TRANSLATED TO *
!*    EBCIDIC USING THE MASTER TRANSLATE TABLES. EBCIDIC DATA IS       *
!*    TRANSLATED TO A SUBSET OF EBCIDIC DEPENDING ON THE REP IN THE    *
!*    PRINTER TO AVOID NON PRINTING CHARACTERS FOULING THINGS UP.      *
!*    BINARY DATA IS LEFT ALONE.                                       *
!*    CAN MANAGE UP TO 10 PRINTERS (LP0-LP9) USING A 128 BYTE AREA FOR *
!*    EACH WHICH IS PROVIDED ON ALLOCATION                             *
!***********************************************************************
   %RECORDSPEC MESS(PE)
   %RECORDFORMAT GPCF(%INTEGER SER, GPTSM, PROPADDR, TICKS, CAA, GRCB AD, LBA,  %C
         ALA, STATE, RESP0, RESP1, SENSE1, SENSE2, SENSE3, SENSE4, REPSNO, BASE,  %C
         ID, DLVN, MNEMONIC, ENTSIZE, PAW, USAW0, URCB AD, SENSE AD, LOGMASK,  %C
         TRT AD, UA SIZE, UA AD, TIMEOUT, PROPS0, PROPS1)
      %IF SSERIES = NO %START
      %RECORDFORMAT DAF(%INTEGER LST0, LST1, RCB0, RCB1, RCB2, RCB3, RCB4, RCB5,  %C
            RCB6, RCB7, LB0, LB1, LB2, AL00, AL01, STREAM NO, MODE, BUFFER NO,  %C
            TRT AD, IDENT)
      %FINISH %ELSE %START
      %RECORDFORMAT DAF(%HALFINTEGER STREAM, BUFFER NO, %C
      %INTEGER TRT AD,ADDR TCB, %BYTEINTEGER MODE,IDENT,DEVICE NO,S, %C
      %INTEGER TCB0 COMMAND, TCB0 STE, TCB0 LENGTH, %C
      TCB0 ADDRESS, TCB0 NEXT TCB, TCB0 RESPONSE, %C
      %INTEGERARRAY TCB0 PREAMBLE, TCB0 POSTAMBLE(0 : 3), %C
      %INTEGER TCB1 COMMAND, TCB1 STE, TCB1 LENGTH, TCB1 ADDRESS, %C
    TCB1 NEXT TCB, TCB1 RESPONSE, %INTEGERARRAY TCB1 PREAMBLE(0 : 3) %C
      , TCB1 POSTAMBLE(0 : 2), %BYTEINTEGERARRAY CHARS(0 : 131))
      %FINISH
   %ROUTINESPEC REPLY TO COMMS CONTROL(%INTEGER ACT, STREAM NO, FLAG)
   %ROUTINESPEC GET NEXT BUFFER
   %OWNINTEGERARRAY DEVICE TO DA AD(0 : 9) =           %C
      NOT ALLOCATED(10)
   %RECORD REQ(PE)
   %RECORD REP(PE)
   %RECORDNAME GPC ENTRY(GPCF)
   %RECORDNAME DEVICE AREA(DAF)
   %RECORDNAME STREAM(SR)
   %RECORDNAME BUFFER(BR)
   %INTEGER STREAM NO, BUFFER NO, STATE, START
   %INTEGER BUFFER END, SENT, DEVICE NO, ACT
   %INTEGER I, CHAR, LINE, LONG LINE, LINE BEGIN, TRT AD, INTERRUPT ANALYSIS FLAGS
%SWITCH DACT(0 : SEND CONTROL)
%SWITCH STATES(0 : ACTIVE)
      %IF MON LEVEL&2 # 0 %AND (KMON>>LP SERVICE)&1 # 0 %START
         PKMON REC("LP Adaptor: ",MESS)
      %FINISH
      ACT = MESS_DSERV&SHORT MASK
      %IF ACT = INTERRUPT %START;             !FROM GPC MESS_P3=GPC AREA ADRRESS
         INTERRUPT ANALYSIS FLAGS = MESS_P1>>20&15
         GPC ENTRY == RECORD(MESS_P3)
         DEVICE AREA == RECORD(GPC ENTRY_UA AD)
         STREAM NO = DEVICE AREA_STREAM NO
      %FINISH %ELSE %START
         %IF ACT >= GO AHEAD %START;          !FROM COMMS MESS_P1=STRM&SHORT MASK
            STREAM NO = MESS_P1&SHORT MASK
            STREAM == STREAM TAB(STREAM INDEX(STREAM NO))
            DEVICE NO = STREAM_DEVICE NO
         %FINISH
      %FINISH
      -> DACT(ACT)
!*
!*
DACT(INTERRUPT):!** DEVICE INTERRUPT (NORMAL, ABNORMAL, ATTENTION)
      %UNLESS INTERRUPT ANALYSIS FLAGS = ATTENTION %START
         %IF SSERIES = NO %START
            I = (DEVICE AREA_RCB3-ADDR(DEVICE AREA_LB0))>>2+GPC ENTRY_RESP0&BYTE MASK
         %FINISH %ELSE %START
            %IF MESS_P5 = ADDR(DEVICE AREA_TCB1 COMMAND) %THEN I = 2 %ELSE I = 0
         %FINISH
         %IF INTERRUPT ANALYSIS FLAGS&NORMAL TERMINATION # NORMAL TERMINATION %C
            %AND MESS_P4&X'FF' = NORMAL  TERMINATION<<4 %C
            %AND INTEGER(GPC ENTRY_SENSE AD) = X'20000004' %C
            %THEN LONG LINE = YES %ELSE LONG LINE = NO
         %IF I = 2 %AND MESS_P2 # -1 %START;  !ON THE WRITE DATA AND NOT A TIME OUT
            %IF SSERIES = NO %START
               SENT = DEVICE AREA_AL00-GPC ENTRY_RESP1&SHORT MASK
            %FINISH %ELSE %START
               SENT = DEVICE AREA_TCB1 LENGTH-DEVICE AREA_TCB1 RESPONSE&SHORT MASK
            %FINISH
            %IF LONG LINE = NO %START;        !WAS IT A LONG LINE THEN NO NEED TO SCAN BACK
               %IF SENT > 0 %START
                  BUFFER == BUFFER TAB(DEVICE AREA_BUFFER NO)
                  START = VIRTUAL+BUFFER_REAL ADDRESS+BUFFER_OFFSET
                  BUFFER END = START+SENT-1
                  %IF SENT > 133 %THEN LINE BEGIN = BUFFER END-132 %C
                     %ELSE LINE BEGIN = START
                  LINE = BUFFER END - LINE BEGIN + 1;   !IN CASE PRECEEDING LINE WAS LONG
                  %CYCLE I = BUFFER END,-1,LINE BEGIN
                     CHAR = BYTE INTEGER(I)
                     %IF CHAR = EBC NL %OR CHAR = EBC CR %OR CHAR = EBC FF %THEN LINE = BUFFER END - I %AND %EXIT
                  %REPEAT
!SET UP CHAIN TO MERGE THE END OF THE LINE ONTO THE HEAD
                  %IF LINE = 0 %START
                     %IF SSERIES = NO %START
                        DEVICE AREA_RCB3 = ADDR(DEVICE AREA_LB2)
                        DEVICE AREA_LB2 = X'80700300'
                     %FINISH %ELSE %START
                        DEVICE AREA_ADDR TCB = ADDR(DEVICE AREA_TCB1 COMMAND)
                     %FINISH
                  %FINISH %ELSE %START
                     %IF SSERIES = NO %START
                        DEVICE AREA_RCB3 = ADDR(DEVICE AREA_LB0)
                        DEVICE AREA_LB1 = X'8A700000'!LINE
                        DEVICE AREA_LB2 = X'80700000'
                     %FINISH %ELSE %START
                        DEVICE AREA_ADDR TCB = ADDR(DEVICE AREA_TCB0 COMMAND)
                        DEVICE AREA_TCB0 LENGTH = LINE
                        DEVICE AREA_CHARS(0) = EBC SP
                     %FINISH
                  %FINISH
               %FINISH
            %FINISH %ELSE %START;             !TAG ON A NEW LINE
               %IF SSERIES = NO %START
                  DEVICE AREA_RCB3 = ADDR(DEVICE AREA_LB1)
                  DEVICE AREA_LB1 = X'8A700300'!EBC NL
                  DEVICE AREA_LB2 = X'80700000'
               %FINISH %ELSE %START
                  DEVICE AREA_ADDR TCB = ADDR(DEVICE AREA_TCB0 COMMAND)
                  DEVICE AREA_TCB0 LENGTH = 1
                  DEVICE AREA_CHARS(0) = EBC NL
               %FINISH
               SENT = SENT-1;                 !SEND THE CHAR THAT CAUSED THE LONG LINE AGAIN
            %FINISH
         %FINISH %ELSE SENT = 0
!SEND REPLY TO COMMS CONTROL
         REQ = 0
         REQ_SSERV = MESS_DSERV
         REQ_DSERV = TRANSFER COMPLETE
         REQ_P1 = STREAM NO
         REQ_P2 = 3;                          !GET NEXT PAGE AND DONT RECAPTURE
         %IF INTERRUPT ANALYSIS FLAGS&NORMAL TERMINATION = 0 %AND LONG LINE = NO %START
            REQ_P2 = REQ_P2!4;                !INFORM USER
            REQ_P5 = INTEGER(GPC ENTRY_SENSE AD)
            REQ_P6 = INTEGER(GPC ENTRY_SENSE AD+4)
         %FINISH
         REQ_P3 = SENT
         PON(REQ)
         GET NEXT BUFFER %IF LONG LINE = YES %C
            %AND INTERRUPT ANALYSIS FLAGS&NORMAL TERMINATION = 0
      %FINISH %ELSE %START
!** ATTENTION INTERRUPT
         GET NEXT BUFFER %IF MESS_P1&AUTO = AUTO
      %FINISH
      %RETURN
!*
!*
DACT(EXECUTE FAILS):!** EXECUTE CHAIN FAILS
      GPC ENTRY == RECORD(MESS_P3)
      DUMP TABLE(81,MESS_P3,GPC ENTRY_ENT SIZE)
      %RETURN
!*
!*
DACT(ALLOCATED):!** DEVICE ALLOCATED BY GPC
      %IF MESS_P1 = 0 %START;                 !ALLOCATED
         GPC ENTRY == RECORD(MESS_P3)
         DEVICE AREA == RECORD(GPC ENTRY_UA AD)
         DEVICE NO = GPC ENTRY_MNEMONIC&BYTE MASK-'0'
         DEVICE TO DA AD(DEVICE NO) = GPC ENTRY_UA AD
         DEVICE AREA_IDENT = MESS_P2;         !SAVE GPC IDENTIFIER
         DEVICE AREA_MODE = 0;                !ISO BY DEFAULT
         DEVICE AREA_STREAM NO = MESS_P5
         DEVICE AREA_BUFFER NO = 0
         DEVICE AREA_TRT AD = GPC ENTRY_TRT AD
         %IF SSERIES = NO %START
            DEVICE AREA_LST0 = X'00F01000';   !LOCAL SEGMENT TABLE ENTRY R15 MAX LEN 1 EPAGE
            DEVICE AREA_LST1 = 0
            DEVICE AREA_RCB0 = X'0000000F';   !RCB FLAGS ETC
            DEVICE AREA_RCB1 = REALISE(ADDR(DEVICE AREA_LST0))
            DEVICE AREA_RCB2 = 12;            !LOGIC BLOCK BYTE COUNT
            DEVICE AREA_RCB3 = ADDR(DEVICE AREA_LB2);   !LOGIC BLOCK ADDRESS 
            DEVICE AREA_RCB4 = 8;             !ADDRESS_LIST BYTE COUNT
            DEVICE AREA_RCB5 = ADDR(DEVICE AREA_AL00)
                                              !ADDRESS LIST ADDRESS
            DEVICE AREA_RCB6 = 0;             !UNUSED
            DEVICE AREA_RCB7 = 0;             !UNUSED
            DEVICE AREA_LB0 = X'8A700300'!EBC MS
            DEVICE AREA_LB1 = X'8A700000';    !NUMBER OF SPACES
            DEVICE AREA_LB2 = X'80700300';    !WRITE BUFFER
            DEVICE AREA_AL00 = 0;             !BUFFER LENGTH
            DEVICE AREA_AL01 = 0;             !BUFFER OFFSET
         %FINISH %ELSE %START
            DEVICE AREA_TCB0 COMMAND = X'24404083'
            DEVICE AREA_TCB0 STE = INTEGER(ADDR(DEVICE AREA)<<1>>19<<3+COM AREA_ %C
               PSTVA+4)!1
            DEVICE AREA_TCB0 LENGTH = 0
            DEVICE AREA_TCB0 ADDRESS = ADDR(DEVICE AREA_CHARS(0))
            DEVICE AREA_TCB0 NEXT TCB = ADDR(DEVICE AREA_TCB1 COMMAND)
            DEVICE AREA_TCB1 COMMAND = X'24004083'
            DEVICE AREA_TCB1 STE = 0
            DEVICE AREA_TCB1 LENGTH = 0
            DEVICE AREA_TCB1 ADDRESS = 0
            DEVICE AREA_TCB1 NEXT TCB = 0
            DEVICE AREA_TCB0 RESPONSE = 0
            DEVICE AREA_ADDR TCB = ADDR(DEVICE AREA_TCB1 COMMAND)
            %CYCLE I = 0,1,131
               DEVICE AREA_CHARS(I) = EBC SP
            %REPEAT
         %FINISH
      %FINISH
      REPLY TO COMMS CONTROL(MESS_P4,MESS_P5,MESS_P1)
      %RETURN
!*
!*
DACT(DEALLOCATED):!** DEVICE DEALLOCATED BY GPC
      DEVICE NO = MESS_P6
      DEVICE TO DA AD(DEVICE NO) = NOT ALLOCATED
      REPLY TO COMMS CONTROL(MESS_P4,MESS_P5,MESS_P1)
      %RETURN
!*
!*
DACT(GO AHEAD):!** TRANSFER GO AHEAD
      BUFFER NO = MESS_P2
      BUFFER == BUFFER TAB(BUFFER NO)
      DEVICE AREA == RECORD(DEVICE TO DA AD(DEVICE NO))
      DEVICE AREA_BUFFER NO = BUFFER NO
      START = VIRTUAL+BUFFER_REAL ADDRESS+BUFFER_OFFSET
      I TO E(START,BUFFER_LENGTH) %IF DEVICE AREA_MODE = ISO
      %IF DEVICE AREA_MODE # BINARY %START;   !TRANSLATE IT TO THE EBCIDIC CHARACTERS IN THE CURRENT REP
         I = BUFFER_LENGTH
         TRT AD = DEVICE AREA_TRTAD
         %IF BYTEINTEGER(START) = EBC VP %AND DEVICE AREA_MODE = EBCIDIC %C
            %AND I > 1 %START
!DO NOT TRANSLATE VP AND QUALIFIER
            I = I-2
            START = START+2
         %FINISH
         *LB_I ;  *JAT_14,<L99>
         *LDTB_X'18000000';  *LDB_%B;  *LDA_START
         *LSS_TRT AD;  *LUH_X'18000100'
         *TTR_%L=%DR
L99:
      %FINISH
!AND INITIATE THE  TRANSFER
      %IF SSERIES = NO %START
         DEVICE AREA_LST1 = X'80000001'!BUFFER_REAL ADDRESS
         DEVICE AREA_AL00 = BUFFER_LENGTH
         DEVICE AREA_AL01 = BUFFER_OFFSET
      %FINISH %ELSE %START
         DEVICE AREA_TCB0 RESPONSE = 0
         DEVICE AREA_TCB1 STE = BUFFER_REAL ADDRESS!1
         DEVICE AREA_TCB1 LENGTH = BUFFER_LENGTH
         DEVICE AREA_TCB1 ADDRESS = BUFFER_OFFSET
         DEVICE AREA_TCB1 RESPONSE = 0
      %FINISH
      REQ = 0
      REQ_DSERV = EXECUTE CHAIN
      REQ_SSERV = LP EXECUTED
      %IF SSERIES = NO %START
         REQ_P1 = ADDR(DEVICE AREA_RCB0)
      %FINISH %ELSE %START
         REQ_P1 = DEVICE AREA_ADDR TCB
      %FINISH
      REQ_P2 = DEVICE AREA_IDENT
      %IF SSERIES = NO %START
         REQ_P3 = 1<<4!1;                     !(PAW FUNCTION)<<4!SAW FLAGS
      %FINISH
      PON(REQ)
      %RETURN
!*
!*
DACT(SEND CONTROL):!** CONTROL MESSAGE
      STATE = MESS_P2>>24
      -> STATES(STATE)
!*
!*
STATES(CONNECTING):!** ALLOCATE DEVICE
      REQ = 0
      REQ_DSERV = ALLOCATE DEVICE
      REQ_SSERV = LP ALLOCATED
      REQ_P1 = M'LP0'!DEVICE NO
      REQ_P2 = LP INTERRUPT
      REQ_P4 = MESS_P1>>16
      REQ_P5 = STREAM NO
      PON(REQ)
      %RETURN
!*
!*
STATES(ENABLING):!** REQUEST FIRST PAGE
      REPLY TO COMMS CONTROL(MESS_P1>>16,STREAM NO,0)
      DEVICE AREA == RECORD(DEVICE TO DA AD(DEVICE NO))
      DEVICE AREA_MODE = MESS_P2>>20&3;       !ISO EBCIDIC OR BINARY
      %IF MESS_P2>>16&3 = SEQUENTIAL %START;  !INSERT A FORM FEED IF NOT A SEQUENTIAL CONTINUATION
         %IF SSERIES = NO %START
            DEVICE AREA_RCB3 = ADDR(DEVICE AREA_LB1)
            DEVICE AREA_LB1 = X'8A700300'!EBC FF
         %FINISH %ELSE %START
            DEVICE AREA_ADDR TCB = ADDR(DEVICE AREA_TCB0 COMMAND)
            DEVICE AREA_TCB0 LENGTH = 1
            DEVICE AREA_CHARS(0) = EBC FF
         %FINISH
      %FINISH
      GET NEXT BUFFER
      %RETURN
!*
!*
STATES(DISCONNECTING):!** DEALLOCATE DEVICE
      REQ = 0
      REQ_DSERV = DEALLOCATE DEVICE
      REQ_SSERV = LP DEALLOCATED
      REQ_P1 = M'LP0'!DEVICE NO
      REQ_P4 = MESS_P1>>16
      REQ_P5 = STREAM NO
      REQ_P6 = DEVICE NO
      PON(REQ)
      %RETURN
!*
!*
STATES(SUSPENDING):
STATES(ABORTING):
      REPLY TO COMMS CONTROL(MESS_P1>>16,STREAM NO,0)
                                              !ALWAYS REPLY
      %RETURN
!*
!*

      %ROUTINE REPLY TO COMMS CONTROL(%INTEGER ACT, STREAM NO, FLAG)
         REP = 0
         REP_DSERV = COMMS COMMAND!ACT
         REP_SSERV = MESS_DSERV
         REP_P1 = STREAM NO
         REP_P2 = FLAG
         PON(REP)
      %END;                                   !OF ROUTINE REPLY TO COMMS CONTROL
!*
!*

      %ROUTINE GET NEXT BUFFER
         REQ = 0
         REQ_DSERV = REQUEST TRANSFER
         REQ_SSERV = LP COMMAND
         REQ_P1 = STREAM NO
         PON(REQ)
      %END;                                   !OF ROUTINE GET NEXT BUFFER
!*
!*
   %END;                                      !OF ROUTINE LP ADAPTOR
!*
!*
!*
!*
!*
!*
!*
!*

   %EXTERNALROUTINE CR ADAPTOR(%RECORDNAME MESS)
!***********************************************************************
!*    SERVICE 52 (X34)                                                 *
!*    DRIVES THE CARD READER TO COMMS CONTROLLER SPEC WITH SOME        *
!*    NASTY FIDDLES WHEN A CARD WONT FIT INTO REMAINS OF AN EPAGE      *
!*    CAN MANAGE UP TO 10 READERS (CR0-CR9) USING A 512 BYTE AREA FOR  *
!*    EACH WHICH IS PROVIDED ON ALLOCATION                             *
!***********************************************************************
   %RECORDSPEC MESS(PE)
   %INTEGER I, J, STREAM NO, DEVICE NO, STATE, SENT, ACT, BUFFER NO,  %C
         INTERRUPT ANALYSIS FLAGS, START
   %RECORDFORMAT GPCF(%INTEGER SER, GPTSM, PROPADDR, TICKS, CAA, GRCB AD, LBA,  %C
         ALA, STATE, RESP0, RESP1, SENSE1, SENSE2, SENSE3, SENSE4, REPSNO, BASE,  %C
         ID, DLVN, MNEMONIC, ENTSIZE, PAW, USAW0, URCB AD, SENSE AD, LOGMASK,  %C
         TRT AD, UA SIZE, UA AD, TIMEOUT, PROPS0, PROPS1)
   %RECORDFORMAT DAF(%INTEGER LST0, LST1, RCB0, RCB1, RCB2, RCB3, RCB4, RCB5,  %C
         RCB6, RCB7, STREAM NO, MODE, GOAH AD, GOAH LEN, CURTRLEN, BUFFER NO,  %C
         IDENT, BLOCKED, %INTEGERARRAY LBE(0 : 35), ALE(0 : 71))
   %OWNINTEGERARRAY DEVICE TO DA AD(0 : 9) =        %C
      NOT ALLOCATED(10)
   %ROUTINESPEC TRANSLATE AND SHUFFLE(%INTEGER ADDRESS, %INTEGERNAME LEN)
   %ROUTINESPEC REPLY TO COMMS CONTROL(%INTEGER ACT, STREAM NO, FLAG)
   %ROUTINESPEC GET NEXT BUFFER
   %RECORDNAME GPC ENTRY(GPCF)
   %RECORDNAME DEVICE AREA(DAF)
   %RECORDNAME STREAM(SR)
   %RECORDNAME BUFFER(BR)
   %RECORD REQ, REP(PE)
%CONSTINTEGERARRAY CARD BYTES(ISO : BINARY) = %C
80, 80, 160
%CONSTINTEGERARRAY CARD SIZE( ISO : BINARY) = %C
81, 80, 160
%SWITCH DACT(0 : SEND CONTROL)
%SWITCH STATES(0 : ACTIVE)
      %IF MON LEVEL&2 # 0 %AND (KMON>>CR SERVICE)&1 # 0 %START
         PKMON REC("CR Adaptor: ",MESS)
      %FINISH
      ACT = MESS_DSERV&SHORT MASK
      %IF ACT = INTERRUPT %START;             !FROM GPC MESS_P3=GPC AREA ADRRESS
         INTERRUPT ANALYSIS FLAGS = MESS_P1>>20&15
         GPC ENTRY == RECORD(MESS_P3)
         DEVICE AREA == RECORD(GPC ENTRY_UA AD)
         STREAM NO = DEVICE AREA_STREAM NO
      %FINISH %ELSE %START
         %IF ACT >= GO AHEAD %START;          !FROM COMMS MESS_P1=STRM&SHORT MASK
            STREAM NO = MESS_P1&SHORT MASK
            STREAM == STREAM TAB(STREAM INDEX(STREAM NO))
            DEVICE NO = STREAM_DEVICE NO
         %FINISH
      %FINISH
      -> DACT(ACT)
!*
!*
DACT(INTERRUPT):!** DEVICE INTERRUPT (NORMAL, ABNORMAL, ATTENTION)
      %UNLESS INTERRUPT ANALYSIS FLAGS = ATTENTION %START
         BUFFER == BUFFER TAB(DEVICE AREA_BUFFER NO)
         START = VIRTUAL+BUFFER_REAL ADDRESS+DEVICE AREA_GOAH AD
         %IF INTERRUPT ANALYSIS FLAGS&NORMAL TERMINATION = NORMAL TERMINATION %START
            TRANSLATE AND SHUFFLE(START,DEVICE AREA_CUR TR LEN) %C
               %IF DEVICE AREA_MODE = ISO
            DEVICE AREA_GOAH LEN = DEVICE AREA_GOAH LEN-DEVICE AREA_CURTRLEN
            DEVICE AREA_GOAH AD = DEVICE AREA_GOAH AD+DEVICE AREA_CURTRLEN
            -> READ;                          !READ MORE IF ROOM
         %FINISH %ELSE %START
            I = GPC ENTRY_RESP0&X'FF';        !FAILING LBE ENTRY
            SENT = CARD SIZE(DEVICE AREA_MODE)*I;  !BYTES READ OK
            TRANSLATE AND SHUFFLE(START,SENT) %C
               %IF SENT > 0 %AND DEVICE AREA_MODE = ISO
            DEVICE AREA_GOAH LEN = DEVICE AREA_GOAH LEN-SENT
!TELL COMMS CONTROL BY A CALL TO UPDATE CURSOR
            REQ = 0
            REQ_DSERV = TRANSFER COMPLETE
            REQ_SSERV = MESS_DSERV
            REQ_P1 = STREAM NO
            REQ_P2 = 2;                       !NEXT PAGE NOT REQD AND DONT RECAPTURE
            %IF SENT > 0 %START
               REQ_P2 = REQ_P2!4
               REQ_P5 = INTEGER(GPC ENTRY_SENSE AD)
               REQ_P6 = INTEGER(GPC ENTRY_SENSE AD+4)
               DEVICE AREA_BLOCKED = 1
            %FINISH
            REQ_P3 = BUFFER_LENGTH-DEVICE AREA_GOAH LEN
            PON(REQ)
         %FINISH
      %FINISH %ELSE %START
!** ATTENTION INTERRUPT
         GET NEXT BUFFER %IF MESS_P1&AUTO = AUTO %AND DEVICE AREA_BLOCKED = 0
      %FINISH
      %RETURN
!*
!*
DACT(EXECUTE FAILS):!** EXECUTE CHAIN FAILS
      GPC ENTRY == RECORD(MESS_P3)
      DUMP TABLE(82,MESS_P3,GPC ENTRY_ENT SIZE)
      %RETURN
!*
!*
DACT(ALLOCATED):!** DEVICE ALLOCATED BY GPC
      %IF MESS_P1 = 0 %START;                 !ALLOCATED
         GPC ENTRY == RECORD(MESS_P3)
         DEVICE AREA == RECORD(GPC ENTRY_UA AD)
         DEVICE NO = GPC ENTRY_MNEMONIC&BYTE MASK-'0'
         DEVICE TO DA AD(DEVICE NO) = GPC ENTRY_UA AD
         DEVICE AREA_IDENT = MESS_P2;         !SAVE GPC IDENTIFIER
         DEVICE AREA_STREAM NO = MESS_P5
         DEVICE AREA_MODE = 0;                !READ IN NON BINARY MODE
         DEVICE AREA_BLOCKED = 0
         DEVICE AREA_GOAH LEN = 0
         DEVICE AREA_GOAH AD = 0
         DEVICE AREA_CURTR LEN = 0
         DEVICE AREA_BUFFER NO = 0
         DEVICE AREA_LST0 = X'0FF01000'
         DEVICE AREA_LST1 = X'00000000'
         DEVICE AREA_RCB0 = X'0000800F'
         DEVICE AREA_RCB1 = REALISE(ADDR(DEVICE AREA_LST0))
         DEVICE AREA_RCB2 = 144;              !BYTES OF LOGIC BLOCK
         DEVICE AREA_RCB3 = ADDR(DEVICE AREA_LBE(0))
         DEVICE AREA_RCB4 = 288;              !BYTES OF ADDRESS LIST
         DEVICE AREA_RCB5 = ADDR(DEVICE AREA_ALE(0))
         DEVICE AREA_RCB6 = X'FC01';          !SET NR MODE
         DEVICE AREA_RCB7 = 0
         %CYCLE I = 0,2,70
            DEVICE AREA_ALE(I) = X'58000050'; !80 BYTE ENTRY
         %REPEAT
      %FINISH
      REPLY TO COMMS CONTROL(MESS_P4,MESS_P5,MESS_P1)
      %RETURN
!*
!*
DACT(DEALLOCATED):!** DEVICE DEALLOCATED BY GPC
      DEVICE NO = MESS_P6
      DEVICE TO DA AD(DEVICE NO) = NOT ALLOCATED
      REPLY TO COMMS CONTROL(MESS_P4,MESS_P5,MESS_P1)
      %RETURN
!*
!*
DACT(GO AHEAD):!** TRANSFER GO AHEAD
!IE COMMS HAS PAGED IN BUFFER
      BUFFER NO = MESS_P2
      BUFFER == BUFFER TAB(BUFFER NO)
      DEVICE AREA == RECORD(DEVICE TO DA AD(DEVICE NO))
      DEVICE AREA_LST1 = X'80000001'!BUFFER_REAL ADDRESS
      DEVICE AREA_GOAH LEN = BUFFER_LENGTH
      DEVICE AREA_GOAH AD = BUFFER_OFFSET
      DEVICE AREA_BUFFER NO = BUFFER NO
READ:                                         !TRY TO READ CARDS
      %IF DEVICE AREA_GOAH LEN < CARD SIZE(DEVICE AREA_MODE) %START
!NOT ROOM FOR EVEN 1 CARD
         %IF DEVICE AREA_GOAH LEN > 0 %START
            J = VIRTUAL+BUFFER_REAL ADDRESS+DEVICE AREA_GOAH AD
            %CYCLE I = 0,1,DEVICE AREA_GOAH LEN-1
               BYTE INTEGER(I+J) = 0;         !FILL PARTCARD WITH NULLS
            %REPEAT
         %FINISH
         REQ = 0
         REQ_DSERV = TRANSFER COMPLETE
         REQ_SSERV = CR COMMAND
         REQ_P1 = STREAM NO
         REQ_P2 = 3;                          !PLEASE PROVIDE NEXT PAGE AND DONT RECAPTURE THIS ONE
         REQ_P3 = BUFFER_LENGTH
         PON(REQ)
         %RETURN
      %FINISH
!THERE IS ROOM FOR AT LEAST ONE CARD. SET UP CHAIN
      I = 0
      %CYCLE
         %EXIT %IF I > 35 %OR DEVICE AREA_GOAH LEN-CARD SIZE(DEVICE AREA_MODE)*(I+1) <  %C
            0
         DEVICE AREA_LBE(I) = X'04400200'+2*I;!CHAIN IGNRE LONGBLK&READ
         DEVICE AREA_ALE(2*I) = X'58000000'!CARD BYTES(DEVICE AREA_MODE)
         DEVICE AREA_ALE(2*I+1) = DEVICE AREA_GOAH AD+CARD SIZE(DEVICE AREA_MODE)*I
         I = I+1
      %REPEAT
      DEVICE AREA_LBE(I-1) = DEVICE AREA_LBE(I-1)&X'F3FFFFFF'
!DECHAIN
      DEVICE AREA_CUR TR LEN = CARD SIZE(DEVICE AREA_MODE)*I
      %IF DEVICE AREA_MODE # BINARY %THEN DEVICE AREA_RCB6 = X'FC01' %C
         %ELSE DEVICE AREA_RCB6 = X'FC02'
      REQ = 0
      REQ_DSERV = EXECUTE CHAIN
      REQ_SSERV = CR EXECUTED
      REQ_P1 = ADDR(DEVICE AREA_RCB0)
      REQ_P2 = DEVICE AREA_IDENT
      REQ_P3 = 1<<4!1;                        !(PAW FUNCTION)<<4!SAW FLAGS
      PON(REQ)
      %RETURN
!*
!*
DACT(SEND CONTROL):!** CONTROL MESSAGE
      STATE = MESS_P2>>24
      -> STATES(STATE)
!*
!*
STATES(CONNECTING):!** ALLOCATE DEVICE
      REQ = 0
      REQ_DSERV = ALLOCATE DEVICE
      REQ_SSERV = CR ALLOCATED
      REQ_P1 = M'CR0'!DEVICE NO
      REQ_P2 = CR INTERRUPT
      REQ_P4 = MESS_P1>>16
      REQ_P5 = STREAM NO
      PON(REQ);                               !TRY TO ALLOCATE
      %RETURN
!*
!*
STATES(DISCONNECTING):!** DEALLOCATE DEVICE
      REQ = 0
      REQ_DSERV = DEALLOCATE DEVICE
      REQ_SSERV = CR DEALLOCATED
      REQ_P1 = M'CR0'!DEVICE NO
      REQ_P4 = MESS_P1>>16
      REQ_P5 = STREAM NO
      REQ_P6 = DEVICE NO
      PON(REQ)
      %RETURN
!*
!*
STATES(ENABLING):!** REQUEST FIRST PAGE
      REPLY TO COMMS CONTROL(MESS_P1>>16,STREAM NO,0)
      DEVICE AREA == RECORD(DEVICE TO DA AD(DEVICE NO))
      DEVICE AREA_MODE = MESS_P2>>20&3;       !ISO EBCIDIC OR BINARY?
      %IF DEVICE AREA_BLOCKED = 0 %THEN GET NEXT BUFFER %ELSE DEVICE AREA_BLOCKED = 0
      %RETURN
!*
!*
STATES(SUSPENDING):
STATES(ABORTING):
      REPLY TO COMMS CONTROL(MESS_P1>>16,STREAM NO,0)
      %RETURN
!*
!*

      %ROUTINE REPLY TO COMMS CONTROL(%INTEGER ACT, STREAM NO, FLAG)
         REP = 0
         REP_DSERV = COMMS COMMAND!ACT
         REP_SSERV = MESS_DSERV
         REP_P1 = STREAM NO
         REP_P2 = FLAG
         PON(REP)
      %END;                                   !OF ROUTINE REPLY TO COMMS CONTROL
!*
!*

      %ROUTINE GET NEXT BUFFER
         REQ = 0
         REQ_DSERV = REQUEST TRANSFER
         REQ_SSERV = CR COMMAND
         REQ_P1 = STREAM NO
         PON(REQ)
      %END;                                   !OF ROUTINE GET NEXT BUFFER
!*
!*

      %ROUTINE TRANSLATE AND SHUFFLE(%INTEGER START, %INTEGERNAME LEN)
      %INTEGER CARD ADDR, L, TO
         E TO I(START,LEN);                   !TRANSLATE CARDS TO ISO
         TO = START;                          !PLACE CARD IS TO BE SHUFFLED TO
         %CYCLE CARD ADDR = START,81,START+LEN-81; !CYCLE UP EACH CARD
            %CYCLE L = 79,-1,0;               !CYCLE DOWN EACH CHARACTER
               -> M %IF BYTEINTEGER(CARD ADDR+L) # ' '
!CHAR NOT A SPACE
            %REPEAT
            L = -1;                           !CARD ALL SPACES
M:          BYTEINTEGER(CARD ADDR+L+1) = NL
            L = L+2;                          !LENGTH OF CARD
            MOVE(L,CARD ADDR,TO) %IF CARD ADDR # TO
            TO = TO+L
         %REPEAT
         LEN = TO-START;                      !NUMBER OF CHARS REMAINING
      %END;                                   !OF ROUTINE TRANSLATE AND SHUFFLE
!*
!*
   %END;                                      !OF ROUTINE CR ADAPTOR
!*
!*
!*
!*
!*
!*
   %IF CP FITTED = YES %START
!*
!*

      %EXTERNALROUTINE CP ADAPTOR(%RECORDNAME MESS)
!***********************************************************************
!*    SERVICE 53 (X35)                                                 *
!*    DRIVES THE CARD PUNCH TO COMMS CONTROLLER SPEC WITH SOME         *
!*    NASTY FIDDLES WHEN A CARD WONT FIT INTO REMAINS OF AN EPAGE      *
!*    CARDS ARE JUST PUNCHED FROM DISC IN EBCDIC AND SPOOLER INFORMED  *
!*    CAN MANAGE UP TO 10 PUNCHES (CP0-CP9) USING A 160 BYTE AREA FOR  *
!*    EACH WHICH IS PROVIDED ON ALLOCATION                             *
!***********************************************************************
      %RECORDSPEC MESS(PE)
      %INTEGER I, J, STREAM NO, DEVICE NO, STATE, ACT, BUFFER NO, SYM,  %C
            INTERRUPT ANALYSIS FLAGS
      %RECORDFORMAT GPCF(%INTEGER SER, GPTSM, PROPADDR, TICKS, CAA, GRCB AD, LBA,  %C
            ALA, STATE, RESP0, RESP1, SENSE1, SENSE2, SENSE3, SENSE4, REPSNO,  %C
            BASE, ID, DLVN, MNEMONIC, ENTSIZE, PAW, USAW0, URCB AD, SENSE AD,  %C
            LOGMASK, TRT AD, UA SIZE, UA AD, TIMEOUT, PROPS0, PROPS1)
      %RECORDFORMAT DAF(%INTEGER LST0, LST1, RCB0, RCB1, RCB2, RCB3, RCB4, RCB5,  %C
            RCB6, RCB7, LBE0, ALE0, ALE1, STREAM NO, BUFFER NO, GOAH AD, GOAH LEN %C
            , POSN, MODE, IDENT, %BYTEINTEGERARRAY CARD(0 : 79))
      %OWNINTEGERARRAY DEVICE TO DA AD(0 : 9) =        %C
         NOT ALLOCATED(10)
      %ROUTINESPEC REPLY TO COMMS CONTROL(%INTEGER ACT, STREAM NO, FLAG)
      %ROUTINESPEC GET NEXT BUFFER
      %RECORDNAME GPC ENTRY(GPCF)
      %RECORDNAME DEVICE AREA(DAF)
      %RECORDNAME STREAM(SR)
      %RECORDNAME BUFFER(BR)
      %RECORD REQ, REP(PE)
%SWITCH DACT(0 : SEND CONTROL)
%SWITCH STATES(0 : ACTIVE)
         %IF MON LEVEL&2 # 0 %AND (KMON>>CP SERVICE)&1 # 0 %START
            PKMON REC("CP Adaptor: ",MESS)
         %FINISH
         ACT = MESS_DSERV&SHORT MASK
         %IF ACT = INTERRUPT %START;          !FROM GPC MESS_P3=GPC AREA ADRRESS
            INTERRUPT ANALYSIS FLAGS = MESS_P1>>20&15
            GPC ENTRY == RECORD(MESS_P3)
            DEVICE AREA == RECORD(GPC ENTRY_UA AD)
            STREAM NO = DEVICE AREA_STREAM NO
         %FINISH %ELSE %START
            %IF ACT >= GO AHEAD %START;       !FROM COMMS MESS_P1=STRM&SHORT MASK
               STREAM NO = MESS_P1&SHORT MASK
               STREAM == STREAM TAB(STREAM INDEX(STREAM NO))
               DEVICE NO = STREAM_DEVICE NO
            %FINISH
         %FINISH
         -> DACT(ACT)
!*
!*
DACT(INTERRUPT):!** DEVICE INTERRUPT (NORMAL, ABNORMAL, ATTENTION)
         %UNLESS INTERRUPT ANALYSIS FLAGS = ATTENTION %START
            BUFFER == BUFFER TAB(DEVICE AREA_BUFFER NO)
            %IF INTERRUPT ANALYSIS FLAGS&NORMAL TERMINATION = NORMAL TERMINATION %START
               DEVICE AREA_POSN = 0;          !NO UNPUCHED CARD IN BUFFER
               I = 0 %AND -> EPAGE USED %IF DEVICE AREA_GOAH LEN = 0
                                              !FINISHED BUFFER EMPTY
               -> PUNCH;                      !PUNCH MORE IF ROOM
            %FINISH %ELSE %START
!TELL COMMS CONTROL BY A CALL TO UPDATE CURSOR
               REQ = 0
               REQ_DSERV = TRANSFER COMPLETE
               REQ_SSERV = CP COMMAND
               REQ_P1 = STREAM NO
               REQ_P2 = 6;                    !NEXT PAGE NOT REQD AND DONT RECAPTURE THIS ONE
               REQ_P3 = BUFFER_LENGTH-DEVICE AREA_GOAH LEN
               REQ_P5 = INTEGER(GPC ENTRY_SENSE AD)
               REQ_P6 = INTEGER(GPC ENTRY_SENSE AD+4)
               PON(REQ)
            %FINISH
         %FINISH %ELSE %START
!** ATTENTION INTERRUPT
            GET NEXT BUFFER %IF MESS_P1&AUTO = AUTO
         %FINISH
         %RETURN
!*
!*
DACT(EXECUTE FAILS):!** EXECUTE CHAIN FAILS
         GPC ENTRY == RECORD(MESS_P3)
         DUMP TABLE(83,MESS_P3,GPC ENTRY_ENT SIZE)
         %RETURN
!*
!*
DACT(ALLOCATED):!** DEVICE ALLOCATED BY GPC
         %IF MESS_P1 = 0 %START;              !ALLOCATED
            GPC ENTRY == RECORD(MESS_P3)
            DEVICE AREA == RECORD(GPC ENTRY_UA AD)
            DEVICE NO = GPC ENTRY_MNEMONIC&BYTE MASK-'0'
            DEVICE TO DA AD(DEVICE NO) = GPC ENTRY_UA AD
            DEVICE AREA_IDENT = MESS_P2;      !SAVE GPC IDENTIFIER
            DEVICE AREA_STREAM NO = MESS_P5
            DEVICE AREA_MODE = 0;             !READ IN NON BINARY MODE
            DEVICE AREA_LST0 = X'00000000';   !NOT USED
            DEVICE AREA_LST1 = X'00000000';   !NOT USED
            DEVICE AREA_RCB0 = X'00008002'
            DEVICE AREA_RCB1 = X'00000000';   !NOT USED
            DEVICE AREA_RCB2 = 4;             !BYTES OF LOGIC BLOCK
            DEVICE AREA_RCB3 = ADDR(DEVICE AREA_LBE0)
            DEVICE AREA_RCB4 = 8;             !BYTES OF ADDRESS LIST
            DEVICE AREA_RCB5 = ADDR(DEVICE AREA_ALE0)
            DEVICE AREA_RCB6 = X'FC01';       !SET NR MODE
            DEVICE AREA_RCB7 = 0
            DEVICE AREA_LBE0 = X'80000300'
            DEVICE AREA_ALE0 = X'58000050'
            DEVICE AREA_ALE1 = ADDR(DEVICE AREA_CARD(0))
            DEVICE AREA_BUFFER NO = 0
            DEVICE AREA_GOAH AD = 0
            DEVICE AREA_GOAH LEN = 0
            DEVICE AREA_POSN = 0
         %FINISH
         REPLY TO COMMS CONTROL(MESS_P4,MESS_P5,MESS_P1)
         %RETURN
!*
!*
DACT(DEALLOCATED):!** DEVICE DEALLOCATED BY GPC
         DEVICE NO = MESS_P6
         DEVICE TO DA AD(DEVICE NO) = NOT ALLOCATED
         REPLY TO COMMS CONTROL(MESS_P4,MESS_P5,MESS_P1)
         %RETURN
!*
!*
DACT(GO AHEAD):                                !TRANSFER GO AHEAD
!IE COMMS HAS PAGED IN BUFFER
         BUFFER NO = MESS_P2
         BUFFER == BUFFER TAB(BUFFER NO)
         DEVICE AREA == RECORD(DEVICE TO DA AD(DEVICE NO))
         DEVICE AREA_GOAH LEN = BUFFER_LENGTH
         DEVICE AREA_GOAH AD = BUFFER_OFFSET
         DEVICE AREA_BUFFER NO = BUFFER NO
PUNCH:                                        !TRY TO PUNCH CARDS
!COPY A CARD INTO DEVICE AREA_CARD AND SPACEFILL. SPANNED CARDS HAVE
!1ST PART IN BUFFER AND DEVICE AREA_POSN INDICATES NEXT FREE BYTE. 
!DEVICE AREA_POSN=80 INDICATES COMPLETE CARD IN BUFFER IN EBCDIC
!AS HAPPENS AFTER AN ABNORMAL TERMINATION
         I = DEVICE AREA_POSN
         -> FIRE %IF I = 80;                  !CARD ALL READY TO FIRE IO
         J = 0
         %CYCLE
            SYM = BYTE INTEGER(VIRTUAL+DEVICE AREA_GOAH AD+J+BUFFER_REAL ADDRESS)
            %IF DEVICE AREA_MODE # BINARY %START
               %IF DEVICE AREA_MODE = ISO %START
                  %EXIT %IF SYM = NL %OR SYM = 12; !CONTROL CHAR
               %FINISH %ELSE %START
                  %EXIT %IF SYM = EBC NL %OR SYM = EBC LF %OR SYM = 12
               %FINISH
            %FINISH
!IF BUFFER FULL THEN TREAT AS NEWLINE EXCEPT ARRANGE FOR THE LAST CHAR
!TO BECOME THE FIRST CHAR ON THE NEXT(OVERFLOW) CARD UNLIKE THE NL CHAR
!WHICH IS DISCARDED. NB CARE NEEDED NOT TO GET BLANK CARD UNNECESSARILY
            %IF I >= 80 %THEN J = J-1 %AND %EXIT
            DEVICE AREA_CARD(I) = SYM
            I = I+1
            J = J+1
            -> EPAGE USED %IF J >= DEVICE AREA_GOAH LEN
         %REPEAT
!CONTROL CHAR FOUND
         DEVICE AREA_GOAH LEN = DEVICE AREA_GOAH LEN-(J+1)
         DEVICE AREA_GOAH AD = DEVICE AREA_GOAH AD+J+1
         %IF DEVICE AREA_MODE # BINARY %START
            %IF DEVICE AREA_MODE = ISO %THEN SYM = ' ' %ELSE SYM = EBC SP
            %WHILE I < 80 %THEN DEVICE AREA_CARD(I) = SYM %AND I = I+1
            I TO E(ADDR(DEVICE AREA_CARD(0)),80) %IF DEVICE AREA_MODE = ISO
         %FINISH
         DEVICE AREA_POSN = 80;               !CARD READ IN EBCDIC
FIRE:
         REQ = 0
         REQ_DSERV = EXECUTE CHAIN
         REQ_SSERV = CP EXECUTED
         REQ_P1 = ADDR(DEVICE AREA_RCB0)
         REQ_P2 = DEVICE AREA_IDENT
         REQ_P3 = 1<<4!1
         PON(REQ)
         %RETURN
EPAGE USED:                                   !AWAIT NEXT GO AHEAD
         DEVICE AREA_POSN = I;                !SOME ISO CHARS IN BUFFER
         REQ = 0
         REQ_DSERV = TRANSFER COMPLETE
         REQ_SSERV = CP COMMAND
         REQ_P1 = STREAM NO
         REQ_P2 = 3;                          !PLEASE PROVIDE NEXT PAGE AND DONT RECAPTURE THIS ONE
         REQ_P3 = BUFFER_LENGTH
         PON(REQ)
         %RETURN
!*
!*
DACT(SEND CONTROL):!** CONTROL MESSAGE
         STATE = MESS_P2>>24
         -> STATES(STATE)
!*
!*
STATES(CONNECTING):!** ALLOCATE DEVICE
         REQ = 0
         REQ_DSERV = ALLOCATE DEVICE
         REQ_SSERV = CP ALLOCATED
         REQ_P1 = M'CP0'!DEVICE NO
         REQ_P2 = CP INTERRUPT
         REQ_P4 = MESS_P1>>16
         REQ_P5 = STREAM NO
         PON(REQ);                            !TRY TO ALLOCATE
         %RETURN
!*
!*
STATES(DISCONNECTING):!** DEALLOCATE DEVICE
         REQ = 0
         REQ_DSERV = DEALLOCATE DEVICE
         REQ_SSERV = CP DEALLOCATED
         REQ_P1 = M'CP0'!DEVICE NO
         REQ_P4 = MESS_P1>>16
         REQ_P5 = STREAM NO
         REQ_P6 = DEVICE NO
         PON(REQ)
         %RETURN
!*
!*
STATES(ENABLING):!** REQUEST FIRST PAGE
         REPLY TO COMMS CONTROL(MESS_P1>>16,STREAM NO,0)
         DEVICE AREA == RECORD(DEVICE TO DA AD(DEVICE NO))
         DEVICE AREA_MODE = (MESS_P2>>20)&3;  !ISO EBC OR BINARY
         GET NEXT BUFFER
         %RETURN
!*
!*
STATES(SUSPENDING):
STATES(ABORTING):
         REPLY TO COMMS CONTROL(MESS_P1>>16,STREAM NO,0)
         %RETURN
!*
!*

         %ROUTINE REPLY TO COMMS CONTROL(%INTEGER ACT, STREAM NO, FLAG)
            REP = 0
            REP_DSERV = COMMS COMMAND!ACT
            REP_SSERV = MESS_DSERV
            REP_P1 = STREAM NO
            REP_P2 = FLAG
            PON(REP)
         %END;                                !OF ROUTINE REPLY TO COMMS CONTROL
!*
!*

         %ROUTINE GET NEXT BUFFER
            REQ = 0
            REQ_DSERV = REQUEST TRANSFER
            REQ_SSERV = CP COMMAND
            REQ_P1 = STREAM NO
            PON(REQ)
         %END;                                !OF ROUTINE GET NEXT BUFFER
!*
!*
      %END;                                   !OF ROUTINE CP ADAPTOR
   %FINISH
%ENDOFFILE