Trailing-Edge
-
PDP-10 Archives
-
AP-D471B-SB_1978
-
devser.bli
There are no other files named devser.bli in the archive.
!***COPYRIGHT (C) 1974, 1975, 1976, 1977 DIGITAL EQUIPMENT CORP., MAYNARD, MASS.***
MODULE DEVSER(RESERVE(#11,#12,#13,#14),SREG=#17,FREG=#16,DREGS=4,
VREG=#15,MLIST,TIMER=EXTERNAL(SIX12),FSAVE)=
BEGIN
REQUIRE DATA.BLI;
EXTERNAL MOVE,
UNLINK;
COMMENT;
! THIS FILE CONTAINS THE DISK SERVICE ROUTINES FOR ROLLING AND/OR
! FAILSOFTING INCOMING MESSAGES, AND "FAILSOFTING" OUTGOING
! DEFERRED MESSAGES.
! THE FAILSOFT/ROLLOUT FILE FOR INCOMING MESSAGES IS TREE STRUCTURED
! IN THE FOLLOWING MANNER:
! ( GROUP ON DISK )
! ----------- --------- ----------- -----------
! ! LEAF !====>! DSKGH !====>! DSK !====>! DSK !...
! ! POINTER ! ! TABLE ! ! MESSAGE !==V ! MESSAGE !
! ! TABLE ! --------- ! HEADER ! : ! HEADER !
! ----------- . ----------- : -----------
! . :
! . V
! ----------
! ! CHUNKS !
! ----------
!
! ( GROUP IN CORE )
! ----------- --------- ---------- -----------
! ! LEAF !====>! GROUP !====>! MESSAGE !====>! MESSAGE ! ...
! ! OF THE ! ! HEADER! ! HEADER !==V ! HEADER !
! ! TREE ! --------- ----------- : -----------
! ----------- . :
! . :
! . V
! ----------
! ! CHUNKS !
! ----------
! LEAF POINTER TABLE
!
! -----------
! ! T1 ! T2 !
! -----------
! ! . !
! ---- . ----
! ! . !
! ---- . ----
! ! . !
! -----------
! ! ! TN !
! -----------
!
! THE GROUP POINTER TABLE CONSISTS OF ONE HALF-WORD PER LEAF AND
! THE CONTENTS OF THESE HALFWORDS ARE POINTERS TO THE BEGINNING
! OF THE DSKGHTAB ASSOCIATED WITH EACH LEAF. THE GROUP POINTER
! TABLE RESIDES AT A FIXED LOCATION IN THE ROLLOUT FILE. AS OF
! THIS WRITING, THAT LOCATION IS PTL .IBASE AND UP AS REQUIRED.
! CHECK DATBAS FOR THE CURRENT ADDRESS.
! DISK GROUP HEADER TABLE (DSKGHTAB):
!
! -------------------------------------------------
! WORD 0: ! POINTER TO PREV DSKGH ! POINTER TO NEXT DSKGH ! ====V
! ------------------------------------------------- :
! ! UNUSED ! (2):
! ------------------------------------------------- :
! ! UNUSED ! (3):
! ------------------------------------------------- :
! ! UNUSED ! (4):
! ------------------------------------------------- :
! ENTRY 1: ! UNUSED ! POINTER TO FIRST DSKMH! (5):
! ------------------------------------------------- :
! ! TRANSACTION SEQUENCE NUMBER ! (6):
! ------------------------------------------------- :
! ! SENDER ID ! :
! ------------------------------------------------- :
! ! UNUSED ! :
! ------------------------------------------------- :
! ENTRY 2: ! . ! :
! ------------------------------------------------- :
! ! . ! :
! ------------------------------------------------- :
! ! . ! :
! ------------------------------------------------- :
! :
! V=============================================================<
! :
! : -------------------------------------------------
! >====> ! POINTER TO PREV DSKGH ! POINTER TO NEXT DSKGH !
! -------------------------------------------------
! ! 0 !
! -------------------------------------------------
! ! 0 !
! -------------------------------------------------
! ! 0 !
! -------------------------------------------------
! ENTRY N: ! !
! -------------------------------------------------
! ! . !
! -------------------------------------------------
! ! . !
! -------------------------------------------------
! ! . !
! -------------------------------------------------
! ! . !
! -------------------------------------------------
! DISK MESSAGE HEADER (DSKMH):
!
! -------------------------------------------------
! WORD 0: ! POINTER TO PREV DSKMH ! POINTER TO NEXT DSKMH ! (1)
! -------------------------------------------------
! ! DATE ! (2)
! -------------------------------------------------
! ! TIME ! (3)
! -------------------------------------------------
! ! POINTER TO DSKGHTAB ! (4)
! -------------------------------------------------
! ! TRANSACTION SEQUENCE NUMBER ! (?)
! -------------------------------------------------
! ! SOT ! ENDI ! (?)
! -------------------------------------------------
! FIRST RI: ! RETRIEVAL INFORMATION ! (5)
! -------------------------------------------------
! ! . !
! -------------------------------------------------
! ! . !
! -------------------------------------------------
! ! . !
! -------------------------------------------------
! LAST RI: ! -1 (6) ! NEXT BLOCK OF RI'S ! ====V
! ------------------------------------------------- :
! :
! V=============================================================<
! :
! : -------------------------------------------------
! >====> ! COPY FROM FIRST MH ! (7)
! -------------------------------------------------
! ! 0 !
! -------------------------------------------------
! ! 0 !
! -------------------------------------------------
! ! 0 !
! -------------------------------------------------
! FIRST RI: ! RETRIEVAL INFORMATION !
! -------------------------------------------------
! ! . !
! -------------------------------------------------
! ! . !
! -------------------------------------------------
! ! . !
! -------------------------------------------------
! LAST RI: ! -1 ! NEXT BLOCK OF RI'S ! ====:
! ------------------------------------------------- :
! ETC.
!
! (1) POINTER TO NEXT MH IS THE PARTICLE NUMBER OF THE NEXT
! DSKMH PARTICLE ( OR ZERO IF NONE OR IF EXTENDED MH)
! IF EXTENDED MH, ONLY THE LAST EXTENSION HAS A POINTER
! TO THE NEXT
! (2) IF FAILSOFTING THEN THE DATE OF THE MESSAGE IS STORED,
! OTHERWISE 0.
! (3) TIME IS TREATED THE SAME AS DATE
! (4) XWD OFFSET_IN_THE_PARTICLE, PARTICLE_NUMBER
! (5) RI ( RETREIVAL INFORMATION ) CONSISTS OF XWD NUMBER_OF_
! CONTIGIOUS_PARTICLES, PARTICLE_NUMBER_OF_THE_FIRST_PARTICLE
! (6) IF THE NUMBER_OF_PARTICLES IS -1 THEN THE PARTICLE_
! NUMBER_OF_THE_FIRST_PARTICLE IS THE POINTER TO THE NEXT
! EXTENSION.
! (7) THE HEADER INFORMATION IN THE EXTENSIONS IS JUST A COPY
! OF THAT IN THE FIRST
!
!
!
! CHUNK_PARTICLE FORMAT:
!
! SAME AS IN-CORE:
!
! -------------------------------------------------
! ! POS ! SIZE ! ENDI ! GARBAGE !
! -------------------------------------------------
! ! WORD COUNT ! BYTE COUNT !
! -------------------------------------------------
! ! DATA !
! -------------------------------------------------
! ! . !
! -------------------------------------------------
! ! . !
! -------------------------------------------------
! ! . !
! -------------------------------------------------
FORWARD
DSKMSG(4),
STOREDSKGH(6),
STORENEXTMH(1),
DELDSKGH(2),
FINDNEXTMH(1),
ZAPDSKMHS(1);
EXTERNAL TRMDSKTAB,
LEAFDSKTAB;
COMMENT;
! 1. FAILSOFT GROUP HEADER
! == ========== ===== ======
! CALLING ARGUEMENTS:
! 1) LEAF NUMBER THIS TRANSACTION BELONGS TO
! 2) TRANSACTION SEQUENCE NUMBER
! 3) THE SENDER
! 4) POINTER TO MESSAGE HEADER
! RETURNS: DISK GH ADDR
! SIDE EFFECTS: DISK ADDRESS SET IN MH
! BLISS CALLING SEQUENCE: IF .FAILSOFTING THEN
! CHKGH( .LEAFNO, .TSN, .SENDERID, .MHPTR )
! FUNCTION: WRITE THE MESSAGE ON A FAILSOFT FILE
! AND MODIFY THE LEAF DISK ADDR TABLE
GLOBAL ROUTINE CHKGH( LEAFNO, TSN, SENDERID, MHPTR)=
BEGIN
REGISTER FIRSTMH,
DSKGH;
MAP FORMAT MHPTR;
MHPTR[ M0DSKADDR ] _ FIRSTMH _ DSKALL( 1 ); ! ALLOCATE THE FIRST MH OF THE GROUP
DSKGH _ STOREDSKGH(.LEAFNO,.FIRSTMH,.TSN,.SENDERID,LEAFDSKTAB,.IBASE);
!ADD ENTRY TO FAILSOFT FILE
STORENEXTMH( .DSKGH, .FIRSTMH); ! ADD AN ENTRY TO THE NEXTMH TABLE
DSKMSG( .MHPTR[ M0INCHNK ], ! FAILSOFT
.MHPTR[ M0DATE ], ! THE CHUNK
.MHPTR[ M0TIME],
.DSKGH,
.MHPTR[ M0SOT ]);
! RETURN XWD
.DSKGH ! OFFSETT,,DSKGHADDR
END;
COMMENT;
! 2. FAILSOFT MESSAGE
! == ========== =======
! CALLING ARGUEMENTS:
! 1) DISK ADDR OF DSKGH
! 2) THE DATE THIS MESSAGE WAS RECEIVED
! 3) THE TIME THIS MESSAGE WAS RECEIVED
! 4) POINTER TO THE CHUNKS OF THE MESSAGE
! 5) START OF TEXT
! RETURNS: LH) 0
! RH) DISK ADDRESS OF THE MESSAGE
! BLISS CALLING SEQUENCE: IF .FAILSOFTING THEN
! CHKMSG(.DSKADDR,.DATE,.TIME,
! .CHUNKPTR,.SOT)
! FUNCTION: WRITE THE MESSAGE ON A FAILSOFT FILE
GLOBAL ROUTINE CHKMSG(DSKGHADDR,DATE,TIME,CHUNKPTR,SOT)=
BEGIN
DSKMSG( .CHUNKPTR, .DATE, .TIME, .DSKGHADDR, .SOT)
END;
COMMENT;
! 3. GPURGE
! == ======
! CALLING ARGUEMENTS: DISK ADDRESS OF THE DSKGH
! RETURNS: 0 ALWAYS
! BLISS CALLING SEQUENCE: GPURGE(.DSKADDR)
! FUNCTION: PURGE THE GROUP FROM THE FAILSOFT/ROLLOUT FILE ON DISK
GLOBAL ROUTINE GPURGE(DSKGHADDR)=
BEGIN
REGISTER
DSKMH;
DSKMH _ DELDSKGH( .DSKGHADDR, LEAFTYPE); ! DELETE THE DISK GROUP HEADER
DPAT(); ! FORCE THE PARTICLE ALLOCATION TABLES TO BE WRITTEN
! NOTE: IF THE SYSTEM CRASHES BEFORE THIS ROUTINE IS
! COMPLETED, THERE WILL BE LOST PARTICLES, BUT
! THE GROUP WILL BE DELETED AS FAR AS DSKGHTAB
! IS CONCERNED.
ZAPDSKMHS( .DSKMH); ! KILL ALL THE MESSAGES IN THIS GROUP
DPAT(); ! FORCE THE PARTICLE ALLOCATION TABLES TO BE WRITTEN
END;
COMMENT;
! 4. ROLLIN
! == ======
! CALLING ARGUEMENTS: 1) DISK ADDRESS OF MESSAGE TO ROLLIN
! 2) THE DISK ADDRESS OF THE GROUP HEADER
! FOR THIS MESSAGE
! RETURNS: POINTER TO FIRST CHUNK OF THE MESSAGE
! BLISS CALLING SEQUENCE: POINTER _ ROLIN(.DSKADDR,.DSKGHADDR)
! FUNCTION: ROLL IN A MESSAGE FROM THE ROLLOUT OR FAILSOFT FILE
! MAKES CHUNKS IN CORE.
GLOBAL ROUTINE ROLIN(DSKADDR, DSKGHADDR)=
BEGIN
OWN DSKMH[PTLSIZE];
STRUCTURE STR[I,J,K] = .STR[.I]<.J,.K>;
MAP STR DSKMH;
LABEL LOOP;
REGISTER
J,
PTLTOREAD,
CHUNKPTR,
PTLLEFT;
LOCAL
FIRST,
LAST,
AMT,
BUFFADDR;
MAP FORMAT CHUNKPTR;
MAP FORMAT BUFFADDR;
MAP FORMAT LAST;
MOVE( DREAD( .DSKADDR), DSKMH, PTLSIZE);
! GET THE FIRST DSKMH OF THE MSG TO ROLL IN
J _ FIRST _ 0; ! INITALIZE
PTLTOREAD _ .DSKMH[DM0RIPTL(.J)]; ! GET RETRIEVAL INFORMATION (RI)
PTLLEFT _ .DSKMH[DM0RICOUNT(.J)];
LOOP: REPEAT
BEGIN
BUFFADDR _ DREAD( .PTLTOREAD); ! READ IN A BUFFER
CHUNKPTR _ GETMEM( AMT _ .BUFFADDR[C0WORDS] + C0HEADSIZE);
! GET ROOM FOR THE CHUNK IN CORE
MOVE( .BUFFADDR, .CHUNKPTR, .AMT);
! AND MOVE IT INTO CORE
IF .FIRST EQL 0 THEN ! IF FIRST CHUNK
LAST _ FIRST _ .CHUNKPTR ! SAVE ADDR OF FIRST CHUNK
! AND POINTER TO PREVIOUS
ELSE LAST _ LAST[C0LINK] _ .CHUNKPTR;
! ELSE LINK PREVIOUS CHUNK TO THIS ONE
IF .CHUNKPTR[C0LINK] EQL 0 THEN LEAVE LOOP;
! IF LAST CHUNK THEN GET OUT OF THIS LOOP
! OTHERWISE GET NEXT PARTICLE TO READ
IF (PTLLEFT _ .PTLLEFT - 1) GTR 0 THEN
PTLTOREAD _ .PTLTOREAD + 1
ELSE
BEGIN
J _ .J + 1;
SELECT (PTLLEFT _ .DSKMH[DM0RICOUNT(.J)]) OF
NSET
#777777: BEGIN
MOVE( DREAD( .DSKMH[DM0RIPTL(.J)]), DSKMH, PTLSIZE);
J _ 0;
PTLLEFT _ .DSKMH[DM0RICOUNT(.J)];
PTLTOREAD _ .DSKMH[DM0RIPTL(.J)]
END;
0: INFORM ( PAZ 'FRI NOT FOUND@', 0, 0, 0, 0, 0);
OTHERWISE: PTLTOREAD _ .DSKMH[DM0RIPTL(.J)];
TESN
END
END;
.FIRST ! RETURN POINTER TO THE FIRST CHUNK
END;
COMMENT;
! 5. ROLLOUT GROUP HEADER
! == ======= ===== ======
! FUNCTION: FAILSOFT THE GROUP HEADER, MESSAGE HEADER, RETURN
! DISK ADDRESS. DEALLOCATE THE CORE BELONGING TO THE
! CHUNKS OF THE MESSAGE.
GLOBAL ROUTINE ROLGH(LEAFNO, TSN, SENDERID, MHPTR)=
BEGIN
EXTERNAL DCHNKS;
REGISTER FIRSTMH,
DSKGH;
MAP FORMAT MHPTR;
MHPTR[ M0DSKADDR ] _ FIRSTMH _ DSKALL( 1 ); ! ALLOCATE THE FIRST MH OF THE GROUP
DSKGH _ STOREDSKGH(.LEAFNO,.FIRSTMH,.TSN,.SENDERID,LEAFDSKTAB,.IBASE);
STORENEXTMH( .DSKGH, .FIRSTMH); ! ADD AN ENTRY TO THE NEXTMH TABLE
DSKMSG( .MHPTR[ M0INCHNK ],0,0,.DSKGH,.MHPTR[ M0SOT ]);
DCHNKS(.MHPTR); ! RETURN THE CHUNKS
.DSKGH ! OFFSETT,,DSKGHADDR
END;
COMMENT;
! 6. ROLLOUT MESSAGE
! == ======= =======
! CALLING ARGUEMENTS: 1) THE DISK GH ADDRESS
! 2) POINTER TO FIRST CHUNK OF THE MESSAGE
! 3) START OF TEXT
! RETURNS: LH) 0
! RH) DISK ADDRESS OF THE MESSAGE
! BLISS CALLING SEQUENCE: IF NOT .FAILSOFTING THEN
! DSKADDR _ ROLMSG(.DSKGHADDR,.CHUNKPTR,.SOT)
! FUNCTION: ALLOCATE DISK SPACE, INITIATE DISK WRITE, AND RETURN
! DISK ADDRESS. DEALLOCATE THE CORE BELONGING TO THE
! CHUNKS OF THE MESSAGE.
GLOBAL ROUTINE ROLMSG(DSKGHADDR,CHUNKPTR,SOT)=
BEGIN
REGISTER VALUE,
CHNK;
MAP FORMAT CHNK;
VALUE _ DSKMSG( .CHUNKPTR, 0, 0, .DSKGHADDR, .SOT);
CHNK _ .CHUNKPTR;
DO DELCHNK(.CHNK) WHILE (CHNK _ .CHNK[ C0LINK ]) NEQ 0;
.VALUE
END;
COMMENT;
! 7. DEFERRED GROUP ROLLOUT
! == ======== ===== =======
! CALLING ARGUEMENTS: 1) THE LOGICAL TERMINAL NUMBER
! 2) THE DISK ADDRESS OF THE DSKMH
! 3) THE TSN ASSIGNED
! 4) THE SENDER
! RETURNS: DISK GROUP ADDRESS
! BLISS CALLING SEQUENCE: DSKADDR _ DROLGH(.SRCPTR[S0LTTYNO],
! .DSKMHADDR,
! .MHPTR[M0TSN],
! .MHPTR[M0SOT);
! FUNCTION: TO WRITE A DISK GROUP HEADER AND LINK IT TO THE
! DSK DESTINATION POINTER TABLE
GLOBAL ROUTINE DROLGH( LTTYNO, DSKMH, TSN, SENDERID)=
BEGIN
STOREDSKGH( .LTTYNO, .DSKMH, .TSN, .SENDERID, TRMDSKTAB, .OBASE ) ! ENTER INTO FAILSOFT
END;
COMMENT;
! 8. DEFERRED MESSAGE ROLLOUT
! == ======== ======= =======
! CALLING ARGUEMENTS: 1) POINTER TO THE FIRST CHUNK OF THE
! MESSAGE
! 2) THE DISK ADDRESS OF THE MESSAGE HEADER
! OF THE LAST MESSAGE
! RETURNS: LH) 0
! RH) DISK ADDRESS OF THE MESSAGE
! BLISS CALLING SEQUENCE: DSKADDR _ DROLMSG(.CHUNKPTR,
! .DSKMHADDR)
! FUNCTION: ALLOCATE DISK SPACE, INITIATE DISK WRITE, DELETES CHUNKS,
! AND RETURN DISK ADDRESS.
GLOBAL ROUTINE DROLMSG(CHUNKPTR, DSKMHADDR)=
BEGIN
REGISTER
NEXT,
FIRSTMH,
RET;
MAP FORMAT CHUNKPTR;
RET _ IF .DSKMHADDR EQL 0 THEN
BEGIN
FIRSTMH _ DSKALL( 1 ); ! ALLOCATE THE FIRST MH OF THE GROUP
FIRSTMH _ .FIRSTMH<RH>; !WANT ONLY PARTICLE NUMBER
STORENEXTMH( .FIRSTMH, .FIRSTMH); ! ADD AN ENTRY TO THE NEXTMH TABLE
DSKMSG( .CHUNKPTR, ! FAILSOFT
0, ! THE CHUNK
0,
.FIRSTMH,
0 )
END
ELSE DSKMSG( .CHUNKPTR, 0, 0, .DSKMHADDR, 0);
WHILE .CHUNKPTR NEQ 0 DO
BEGIN
NEXT _ .CHUNKPTR[C0LINK];
DELCHNK( .CHUNKPTR );
CHUNKPTR _ .NEXT
END;
.RET
END;
COMMENT;
! 9. DEFERRED GROUP DELETION
! == ======== ===== ========
! CALLING ARGUEMENTS: 1) DISK ADDRESS OF GROUP TO DELETE
! 2) SAVE FLAG - IF TRUE THEN DON'T DELETE
! THE MESSAGES OF THIS GROUP
! BECAUSE THEY BELONG TO SOMEONE ELSE
! ALSO
! RETURNS: 0 ALWAYS
! BLISS CALLING SEQUENCE: POINTER _ DGPURGE(.DSKGHADDR,.SAVEFLAG)
! FUNCTION:
GLOBAL ROUTINE DGPURGE(DSKGHADDR,SAVEFLAG)=
BEGIN
REGISTER
DSKMH;
DSKMH _ DELDSKGH( .DSKGHADDR, TERMINAL ); ! DELETE THE GROUP HEADER
DPAT(); ! FORCE THE PARTICLE ALLOCATION TABLES TO BE WRITTEN
IF .SAVEFLAG THEN RETURN;
ZAPDSKMHS( .DSKMH ); ! THEN DELETE THE MESSAGES
DPAT(); ! FORCE THE PARTICLE ALLOCATION TABLES TO BE WRITTEN
END;
COMMENT;
! AUX ROUTINES
! ROUTINE DSKMSG
! ======= ======
! THIS ROUTINE WRITES A MESSAGE TO THE DISK.
! IT CREATES A DSKMH, AND WRITES EACH PARTICLE OUT, STORING
! THE PARTICLE'S ADDRESS IN THE DSKMH. WHEN ALL THE PARTICLES
! ARE WRITTEN, THE DSKMH IS ITSELF WRITTEN TO DISK
ROUTINE DSKMSG( CHUNKPTR, DATE, TIME, DSKGHADDR, SOT) =
BEGIN
OWN DSKMH[PTLSIZE];
STRUCTURE STR[I,J,K] = .STR[.I]<.J,.K>;
MAP STR DSKMH;
LABEL LOOP;
REGISTER
BUFFADDR,
PTLLEFT,
J,
PTLTOFILL;
LOCAL NEXTMH,
DSKMHPTL,
INDEX,
PTLREQ,
CHUNK,
ENDI,
PTLGOTTEN;
MAP FORMAT INDEX;
MAP FORMAT CHUNK;
MAP FORMAT BUFFADDR;
MAP FORMAT CHUNKPTR;
IF (INDEX _ FINDNEXTMH( .DSKGHADDR)) EQL -1 THEN
INFORM( PAZ 'FDSKMSG CALLED BUT NO NEXT GH@',0,0,0,0,0);
DSKMHPTL _ .INDEX[ NM0NEXTMH ];
ZERO( DSKMH, DSKMH + PTLSIZE - 1);
DSKMH[DM0DATE] _ .DATE;
DSKMH[DM0TIME] _ .TIME;
DSKMH[DM0DSKGHADDR] _ .DSKGHADDR;
DSKMH[DM0SOT] _ .SOT;
PTLREQ _ 0;
CHUNK _ .CHUNKPTR;
WHILE .CHUNK NEQ 0 DO
BEGIN
PTLREQ _ .PTLREQ + 1;
ENDI _ .CHUNK[ C0ENDI];
CHUNK _ .CHUNK[C0LINK]
END;
DSKMH[DM0ENDI] _ .ENDI;
IF .ENDI GEQ EGI THEN
BEGIN
INDEX[ NM0GHADDR ] _ INDEX[ NM0NEXTMH ] _ NEXTMH _ 0
END
ELSE
BEGIN
NEXTMH _ INDEX[ NM0NEXTMH ] _ DSKALL( 1 );
END;
J _ PTLLEFT _ BUFFADDR _ 0;
WHILE .CHUNKPTR NEQ 0 DO
BEGIN
! GET PTL TO FILL
IF (PTLLEFT _ .PTLLEFT - 1) GTR 0 THEN PTLTOFILL _ .PTLTOFILL + 1
ELSE
BEGIN
LOOP: REPEAT
BEGIN
PTLGOTTEN _ DSKALL( .PTLREQ);
PTLLEFT _ .PTLGOTTEN<LH>;
PTLTOFILL _ .PTLGOTTEN<RH>;
PTLREQ _ .PTLREQ - .PTLLEFT;
IF .J EQL LASTRI AND .PTLREQ NEQ 0 THEN
BEGIN
DSKMH[DM0RICOUNT(.J)] _ -1;
DSKMH[DM0RIPTL(.J)] _ .PTLTOFILL;
PTLREQ _ .PTLREQ + .PTLLEFT;
BUFFADDR _ DGET( .DSKMHPTL);
MOVE( DSKMH, .BUFFADDR, PTLSIZE);
DWRITE( .BUFFADDR, .DSKMHPTL);
ZERO( DSKMH + FIRSTRI, DSKMH + PTLSIZE - 1); !ZAP RI'S IN NEW DSKMH
DSKMHPTL _ PTLTOFILL;
IF .PTLLEFT GTR 1 THEN DSKDEA(.PTLLEFT-1,.PTLTOFILL+1);
J _ 0
END
ELSE
BEGIN
DSKMH[DM0RI(.J)] _ .PTLGOTTEN;
J _ .J + 1;
LEAVE LOOP
END
END
END;
! GOT PTL TO FILL
BUFFADDR _ DGET( .PTLTOFILL); ! GET THE BUFFER TO STUFF
MOVE( .CHUNKPTR, .BUFFADDR<RH>, .CHUNKPTR[C0WORDS] + C0HEADSIZE); ! AND STUFF AWAY
DWRITE( .BUFFADDR<RH>, .PTLTOFILL); ! THEN SEND IT OUT
CHUNKPTR _ .CHUNKPTR[C0LINK]
END;
DSKMH[DM0NEXTMH] _ .NEXTMH;
BUFFADDR _ DGET( .DSKMHPTL);
MOVE( DSKMH, .BUFFADDR, PTLSIZE);
DWRITE( .BUFFADDR, .DSKMHPTL);
IF .NEXTMH NEQ 0 THEN !IF AWAITING MORE INPUT
BEGIN
BUFFADDR _ DGET(.NEXTMH); !GET THE NEXT MH PARTICLE
ZERO(.BUFFADDR, .BUFFADDR + PTLSIZE -1); !AND CLEAR IT
DWRITE(.BUFFADDR, .NEXTMH) !CAUSE IT'S REALLY EMPTY
END;
DPAT(); !FORCE OUT FAILSOFT FILE
.DSKMHPTL
END;
COMMENT;
! ROUTINE ZAPDSKMHS
! ======= =========
! THIS ROUTINE DELETES ALL THE MESSAGES OF A GIVEN HEAD MH
ROUTINE ZAPDSKMHS( DSKMH) =
BEGIN
REGISTER
BUFFADDR,
NEXTMH,
PREVMH;
LOCAL
COUNT;
MAP FORMAT BUFFADDR;
LABEL
LOOP,
BIGLOOP;
BUFFADDR _ DREAD(.DSKMH); ! MAKE SURE THE BUFFER IS STILL AVAILABLE
NEXTMH _ .BUFFADDR[DM0NEXTMH]; ! SAVE THE NEXT ONE TO GET
BIGLOOP:
REPEAT ! FOR EACH DSKMH IN THIS GROUP
BEGIN
LOOP: INCR RI FROM 0 TO LASTRI DO ! ZAP ALL THE CHUNKS IN EACH
BEGIN
SELECT (COUNT _ .BUFFADDR[DM0RICOUNT( .RI)] ) OF
NSET
#777777: BEGIN ! IF EXTENDED DSKMH, PICK UP THE EXTENSION
PREVMH _ .DSKMH;
DSKMH _ .BUFFADDR[DM0RIPTL(.RI)];
BUFFADDR _ DREAD( .DSKMH);
DSKDEA( 1, .PREVMH);
RI _ -1 ! RESTART THE INCR LOOP
END;
0: LEAVE LOOP; ! IF NONE LEFT THEN DONE
! ALSO DONE IF INCR LOOP FINISHES
OTHERWISE: DSKDEA( .COUNT, .BUFFADDR[DM0RIPTL(.RI)]);
! IF NORMAL RI THEN ZAP THE CHUNKS/PARTICLES
TESN
END;
DSKDEA( 1, .DSKMH); ! ZAP THE DSKMH
IF (DSKMH _ .NEXTMH) EQL 0 THEN LEAVE BIGLOOP;
! IF OUT OF DSKMH'S THEN DONE
BUFFADDR _ DREAD( .DSKMH); ! OTHERWISE TRY ANOTHER
NEXTMH _ .BUFFADDR[DM0NEXTMH]
END
END;
COMMENT;
! ROUTINE STOREDSKGH
! ======= ==========
! THIS ROUTINE PUTS AN ENTRY INTO THE DSKGHTAB OF THE TABLE.
ROUTINE STOREDSKGH( LOFFSET, MHPTR, TSN, SENDERID, GHTABLE, BASEPTL ) =
BEGIN
MACRO
STOREARGS(X) =
BUFFADDR[DG0MH( X)] _ .MHPTR;
BUFFADDR[DG0TSN( X)] _ .TSN;
BUFFADDR[DG0SENDER( X)] _ .SENDERID$;
MACRO MAKENEW( LINKS, LOFFSET ) =
BEGIN
BUFFADDR _ DGET(DSKALL(1)); ! GET A PARTICLE FOR A DSKGH
ZERO( .BUFFADDR, .BUFFADDR + PTLSIZE - 1); ! ZERO IT
BUFFADDR[DG0LINKS] _ LINKS; ! STORE THE FORWARD AND REVERSE LINKS
BUFFADDR[DG0LEAF] _ LOFFSET; ! STORE A POINTER TO THE OWNER
STOREARGS(1); ! STORE THE INFO ABOUT THE CORE GH
DWRITE( .BUFFADDR<RH>, .BUFFADDR<LH>); ! WRITE IT OUT
(.GHTABLE + LOFFSET)<RH> _ .BUFFADDR<LH>; ! SAVE THE PTL NUMBER
J _ 1; ! AND MARK THE OFFSET IN THE PTL AS ONE
.BUFFADDR<LH> ! RETURN THE PARTICLE NUMBER
END$;
REGISTER
BUFFADDR,
J,
PTL,
LAST;
LOCAL LINKS,
RET;
MAP FORMAT BUFFADDR;
! BEGIN !
IF .(.GHTABLE + .LOFFSET) EQL 0 THEN ! IS THERE ALREADY A DSKGH
BEGIN ! *NO* THEN
PTL _ MAKENEW( 0, .LOFFSET); ! MAKE A DSKGH
BUFFADDR _ DREAD( .BASEPTL + .LOFFSET / LEAFPERPTL); !PUT IT IN THE DISK LEAF POINTER TABLE
BUFFADDR[DD0LEAF((.LOFFSET MOD LEAFPERPTL))] _ .PTL; ! ...
DWRITE( .BUFFADDR<RH>, .BUFFADDR<LH>) ! ...
END
ELSE
BEGIN ! *YES* ALREADY A DSKGH
IF ( J _ .(.GHTABLE + .LOFFSET)<LH>) EQL 0 THEN ! ANY ROOM LEFT IN IT
BEGIN ! *NO*
LINKS _ 0; ! MAKE NEW LINKAGE
LAST _ LINKS<LH> _ .(.GHTABLE + .LOFFSET)<RH>; ! GET PTL NUMBER OF LAST DSKGH
PTL _ MAKENEW( .LINKS, .LOFFSET); ! MAKE A NEW DSKGH
BUFFADDR _ DREAD( .LAST); ! READ THE OLD ONE
BUFFADDR[ DG0NEXT] _ .PTL; ! LINK THE OLD TO THE NEW
DWRITE( .BUFFADDR, .LAST) ! WRITE THE OLD
END
ELSE
BEGIN ! *YES* ROOM LEFT IN THE DSKGH
BUFFADDR _ DREAD( PTL _ .(.GHTABLE + .LOFFSET)<RH>); ! READ THE OLD DSKGH
STOREARGS(.J); ! STORE WHAT WE'RE HERE TO SAVE
DWRITE( .BUFFADDR<RH>, .BUFFADDR<LH>) ! WRITE THE OLD DSKGH
END
END;
RET<LH> _ .J;
RET<RH> _ .PTL;
(.GHTABLE + .LOFFSET)<LH> _ IF (J _ .J + 1) GTR DG0MAX THEN 0 ELSE .J;
! STORE THE NEXT AVAILABLE SLOT ( IF ANY )
! IN THE DSKGH
DPAT(); !FORCE OUT DSKGHTABLE
.RET
END;
COMMENT;
! ROUTINE DELDSKGH
! ======= =========
! THIS ROUTINE REMOVES AN ENTRY FROM THE LEAFGHTAB OR TRMGHTAB AND CLEANS
! UP AS REQUIRED
GLOBAL ROUTINE DELDSKGH( PAIR, WHICHTAB ) =
BEGIN
REGISTER
BUFFADDR,
PREV,
NEXT;
MAP FORMAT BUFFADDR;
LABEL LOOP;
LOCAL OFFSETT,
GH,
PTL,
DSKMH,
LEAFNO;
! SPLIT PAIR INTO PTL AND INDEX INTO PTL
PTL _ .PAIR<RH>;
OFFSETT _ .PAIR<LH>;
BUFFADDR _ DREAD( .PTL); ! READ THE DSKGHTAB PARTICLE
DSKMH _ .BUFFADDR[DG0MH( .OFFSETT)]; ! GET THE POINTER TO THE DSKMH
GH _ BUFFADDR[DG0ENTRY( .OFFSETT)]; ! GET A POINTER TO THE ENTRY WE ARE INTERESTED IN
ZERO( .GH, .GH + DG0SIZE - 1); ! AND ZAP IT
IF ! SEE IF THE WHOLE PARTICLE IS ZERO
LOOP: BEGIN
INCR I FROM BUFFADDR[DG0ENTRY(1)] TO BUFFADDR[DG0ENTRY(DG0MAX)] DO
IF @@I NEQ 0 THEN LEAVE LOOP WITH TRUE;
FALSE
END
THEN RETURN( DWRITE( .BUFFADDR<RH>, .PTL); .DSKMH ); ! IF NOT ALL ZERO REWRITE IT AND EXIT
NEXT _ .BUFFADDR[DG0NEXT];
PREV _ .BUFFADDR[DG0PREV];
IF .NEXT EQL 0 THEN ! NEVER REMOVE THE LAST TABLE
BEGIN
IF .WHICHTAB EQL TERMINAL THEN
TRMDSKTAB[ .BUFFADDR[DG0LEAF]] _ 1 ^ 18 + .PTL
ELSE
LEAFDSKTAB[ .BUFFADDR[DG0LEAF]] _ 1 ^ 18 + .PTL;
RETURN( DWRITE( .BUFFADDR, .PTL); .DSKMH )
END;
IF .PREV NEQ 0 THEN
BEGIN
BUFFADDR _ DREAD( .PREV);
BUFFADDR[ DG0NEXT] _ .NEXT;
DWRITE( .BUFFADDR, .PREV)
END
ELSE
BEGIN
LEAFNO _ .BUFFADDR[ DG0LEAF ];
IF .WHICHTAB EQL TERMINAL THEN
BEGIN
BUFFADDR _ DREAD( .OBASE + .LEAFNO / LEAFPERPTL);
BUFFADDR[DD0LEAF((.LEAFNO MOD LEAFPERPTL))] _ .NEXT;
DWRITE( .BUFFADDR<RH>, .BUFFADDR<LH>)
END
ELSE
BEGIN
BUFFADDR _ DREAD( .IBASE + .LEAFNO / LEAFPERPTL);
BUFFADDR[DD0LEAF((.LEAFNO MOD LEAFPERPTL))] _ .NEXT;
DWRITE( .BUFFADDR<RH>, .BUFFADDR<LH>)
END
END;
IF .NEXT NEQ 0 THEN
BEGIN
BUFFADDR _ DREAD( .NEXT);
BUFFADDR[DG0PREV] _ .PREV;
DWRITE( .BUFFADDR, .NEXT)
END;
DSKDEA( 1, .PTL); ! AND DEALLOCATE IT
.DSKMH
END;
COMMENT;
OWN NEXTMHTAB;
! ROUTINE STORENEXTMH
! ======= ===========
! THIS ROUTINE FINDS AN AVAILABLE LOCATION IN THE NEXTMHTABLE
! AND STORE THE VALUE GIVEN THERE
ROUTINE STORENEXTMH( DSKGHADDR, MHPTR ) =
BEGIN
REGISTER
ADDR;
MAP FORMAT ADDR;
IF (ADDR _ FINDNEXTMH(.DSKGHADDR)) NEQ -1 THEN
BEGIN
ADDR[NM0NEXTMH] _ .MHPTR;
RETURN
END;
IF (ADDR _ .NEXTMHTAB) EQL 0 THEN ! IF NO TABLE YET THEN
BEGIN
ADDR _ NEXTMHTAB _ GETMEM( NM0SIZE); ! MAKE SOME ROOM
ZERO( .ADDR, .ADDR + NM0SIZE - 1); ! AND ZAP IT
ADDR[NM0END0] _ -1; ! MARK THE END
ADDR[NM0END1] _ -1; ! MARK THE END
ADDR[NM0GHADDR] _ .DSKGHADDR; ! STORE WHAT WE WANTED
ADDR[NM0NEXTMH] _ .MHPTR; ! SAVE MH'S
RETURN ! ALL DONE FOR NOW
END;
REPEAT ! IF WE HAVE A TABLE ALREADY THEN
BEGIN ! UNTIL WE FIND A PLACE TO PUT THE DSKGHADDR GO
SELECT .ADDR[NM0GHADDR] OF
NSET
-1: IF .ADDR[NM0NEXTMH] EQL #777777 THEN ! IF END OF TABLE THEN
BEGIN
ADDR _ ADDR[NM0NEXTMH] _ GETMEM( NM0SIZE); ! MAKE SOME ROOM
ZERO( .ADDR, .ADDR + NM0SIZE - 1); ! AND ZAP IT
ADDR[NM0END0] _ -1; ! MARK THE END
ADDR[NM0END1] _ -1; ! MARK THE END
ADDR[NM0GHADDR] _ .DSKGHADDR; ! STORE WHAT WE WANTED
ADDR[NM0NEXTMH] _ .MHPTR; ! SAVE MH'S
RETURN ! ALL DONE FOR NOW
END
ELSE ADDR _ .ADDR[NM0NEXTMH]; !STEP TO NEXT TABLE
0: ( ADDR[NM0GHADDR] _ .DSKGHADDR;
ADDR[NM0NEXTMH] _ .MHPTR; ! SAVE MH'S
RETURN); ! FOUND A PLACE SO STORE WHAT WE WANTED
OTHERWISE: ADDR _ .ADDR + NM0ENTRYSIZE; ! OTHERWISE TRY THE NEXT LOCATION
TESN
END
END;
COMMENT;
! ROUTINE FINDNEXTMH
! ======= ==========
! THIS ROUTINE IS USED TO SEARCH THE NEXTMHTAB TO FIND THE NEXT
! DSKMH TO USE FOR THE GROUP WHOSE DSKMH IS PROVIDED.
! RETURNS: INDEX INTO THE NEXTMH TABLE
ROUTINE FINDNEXTMH( DSKGHADDR) =
BEGIN
REGISTER
ADDR,
GHADDR;
MAP FORMAT ADDR;
GHADDR _ .DSKGHADDR; ! PUT THE DSKGHADDR IN A REGISTER FOR SPEED
IF (ADDR _ .NEXTMHTAB) EQL 0 THEN RETURN -1; ! ERROR RETURN (NO TABLE)
REPEAT ! UNTIL MATCH OR END OF TABLE DO
BEGIN
SELECT .ADDR[NM0GHADDR] OF
NSET
-1: IF (ADDR _ .ADDR[NM0NEXTMH]) EQL #777777 THEN RETURN -1; ! OOPS END OF TABLE
.GHADDR: RETURN .ADDR; ! JUST WHAT WE WANTED
OTHERWISE: ADDR _ .ADDR + NM0ENTRYSIZE; ! TRY ANOTHER
TESN
END
END;
COMMENT;
! THIS FILE CONTAINS THE ROUTINES USED TO WRITE THE LOG OR AUDIT
! FILES. THESE ROUTINES TAKE RELATIVELY COMPLEX CALLS AND
! BREAK THEM DOWN INTO PRIMATIVE WRITE WORD AND WRITE VECTOR FOR KERNEL
! CALLS
COMMENT;
! 1. INLOGMSG
! == ========
! CALLING ARGUEMENTS: 1) THE TRANSACTION SEQUENCE NUMBER OF
! THE MESSAGE (ONE WORD)
! 2) THE DATE THIS MESSAGE WAS RECEIVED
! (ONE WORD, EQUIVALENT OF RESULT OF
! DATE UUO)
! 3) THE TIME THIS MESSAGE WAS RECEIVED
! (ONE WORD, EQUIVALENT OF RESULT OF
! MSTIME UUO)
! 4) POINTER TO THE LOGICAL NAME OF THE
! TERMINAL WHO SENT THIS MESSAGE
! (THE LEFT HALF OF THE GLOBAL SYMBOL
! SRCTAB CONTAINS THE MAXIMUM NUMBER OF
! CHARACTERS IN A LOGICAL SOURCE NAME)
! 5) POINTER TO THE TRANSACTION CODE OF
! THE MESSAGE (TRCODELENGTH WILL BE
! A GLOBAL SYMBOL WHICH CONTAINS THE
! MAXIMUM LENGTH OF A TRANSACTION CODE)
! 6) A POINTER TO THE FIRST CHUNK OF THE
! MESSAGE
! RETURNS: 0 ALWAYS
! BLISS CALLING SEQUENCE: IF .INLOGGING THEN INLOGMSG(.TSN,.DATE,
! .TIME,.SENDERID,.TRCODEPTR,.CHUNKPTR)
! FUNCTION: WRITE THE LOG FILE (IF THE USER HAS REQUESTED IT AND
! THE LOGGING DEVICE IS AVAILABLE)
! THE FORMAT OF A LOG FILE RECORD:
! WORD 1: -1
! WORD 2: WORD COUNT OF THE WORDS IN THIS RECORD (M+1)
! WORD 2: CHECKSUM OF THE WORDS IN THIS RECORDS (EXCLUDING THE CHECKSUM WORDS)
! WORD 3: TRANSACTION SEQUENCE NUMBER OF THIS MESSAGE
! WORD 4: DATE OF THIS MESSAGE
! WORD 5: TIME THIS MESSAGE WAS RECEIVED
! WORDS 6 THRU 8:
! SOURCE TERMINAL NAME
! WORD 9: SIZE OF THE TRANSACTION CODE FIELD
! WORD 10 THRU WORD (10 + CONTENTS OF WORD 9):
! THE TRANSACTION CODE OF THIS MESSAGE
! WORD (11 + CONTENTS OF WORD 9):
! THE NUMBER OF CHUNKS TO FOLLOW
! WORD (12+CONTENTS OF WORD 9) THRU WORD M
! THE CHUNKS OF THIS MESSAGE
! (M DEPENDS ON THE LENGTH AND NUMBER OF THE CHUNKS)
! WORD M+1: -1
GLOBAL ROUTINE INLOGMSG(TSN,DATE,TIME,SENDERPTR,TRCODEPTR,CHUNKPTR)=
BEGIN
REGISTER
CHUNK,
COUNT,
CHUNKCOUNT;
MAP FORMAT CHUNKPTR;
MAP FORMAT CHUNK;
COUNT _ 11 + .TRCW; ! LENGTH OF RECORD IS 11 + TRANSACTION CODE LENGTH + MSG LENGTHS
CHUNKCOUNT _ 0;
CHUNK _ .CHUNKPTR;
DO
BEGIN
CHUNKCOUNT _ .CHUNKCOUNT + 1;
COUNT _ .COUNT + ( .CHUNK[C0WORDS] + C0HEADSIZE )
END
WHILE (CHUNK _ .CHUNK[C0LINK]) NEQ 0;
INAUDW( -1 );
INAUDW(.COUNT);
INAUDW(.TSN);
INAUDW(.DATE);
INAUDW(.TIME);
INAUDVECTOR( .SENDERPTR<RH>, SRCNAMLEN); ! OUTPUT THE SENDER ID.
INAUDW( .TRCLEN ); ! WRITE THE TRANSACTION CODE LENGTH
SELECT .TRCODEPTR OF
NSET
0: DECR I FROM .TRCW - 1 TO 0 DO INAUDW(0);
#777777: DECR I FROM .TRCW - 1 TO 0 DO INAUDW(.(PAZ '??????????'));
OTHERWISE: INAUDVECTOR(.TRCODEPTR<RH>,.TRCW);
TESN;
INAUDW( .CHUNKCOUNT );
DO
BEGIN
INAUDVECTOR(.CHUNKPTR<RH>, .CHUNKPTR[C0WORDS] + C0HEADSIZE)
END
WHILE (CHUNKPTR _ .CHUNKPTR[C0LINK]) NEQ 0;
INAUDW( -1 ); ! OUTPUT THE FINAL -1
END; ! END OF INLOGMSG
COMMENT;
! 2. OUTLOGMSG
! == =========
! CALLING ARGUEMENTS: 1) THE TRANSACTION SEQUENCE NUMBER OF
! THE MESSAGE
! 2) THE DATE THIS MESSAGE WAS SENT
! 3) THE TIME THIS MESSAGE WAS SENT
! 4) POINTER TO THE MESSAGE CLASS OF THIS
! MESSAGE IN THE LIBOL-MCS PAGE (THE
! MESSAGE CLASS IS ALWAYS EIGHT
! CHARACTERS LONG)
! 5) THE JSN OF THE JOB WHICH SENT
! THIS MSG
! 6) THE DESTINATION COUNT
! 7) POINTER TO THE DESTINATION TABLE IN
! THE LIBOL-MCS COMMUNICATIONS PAGE
! (SEE THE LIBOL SPEC FOR THE FORMAT)
! THIS PAGE CONTAINS THE LOGICAL
! DESTINATION NAMES.
! 8) A POINTER TO THE FIRST CHUNK OF THE
! MESSAGE
! RETURNS: 0 ALWAYS
! BLISS CALLING SEQUENCE: IF .OUTLOGGING THEN OUTLOGMSG(.TSN,
! .DATE,.TIME,.MSGCLASS,.JSN,
! .DESTCNT,.DESTTAB,.CHUNKPTR)
! FUNCTION: WRITE THE LOG FILE (IF THE USER HAS REQUESTED IT AND
! THE LOGGING DEVICE IS AVAILABLE)
! THE FORMAT OF A LOG FILE RECORD:
! WORD 1: -1
! WORD 2: WORD COUNT OF THE WORDS IN THIS RECORD
! WORD 3: TRANSACTION SEQUENCE NUMBER OF THIS MESSAGE
! WORD 4: DATE OF THIS MESSAGE
! WORD 5: TIME THIS MESSAGE WAS RECEIVED
! WORD 6 THRU WORD 7:
! MESSAGE CLASS
! WORD 8 AND 9: DEVICE NAME (ASCIZ)
! WORD 10 AND 11: FILE NAME (ASCIZ)
! WORD 12: PPN (HALF WORD PAIR)
! WORD 13: A COUNT OF THE NUMBER OF DESTINATIONS ( CALL THE
! CONTENTS OF THIS WORD N)
! WORD 14 THRU WORD 14+N*3:
! DESTINATION NAMES
! WORD 15+N*3:
! THE NUMBER OF CHUNKS TO FOLLOW
! WORD 16+N*3 THRU WORD M:
! THE CHUNKS OF THIS MESSAGE
! (M DEPENDS ON THE LENGTH AND NUMBER OF THE CHUNKS)
! WORD M+1: -1
! NOTE: THE TWO TYPES OF LOGS ARE DISTINGUISHABLE BECAUSE THE
! TRANSACTION SEQUENCE NUMBER OF AN INPUT MESSAGE HAS A
! ZERO RIGHT HALF, AND AN OUTPUT MESSAGE HAS A NON-ZERO
! RIGHT HALF
GLOBAL ROUTINE OUTLOGMSG(TSN,DATE,TIME,MSGCLASSPTR,JSN,DESTCOUNT,DESTTABPTR,CHUNKPTR)=
BEGIN
EXTERNAL ?JB$MPP; ! CONTAINS A POINTER TO THE MPP PROTOTYPE
! INDEXED BY THE JSN
BIND DEV = 1,
NAME = 3,
PPN = 5,
DEBUGMPP = PLIT( 0, ! DEBUGGING MPP'S JUST GET THIS IN THE JOURNAL NOW
0,
0,
'DEBUG',
0,
0 );
REGISTER
COUNT,
CHUNKCOUNT,
PROTOBLOCK,
CHUNK;
MAP FORMAT CHUNKPTR;
MAP FORMAT CHUNK;
IF (PROTOBLOCK _ .?JB$MPP[.JSN] ) EQL 0 THEN PROTOBLOCK _ DEBUGMPP;
COUNT _ 15 + ( .DESTCOUNT * 3 ); ! RECORD LENGTH IS 15 + DESTINATION TABLE SIZE + MSG LENGTHS
CHUNKCOUNT _ 0;
IF ( CHUNK _ .CHUNKPTR ) NEQ 0 THEN
DO
BEGIN
CHUNKCOUNT _ .CHUNKCOUNT + 1;
COUNT _ .COUNT + ( .CHUNK[C0WORDS] + C0HEADSIZE )
END
WHILE (CHUNK _ .CHUNK[C0LINK]) NEQ 0;
OTAUDW( -1 );
OTAUDW(.COUNT);
OTAUDW(.TSN);
OTAUDW(.DATE);
OTAUDW(.TIME);
OTAUDVECTOR( .MSGCLASSPTR<RH>, 2); ! OUTPUT MESSAGE CLASS
OTAUDVECTOR( (.PROTOBLOCK)[DEV], 2); ! OUTPUT THE MPPSPEC
OTAUDVECTOR( (.PROTOBLOCK)[NAME], 2);
OTAUDW( .(.PROTOBLOCK)[PPN]);
OTAUDW( .DESTCOUNT );
OTAUDVECTOR( .DESTTABPTR<RH>, .DESTCOUNT * 3); ! OUTPUT THE DESTTAB
OTAUDW( .CHUNKCOUNT );
IF .CHUNKPTR NEQ 0 THEN DO
BEGIN
OTAUDVECTOR( .CHUNKPTR<RH>, .CHUNKPTR[C0WORDS] + C0HEADSIZE )
END
WHILE (CHUNKPTR _ .CHUNKPTR[C0LINK]) NEQ 0;
OTAUDW( -1 )
END; ! END OF OUTLOGMSG
END;