!TITLE Contingency Handling
! The contingency scheme works as follows.  If the subsystem has not
! called PRIME CONTINGENCY, and a contingency occurs, then the process
! is stopped with an Oper message SIGNAL FAIL 1.  If PRIME CONTINGENCY
! has been called, the ONTRAP routine is executed, with LNB set to the
! value of SF at the time of the contingency, and SF set 7 words higher.
! (If there is deemed to be insufficient space above SF to execute the
! ONTRAP routine, LNB is set to a suitably lower value.  In this case an
! "OFF STACK TOP" contingency (Class 67) is generated.  The ONTRAP
! routine should be declared to have two integer parameters CLASS and
! SUBCLASS.  CLASS will be set (by Director when it calls the routine)
! according to the table below.
!
! SUBCLASS will normally be zero, except as shown below:
!
!     (i) for single-character INT: messages (Class 65) it contains the
!         INT: character.
!
!    (ii) for text messages (Class 66), if non-zero, it contains two
!         half-word pointers, being offsets of the first byte and byte
!         after last byte of a broadcast text message in the file
!         VOLUMS.BROADCAST.
!
!   (iii) for "off stack top" (Class 67) it is the Class of the original
!         contingency.
!
!    (iv) for a System Call error (Class 16) it is
!
!          0  I-value out of range
!          1  J-value out of range
!          2  ACR check (insufficient privilege)
!          3  New stack invalid (validate fails, SSN+1 not resident, or
!             new stack=current stack)
!          4  Task call (not implemented)
!          5  Invalid inward return
!         33  ACR check (outward call ACR < current ACR, or inward call
!             ACR > current ACR)
!         34  Invalid code or PLT descriptor in SCT entry.
!
! Once PRIME CONTINGENCY has been called, the ONTRAP routine environment
! remains recorded, and is invoked on the specified contingencies until
! a further call of PRIME CONTINGENCY specifies a different ONTRAP
! routine or until a call of DRESET CONTINGENCY (see below) causes the
! first-set ONTRAP routine environment (if any) to be reinstated.
!
! However, before the ONTRAP routine can be re-invoked for a further
! contingency after a contingency has occurred, the function DRESUME
! (see below) must be called.  Indeed, occurrence of a further
! synchronous interrupt, other than "instruction counter", before DRESUME
! is called will cause repeated contingencies, culminating in a SIGNAL
! FAIL 3 (signal loop stop).  Execution of the ONTRAP routine will
! normally be followed by a call of READ ID (read interrupt data,
! described below), which gives details of the process environment at the
! time of the contingency, and then a call of DRESUME.
!
! If an asynchronous interrupt has occurred and the ONTRAP routine is
! entered, further asynchronous interrupts are inhibited until a call of
! DRESUME is made.  Inhibited asynchronous interrupts are queued up to a
! current maximum of 4, after which they are discarded, with a main Oper
! message MESSAGE LOST.
!
! Note that, at process start-up, asynchronous messages are inhibited
! until the subsystem calls DRESUME, LNB=-2.  Also note that only
! asynchronous contingencies can be queued.
!
!              Contingency           CLASS
!
!              Floating overflow         0
!              Floating underflow        1
!              Fixed overflow            2
!              Decimal overflow          3
!              Zero divide               4
!              Bound check               5
!              Size error                6
!              B overflow                7
!              Stack error               8
!              Privilege                 9
!              Descriptor               10
!              String                   11
!              Instruction              12
!              Accumulator              13
!              System Call              16
!              Instruction counter      17
!              Disc transfer fail       18
!              Block wrong length       19
!              ("should not occur")
!              Hard store fault         20
!              Illegal OUT              21
!              Local Controller error   22
!              Virtual store error      32
!              Instruction counter      64
!              Single-character "INT:"  65
!              Text message             66
!              Off stack top            67
!
! Failures on the Signal Stack
! ----------------------------
!
! Director sometimes fails on the 'signal stack' for one of the following
! reasons:
!
!
 conststring (36)array  SIGFAIL(1:16) =  C 
      "1  NO CONTINGENCY INFO",
      "2  PROGERR IN ONTRAP RT",
      "3  SIGNAL LOOP STOP",
      "4  FINAL IC INTERRUPT",
      "5  INT STACK OVERFLOW",
      "6  DIRECTOR FAILED",
      "7  ILLEGAL RESUME (LNB TOO HIGH)",
      "8  ILLEGAL RESUME (NOT STACK 1)",
      "9  ILLEGAL RESUME (AFTER VSERROR)",
      "10 ILLEGAL RESUME (NOT FROM ONTRAP)",
      "11 SHOULD NOT OCCUR",
      "12 INVALID UNINHIBIT",
      "13 XST RECEIVED",
      "14 ST RECEIVED",
      "15 TOO MANY LOST MESSAGES",
      "16 DSTOP on user stk. (Not an error)"
!
!<DASYNCINH
externalintegerfn  DASYNC INH(integer  ACT, ADR2)
!
! This procedure supplies a subsystem with a means of inhibiting
! asynchronous contingencies for short periods of time (such as that
! required for updating critical tables).  A call with ACT=0 is used to
! initialise the arrangements for inhibiting contingencies, ADR2 must be
! the address of two words permanently available in read and write modes
! to Director (if the words are, or become, inaccessible, Director treats
! asynchronous contingencies as though this call had not been made).
!
! The first of the two words is inspected by Director whenever an
! asynchronous contingency is received.  The contingency is queued if the
! word is positive, otherwise it takes immediate effect.  The second word
! is updated (if accessible) by Director whenever an asynchronous message
! is received and whenever one is un-queued, the word contains the number
! of messages currently queued, and may be inspected by the subsystem,
! for example when it wishes to uninhibit contingencies by setting the
! first word to zero.  If the second word is positive it may then choose
! to invoke the queued contingency, by calling
!              DASYNC INH(1,0)
!>
!     ACT = 0     SET USER-INHIBIT FEATURE
!           1     PROVOKE A Q'D ASYNC INT
      RESULT =8 UNLESS  0<=ACT<=1
      IF  ACT=0 THEN  UINH AD=ADR2 ELSE  DRESUME(-3,0,0)
      RESULT =0
END ; ! DASYNC INH
!
!-----------------------------------------------------------------------
!
EXTERNALINTEGERFN  D SET INT MESSAGE(STRING (255)S)
      RESULT  = 53 IF  UINF_REASON # INTER
      IOSTAT_INTMESS <- S
      RESULT  = 0
END 
!
!-----------------------------------------------------------------------
!
ROUTINE  LMSG(STRING (255) S)
      WRSS("SIGNAL ------------- ", S)
END ; ! LMSG
!
!-----------------------------------------------------------------------
!
ROUTINE  SET BAD PAGE(INTEGER  INTPARAM)
INTEGER  SEG,RELP,RELK,J,DA,ENT,REL EPG IN SEG,REL EPG IN BLK
      SEG=INTPARAM>>18
      RELK=(INTPARAM>>10)&X'FF'; ! offset in Kbytes from SEG
      REL EPG IN SEG=RELK//EPAGE SIZE
      REL EPG IN BLK=REL EPG IN SEG&(BLKSI-1)
      RELP=0
      DA=0
      ENT=SST(SEG)
!
      UNLESS  ENT = ENDSST START 
         ENT = ENT + 1 UNLESS  REL EPG IN SEG < BLKSI
         DA = CBTA(ENT)_DA + REL EPG IN BLK
         J = BAD PAGE(1, DA>>24, DA<<8>>8) UNLESS  DA = 0
      FINISH 
END ; ! SET BAD PAGE
!
!-----------------------------------------------------------------------
!
STRINGFN  TIM(INTEGER  SECS)
INTEGER  MINS
STRING (31) WK1
      MINS=SECS//60
      SECS=SECS - MINS*60
      WK1=""
      IF  MINS>0 THEN  WK1=ITOS(MINS)."M "
      RESULT =WK1.ITOS(SECS)
END ; ! TIM
!
!-----------------------------------------------------------------------
!
INTEGERFN  UIVAL
      RESULT  = 0 IF  UINH AD & 3 > 0
!
      RESULT  = 0 UNLESS  UINH AD >> 18 = 34 ORC 
                           X'100000' <= UINH AD < X'107000'
!
      RESULT  = VAL(UINH AD, 8, 1, 0)
END ; ! UIVAL
!
!-----------------------------------------------------------------------
!
!
EXTERNALROUTINE  SIGNAL
                                    ! ACTIVATED BY LOCAL CONTROLLER FOR 3 REASONS
INTEGER  SSNP1AD,SIGWORD,STKNO,WAITSNO,J,K,DACT,A3,LNB,PC,STK LIM
INTEGER  ERRTYPE,DIRFAIL,PSRHERE,REASON,STK1PSR,SSNAD
INTEGER  LNB AT FAIL,INTPARAM,CLASS,SUBCLASS,CONTPC
INTEGER  OPERNO,RLNBSEG,RSFSEG
!
RECORDFORMAT  SIGOUTPF(RECORD  (PARMF) P,  C 
   INTEGER  ERRTYPE,STKNO,SSNAD,WAITSNO)
!
STRING (255) S,S1,S2
!
ROUTINESPEC  PRINT DUMPS
!
RECORDFORMAT  OBJF(INTEGER  NEXTFREEBYTE,CODERELST,GLARELST,LDRELST)
RECORD (OBJF)NAME  H
RECORD (ACF)NAME  ACCTS
RECORD (PARMF)NAME  P
RECORD (PARMF)NAME  DP
RECORD (SIGOUTPF)NAME  PP
RECORD (PARMF)NAME  PS
RECORD  (PARMF)ASYNCM
!
RECORD  (SIGOUTPF)COPY SIGOUTP
!
OWNINTEGER  LOOPCOUNT=0
OWNINTEGER  LOSTCOUNT = 0
!
!
OWNRECORD (PARMF)ARRAY  AMS(0:3)
OWNINTEGER  IN,NEXT
!
RECORD (RF)NAME  R
RECORDFORMAT  DISPLAF(INTEGER  PREVLNB,LK0,LK1,PLT0,PLT1,  C 
   CLASS,SUBCLASS)
RECORD (DISPLAF)NAME  DISPLAY
!
CONSTSTRING (8)ARRAY  ASTYPE(0:TOPASACT)= C 
"INVALID", "INT: MSG", "RESUME", "TEXT MSG",
"UNINHIB", "DIR MSG", "NULL ACT", "USECOUNT",
"CLOSEMSG", "INTER"
SWITCH  ASACT(0:TOPASACT)
SWITCH  POST ACT(1 : 11)
!
!
CONSTSTRING (24)ARRAY  PEI(0:TOPEI) =  C 
      "Floating point overflow",
      "Floating point underflow",
      "Fixed point overflow",
      "Decimal underflow",
      "Zero divide",
      "Bound check",
      "Size",
      "B overflow",
      "Stack",
      "Privilege",
      "Descriptor",
      "String",
      "Instruction",
      "Accumulator",
      "Esr errors",
      "--15--",
      "System call",
      "Instruction counter",
      "Disc transfer fail",
      "Block wrong length",
      "Hard store fault",
      "Illegal OUT",
      "Local Controller Error"
!
!
SWITCH  TYPE(1:3)
!
ROUTINE  OPER(INTEGER  SRCE,STRING (255)S)
RECORD (PARMF)NAME  P
INTEGER  L
      SRCE = X'320007' IF  SRCE>>8 = 0; ! JUST IN CASE I FORGOT !
      P == RECORD(OUTPAD)
!
      WRSS("SIGOP: ", S)
!
      CYCLE 
         P = 0
         P_DEST = SRCE
         P_S <- S
         *OUT_6
         L = LENGTH(S) - 23
         RETURN  UNLESS  L > 0
         LENGTH(S) = L
         MOVE(L, ADDR(S)+24, ADDR(S)+1)
      REPEAT 
END ; ! OF LOCAL VERSION OF OPER WHICH REPLIES TO CALLER
!
!-----------------------------------------------------------------------
!
ROUTINE  PRINTREGS
INTEGER  J
INTEGERNAME  I
CONSTSTRING (3)ARRAY  PRE(0:12) = "LNB", "PSR", "PC ", "SSR", "SF ",
      "IT ", "IC ", "CTB", "XNB", "B  ", "DR ", "", "ACC"
      LMSG("CONTINGENCY OCCURRED")
!
      CYCLE  J = 0, 1, 15
         I == INTEGER(SSNP1AD + J << 2)
         IF  J # 11 AND  J < 13 START 
            NEWLINE
            PRINTSTRING(PRE(J))
         FINISH 
         SPACE
         PRHEX(I)
      REPEAT 
      NEWLINE
END ; ! OF PRINTREGS
!
!-----------------------------------------------------------------------
!
BACK IN:
      OUTPAD=SIGOUTP0
      DP == RECORD(DIROUTP0); ! The DIROUT record
      PP==RECORD(SIGOUTP0)
      COPY SIGOUTP=PP
      PP==COPY SIGOUTP
      P==RECORD(ADDR(COPY SIGOUTP))
      INTPARAM=P_P1
      ERRTYPE=PP_ERRTYPE
      STKNO=PP_STKNO; ! 1 = LOCAL 4,  2 = SIG STACK LOCAL 6,  3 = USER STACK (UP TO SS)
      SSNAD=PP_SSNAD
      WAITSNO=PP_WAITSNO
      IF  PROCUSER="DIRECT" THEN  LOG ACTION=LOG ACTION ! LOG
      SIGWORD=SIGMO
      SSNP1AD=SSNAD + 1<<18
      DIRFAIL=0
      S1=""
      LOOPCOUNT=LOOPCOUNT + 1
!
      IF  LOOPCOUNT>=SIGLOOPSTOP START 
         IF  LOOPCOUNT>SIGLOOPSTOP THEN  DSTOP(2)
         REASON=3
         -> MON OUT
      FINISH 
!
!
!
      SUBCLASS=0
      R==RECORD(SSNP1AD); ! REGISTER SET IN OLD SSN+1
!
      IF  ONTRAP=0 START 
         SAVE DIROUTP = DP
         RESUMEREGS=R
         RESUMEWAIT=WAITSNO
         RESUMESTACK=STKNO
      FINISH 
!
      UNLESS  SIGWORD & 2 = 0 START 
         PREC("SIGNAL: ", P, 1)
         WRSN(" ERRTYPE ", ERRTYPE)
      FINISH 
!
      PRINTREGS IF  SIGWORD&64#0
      -> TYPE(ERRTYPE)
!-----------------------------------------------------------------------
TYPE(1):! VIRTUAL STORE ERROR
      S1="VIRTUAL STORE ERROR, PARAMETER=".HTOS(INTPARAM,8)
      CLASS=32; ! JOBBER-DEFINED
      SUBCLASS=INTPARAM
      VSERR=1; ! USED TO PREVENT STRAIGHT RESUMPTION AFTER VS ERROR
      -> DIAGNOSE
TYPE(2):! PROGRAM ERROR
      CLASS=INTPARAM&X'7F'
      SUBCLASS=(INTPARAM>>8) & 255
      S1="PROGRAM ERROR, PARAMETER=".HTOS(INTPARAM,8).  C 
      "   CLASS=".ITOS(CLASS)."/".ITOS(SUBCLASS)."    "
      S1=S1.PEI(CLASS) IF  CLASS<=TOPEI
! EXCLUDE ARITHMETIC EXCEPTIONS FROM CONTINGENCY LOOP COUNT
      IF  CLASS<=4 THEN  LOOPCOUNT=LOOPCOUNT - 1
      IF  CLASS=18 THEN  SET BAD PAGE(INTPARAM); ! disc transfer fail, INTPARAM=VA of the page
      -> DIAGNOSE
!------------------------------ ASYNCHRONOUS INTERRUPT  ---------------
!
TYPE(3):    ! ASYNCHRONOUS INTERRUPT
      LOOP COUNT=LOOP COUNT - 1
      DACT=P_DEST&X'FFFF'
      UNLESS  0<DACT<=TOPASACT THEN  DACT=0
!
      IF  SIGWORD&64#0 START 
         WRSS("Async int received, SIGOUTP TYPE = ", ASTYPE(DACT))
         DDUMP(ADDR(COPY SIGOUTP),ADDR(COPY SIGOUTP)+48,-1,-1)
         PRINT DUMPS
      FINISH 
!
      OPERNO=0
      ! LNB AT FAIL is required only so that SIGMON can be used to get a
      ! dump of the stack asynchronously.
      LNB AT FAIL=R_LNB
      CLASS=99; ! not required, but otherwise unassigned, and confusing
      ! Remember here that we may be in the process of shuffling the CBT
      ! (not implemented at Feb 80). Make sure that actions which are not
      ! inhibited do not refer to any VM other than Dir code, Dir GLA, stack
      ! or sig stack.
      -> ASACT(DACT)
!
ASACT(INTDACT):   ASACT(TXTDACT):    ! INT MSG,  TXT MSG
ASACT(CLODACT):                      ! CLOSE MSG
ASACT(9):                            ! receive class 68 int, twixt process
QUEUE IT:
      IF  AQD>MAXM1 START 
         OPER(OPERNO,"MESSAGE LOST")
         LOST COUNT = LOST COUNT + 1
         -> RESUME IF  LOST COUNT < 0; ! enough is enough!!
         PREC("LOST: ", PP, 0)
!
         ASYNCM = LOUTP
         S = LOUTP STATE
         K = ADDR(ASYNCM)
         PRINTSTRING("SRCE: ")
         PRINTSTRING(LOUTP STATE)
         CYCLE  J = 0, 4, 28
            SPACE
            PRHEX(INTEGER(K+J))
         REPEAT 
         NEWLINE
         IF  LOST COUNT > 5 START 
            LOST COUNT = -1000; ! If more are queued, they just resume
                                ! and let the process get on with stopping
            REASON = 15
            -> MONOUT
         FINISH 
         -> RESUME
      FINISH 
!
      PREC("QUEUE: ", PP, 0) UNLESS  SIGWORD & 2 = 0
!
      AMS(IN) = PP_P
      IN = (IN+1) & MAXM1
      AQD = AQD+1
GIVE NEXT:
      K = UIVAL; ! K is 0 if UINH AD is BAD
      INTEGER(UINH AD+4) = AQD IF  K > 0
!
UNLESS  SIGWORD & 2 = 0 START 
      WRSN("ASYNC INH ", ASYNCINHIB)
      WRSN("K         ", K)
      WRSN("UINH AD   ", INTEGER(UINHAD)) UNLESS  K = 0
FINISH 
!
      -> RESUME IF  ONTRAP # 0 ORC 
                    ASYNC INHIB # 0  ORC 
                    (K # 0 AND  INTEGER(UINH AD) > 0)  
      ASYNCM = AMS(NEXT)
      NEXT = (NEXT+1) & MAXM1
      AQD = AQD - 1
!
      PREC("NEXT: ", ASYNCM, 0) UNLESS  SIGWORD & 2 = 0
!
      INTEGER(UINH AD+4) = AQD IF  K > 0
      DACT = ASYNCM_DEST & X'FFFF'
      -> POST ACT(DACT)
!
ASACT(UHIDACT):      ! KICK TO DISPATCH ASYNC INTS, POSSIBLY
      IF  ONTRAP#0 OR  AQD=0 THEN  REASON=12 AND  -> MON OUT
      -> GIVE NEXT
!
ASACT(DIRDACT):      ! DIR MSGS
      OPERNO=P_SRCE
      PS==P
      S=PS_S
!
      IF  S = "PMON" AND  PAGEMON # 0 AND  INTEGER(PAGEMON) = 32 START 
         J = DMON("SWITCHON")
         OPER(OPERNO, "J = " . ITOS(J))
         -> RESUME
      FINISH 
!
      IF  S="TIME?" AND  PROCUSER # "DIRECT" START 
         ACCTS==RECORD(ACCTSA)
         J=UINF_PREVIC
         K=GETIC
         OPER(OPERNO,"T=".TIM((ACCTS_KINSTRS+(J-K))  C 
            //COM_KINSTRS).",PT=".  C 
            ITOS(ACCTS_PTRNS))
         -> RESUME
      FINISH 
!
      IF  S->S1.("XST").S2 AND  S1="" START 
         IF  ASYNC INHIB#0 START 
            OPER(OPERNO,"Async msgs inhibited")
            -> RESUME
         FINISH 
         IF  S2 = "" THEN  REASON = 13 ELSE  REASON = P_P6 AND  -> MONOUT1
         -> MONOUT
      FINISH 
      IF  S = "CMP" THEN  PRINTMP(0,0) AND  -> RESUME
      IF  S = "ST" THEN  PS_DEST = 2 AND  -> QUEUE IT
      IF  S = "VST" THEN  PS_DEST = 10 AND  -> QUEUE IT
      IF  S = "EMPTYDVM" THEN  PS_DEST = 11 AND  -> QUEUE IT
!
      IF  S -> ("INT ") . S START 
!
         -> QUERY IF  S = ""
!
         IF  LENGTH(S) > 1 START 
            J = DSETINTMESSAGE(S)
            -> RESUME
         FINISH 
         ! SINGLE CH
         PS_DEST = 1
         STRING(ADDR(P_P3)) = S
         -> QUEUE IT
      FINISH 
!
      IF  S="SRCE" START 
         ASYNCM=LOUTP; ! use as work area
         S=LOUTP STATE
         K=ADDR(ASYNCM)
         CYCLE  J=0,8,24
            OPER(OPERNO,HTOS(INTEGER(K+J),8)." ".  C 
               HTOS(INTEGER(K+J+4),8))
         REPEAT 
         OPER(OPERNO,S)
         OPER(OPERNO, "PC=" . HTOS(R_PC, 8))
         PRINT DUMPS
         -> RESUME
      FINISH 
!
      IF  S->("USECOUNT").S START 
         IF  S="" THEN  J=-1 ELSE  START 
            WHILE  S->(" ").S CYCLE ; REPEAT 
            J=STOI(S)
            FINISH 
         UNLESS  -1<=J<=99 THEN  -> QUERY
         J=SHOW USECOUNT(J, 1, OPERNO); ! local process count
         -> RESUME
      FINISH 
!
      IF  S="FAIL" THEN  J=INTEGER(-1) AND  -> RESUME
!
      IF  S->S1.(" ").S2 START 
         J=STOI(S2)
         UNLESS  J#INVI AND   C 
            (S1="DIRMON" OR  S1="SIGMON") THEN  -> QUERY
         IF  S1="DIRMON" THEN  DIRMON=J  C 
            ELSE  SIGMO=J
         -> RESUME
      FINISH 
QUERY:
      OPER(OPERNO,S."?")
      -> RESUME
ASACT(USEDACT):   ! entry to PON a use-count number for fsys P_P1
                  ! back to the DIRECT process.
      P_DEST=P_SRCE
      P_P2=FSYS USECOUNT(P_P1)
      DPONI(P)
      -> RESUME
!------------------------------------------------------------------------------------
!
POST ACT(1):      ! INT MSG
      CLASS=65
      SUBCLASS=BYTEINTEGER(ADDR(ASYNCM_P3)+1); ! THE SINGLE INT: CHAR
      IF  SUBCLASS=0 START 
         ! Interactive terminal address, currently 4 bytes as follows
         ! binary 3 (no of bytes following)
         ! Node no, Terminal no, Buffer no.
         UINF_ITADDR0=(ASYNCM_P3<<16) ! (ASYNCM_P4>>16)
         -> RESUME
      FINISH 
!
      SUBCLASS = 'Y' IF  SUBCLASS = 25
      DOPER2(PROCUSER.": EOT") IF  SUBCLASS = 'Y'
      SUBCLASS = 'Y' AND  DOPER2(PROCUSER.": TCP FAIL") IF  SUBCLASS = 26
!
      -> ACTIVATE ONTRAP
!
POST ACT(2):      ! "ST" RECEIVED
PA2:
      CLASS=65; ! single-char INT message
      SUBCLASS='X'
      -> ACTIVATE ONTRAP
POST ACT(3):      ! TXT MSG
PA3:
      CLASS=66
      SUBCLASS=ASYNCM_P3; ! THE TEXT POINTERS
      -> ACTIVATE ONTRAP
POST ACT(8):                      ! CLOSE MSG
      !
      ! This msg acts exactly as TXT MSG or as DIR MSG "ST" (according to
      ! the setting of P_P1), except that the message is ignored if the
      ! process has zero use-counts for all discs which are to be closed.
      ! The variable FSYS WARN is set (if the user might have to go) or
      ! cleared (if he now has no segments connected on closing FSYSes).
      ! This variable is used (only) in routine DDISCONNECTI, before return,
      ! to decide whether or not to clear the index area VM.
      !
      K=0
RECYCLE:
      CYCLE  J=99,-1,0
         IF  FSYS USECOUNT(J)#0 AND  AV(J,0)=0 START 
            IF  K=0 START 
               EMPTY DVM
               K=1
               -> RECYCLE
            FINISH 
            FSYS WARN=1
            IF  ASYNCM_P1=0 START 
               ! This next line relates only to async messages sent by DIRECT
               ! to the executive processes just to get the FSYS WARN variable
               ! set.
               IF  ASYNCM_P3=0 THEN  -> RESUME
               -> PA3
            FINISH  ELSE  -> PA2
         FINISH 
      REPEAT 
      FSYS WARN=0
      -> RESUME
POST ACT(9):
      CLASS = 68
      SUBCLASS = ASYNCM_P1
      -> ACTIVATE ONTRAP
POST ACT(10):      ! "VST" received (end of session)
      CLASS = 65
      SUBCLASS = 'V'
      -> ACTIVATE ONTRAP
POST ACT(11):
      EMPTYDVM
      -> RESUME
!
!
!-------------------------------------------------------------------------
!
DIAGNOSE:
      LNB AT FAIL=R_LNB
      ! FOR A SYSTEM CALL ERROR SIGNAL, DROP LNB TO PREVIOUS LEVEL,
      ! BECAUSE PRECALL SEQUENCE HAS BEEN EXECUTED.
      ! LIKEWISE FOR AN INSTRUCTION-COUNTER INTERRUPT, BECAUSE THE INTERRUPT
      ! (WHICH IS NON-STACK-SWITCHING) HAS MADE A NEW DISPLAY.
      ! AND LIKEWISE IF THE CAUSE OF THE PROGRAM ERROR WAS AN ATTEMPTED
      ! JUMP TO AN UNSATISFIED EXTERNAL REFERENCE, WHEN DR0 HAS "NORT"
      ! IN IT, THE PRECALL SEQUENCE HAS BEEN EXECUTED.
      IF  CLASS=16 OR  CLASS=17 OR  R_DR0=M'NORT' START 
         DISPLAY==RECORD(LNB AT FAIL)
         R_PSR=DISPLAY_LK0
         R_PC=DISPLAY_LK1
         R_SF=LNB AT FAIL
         LNB AT FAIL=INTEGER(LNB AT FAIL)
         R_LNB=LNB AT FAIL
      FINISH 
      !
      ! DID ERROR OCCUR IN A DIRECTOR ROUTINE ?
      ! CHECK CURRENT ACR AGAINST ACR IN DISPLAY ON STACK (AT LNB+1WORD).
      ! WE TRUST, FOR NOW, THAT THIS WORD IS ACCESSIBLE.. BE MORE CAREFUL LATER.
      *LSS_(LNB +1)
      *ST_PSRHERE
      STK1PSR=R_PSR
      !
      ! SET DIRFAIL IF WE CAN TELL THAT THE FAILURE WAS IN DIRECTOR.
      ! FOR A DIRECTOR FAIL, PC WILL BE <= TOP OF DIRECTOR CODE SEGMENT (INCLUDES
      ! PUBLIC CODE SEGS) AND ACR MUST BE THE SAME AS THAT HERE ON THE SIGNAL
      ! STACK. WE ALSO DON'T WANT DIRFAIL SET FOR ASYNCHRONOUS EVENTS.
      DIRFAIL = 1 IF  0<R_PC<=CODEAD + X'3FFFF' C 
          AND   C 
         (STK1PSR>>20)&15 = (PSRHERE>>20)&15 C 
          AND  C 
          ERRTYPE#3
      DIRFAIL = 1 IF  STKNO=2; ! ERROR ON SIGNAL STACK
      DIRFAIL = 1 IF  CLASS = 22; ! Local Controller error
!
      IF  CLASS=17 START 
         !
         ! MAP INSTRUCTION-COUNTER INTERRUPT TO JOBBER-DEFINED PROCESS TIME
         ! INTERVAL CLASS
         ! SESSION LIMIT REACHED?
         IF  INTEGER(AREVS)<-1 THEN  REASON=4 AND  -> MONOUT
         CLASS=64
         DIRFAIL=0; ! AASIGN FAILURE TO USER
         FINISH 
      IF  DIRFAIL#0 THEN  SIGWORD = SIGWORD ! 99 AND  PRINTREGS
      REASON=6
      WRS(S1) IF  SIGWORD & 2 > 0
      PRINT DUMPS
      IF  DIRFAIL#0 THEN  -> MON OUT
!
! LIMIT NUMBER OF CONTINGENCIES TO A FINITE NUMBER
!
!================== ACTIVATE CONTINGENCY PROCEDURE ======================
ACTIVATE ONTRAP:
      REASON=1
      IF  CALLDR0=0 THEN  -> MON OUT
      ONTRAP=1
ACTIVATE STOP:
! CHECK THAT THERE'S ENOUGH (SAY 4096 BYTES) ROOM ON THE STACK TO
! ACTIVATE THE ONTRAP ROUTINE.
      ST==ARRAY(0,STF)
      STK LIM=(ST(SSNP1AD>>19<<1)_APFLIM & X'3FF80') ! X'7F'
! These next tests are for the cases of
!     a program error caused by SF getting into a different segment from LNB.
!     for when LNB is just not (reasonably) valid.
      RLNBSEG=R_LNB>>18
      RSFSEG=R_SF>>18
      UNLESS  (RLNBSEG=NORMAL STACK SEG OR  32<=RLNBSEG<=(HISEG-1)&(¬1)) ANDC 
         RLNBSEG=RSFSEG ANDC 
         R_SF&X'3FFFF'<=STK LIM - X'1000' C 
      START 
         STK LIM=(ST(NORMAL STACK SEG>>18)_APFLIM & X'3FF80') ! X'7F'
         J=STK LIM - X'2000'
         J=0 IF  J<0; ! Ultra-cautious!
         R_SF=NORMAL STACK SEG<<18 + J
         SUBCLASS=CLASS
         CLASS=67
      FINISH 
!
      IF  PROCUSER = "DIRECT" C 
      THEN  R_LNB = PROC1 LNB C 
      ELSE  R_LNB = R_SF
!
      R_SF=R_LNB+28; ! TWO INTEGER PARAMS TO CONT PROCEDURE (7 WORDS DISPLAY)
      J =CALLDR0>>24; ! DRTYPE
      CONTPC=0
      CONTPC = CALLDR1 IF  J=X'E1' OR  J = X'E0'; ! CODE DESCRIPTOR
      CONTPC = INTEGER(CALLDR1 + 4) IF  J=X'B0' OR  J=X'B1'
      MONITOR  IF  CONTPC=0
      R_PSR=CALLPSR
      R_PC=CONTPC
      R_SSR=R_SSR & X'7FFFF7FF'; ! REMOVE II BIT FROM SSR AND ALLOW IC INTERRUPTS
      R_XNB=CALLXNB
      R_DR0=CALLDR0
      R_DR1=CALLDR1
      IF  SIGWORD&64#0 START 
         LMSG("ACTIVATE ONTRAP. SSN+1")
         WRSNT("Class ", CLASS, 5)
         WRSNT(" Subclass ", SUBCLASS, 2)
         DDUMP(ADDR(R),ADDR(R)+X'50',-1,-1)
      FINISH 
! AND SET UP THINGS IN THE DISPLAY, GIVING a nutty ADDRESS as link PC
      DISPLAY==RECORD(R_LNB)
      DISPLAY_PREVLNB=LNB AT FAIL
      DISPLAY_LK0=X'00100000'; ! ACR 1 to give prog err at exit
      DISPLAY_LK1=M'TRAP'
      DISPLAY_CLASS=CLASS
      DISPLAY_SUBCLASS=SUBCLASS
!
! Set STKNO to be what R_LNB describes
      STKNO=1
      IF  R_LNB>>18 # 4 THEN  STKNO=3
      OUTPAD=DIROUTP0
      P==RECORD(SIGOUTP0)
      P_P1=STKNO
      P_P2=0; ! "RUN"
      *OUT_19
      IF  P_DEST=-1 START 
         OUTPAD=SIGOUTP0
         WRSS(PROCUSER, " OUT 19 FAILED")
         DDUMP(0,4096,-1,-1)
         DOPER2(PROCUSER." OUT_19 FAILED")
         P = 0
         P_P2=INVOC
         STRING(ADDR(P_P3))=PROCUSER
         *OUT_0
      FINISH 
      -> BACK IN
ASACT(6):      ! NULL, EXCEPT UPDATE ACCOUNTS
ASACT(0):      ! INVALID
RESUME:
      OUTPAD=DIROUTP0
      P==RECORD(SIGOUTP0)
      P_P1=STKNO; ! STACK NO TO RESUME TO
      P_P2=WAITSNO
      *OUT_19; ! RESUME
      -> BACK IN
!======================================================================
!
ASACT(RESDACT):      ! RESUME
      IF  ONTRAP=0 THEN  REASON=10 AND  -> MON OUT
      LNB=P_P1
      PC=P_P2
      A3=P_P3; ! ADDRESS OF 18 WORDS CONTAINING REGS AT TIME OF CONTINGENCY
               ! (FOR LNB>0).
      IF  LNB=0 THEN  R=RESUMEREGS AND  DP = SAVE DIROUTP ELSE  START 
         RESUMESTACK=1
         IF  LNB>>18 # 4 THEN  RESUMESTACK=3
         SSNP1AD=LNB>>18<<18 ! X'40000'
         R==RECORD(SSNP1AD)
         R_LNB=LNB
         R_PC=PC
         ! FOR JOBBER SOFTWARE, ACC TO CONTAIN DESCRIPTOR TO 18 WORDS
         ! CONTAINING REGS AT TIME OF CONTINGENCY.
         R_A2=X'28000012'
         R_A3=A3
! TAKE PSR FROM ORIGINAL POINT
         R_PSR=(CALLPSR & (¬3)) ! 2; ! SET ACCSIZE TO 64 BITS
         RESUMEWAIT=0; ! RUN
      FINISH 
      ONTRAP=0; VSERR=0
      IF  AQD>0 START 
          SAVE DIROUTP = DP 
          RESUMEREGS=R 
          -> GIVE NEXT
      FINISH 
      R_SSR=R_SSR & ALLOW IC INTS
      IF  SIGWORD&64#0 START 
         LMSG("RESUME.  SSN+1 :")
         DDUMP(SSNP1AD,SSNP1AD+X'50',-1,-1)
         WRSNT("WAITSNO=", RESUMEWAIT, 2)
      FINISH 
      OUTPAD=DIROUTP0
      P==RECORD(SIGOUTP0)
      P_P1=RESUMESTACK
      P_P2=RESUMEWAIT
      *OUT_19
      -> BACK IN
!=======================================================================
MON OUT:
      S=PROCUSER." SIGNAL FAIL ".SIGFAIL(REASON)
      LMSG(S)
      LENGTH(S)=21
      DOPER2(S); ! (no oper msg for DSTOP on user stack)
MON OUT1:
      IF  SIGWORD#0 = DIRFAIL START 
         WRS(S1)
         MONITOR  IF  LNB AT FAIL#X'00100004'
         PRINT DUMPS
      FINISH 
! EXECUTE THIS STOP ROUTINE ON THE USER STACK, BECAUSE WE ARE NOT ALLOWED
! TO "SUSPEND" (WHICH CAN HAPPEN FOR SEMAPHORES OR OTHER "OUT"S")
! ON THE SIGNAL STACK
      STKNO=1
      SSNP1AD=(NORMAL STACK SEG ! 1)<<18; ! ALWAYS STOP ON NORMAL STACK
      R==RECORD(SSNP1AD)
      CLASS=0; SUBCLASS=0
      AQD=0; ! THROW AWAY ASYNC MSGS
      SET STOP
      CLASS = REASON
      -> ACTIVATE STOP
!-----------------------------------------------------------------------
ROUTINE  SEGMENT TABLE
INTEGER  I, J, SAVE
      WRS("Segment table")
      SAVE = LOG ACTION
      LOG ACTION = SAVE & (¬DT)
      J = 0
      CYCLE  I = 0, 1, 127
         UNLESS  ST(I)_APFLIM = X'7F' AND  ST(I)_USERA = 0 START 
            WRITE(I, 4)
            SPACE
            PRHEX(ST(I)_APFLIM)
            SPACE
            PRHEX(ST(I)_USERA)
!
            J = J + 1
            NEWLINE IF  J & 3 = 0
         FINISH 
      REPEAT 
      NEWLINE
      LOG ACTION = SAVE
!
      WRS("Director variables...")
      WRSN("D Callers ACR", D CALLERS ACR)
      WRSNT("D Callers PSR", D CALLERS PSR, 2)
END 
!
!
!
ROUTINE  PRINT DUMPS
INTEGER  SAVELNB
!
      IF  SIGWORD & 1 > 0 C 
          AND  C 
          LNB AT FAIL # X'00100004' C 
      START 
!
         *LSS_(LNB +0)
         *ST_SAVELNB
!
         *LSS_LNB AT FAIL
         *ST_(LNB +0)
         MONITOR 
!
         *LSS_SAVELNB
         *ST_(LNB +0)
!
      FINISH 
!
      IF  SIGWORD & 32 > 0 C 
      START 
         NCODE(R_PC)
         SEGMENT TABLE
      FINISH 
!
      IF  SIGWORD & 16 > 0 START 
         PRINTMP(0,0)
         LMSG("REGISTERS FROM SSN+1 SEGMENT-----------")
         DDUMP(SSNP1AD,SSNP1AD+X'50',-1,-1)
      FINISH 
!
      IF  SIGWORD&4#0 START 
      WRSN("LOCAL STACK    LNB AT FAIL = ", LNB AT FAIL)
         DDUMP(LNB AT FAIL&X'FFFC0000', R_SF+X'40',-1,-1); ! BE GENEROUS BY X40 BYTES
         WRS("DIRECTOR CODE SEGMENT - FILE HEADER")
         DDUMP(CODEAD,CODEAD+X'40',-1,-1)
      FINISH 
      IF  SIGWORD&8#0 START 
         WRS("DIRECTOR GLA AND LOAD DATA")
         H==RECORD(CODEAD)
         DDUMP(GLAD,GLAD+(H_NEXTFREEBYTE-H_GLARELST),-1,-1)
      FINISH 
END ; ! PRINT DUMPS
!
!-----------------------------------------------------------------------
!
END ; ! SIGNAL
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  STOP ONE(INTEGER  A,B)
! This routine is used in the 'SIGNAL FAIL n' sequence. The routine
! is passed as a parameter to PRIME CONTINGENCY in order to activate it
! as an 'ONTRAP' routine.
      DSTOP(A)
END ; ! STOP ONE
!
!-----------------------------------------------------------------------
!
!<DCLEARINTMESSAGE
externalintegerfn  DCLEAR INT MESSAGE
!
! When a terminal user presses the ESC key, the TCP software generates
! a prompt string "INT:".  The message input by the user in response is
! called an "INT: message" and is transmitted to the user process
! independently of the input and output streams enabled by the DENABLE
! TERMINAL STREAM procedure.  INT: messages may be at most 15 characters
! long, the character used to terminate the message not forming part of
! the message.
!
! The EMAS 2900 System distinguishes single-character INT: messages from
! multi-character INT: messages (null INT: messages are discarded by the
! TCP).  In the former case, the "contingency mechanism" is invoked.  The
! DCLEAR INT MESSAGE procedure has no relevance in this case.
!
! Multi-character INT: messages, by contrast, are placed in a location
! visible to programs running at any level of privilege, and in
! particular to the subsystem.  Thus an INT: message may arrive
! asynchronously with respect to the user process and may be used by it,
! for example, to re-direct the course of the computation or to take a
! particular action and then resume the computation.
!
! The location of the INT: message may be referenced by using the
! address supplied in the AIOSTAT field of the UINF record .  The format
! of the record at this address is given by IOSTATF, below.  Access to
! the location can be achieved as in the following example:
!        .
!        .
!      %recordformat IOSTATF(%integer IAD, %string(15)INT MESS,
!         %integer INBUFLEN, OUTBUFLEN, INSTREAM, OUTSTREAM)
!      %record(IOSTATF)%name IOSTAT 
!      %stringname INT MESSAGE
!        .
!        .
!      IOSTAT == RECORD(UINF_AIOSTAT)
!      INT MESSAGE == IOSTAT_INT MESS
!        .
!        .
!      %if INT MESSAGE = "STOP" %start
!        .
!        .
! When the process has noted the presence of a multi-character INT:
! message, the message should be cleared so that further examination
! of the message location will not yield the same message.  The
! message can only be cleared by a call of the procedure DCLEAR INT
! MESSAGE, since the message location is not writable at the subsystem's
! level of privilege.
!
! Any INT: message arriving before a previous message has been cleared
! overwrites the previous message.
!>
INTEGER  J
      J = IN2(256 + 10)
      -> OUT UNLESS  J = 0
!
      J = 53
      -> OUT UNLESS  UINF_REASON = INTER
!
      J = 0
      IOSTAT_INT MESS=""
OUT:
      RESULT  = OUT(J, "")
END ; ! DCLEAR INT MESSAGE
!
!-----------------------------------------------------------------------
!
!<DISCID
externalintegerfn  DISCID
!
! This function has exactly the same effect as calling DRESUME(-1, 0, 0).
! It should be called from the ONTRAP routine and uninhibits asynchronous
! contingencies.
!>
      DRESUME(-1,0,0); ! ALLOW ANY FURTHER ASYNC INTS
      RESULT =0
END ; ! DISCID
!
!-----------------------------------------------------------------------
!
!<DRESETCONTINGENCY
externalroutine  DRESET CONTINGENCY
!
! This procedure causes the first-set ONTRAP routine to be reinstated as
! the contingency procedure.
!>
INTEGER  J
      J = IN2(256 + 74)
      -> OUT UNLESS  J = 0
!
      CALLDR0=ACALLDR0
      CALLDR1=ACALLDR1
      CALLPSR=ACALLPSR
      CALLXNB=ACALLXNB
OUT:
      J = OUT(J, "")
END ; ! DRESET CONTINGENCY
!
!-----------------------------------------------------------------------
!
!<DRESUME
externalroutine  DRESUME(integer  LNB, PC, ADR18)
!
! This routine is provided first to notify Director that the subsystem's
! ONTRAP routine (see the PRIME CONTINGENCY procedure) has completed that
! section of its code during execution of which it could not reasonably
! resume following a subsequent asynchronous contingency.  Asynchronous
! contingencies are inhibited by Director until this notification is
! received, program or virtual store errors will cause looping.
!
! Likely tasks in this inhibited state (called the "ONTRAP state") are
!
!      i) determination of a new environment (e.g. an "ABORT" position,
!         or a diagnostic routine), or
!
!     ii) output of a text message, or
!
!    iii) determination of other subsequent action (e.g. ignore
!         asynchronous interrupt, and continue the former computation).
!
!
! Secondly, DRESUME provides the means for the ONTRAP routine to specify
! the original or a chosen new environment, for resumption of normal
! computation.
!
! The action taken is determined by the value of the parameter LNB.
! Parameter ADR18 is relevant only when LNB=0, and PC only when LNB is
! greater than zero.
!
!   LNB=-2      At process start-up, asynchronous interrupts are
!               inhibited until the subsystem calls this entry point
!               to DRESUME. This enables the subsystem to initialise
!               itself properly for action.  Subsequent calls of
!               this entry point have no effect.
!
!   LNB=-1      This entry point tells Director that the ONTRAP
!               routine has completed its critical operations and can
!               again accept asynchronous interrupts.  If a
!               contingency is in fact queued at the time that this
!               entry point is called by the ONTRAP routine, a new
!               invocation of the ONTRAP routine is immediately
!               created. The environment supplied, by procedure READ
!               ID, to the new ONTRAP routine is that following the
!               call of DRESUME in the previous ONTRAP routine.
!
!   LNB=0       This entry point may be called only from the ONTRAP
!               state (otherwise the process stops with a main Oper
!               message SIGNAL FAIL 10) following an asynchronous
!               contingency or program error (SIGNAL FAIL 9 following
!               a virtual store error).  The call informs Director
!               that computation is to be resumed at the point of
!               interruption.  Parameter ADR18 must point to a copy
!               of the environment supplied to the subsystem by the
!               READ ID procedure.  The process leaves the ONTRAP
!               state.
!
!   LNB>0       The values LNB, PC are transferred to the process
!               registers LNB and PC, and computation continues at PC
!               The value ADR18 is placed in the ACC, single length,
!               as a means of passing information to the new
!               computation.
!               The values assigned to the other registers are
!               indeterminate, therefore it is expected that PC will
!               point at or near an EXIT instruction so that a well-
!               defined environment can be reached.  The LNB value
!               must be more than 5 words below the SF at the time
!               of the contingency, otherwise SIGNAL FAIL 7 occurs.
!               The process leaves the ONTRAP state.
!>
INTEGER  CALLERS LNB,CALLERS PC,TYPE,DACT
RECORD  (PARMF)Q
RECORD (PARMF)NAME  P
SWITCH  DRES(-4:1)
      DACT=RESDACT
      *LSS_(LNB +0); ! CALLER'S LNB
      *ST_CALLERS LNB
      *LSS_(LNB +2)
      *ST_CALLERS PC; ! REQUIRED ONLY FOR DIAGNOSTIC PURPOSES, SEE BELOW
      TYPE=LNB
      TYPE=1 IF  TYPE>0 OR  TYPE<-4; ! INCLUDES REASONABLE LOCAL AND PUBLIC LNB VALUES
      -> DRES(TYPE)
DRES(-4):   ! DIRECTOR ENTRY TO DISPATCH ASYNC INTS
            ! (but only if there's something to dispatch and user is
            !  not in ONTRAP state).
      IF  AQD=0 OR  ONTRAP#0 THEN  RETURN 
      DACT=UHIDACT
      -> PONMESS
DRES(-2):      ! CLEAR INITIAL 'ASYNC INHIB' VALUE (SET NON-ZERO UNTIL
               ! SUBSYSTEM IS SET UP AND CALLS THIS ENTRY
      IF  ASYNC INHIB=100 START ; ! THIS HAS TO HAPPEN ONCE ONLY !
         ASYNC INHIB=0
         AQD=0; !  THROW AWAY
         FINISH 
      RETURN 
DRES(-1):      ! Leave 'ontrap' state and kick to dispatch async ints,possibly.
               ! (Subsystem entry)
      ! Cancel the VSerr flag for the case following a VSerr where we don't
      ! go through the ASACT(RESDACT) code. (The flag is intended only to
      ! stop a resume-where-you-were following a VSerr.
      VSERR=0
      ONTRAP=0
DRES(-3):      ! Kick to dispatch async ints.
      IF  AQD=0 THEN  RETURN 
      DACT=UHIDACT
      -> PONMESS
DRES(0):       ! RESUME WHERE YOU WERE
      IF  ONTRAP=0 THEN  DSTOP(10)
      IF  VSERR#0 THEN  DSTOP(9)
! SHOULD CHECK VALIDITY OF INFO SUPPLIED !!
      -> PONMESS
DRES(1):       ! RESUME AT THIS LNB/PC
      IF  LNB>>18=6 THEN  DSTOP(8)
      IF  ONTRAP=0 START 
         UNLESS  LNB<CALLERS LNB - 20 THEN  DSTOP(7)
         *LSS_(LNB +1)
         *ST_(1); ! SET BACK TO CALLER'S PSR
         *LSS_PC
         *ST_TOS 
         *LDTB_X'28000012'
         *LDA_ADR18
         *CYD_0
         *LLN_LNB
         *J_TOS 
      FINISH  ELSE  START 
         UNLESS  LNB<=RESUMEREGS_SF-20<CALLERS LNB THEN  DSTOP(7)
      FINISH 
PONMESS:
      P==RECORD(DIROUTP0)
      Q=0
      Q_DEST=((COM_ASYNC DEST + PROCESS)<<16) ! DACT
      Q_P1=LNB
      Q_P2=PC
      Q_P3=ADR18
      Q_P4=CALLERS PC; ! ONLY TO GET IT DISPLAYED IN SIGNAL DIAGNOSTICS
      IF  SIGMO&64#0 START 
         WRS("BEFORE OUT 6")
         DDUMP(ADDR(Q),ADDR(Q)+32,-1,-1)
         FINISH 
      P=Q
      *OUT_6; ! PON AND CONTINUE
! SHOULD NOT RETURN, EXCEPT FOR THE "UNINHIBIT" ENTRY
      RETURN  IF  DACT=UHIDACT
      Q=P
      WRS("AFTER OUT 6")
      DDUMP(ADDR(Q),ADDR(Q)+32,-1,-1)
      DSTOP(11); ! SHOULD NOT OCCUR
END ; ! DRESUME
!
!-----------------------------------------------------------------------
!
!<PRIMECONTINGENCY
externalintegerfn  PRIME CONTINGENCY(integer  DR0, DR1, J, XNBVALUE)
!
! The purpose of this procedure is to nominate a routine (the actual
! parameter) which is to be executed on the occurrence of various
! contingencies, both synchronous and asynchronous.  The types of
! contingencies which will invoke the ONTRAP routine are as follows:
!
!   Synchronous
!
!      *Virtual store errors.
!
!      *Program errors.
!
!      *Instruction counter interrupt (treated as program error).
!
!   Asynchronous
!
!      *Arrival of single-character "INT:" messages (as in INT:A, for
!         example, from an interactive terminal, or a TERMINATE to a
!         batch process from the main Oper console).
!
!      *Arrival of text messages from the machine operator or from
!         other processes.
!
!      *Arrival of "POFF" messages from other processes or from the
!         Supervisor (available only to privileged processes).
!>
! THIS FUNCTION IS CALLED WITH FORMAL AND ACTUAL PARAMETER OF TYPE
! ROUTINE, WHCIH LEADS TO FOUR WORDS BEING PASSED.
!     FOR AN EXTERNAL ROUTINE PARAMETER
!        DR0, DR1   ARE AN "EXT RT SPEC" DESCRIPTOR-DESCRIPTOR TO
!                    THE ROUTINE
!         Z          IS ZERO
!         XNBVALUE IS ZERO (NOT USED OR REQUIRED, THAT IS)
!     FOR AN INTERNAL ROUTINE PARAMETER
!        DR0, DR1    ARE A CODE DESCRIPTOR TO THE ROUTINE
!        Z           IS ZERO
!        XNBVALUE     POINTS TO THE DISPLAY OF THE OUTER PROCEDURE
!                     (THE ONE CALLING THIS FUNCTION)
      J=IN2(91)
      IF  J = 0 START 
         CALLPSR=D CALLERS PSR
         CALLDR0=DR0
         CALLDR1=DR1
         CALLXNB=XNBVALUE
!
         IF  ACALLDR0=-1 START 
            ! REMEMBER FIRST ENVIRONMENT SET, SO THAT IT CAN BE RESET BY "RESET CONTINGENCY"
            ACALLDR0=CALLDR0
            ACALLDR1=CALLDR1
            ACALLPSR=CALLPSR
            ACALLXNB=CALLXNB
         FINISH 
!
         ASYNC INHIB = 1 AND  AQD = 0 IF  ASYNC INHIB = 101
      FINISH 
!
      RESULT  = OUT(J, "")
END ; ! PRIME CONTINGENCY
!
!-----------------------------------------------------------------------
!
!<READID
externalintegerfn  READID(integer  ADR)
!
! This function provides the process environment as it was at the
! occurrence of the most recent contingency.  The information is written
! to the area whose address is given by parameter ADR.  Currently, 18
! words - the processor registers plus PC of erring or current
! instruction - are returned, but space should be left for 32 words
! altogether (for future extensions).
!
! The interrupt data remains available for repeated reading until a call
! of DRESUME (described below) is executed.  An error result of 53 is
! returned if no interrupt data is available, or of 45 if the address
! supplied is not accessible.
!>
! RESULT = 0    OK
!          53   NO INT DATA
INTEGER  J
RECORD (RF)NAME  R
      RESULT =53 IF  ONTRAP=0
!
      J = IN2(93)
      IF  J = 0 START 
         J = 45
         IF  VAL(ADR, 72, 1, D CALLERS PSR) = YES START 
            R==RECORD(ADR)
            R=RESUMEREGS
            J = 0
         FINISH 
      FINISH 
!
      RESULT  = OUT(J, "")
END ; ! READID
!
!-------------------end-of-included-text---------------------------------
!