!TITLE Testing New Subsystems
! This section addressses the practicalities of testing a new subsystem.
! In particular, it draws attention to the difficulties of testing code
! which initially has no access to the terminal I/O procedures and which
! cannot satisfactorily diagnose programming errors (that is, until
! correct operation of the contingency mechanisms has been mastered).
!
! We first describe in more detail the fields in the user record which
! are specially relevant to this situation, namely:
!
!               Field                                  DSFI TYPE value
!           Base file name                                   0
!           Director version number                          8
!           Director monitor level                          16
!           Contingency monitor level                       17
!           Director monitor filename                       19
!           Test basefile name                              35
!
! Director monitor level and contingency monitor level are cleared to
! zero following an IPL.
!<Test basefile name
! We have stated that "basefile name" specifies the name of the object
! file that is to be entered as the "subsystem", and that the version
! residing on a fixed site on the System disc is used if that name is
! null.  If the "test basefile name", is non-null, it overrides the
! setting of "basefile name", even if the latter is null, for the next
! invocation.  Thus if the subsystem under test fails disastrously the
! next log-on will yield the default or specified subsystem, which can
! then be used to study the failure and remake the test subsystem.
!>
endoflist 
!<Director monitor filename
! The Director monitor filename, if non-null, specifies a file belonging
! to the process owner, into which Director places the following:
!    * Process failure messages not diagnosed by the subsystem.
!    * Extra monitoring of Director procedure entries (see 'Director
!      monitor level'), or extra monitoring of contingencies.
!    * Text specified, by the process, to Director procedure DPRINTSTRING
!    * The contents of areas of store, printed by DDUMP.
!
! This monitoring file, which must not exceed 512 Kbytes in size, is
! used in a circular manner.
!>
!<Contingency monitor level
! For the case of being unable to satisfy references to Director
! procedures, and for other programming errors not otherwise diagnosable,
! the  field "contingency monitor level" will be useful.  This
! field is zeroed at IPL, it can be set by a call of Director procedure
! DSFI (TYPE=17).  Bits in this word have meanings as follows:
!
!   Bit value   Meaning and use
!      1        Print a routine trace-back on the occurrence of a
!               contingency, with contents of stack frames.
!      2        Print text describing the contingency in words and codes
!               ("CLASS/SUBCLASS").
!      4        Print a hexadecimal dump of the stack segment.
!      8  2**3      Print a hexadecimal dump of Director GLA.
!     16        Print the contents of the virtual memory (relating
!               segment number to filename and to disc address).
!     32        Print a de-assembly from the code segment of the area
!               around the value of the PC register at the time of the
!               contingency.
!     64        Print out the machine registers.
!
! A useful value for this field is 99.
!
! In order to diagnose the situation in which a subsystem has been unable
! to satisfy its references to Director procedures, it is useful to have
! a recognisable pattern in the places where the System Call descriptors
! are intended to be placed.  With the contingency monitor level 64, that
! pattern will be found in the printing of the machine registers DR0 and
! DR1 if the filling of the System Call descriptor was unsuccessful.
!
! The contingency monitor level field can also be set by typing, at the
! Oper console, a command of the form:
!           n/SIGMON m 0
! where   n   is the process number (as displayed on the Oper screen)
!         m   is an integer specifying the required value of the field,
!             as above.
!>
!<Director monitor level
! If Director monitor level is set non-zero, see SETDIRMON, a
! message is written to Dirlog after each Director procedure called,
! giving the values of the parameters supplied and the result returned.
!>
!<Director version
! A System disc has four fixed sites (256 Kbytes each), for up to four
! versions of Director.  Site zero is used by default. Setting the
! Director version field causes the next invocation of the process to use
! the specified Director version (0, 1, 2 or 3) rather than the default
! version (0).  This facility should only be used after consultation with
! the System Manager, as the contents of a given Director site cannot
! always be guaranteed to be valid.
!>
list 
!<DDUMP
externalroutine  DDUMP(integer  START, FINISH, CH, LIM)
!
! Prints a hexadecimal dump from virtual address START to virtual address
! FINISH to DIRLOG. The dump has either 16 if LIM = 16, or 32 bytes per
! line.
!>
INTEGER  SAVELOGACT, SAME, ADR, J, K, A, L, SAMEAS
STRING (31)ZEROES
      SAVELOGACT = LOG ACTION
      LOG ACTION = LOG ACTION & (¬DT)
!
      START = START & (¬3)
      FINISH = FINISH & (¬3)
      LIM = 32 UNLESS  LIM = 16
!
      SAME = 0; ! number of consecutive lines which are the same
      ADR = START & (¬(LIM - 1)); ! start of first 'line'
!
      WHILE  ADR < FINISH CYCLE 
         K = ADR + LIM - 4; ! useful number
!
         A = ADR; ! validation limits
         L = LIM
         IF  ADR < START C 
         THEN  L = L - (START - ADR) AND  A = START
         IF  ADR + LIM > FINISH C 
         THEN  L = L - (ADR + LIM - FINISH)
         IF  VAL(A, L, 0, 0) = 0 C 
         THEN  WRS("Address Validation Fails") AND  -> OUT
!
         IF  START + LIM <= ADR <= FINISH - LIM START 
            ! at least one whole line before and still to do
            SAMEAS = SAME + 1
            ZEROES = "ZEROES"
            CYCLE  J = ADR, 4, K
               SAMEAS = 0 AND  EXIT  UNLESS  INTEGER(J-LIM) = INTEGER(J)
               ZEROES = "SAME AS ABOVE" UNLESS  INTEGER(J) = 0
            REPEAT 
            SAME = SAMEAS
         FINISH  ELSE  SAME = 0
!
         IF  SAME < 2 START ; ! print address of line
            PRINTSTRING("(")
            PRHEX(ADR)
            PRINTSTRING(")  ")
         FINISH 
!
         WRS(ZEROES) IF  SAME = 1
!
         IF  SAME = 0 START 
            CYCLE  J = ADR, 4, K
               PRINTSTRING("  ")
               IF  START <= J < FINISH C 
               THEN  PRHEX(INTEGER(J)) C 
               ELSE  SPACES(8)
            REPEAT 
            SPACES(2)
            CYCLE  J = ADR, 1, K + 3
               CH = 32
               IF  START <= J < FINISH START 
                  CH = BYTEINTEGER(J)
                  CH = 32 UNLESS  32 < CH < 126
               FINISH 
               PRINTSYMBOL(CH)
            REPEAT 
            NEWLINE
         FINISH 
         ADR = ADR + LIM
      REPEAT 
OUT:
      LOG ACTION = SAVELOGACT
END ; ! DDUMP
!
!-----------------------------------------------------------------------
!
!<DPRINTSTRING
externalroutine  DPRINTSTRING(string (255)S)
!
! Allows a privileged process to write a string to DIRLOG.
!>
      PRINTSTRING(S) IF  DTRYING << 27 < 0
END ; ! OF DPRINTSTRING
!
!-----------------------------------------------------------------------
!
SYSTEMROUTINE  PRINTFL(LONGREAL  X,INTEGER  A,B)
END ; ! PRINTFL
!
!-----------------------------------------------------------------------
!
ROUTINE  PRINTSYMBOL(INTEGER  CH)
CONSTINTEGER  CH MAX = 655360; ! MAX NO OF CHS TO BE WRITTEN TO DIRLOG BY ONE PROCESS
CONSTINTEGER  SPOOL AT BYTES = 60000; ! SPOOLING OF DIRLOG TRIGGERED WHEN NO OF BYTES AVAILABLE EXCEEDS THIS VALUE
CONSTINTEGER  SEMANO = (1 <<  31) ! 1
!
CONSTINTEGER  MAX LINE = 132; ! excluding NL character
OWNSTRING (MAX LINE + 1) LINE
OWNINTEGER  LOG I = 1, CH COUNT = 0, SUPPRESS NL = NO
!
STRING (MAX LINE + 1) OUTLINE
INTEGER  I, BYTES, LEN, MUST TELL DIRECT, SAVE LOGACT
INTEGER  ULOG, DIRLOG, MAINLOG, DAT, USE SEMA, IVE GOT SEMA; ! BOOLEANS
INTEGER  SEMADR, J
RECORD (FHDRF)NAME  H
RECORD (DIRCOMF)NAME  DIRCOM
RECORD (PARMF) P
      CH = CH & 255
      RETURN  IF  SUPPRESS NL = YES AND  (CH = NL OR  CH = ' ')
      RETURN  UNLESS  CH = NL OR  32<= CH <=126
!
      SAVE LOGACT = LOG ACTION
      DAT = SAVE LOGACT & DT
!
      IF  LINE = "" START ; ! start of line
         SUPPRESS NL = NO
         UNLESS  DAT = NO C 
      THEN  LINE="DT: ".DATE." ".TIME." ".PROCUSER." ".ITOS(PROCESS)." "
      FINISH 
!
      LINE = LINE.TOSTRING(CH)
      LEN = LENGTH(LINE)
      IF  LEN = MAX LINE START 
         LINE = LINE.SNL 
         LEN = LEN + 1
         CH = NL
      FINISH 
      RETURN  UNLESS  CH = NL
!
! Output the string
!
! Move string to allow recursion (for monitor printing)
      SUPPRESS NL = YES
      OUTLINE = LINE
      LINE = ""
      ULOG = SAVE LOGACT & WRTOF
      DIRLOG = SAVE LOGACT & DLOG
      MAINLOG = SAVE LOGACT & LOG
      IF  DIRLOG#NO AND  VAL(DIRLOGAD, DIRLOG KB<<10, W, 0)=0 C 
      THEN  DIRLOG=NO AND  MAINLOG = YES
!
! Remove dirlog monitoring to avoid screw-ups over the dirlog semaphore.
      LOG ACTION = LOG ACTION & (¬DLOG)
!
! Take sema if appropriate
      IVE GOT SEMA = NO
!
      *STLN_I
!
      IF  I >> 18 = SIG STACK SEG OR  DIRLOG = NO C 
      THEN  USE SEMA = NO C 
      ELSE  USE SEMA = YES
!
      IF  USE SEMA = YES AND  GOT SEMA = NO START 
         DIRCOM == RECORD(SYSAD(DIRCOM KEY, -1))
         SEMADR = ADDR(DIRCOM_DIRLOGSEMA)
         J = PP(SEMADR, SEMANO,"PRINTSYMBOL")
         IVE GOT SEMA = YES IF  J=0
      FINISH 
!
! Move string to appropriate destinations
      J = STRING TO FILE(LEN, ADDR(OUTLINE)+1, FILE1AD) UNLESS  ULOG = NO
      J = STRING TO FILE(LEN, ADDR(OUTLINE)+1, DIRLOGAD) UNLESS  DIRLOG = NO
!
      UNLESS  MAINLOG = NO START 
         CYCLE  J = 1, 1, LEN
            CH = CHARNO(OUTLINE, J)
            BYTEINTEGER(OUTPAD + LOG I) = CH
            IF  LOG I = 31 OR  CH = NL START 
               BYTEINTEGER(OUTPAD) = LOG I
               *OUT_1
               LOG I = 1
            FINISH  ELSE  LOG I = LOG I + 1
         REPEAT 
      FINISH 
      !
      ! Is DIRLOG full enough to require spooling?
      MUST TELL DIRECT = NO
      UNLESS  DIRLOG = NO START 
         H == RECORD(DIRLOGAD)
         BYTES = H_NEXT CYCLIC - H_READ TO
         IF  BYTES < 0 C 
         THEN  BYTES = BYTES + (H_NEXT FREE BYTE - H_TXT REL ST)
         IF  BYTES > SPOOL AT BYTES AND  H_SEMA = 0 START 
            MUST TELL DIRECT = YES
            H_SEMA = -1; ! to show that SOMEONE is telling DIRECT
         FINISH 
         CH COUNT = CH COUNT + LEN
         IF  CH COUNT > CH MAX AND  PROCESS # 1 START 
            SAVE LOGACT = SAVE LOGACT & (¬DLOG)
            DOPER2("TOO MUCH OUTPUT")
         FINISH 
      FINISH 
      IF  IVE GOT SEMA = YES THEN  VV(SEMADR, SEMANO)
      IF  MUST TELL DIRECT = YES START 
         P = 0
         P_DEST = X'FFFF0028'
         J = DPON3I("DIRECT", P, 0, 1, PON AND CONTINUE)
      FINISH 
      LOG ACTION = SAVE LOGACT
END ; ! PRINTSYMBOL
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  PRHEX(INTEGER  I)
      PRINTSTRING(HTOS(I,8))
END ; ! PRHEX
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  PREC(STRING (255)S, RECORD (PARMF)NAME  P, INTEGER  N)
INTEGER  A,I
      PRINTSTRING(S)
      A = ADDR(P)
      CYCLE  I = 0, 4, 28
         PRHEX(INTEGER(A + I))
         SPACE
      REPEAT 
      NEWLINE IF  N = 0
END ; ! OF PREC
!
!-----------------------------------------------------------------------
!
SYSTEMROUTINE  WRITE(INTEGER  I,PL)
INTEGER  SIGN
STRING (31) S
      SIGN = ' '
      IF  I<0 START 
         SIGN = '-'
         IF  I=X'80000000' THEN  I=X'7FFFFFFF' ELSE  I=-I
      FINISH 
      S=ITOS(I)
      IF  LENGTH(S)<PL THEN  SPACES(PL-LENGTH(S))
      PRINTSYMBOL(SIGN)
      PRINTSTRING(S)
END ; ! WRITE
!
!-----------------------------------------------------------------------
!
SYSTEMROUTINE  IOCP(INTEGER  EP,N)
INTEGER  NUM,SYM,J
!%CONSTSTRING(15)%ARRAY IM(0:17)=  %C
!      'INVALID',
!      'READSYMBOL',
!      'NEXTSYMBOL',
!      'PRINTSYMBOL',
!      'READCH',
!      'PRINTCH',
!      'RECONSTRUCT',
!      'PRINTSTRING',
!      'SELECTINPUT',
!      'SELECTOUTPUT',
!      'ISO CARD',
!      'CHOP CUR OUT',
!      'SET INMARG',
!      'SET OUTMARG',
!      'SET READ ADDR',
!      'PRINTSTRING',
!      'CLOSE STREAM',
!      'MULSYMBOL'
SWITCH  IO(0:17)
      EP=0 UNLESS  0<EP<=17
      -> IO(EP)
IO(3):   ! PRINTSYMBOL(N)
IO(5):   ! PRINTCH(N)
      PRINTSYMBOL(N)
      RETURN 
IO(7):   ! PRINTSTRING
IO(15):  ! PRINTSTRING (ONLY VALID CHARS ALLOWED)
      J=0; NUM=BYTEINTEGER(N)
      WHILE  J<NUM CYCLE 
         J=J+1
         PRINTSYMBOL(BYTEINTEGER(N+J))
      REPEAT 
      RETURN 
IO(17):  ! MULSYMBOL
      NUM=(N>>8) & 255
      SYM=N&255
      J=0
      WHILE  J<NUM CYCLE 
         PRINTSYMBOL(SYM)
         J=J+1
      REPEAT 
      RETURN 
IO(0):
IO(1):IO(2):IO(4):IO(6):IO(8):IO(9):
IO(10):IO(11):IO(12):IO(13):IO(14):IO(16):
      WRSN("ILLEGAL CALL ON IOCP, EP =", EP)
END ; ! IOCP
!
!-----------------------------------------------------------------------
!
EXTERNALROUTINE  SYMBOLS(INTEGER  N, SYMBOL)
      IF  0 < N < 256 AND  0 <= SYMBOL < 256 C 
      THEN  IOCP(17, (N << 8) ! SYMBOL)
END 
!
!
!<SETDIRMON
! %externalroutine SETDIRMON(%string(255)S)
!
! If S is null or is the single character '0', Director monitoring is
! switched off, else it is switched on.  When monitoring is on, a record
! is written to Dirlog for each Director procedure called giving the
! parameters used and its result.
!>
!
!-------------------end-of-included-text---------------------------------
!