Google
 

Trailing-Edge - PDP-10 Archives - AP-D471B-SB_1978 - sendm.bli
There are no other files named sendm.bli in the archive.
!***COPYRIGHT (C) 1974, 1975, 1976, 1977 DIGITAL EQUIPMENT CORP., MAYNARD, MASS.***
MODULE SENDM(RESERVE(#11,#12,#13,#14),SREG=#17,FREG=#16,
	     DREGS=4,VREG=#15,MLIST,TIMER=EXTERNAL(SIX12),FSAVE)=
	BEGIN

	REQUIRE  DATA.BLI;		!EVERYONE NEEDS THIS...COMMON

	EXTERNAL	CHKGH,
			CHKMSG,
			INLOGMSG,
			OUTLOGMSG,
			ROLGH,
			ROLMSG,
			GPURGE,
			COMPARE;
COMMENT;

! CHKMCL.BLI
! ====== ===

! ROUTINE CHKMCLASS
! ======= =========

! THIS ROUTINE TAKES THE ADDRESS OF A PAGE, PICKS UP THE MESSAGE CLASS
! CONTAINED IN THAT PAGE, AND COMPARES IT WITH THE ENTRIES IN THE
! MESSAGE CLASS TABLE.  IF THE MESSAGE CLASS MATCHES ONE OF THE TABLE
! ENTRIES, THE INDEX TO THAT ENTRY IS RETURNED, OTHERWISE THE DEFAULT
! VALUE IS RETURNED, AND AN ERROR STATUS IS SET IN THE PAGE

! SIDE EFFECT:
!	THE ERROR STATUS IS SET IN THE COMMAND PAGE IF THE MESSAGE
!	    CLASS IS UNKNOWN

ROUTINE CHKMCLASS(PAGEADDR) =
    BEGIN
	REGISTER
		MCLASSPTR;
	BIND	BLNKS = PLIT(	#201004020100,
				#201004000000);

	MCLASSPTR _ (.PAGEADDR)[P0MCLS];	! SET UP A POINTER BECAUSE WE'LL USE IT SEVERAL TIMES

	IF NOT COMPARE(.MCLASSPTR,BLNKS,MCLASSSIZE) THEN	!IF NON BLANK CLASS
	    BEGIN
		INCR I FROM 0 TO .MCOUNT BY MCLASSSIZE DO	! FOR EACH ENTRY IN THE MESSAGE CLASS TABLE
		    IF COMPARE(.MCLASSPTR, MCLASS[.I], MCLASSSIZE) THEN RETURN (.I ^ (-1));
							! IF WE FIND ONE, ADJUST THE INDEX AND RETURN

		(.PAGEADDR)[P0STATUS] _ STSMCLUNK	! WE DIDN'T FIND ONE SO SET THE ERROR STATUS IN THE PAGE
	    END;

    .MDEFAULT					! IF UNKNOWN MESSAGE CLASS OR MESSAGE CLASS BLANK THEN
						! RETURN THE DEFAULT VALUE IF NO MATCH

END;
	EXTERNAL	LINK;

COMMENT;

! ATTACH.BLI
! ====== ===

! ROUTINE ATTACH
! ======= ======

! THIS ROUTINE IS USED TO LINK A MESSAGE TO AN OUTPUT (TERMINAL TYPE)
! DESTINATION. IT UPDATES ALL GROUP HEADERS (ONE FOR EACH DEST)

ROUTINE ATTACH(IMAHPTR,ENDI,GROUP,MHPTR) =
    BEGIN
	REGISTER
		GHPTR,
		DTAB,
		NTRMS;
	LOCAL	IND;

	EXTERNAL	RUNMSW;

	MAP	FORMAT IMAHPTR;
	MAP	FORMAT GHPTR;
	MAP	FORMAT MHPTR;

	IF (NTRMS _ .IMAHPTR[I0TRMCNT]) EQL 0 THEN RETURN; !QUIT IF NO TERMINALS
	GHPTR _ .GROUP;
	LINK( GHPTR[G0MHS], .MHPTR);			! LINK THE FIRST NORMALLY
	GHPTR[G0ENDI] _ IND _ IF .ENDI GEQ EGI THEN TRUE ELSE FALSE;
	IF .GHPTR[G0OUTMH] EQL 0 THEN GHPTR[G0OUTMH] _ .MHPTR;	! RESET THE OUTMH IF NEEDED
	RUNMSW _ -1;					! START MSGOUT RUNNING
	DTAB _ .IMAHPTR[I0DESTAB];			! GET THE DESTINATION TABLE

	INCR I FROM 1 TO .NTRMS - 1 DO			! FOR EACH TERMINAL TYPE DESTINATION
	    BEGIN
		GHPTR _ .(.DTAB)[.I]<RH>;		! GET IT'S GH
		GHPTR[G0LMHPTR] _ .MHPTR;		! SET THE LAST MH POINTER
		IF .GHPTR[G0OUTMH] EQL 0 THEN GHPTR[G0OUTMH] _ .MHPTR;	! RESET THE OUTMH IF NEEDED
		GHPTR[G0ENDI] _ .IND			! SET THE END INDICATOR
	    END;
    END;
	EXTERNAL	MOVE,
			CHKSRCTAB,
			CHKLFTAB,
			BUILD;

COMMENT;

! SUBROUTINE SEND4
! ========== =====
! LAST MODIFIED:	15 JUN 76  BY  CDO

! THIS MODULE IS CALLED BY SEND MODULE TO PROCESS THE INITIAL "SEND" COMMAND,
! I.E. FUNCTION CODE = SENDRR IN THE PAGE SENT BY A MPP.
! SEND4 PERFORMS THE FOLLOWING FUNCTIONS:
! 1) CHECK THE VALIDITY OF THE MESSAGE CLASS IN THE PAGE.
! 2) IF DESTINATION COUNT IS GREATER THAN 1, A DESTINATION (DTAB) IS CREATED.
!    SIZE OF DTAB IS DETERMINED BY THE MESSAGE-CLASS AND THE DESTINATION COUNT, I.E.
!    IF MESSAGE-CLASS IS NOT "ALL", SIZE OF DTAB = "DESTINATION COUNT" WORDS
!    IF MESSAGE-CLASS IS "ALL", SIZE OF DTAB = "S0CNT" WORDS.
! 3) CHECK THE VALIDITY OF EVERY DESTINATION SPECIFIED IN THE PAGE.
! 4) FILL DTAB, SOURCE TYPE DESTINATIONS ARE STORED STARTING FROM THE LEFT
!    (TOP/LOW ADDRESS) OF DTAB,
!    LEAF TYPE DESTINATIONS ARE STORED STARTING FROM THE RIGHT (BOTTOM/
!    HIGH ADDRESS) OF THE DTAB.
!   THERE IS ONE WORD PER DESTINATION IN THE DTAB, THE CONTENTS OF THE WORD ARE:
!     	LH =	0	IF DEST IS LEAF TYPE,
!		1	IF DEST IS SOURCE TYPE.
!		**** IT MIGHT BE A LITTLE FASTER TO REVERSE THE ABOVE
!			0 FOR TERMINAL, 1 FOR LEAF
!	RH = PTR TO THE SOURCE IN SRCTAB, OR
!	     ENTRY IN THE LFTAB.
! 5) CREATE A NEW RHTSN
! 6) CREATE A NEW IMAH ENTRY IN SENDQ
! 7) CREATE A MH
! 8) SET UP RESPONSES TO MPP IN THE PAGE
! 9) IF FATAL ERROR OCCURS, RETURNS CALLER THE VALUE -1, OTHERWISE RETURNS CALLER
!    THE ADDRESS OF THE IMAH ENTRY JUST CREATED.

GLOBAL ROUTINE SEND4(JOBSLOT,PAGEADDR) =
BEGIN
	REGISTER
		J,
		K,
		M;
	MAP	FORMAT LFTAB:SENDQ;
	LABEL	LOOP;

	BIND
		MCLASSALL = 2,
		MCLASSIMMED = 0;

	LOCAL
		DCNT,
		CLASS,
		DTAB,
		LEAF,
		UNKNOWN,
		SOURCE,
		ATLEASTONEDISABLED,
		DEST,
		FORMAT IMAHPTR
		:MHPTR
		:SRCPTR;

	(.PAGEADDR)[P0STATUS] _ STSGOOD;		! ASSUME GOOD THINGS WILL HAPPEN

	CLASS _ CHKMCLASS(.PAGEADDR);			! OBTAIN A NUMERICAL VALUE FOR THE MESSAGE CLASS

	SOURCE _ LEAF _ UNKNOWN _ J _ 0;		! CLEAR LOTS OF COUNTS AND POINTERS

	IF .CLASS EQL MCLASSALL THEN			! IF THE MESSAGE CLASS IS ALL THEN
	    BEGIN
		CLASS _ -1;				! SET CLASS TO -1
		SOURCE _ .SRCTAB + 1;			! IN CASE ONLY 1 TERMINAL IN CONFIG
		J _ DCNT _ .S0CNT			! DESTINATION COUNT TO THE NUMBER OF TERMINAL ON THE SYSTEM
	    END
	    ELSE DCNT _ .(.PAGEADDR)[P0DSCNT];		! OTHERWISE SET THE DESTINATION COUNT TO THE VALUE GIVEN
							! IN THE PAGE

	IF .DCNT GTR 1 THEN				! IF THE DESTINATION COUNT > 1 THEN
	    BEGIN
		DTAB _ GETMEM(.DCNT);			! GET ROOM FOR A DESTINATION TABLE (DTAB)
		MOVE(0,.DTAB,.DCNT);			! AND CLEAR THE DESTINATION TABLE
		IF .CLASS LSS 0 THEN 			! IF THE CLASS IS ALL
		    BEGIN
			SOURCE _ SRCPTR _ .SRCTAB + 1;
			J _ 0;
			UNTIL @@SRCPTR EQL 0 DO		! PUT THE ADDRESS OF EACH SOURCE TABLE ENTRY IN THE DTAB
			    BEGIN
				(.DTAB)[.J]<RH> _ .SRCPTR;
				(.DTAB)[.J]<LH> _ 1;
				J _ .J + 1;
				SRCPTR _ .SRCPTR + .S0SIZE
			    END
		    END
	    END;

	K _ 1;
	ATLEASTONEDISABLED _ FALSE;

	IF .CLASS GEQ MCLASSIMMED THEN			! IF THE MESSAGE CLASS IS NOT ALL THEN
	    BEGIN
		J _ 0;					! START COUNTING AGAIN
		INCR I FROM 0 TO .DCNT-1 DO		! FOR EACH DESTINATION IN THE PAGE
		    LOOP:	BEGIN

			DEST _ (.PAGEADDR)[P0DTAB+.I*3];	! GET IT'S ADDRESS
			SRCPTR _ CHKSRCTAB(.DEST);		! AND SEE IF IT IS A GOOD TERMINAL

			IF .SRCPTR NEQ 0 THEN			! IF YES THEN
			    BEGIN
				(.DEST+2)<RH> _ STSGOODDST;		! MARK IT AS GOOD IN THE PAGE
				IF .SOURCE EQL 0 THEN SOURCE _ .SRCPTR;	! SAVE THE ADDRESS OF THE FIRST SOURCE
				IF .DCNT GTR 1 THEN			! IF THERE ARE MORE THAN ONE DESTINATIONS
				    BEGIN
					(.DTAB)[.J]<RH> _ .SRCPTR;		! STORE THEM IN THE DTAB
					(.DTAB)[.J]<LH> _ 1;			! AND FLAG IT AS A TERMINAL
					J _ .J + 1
				    END
				    ELSE J _ 1;		!COUNT ONLY TERMINAL

				IF .SRCPTR[S0ED] THEN ATLEASTONEDISABLED _ TRUE; !IF DISABLED THEN REMEMBER IT
				LEAVE LOOP				! SKIP THE REST OF THE LOOP
			    END;

			IF .LFTAB NEQ 0 THEN			! IS A LEAF TYPE DESTINATION POSSIBLE
			    BEGIN
				M _ CHKLFTAB(.DEST);			! YES THEN SEE IF THIS IS ONE

				IF .M NEQ 0 THEN			! IF IT IS THEN
				    BEGIN
					(.DEST+2)<RH> _ STSGOODDST;	! MARK IT AS GOOD IN THE PAGE
					LEAF _ .LEAF + 1;		! COUNT THE NUMBER OF LEAF TYPE DESTINATIONS

					IF .DCNT GTR 1 THEN		! IF THERE ARE MORE THAN DESTINATIONS
					    BEGIN
						(.DTAB)[.DCNT-.K]<RH> _ .M;		! THEN STORE IT IN DTAB
						K _ .K + 1
					    END;

					IF .LFTAB[L0ED(.M)] THEN ATLEASTONEDISABLED _ TRUE;
							!IF DISABLED THEN REMEMBER IT
					LEAVE LOOP
				    END
			    END;
			UNKNOWN _ .UNKNOWN + 1;			! NOT LEAF OR TERMINAL, TOO BAD & COUNT BAD ONES
			(.DEST+2)<RH> _ STSBADDST;		! MARK AS BAD IN THE PAGE
			LEAVE LOOP
		    END
	    END
	    ELSE
	    BEGIN
		CLASS _ MCLASSIMMED			! IF THE MESSAGE CLASS IS ALL, MAKE IT IMMEDIATE
	    END;

	! NOTE:	IF NO DESTINATIONS IN DESTINATION TABLE AND THE MESSAGE
	!	    CLASS IS NOT ALL THEN THE "ALL DESTINATIONS UNKNOWN"
	!	    ERROR RESULTS.

	IF .UNKNOWN EQL .DCNT THEN			! IF ALL DESTINATIONS UNKNOWN
	    BEGIN
		(.PAGEADDR)[P0STATUS] _ STSALLDSTUNK;		! SET STATUS IN PAGE
		IF .DCNT GTR 1 THEN RETMEM(.DTAB,.DCNT);	!CLEAN UP MEMORY
		RETURN -1					! AND LEAVE
	    END;

							! STORE THE STATUS IN THE PAGE
	IF .UNKNOWN NEQ 0 THEN (.PAGEADDR)[P0STATUS] _ STSUNKDST		! IF ANY UNKNOWN THEN SET UNKNOWN
										! DESTINATION(S) STATUS
		ELSE IF .ATLEASTONEDISABLED THEN (.PAGEADDR)[P0STATUS] _ STSDISDST;
							! ELSE IF ANY DISABLED THEN SET THE
							! DISABLED DESTINATION(S) STATUS

	IF (RHTSN _ .RHTSN + 1) EQL 0 THEN RHTSN _ 1;	! GET AN OUTPUT TSN (RIGHT HALF)
							! AND INSURE IT IS NEVER 0
	(.PAGEADDR)[P0TSN]<RH> _ .RHTSN;		! AND STORE IT
	IMAHPTR _ CREATEIMAH(SENDQ[SQ0IMAHS(.JOBSLOT)]);	! MAKE AN IMAH AND ATTACH IT TO THE SENDQ
	MHPTR _ CREATEMH(0);				! MAKE AN MH
	IMAHPTR[I0TSN] _ .(.PAGEADDR)[P0TSN];		! STORE THE TSN IN THE IMAH
	IMAHPTR[I0MHPTR] _ .MHPTR;			!  AND THE MHPTR IN THE IMAH
	IMAHPTR[I0LFCNT] _ .LEAF;			! SAVE THE NUMBER OF LEAVES
	IMAHPTR[I0MCLSS] _ .CLASS;			!  SAVE THE MESSAGE CLASS
	IMAHPTR[I0TRMCNT] _ MHPTR[M0DSCNT] _ .J;	! PROPOGATE TERMINAL COUNT

	IF .DCNT GTR 1 THEN				! IF THE DESTINATION COUNT > 1 THEN
	    BEGIN
		IMAHPTR[I0DESTAB] _ .DTAB;		! SAVE THE DTAB POINTER IN THE IMAH
		IMAHPTR[I0DSTSIZE] _ .DCNT;			! SET DSTSIZE TO THE SIZE OF THE DESTINATION TABLE
	    END
	    ELSE
	    BEGIN
		IMAHPTR[I0DSTSIZE] _ 0;				! OTHERWISE ZERO THE DESTINATION SIZE

		IMAHPTR[I0DESTAB] _ IF.SOURCE EQL 0 THEN .M ! IF NO TERMINAL TYPES SAVE THE LEAF TABLE POINTER
				ELSE .SOURCE;				! OTHERWISE SAVE THE FIRST SOURCE
	    END;

	.IMAHPTR

END;
COMMENT;

! ROUTINE: FINDS
!	   =====

ROUTINE FINDS(TSN,JSLOT)=

! TSN = TRANSACTION SEQUENCE NUMBER TO BE FOUND
! JSLOT = JOB SLOT NUMBER TO USE AS THE INDEX INTO THE SENDQ
! RETURNS:
!	IMAH POINTER IF ONE AVAILABLE
!	0 IF NO IMAH'S AVAILABLE IN THE TREE SPECIFIED

! SIDE EFFECTS:
!	NONE


    BEGIN
	REGISTER IMAHPTR,TSNREG;
	BIND NOTFOUND=0;
	MAP FORMAT IMAHPTR;
	MAP FORMAT SENDQ;

	! IF SENDQ FOR THIS JSN IS EMPTY THEN WE CAN'T FIND A MATCHING TSN
	IF (IMAHPTR _ .SENDQ[SQ0FIMAH(.JSLOT)]) EQL 0 THEN RETURN NOTFOUND;

	TSNREG _ .TSN;

	! LOOP WHILE THE TSN WE WANT AND THE TSN OF THE SENDQ ENTRY DON'T MATCH
	WHILE .TSNREG NEQ .IMAHPTR[I0TSN] DO

	    ! IF THERE ARE NO MORE ENTRIES TO LOOK AT THEN EXIT NOTFOUND
	    IF (IMAHPTR_.IMAHPTR[I0FORE]) EQL 0 THEN RETURN NOTFOUND;
	.IMAHPTR

    END;
	EXTERNAL	MOVE;

COMMENT;

!ROUTINE UNPACK
!====== =======

!LAST MODIFIED:		6 MAY 76  BY  CDO


!SUBROUTINE CHAIN
!========== =====
! THIS ROUTINE LINKS A CHUNK TO THE CHUNK-CHAIN IN THE MH.

GLOBAL ROUTINE CHAIN(MHPTR,CHUNK) =
    BEGIN
	REGISTER   FORMAT OLDOUTCHUNK;
	MAP 	FORMAT MHPTR:CHUNK;

	OLDOUTCHUNK _ .MHPTR[M0OUTCHNK];	! SAVE THE OLD LAST OUTPUT CHUNK
	MHPTR[M0OUTCHNK] _ .CHUNK;		! MAKE THE NEW CHUNK THE LAST OUTPUT CHUNK
	IF .OLDOUTCHUNK EQL 0 THEN MHPTR[M0INCHNK] _ .CHUNK	! IF THERE WAS NO OLD LAST OUTPUT CHUNK THEN
						! MAKE THE NEW LAST THE FIRST ALSO
	    ELSE OLDOUTCHUNK[C0LINK] _ .CHUNK;	! OTHERWISE LINK THE NEW LAST TO THE OLD LAST

    END;
COMMENT;

!SUBROUTINE UNPACK
!========== ======
! THIS ROUTINE UNPACKS TEXT UNITS AND TEXT UNIT DESCRIPTORS IN THE PAGE INTO CHUNKS.
! EACH TEXT UNIT DESCRIPTOR IS STORED IN THE FIRST TWO WORDS OF A CHUNK,
! THE ASSOCIATED TEXT UNIT IS STORED INTO THE DATA AREA OF THE CHUNK.

GLOBAL ROUTINE UNPACK(MHPTR,PAGEADDR,JOBSLOT,IMAHPTR) =
    BEGIN
	REGISTER
		TEXTUNIT,
		CHUNK;

	MAP	FORMAT TEXTUNIT;
	MAP	FORMAT CHUNK;
	MAP	FORMAT MHPTR;
	MAP	FORMAT IMAHPTR;

	LABEL	COPYUNITS;
	LOCAL	LASTENDI;

	LASTENDI _ 0;					!ASSUME NO ENDI
	INCR I FROM 0 TO .(.PAGEADDR)[P0TUC]-1 DO	! FOR ALL THE TEXT UNIT DESCRIPTORS
COPYUNITS:    BEGIN
		TEXTUNIT _ (.PAGEADDR)[P0TUD(.I)];
					! GET A TEXT UNIT DESCRIPTOR IN THE PAGE.
		LASTENDI _ .TEXTUNIT[ C0ENDI ];		!EXTRACT ITS ENDI
		IF .TEXTUNIT[ C0WORDS ] EQL 0 THEN LEAVE COPYUNITS; !SKIP THIS CHUNK OF EMPTY

		WHILE .TEXTUNIT[ C0BCNT ] GTR ( PTLSIZE - C0HEADSIZE ) * ASCIIBYTESPERWORD DO
						! WHILE WE HAVE MORE THAN A PARTICLE WILL HOLD DO
		    BEGIN
			CHUNK _ GETMEM( PTLSIZE );	! GET THE MAXIMUM A PARTICLE WILL HOLD
			CHUNK[ C0BPOS ] _ .TEXTUNIT[ C0BPOS ];
							! SAVE THE BYTE POSITION
			CHUNK[ C0BSIZ ] _ .TEXTUNIT[ C0BSIZ ];
							! AND SIZE INFO
			CHUNK[ C0ENDI ] _ 0;		! MAKE THE CHUNK AS INCOMPLETE
			CHUNK[ C0LINK ] _ 0;		! AND NOT LINKED TO ANYTHING
			CHUNK[ C0BCNT ] _ ( PTLSIZE - C0HEADSIZE - 1 ) * ASCIIBYTESPERWORD +
							( .TEXTUNIT[ C0BPOS ] / ASCIISIZE );
							! BYTE COUNT IS DEPENDENT UPON HOW MANY BYTES
							! ARE IN THE FIRST WORD
			CHUNK[ C0WORDS ] _ PTLSIZE - C0HEADSIZE;
							! WORD COUNT = MAXIMUM
			TEXTUNIT[ C0BCNT ] _ .TEXTUNIT[ C0BCNT ] - .CHUNK[ C0BCNT ];
							! FIX TEXTUNIT DESCRIPTOR FOR THE NEXT TIME
			TEXTUNIT[ C0WORDS ] _ .TEXTUNIT[ C0WORDS ] - .CHUNK[ C0WORDS ];
							! ...
			TEXTUNIT[ C0BPOS ] _ #44;	! THE NEW FIRST BYTE IS IN THE FIRST POSITION
			MOVE( .TEXTUNIT[ C0LINK ] + .PAGEADDR, CHUNK[ C0DATA ], PTLSIZE - C0HEADSIZE );
							! MOVE THE DATA TO THE CHUNK
			TEXTUNIT[ C0LINK ] _ .TEXTUNIT[ C0LINK ] + .CHUNK[ C0WORDS ];
							! MODIFY THE TUD'S POINTER TO THE TEXT
			CHAIN( .MHPTR, .CHUNK )		! ATTACH THE CHUNK TO THE MH
		    END;

		CHUNK _ GETMEM(.TEXTUNIT[C0WORDS] + C0HEADSIZE);
					! GET MEMORY TO STORE THE TEXT UNIT,
					! WHERE C0WORDS IS THE NUMBER OF WORDS THE TEXT AREA REQUIRES,
					! AND THE HEADER OF THE CHUNK NEEDS C0HEADSIZE WORDS.
		MOVE(.TEXTUNIT,.CHUNK,C0HEADSIZE);
					! FORM A CHUNK HEADER BY COPYING THE TUD
		CHUNK[C0LINK] _ 0;	! AND ZEROING THE LINK POINTER
		MOVE(.PAGEADDR+.TEXTUNIT[C0LINK],CHUNK[C0DATA],.TEXTUNIT[C0WORDS]);
					!SET UP THE DATA AREA IN THE CHUNK
		CHAIN(.MHPTR,.CHUNK)	!LINK THE NEW CHUNK TO THE CHUNK-CHAIN
	    END;
	.LASTENDI			!RETURN LAST END INDICTOR SEEN
    END;
	EXTERNAL	LINK,
			CHKRUN,
			NEXTINTSN,
			BUILD;

COMMENT;

! SUBROUTINE LFOUT
! ================

! THIS ROUTINE CREATES A GROUP HEADER AND LINKS IT TO A LEAF, IF THE
! LEAF IS ENABLED FOR OUTPUT. IF THE LEAF IS DISABLED FOR OUTPUT, IT IS
! LINKED TO THE LFTAB

! LAST MODIFIED:	28 JUN 76	BY  ILG
! .DEST CONTAINS THE LOCATION OF AN ENTRY IN DESTAB

ROUTINE LFOUT(DEST,MHPTR,TSN,ENDI) =
    BEGIN
	REGISTER
		I,
		LEAFSTATUS,
		GHPTR,
		LEAFPTR;


	MAP	FORMAT MHPTR;
	MAP	FORMAT LFTAB;
	MAP	FORMAT GHPTR;
	MAP	FORMAT LEAFPTR;

	I _ .DEST;
	LEAFPTR _ .LFTAB[L0LFPTR(.I)];
	IF (LEAFSTATUS_.LFTAB[L0ED(.I)]) THEN GHPTR _ CREATEGH(LFTAB[L0GHS(.I)]) ! IF DISABLED MAKE A GROUP HEADER
									! AND ATTACH TO THE LFTAB
	    ELSE GHPTR _ CREATEGH(LEAFPTR[N0GHS]);			! IF ENABLED MAKE A GROUP HEADER
									! AND ATTACH TO THE LEAF
	LINK(GHPTR[G0MHS],.MHPTR);					! LINK THE MESSAGE TO THE GROUP
	GHPTR[G0LHTSN] _ NEXTINTSN();					! LOAD GH WITH GOODIES
	GHPTR[G0RHTSN] _ 0;
	GHPTR[G0LFPTR] _ .LEAFPTR;
	GHPTR[G0OUTMH] _ .MHPTR;
	GHPTR[G0SENDER] _ LFTAB[L0DST(.I)];				! STORE POINTER TO THE OP LEAF NAME
									!	=======
	GHPTR[G0TYPEOFSENDER] _ LEAFTYPE;				! LET EVERYONE AN MPP SENT THIS

	IF .ENDI GTR EMI THEN GHPTR[G0ENDI] _ TRUE;		! IF IT'S END INDICATOR >= EGI THEN
									! SET GROUP DONE BIT

	IF .CHECKPOINTING AND .LEAFPTR[N0CHKPNT] THEN !IF CHECKPOINTING AND
						    !CHECKPOINTING THIS LEAF
	    BEGIN
		GHPTR[G0DSKGHADDR] _ CHKGH( .LEAFPTR[N0LLFNO],	! CHECKPOINT THE GROUP
				.GHPTR[G0TSN],
				.GHPTR[G0SENDER],
				.MHPTR);

						! AND STORE THE DISK GROUP HEADER ADDRESS
		GHPTR[G0CHKPNT] _ TRUE			! THIS GROUP IS BEING CHECKPOINTED
	    END;

	IF .INLOGGING THEN
		INLOGMSG(	.GHPTR[G0TSN],
				.MHPTR[M0DATE],
				.MHPTR[M0TIME],
				.GHPTR[G0SENDER],
				0,
				.MHPTR[M0INCHNK]);

	IF .LEAFSTATUS EQL 0 THEN CHKRUN(.LEAFPTR,1,.GHPTR);	! IF WE ATTACHED THE GROUP TO A LEAF THEN
								! INCREMENT STUFF AND SEE IF WE NEED TO RUN SOMEONE

	.GHPTR							! RETURN THE GROUP HEADER POINTER

    END;
	EXTERNAL	MOVE;

COMMENT;

! SUBROUTINE COPYMSG
! ==================
! LAST MODIFIED:	9 JUN 76  BY  CDO

! THIS ROUTINE MAKES A COPY OF THE MESSAGE AND MESSAGE HEADER OF
! THE MESSAGE POINTED TO BY THE MHPTR SUPPLIED IN THE CALL

ROUTINE COPYMSG(MHPTR,DATE,TIME) =
    BEGIN

	REGISTER
		SIZE,
		NEWMHPTR,
		CHUNK,
		CHKPTR;

	MAP	FORMAT MHPTR;
	MAP	FORMAT NEWMHPTR;
	MAP	FORMAT CHUNK;
	MAP	FORMAT CHKPTR;

	NEWMHPTR _ CREATEMH(0);			! MAKE A NEW MESSAGE HEADER
	NEWMHPTR[M0DATE] _ .DATE;			!  AND STORE THE DATE IN IT
	NEWMHPTR[M0TIME] _ .TIME;			!  AND TIME TOO

	CHUNK _ .MHPTR[M0INCHNK];		! THEN STARTING WITH THE FIRST CHUNK
	WHILE .CHUNK NEQ 0 DO			!  AND FOR EACH CHUNK OF THE MESSAGE
	    BEGIN
		SIZE _ .CHUNK[C0WORDS] + C0HEADSIZE;
		CHKPTR _ GETMEM(.SIZE);		! REQUEST ENOUGH MEMORY FOR THIS CHUNK
		MOVE(.CHUNK,.CHKPTR,.SIZE);	! COPY OLD INTO NEW
		CHKPTR[C0LINK] _  0;		! CLEAR THE LINK POINTER IN THE CHUNK HEADER
		CHAIN(.NEWMHPTR,.CHKPTR);		! AND ATTACH THE CHUNK TO THE NEW MESSAGE HEADER
		CHUNK _ .CHUNK[C0LINK];		! STEP TO THE NEXT CHUNK TO COPY
	    END;
	NEWMHPTR[ M0OUTCHNK ] _ .NEWMHPTR[ M0INCHNK ];
	.NEWMHPTR					! RETURN A POINTER TO THE NEW MESSAGE HEADER TO THE CALLER
    END;
COMMENT;

! SUBROUTINE SENDLF
! ========== ======
! LAST MODIFIED:	1 JUN 76  BY  CDO

ROUTINE SENDLF(IMAHPTR,DATE,TIME,ENDI) =
    BEGIN

	REGISTER
		DTAB,
		DEST,
		DCNT;

	MAP	FORMAT IMAHPTR;
	LOCAL	START,
		FORMAT GHPTR:MHPTR;

	DCNT _ .IMAHPTR[I0DSTSIZE];		! PICKUP THE TOTAL NUMBER OF DESTINATIONS
	MHPTR _ .IMAHPTR[I0MHPTR];		! PICKUP THE CURRENT MESSAGE HEADER
	DTAB _ .IMAHPTR[I0DESTAB];		! PICKUP THE CURRENT DESTINATION TABLE

	INCR I FROM IF .IMAHPTR[I0TRMCNT] EQL 0 THEN 2 ELSE 1 TO .IMAHPTR[I0LFCNT] DO
	(.DTAB)[.DCNT-.I] _ LFOUT(.(.DTAB)[.DCNT-.I],COPYMSG(.MHPTR,.DATE,.TIME),.IMAHPTR[I0TSN],.ENDI);

	IF .IMAHPTR[I0TRMCNT] EQL 0 THEN		! IF THERE ARE NO SOURCE TYPE DESTINATIONS THEN
	    BEGIN
		MHPTR[M0DATE] _ .DATE;		! DATE AND TIME STAMP THE MESSAGE HEADER
		MHPTR[M0TIME] _ .TIME;
		IF .DCNT EQL 0 THEN DEST _ .DTAB
		    ELSE DEST _ .(.DTAB)[.DCNT-1];	! OTHERWISE GET THE LAST ENTRY OF THE DTAB
		IMAHPTR[I0GHPTR] _ GHPTR _ LFOUT(.DEST,.MHPTR,.IMAHPTR[I0TSN],.ENDI);
		IF .DCNT EQL 0 THEN IMAHPTR[I0DESTAB] _ .GHPTR	! THEN SAVE THE GH IN THE IMAH
		  ELSE (.DTAB)[.DCNT-1] _ .GHPTR;		! SAVE THE GH IN THE DTAB
	    END
    END;
	EXTERNAL	LINK,
			UNLINK,
			DELMH,
			DELGH,
			DMHS;

COMMENT;

!ROUTINE PURGE
! ====== =====

! LAST MODIFIED:	15 DEC 76  BY  CDO

! SUBROUTINE KILGROUP
! ========== ========
! THIS SUBROUTINE IS USED TO DELETE A GROUP FROM CORE AND DISK, AND
! IF UNFINISHED, THEN THE SENDER IS MARKED AS PURGING

GLOBAL ROUTINE KILGROUP(GHPTR, JOBSLOT) =
    BEGIN
	REGISTER
		SRCPTR,
		NEXT,
		CHUNK;
	MAP	FORMAT GHPTR;
	MAP	FORMAT SRCPTR;
	MAP	FORMAT CHUNK;
	MAP	FORMAT RECVQ;

	IF .GHPTR[G0ENDI] EQL 0 THEN
				!IF THE GROUP HAS NOT ENCOUNTERED AN EGI OR EPI,
	    BEGIN
		IF .GHPTR[G0TYPEOFSENDER] EQL TERMINAL THEN	! IF SENDER IS A TERMINAL THEN
		    BEGIN
			SRCPTR _ .GHPTR[G0SENDER];		!THEN  GET THE SENDER OF THE GROUP.
			SRCPTR[S0PURGE] _ TRUE;			!MARK PURGING
			CHUNK _ .SRCPTR[S0FCHNK];
					!GET THE FIRST CHUNK IN SRCTAB.
					!(NOTE: THE INPUT CHUNKS STORED IN SRCTAB ARE CHUNKS
					!THAT BELONG TO AN INCOMPLETE MESSAGE.)
			UNTIL .CHUNK EQL 0 DO
					!DELETE ALL THE INPUT CHUNKS IN THIS SENDER.
			    BEGIN
				NEXT _ .CHUNK[C0LINK];
				DELCHNK(.CHUNK);
				CHUNK _ .NEXT;
			    END;
			SRCPTR[S0CHUNKS] _ 0
		    END
		    ELSE			! ELSE IF SENDER IS AN MPP
		    BEGIN
			GHPTR[G0PURGING] _ TRUE;		! MARK AS PURGING
			DMHS(.GHPTR);				! ZAP ALL THE MESSAGE HEADERS OF THE GROUP
								! BUT NOT THE GH
			IF .JOBSLOT GEQ 0 THEN			! AND IF A JOB SLOT NUMBER WAS GIVEN THEN
				UNLINK(RECVQ[R0GHS(.JOBSLOT)],.GHPTR);	! UNLINK THE GH FROM THE RECVQ
		    END
	    END;

	! IF CHECKPOINTING OR ROLLING THEN ZAP ANYTHING ON THE DISK
	IF .GHPTR[G0DSKGHADDR] NEQ 0 THEN GPURGE(.GHPTR[G0DSKGHADDR]);

	IF .GHPTR[G0TYPEOFSENDER] EQL TERMINAL OR .GHPTR[G0ENDI] NEQ 0 THEN	! IF TERMINAL OR GROUP COMPLETE THEN
		IF .JOBSLOT GEQ 0 THEN			! IF A JOB SLOT NUMBER WAS SPECIFIED THEN
			DELGH(RECVQ[R0GHS(.JOBSLOT)],.GHPTR)	!DELETE THE CURRENT GH
			ELSE DELGH(0, .GHPTR);			! DELETE WITH NO UNLINKING
							! OTHERWISE DON'T

    END;
COMMENT;

! SUBROUTINE PURGEI
! ========== ======
! THIS SUBROUTINE IS CALLED AFTER A EPI HAS BEEN SENT BY AN MPP,
! IS TO STOP ALL THE UNFINISHED INPUT TRANSACTIONS IN THE RECVQ
! THAT BELONG TO THIS MPP.
! IN ORDER TO STOP THE UNFINISHED INPUT TRANSACTIONS, THE PURGE BIT
! IN THE SRCTAB FOR EACH SENDER SHOULD BE SET.
! THE ASSOCIATED GH'S, MH'S, CHUNKS ARE ALL PURGED.

GLOBAL ROUTINE PURGEI(JOBSLOT) =
    BEGIN
	REGISTER
		SRCPTR,
		GHPTR,
		JSN,
		NEXT;

	MAP	FORMAT RECVQ;
	MAP	FORMAT SRCPTR;
	MAP	FORMAT GHPTR;

	JSN _ .JOBSLOT;					! PUT THE JOB SLOT NUMBER IN A REGISTER BECAUSE
							! I DON'T TRUST BLISS' OPTIMIZER
	IF .RECVQ[R0FGH(.JSN)] EQL 0 THEN RETURN;	! IF NOTHING WAS RECEIVED BY THIS JOB THEN LEAVE NOW

	GHPTR _ .RECVQ[R0FGH(.JSN)];		!GET THE FIRST GH IN RECVQ
	UNTIL .GHPTR EQL 0 DO
	    BEGIN
		NEXT _ .GHPTR[G0FORE];

		KILGROUP( .GHPTR, .JSN);	! DELETE THE GROUP

		GHPTR _ .NEXT
					!GET THE NEXT GH IN RECVQ.
	    END
    END;
COMMENT;

! SUBROUTINE PURGEO
! ========== ======
! THIS ROUTINE IS CALLED AFTER A EPI HAS BEEN SENT BY AN MPP,
! IS TO FORCE AN EGI TO ALL THE OUTPUT MESSAGES IN THE SENDQ.

GLOBAL ROUTINE PURGEO(JOBSLOT) =
    BEGIN
	REGISTER
		IMAHPTR,
		MHPTR,
		GHPTR,
		CHUNK;

	MAP 	FORMAT SENDQ;
	MAP 	FORMAT IMAHPTR;
	MAP 	FORMAT MHPTR;
	MAP 	FORMAT GHPTR;
	MAP 	FORMAT CHUNK;

	LOCAL	NEXT, SIZE;

	IMAHPTR _ .SENDQ[SQ0FIMAH(.JOBSLOT)];			!GET THE FIRST IMAH IN SENDQ.
	UNTIL .IMAHPTR EQL 0 DO
					!CHANGE THE LAST CHUNK'S END INCATOR TO EGI,
					!SET THE G0ENDI BIT IN THE ASSOCIATED GH.
					!GET THE NEXT IMAH IN SENDQ.
	    BEGIN
		IF ( GHPTR _ .IMAHPTR[I0GHPTR] ) NEQ 0 THEN	! IF OUTPUT AT LEAST EMI'ED

		BEGIN

		GHPTR[G0ENDI] _ TRUE;
		IF (MHPTR _ .IMAHPTR[I0MHPTR]) EQL 0 THEN	! IF AN MH DOESN'T EXIST THEN
		    BEGIN
			MHPTR _ CREATEMH(0);			! MAKE ONE
			IMAHPTR[I0MHPTR] _ .MHPTR;
			MHPTR[M0DSCNT] _ .GHPTR[G0DSTCNT];
			CHUNK _ GETMEM(C0HEADSIZE);

			! NOTE; ASSUMES 7 BIT BYTES

			CHUNK[C0BPOS] _ 36;
			CHUNK[C0BSIZ] _ 7;
			CHUNK[C0LINK] _ 0;
			CHUNK[C0WORDS] _ 0;
			CHUNK[C0BCNT] _ 0;
			MHPTR[M0INCHNK] _ MHPTR[M0OUTCHNK] _ .CHUNK;
		    END
		    ELSE CHUNK _ .MHPTR[M0OUTCHNK];
		CHUNK[C0ENDI] _ EGI;
		MHPTR[M0OUTCHNK] _ .MHPTR[M0INCHNK];
		ATTACH( .IMAHPTR, EGI, .GHPTR, .MHPTR)

		END ELSE BEGIN
				MHPTR _ .IMAHPTR[I0MHPTR];
				WHILE .MHPTR NEQ 0 DO
				   BEGIN
					NEXT _ .MHPTR[M0FORE];
					MHPTR[M0LINK] _ 0;
					DELMH(0,.MHPTR);
					MHPTR _ .NEXT
				   END;
		END;

		IF (SIZE _ .IMAHPTR[I0DSTSIZE]) NEQ 0 THEN	! PICK UP THE SIZE OF THE DESTTAB FROM THE IMAH
			RETMEM(.IMAHPTR[I0DESTAB],.SIZE);	! IF ANY THEN DELETE THE DESTTAB

		NEXT _ .IMAHPTR[I0FORE];
		DELIMAH(SENDQ[SQ0IMAHS(.JOBSLOT)],.IMAHPTR);	! DELETE THE IMAH
		IMAHPTR _ .NEXT;				! GOTO THE NEXT IMAH
	    END

    END;
COMMENT;

! SUBROUTINE SENDEXIT
! ========== ========
! LAST MODIFIED:	15 JUN 76  BY  CDO

! THIS ROUTINE DOES COMMON CLEAN UP
! CALLED BEFORE LEAVING SEND

ROUTINE SENDEXIT(JOBSLOT,IMAHPTR,ENDI) =
    BEGIN

	REGISTER
		SIZE,
		DTAB,
		GHPTR;

	MAP	FORMAT GHPTR;
	MAP	FORMAT SENDQ;
	MAP	FORMAT IMAHPTR;

	EXTERNAL RUNMSW;

	IMAHPTR[I0MHPTR] _ 0;				! CLEAR MESSAGE HEADER IN IMAH

	RUNMSW _ -1;					! START THE OUTPUT PROCESS

	IF .ENDI LSS EGI THEN RETURN;			! IF NOT EGI OR EPI THEN DONE FOR NOW

	IF (SIZE _ .IMAHPTR[I0DSTSIZE]) NEQ 0 THEN	! PICK UP THE SIZE OF THE DESTAB FROM THE IMAH
		RETMEM(.IMAHPTR[I0DESTAB],.SIZE);	! AND IF NON-ZERO THEN DELETE THE DESTAB

	DELIMAH(SENDQ[SQ0IMAHS(.JOBSLOT)], .IMAHPTR);	! REMOVE IMAH FROM SENDQ

	IF .ENDI EQL EPI THEN				! IF EPI THEN
	    BEGIN
		PURGEI(.JOBSLOT);			! CLEAR THIS MPP'S RECVQ
		PURGEO(.JOBSLOT);			! AND SENDQ
		SETMPPSTATUS(.JOBSLOT,EPIBIT)		! AND TELL THE KERNEL IT IS KILLABLE
	    END

    END;
	EXTERNAL	LINK,
			MSGCHN,
			BUILD,
			OUTLOGMSG,
			DCHNKS,
			INSERT,
			DROLGH,
			DROLMSG;

COMMENT;

! SUBROUTINE SEND
! ===============

! LAST MODIFIED:	29 MAR 77  BY  CDO

GLOBAL ROUTINE SEND(JOBSLOT,PAGEADDR) =
    BEGIN

	LOCAL	DATE,
		TIME,
		GHPTR,
		ENDI,
		VALUE,
		DTAB,
		TRMCNT,
		LTTYNO,
		MHS;

	REGISTER
		IMAHPTR,
		MHPTR,
		DEST;

	MAP	FORMAT IMAHPTR;
	MAP	FORMAT MHPTR;
	MAP	FORMAT GHPTR;
	MAP	FORMAT DEST;

	BIND MCLASSDEF = 1;

	MACRO LOGANDCHKORROLL =
		    IF .INLOGGING THEN INLOGMSG(.DEST[G0TSN],.DATE,.TIME,.DEST[G0SENDER],0,.MHPTR[M0INCHNK]);
		    IF .DEST[G0CHKPNT] THEN
			BEGIN
			    MHPTR[M0DSKADDR]_CHKMSG(.DEST[G0DSKGHADDR],.DATE,.TIME,.MHPTR[M0INCHNK],.MHPTR[M0SOT]);
			    IF .DEST[G0ONDSK] THEN		! IF ROLLING OTHERS
				BEGIN
				    DCHNKS(.MHPTR);		! ZAP THE CHUNKS
				    MHPTR[M0CHNKS] _ 0		! AND MARK IT OUT
				END
			END
			ELSE
			    IF .DEST[G0ONDSK] THEN		!IF ROLLING FOR THIS GROUP
				BEGIN
				    MHPTR[M0DSKADDR]_ROLMSG(.DEST[G0DSKGHADDR],.MHPTR[M0INCHNK],.MHPTR[M0SOT]);
				    MHPTR[M0CHNKS] _ 0
				END;
		$; !!!!!!! END OR LOGANDCHKORROLL

! BEGIN !

	VALUE _ DELETEPAGE;				! ASSUME THIS IS A SEND WITH NO RESPONSE

	IF .(.PAGEADDR)[P0FCN] EQL SENDRR THEN		! IF IT IS A SEND WITH RESPONSE (INITIAL SEND)
	    BEGIN
		VALUE _ SENDPAGE;			! THEN WE WILL SEND THIS PAGE BACK
		IMAHPTR _ SEND4(.JOBSLOT,.PAGEADDR);	! TAKE CARE OF THE INITIAL SET UP
		IF .IMAHPTR LSS 0 THEN RETURN .VALUE;	! IF ERROR LEAVE NOW
		MHPTR _ .IMAHPTR[I0MHPTR]		! PICK UP THE MHPTR MADE BY SEND4
	    END
	    ELSE
	    BEGIN
		IF ( IMAHPTR _ FINDS(.(.PAGEADDR)[P0TSN],.JOBSLOT) ) EQL 0 THEN
				! IF NOT INITIAL SEND, FIND THE OLD DESTINATIONS
		    BEGIN
			(.PAGEADDR)[P0STATUS] _ STSNOTINSEND;
			RETURN .VALUE
		    END;

		IF (MHPTR _ .IMAHPTR[I0MHPTR]) EQL 0 THEN	! ANY MH LEFT FROM A PREVIOUS SEND?
		    BEGIN
			MHPTR _ CREATEMH(0);				! NO THEN MAKE ONE
			IMAHPTR[I0MHPTR] _ .MHPTR;			! AND TELL THE IMAH WE DID
			GHPTR _ .IMAHPTR[I0GHPTR];			! GET THE GROUP HEADER
			MHPTR[M0DSCNT] _ .GHPTR[G0DSTCNT];		! AND COPY THE COUNT FROM IT TO THE NEW MH
				! SEND4 CREATES AN MH, SO THIS CODE ISN'T USED
				! THE FIRST TIME THRU ( WITH SEND4)
		    END
	    END;

	ENDI _ UNPACK(.MHPTR,.PAGEADDR,.JOBSLOT,.IMAHPTR);	! UNPACK

	IF .ENDI LSS EMI THEN RETURN .VALUE;		! IF NOT A FULL MESSAGE THEN LEAVE NOW

	IF (MHPTR[M0OUTCHNK] _ .MHPTR[M0INCHNK]) EQL 0 THEN BEGIN
		DELETE(0,.MHPTR,M0SIZE,-1);		! REMOVE EMPTY HEADER
		SENDEXIT(.JOBSLOT,.IMAHPTR,.ENDI);	! CLEAN UP AFTER CALLER
		RETURN .VALUE				! AND EXIT NOW
	   END;

	TRMCNT _ .IMAHPTR[ I0TRMCNT ];			!REMEMBER TERMINAL COUNTS

	STAMP(DATE,TIME);				! DATE AND TIME STAMP A COMPLETE MESSAGE

	IF .OUTLOGGING THEN				!  LOG THE OUTPUT MESSAGE
	     OUTLOGMSG(.IMAHPTR[I0TSN],
			.DATE,
			.TIME,
			(.PAGEADDR)[P0MCLS],
			.JOBSLOT,
			.(.PAGEADDR)[P0DSCNT],
			(.PAGEADDR)[P0DTAB],
			.MHPTR[M0INCHNK]);

	DTAB _ .IMAHPTR[I0DESTAB];			! GET THE DESTINATION TABLE FROM THE IMAH

	IF ( GHPTR _ .IMAHPTR[I0GHPTR]) EQL 0 THEN	! IF WE DON'T HAVE A GROUP HEADER
	    BEGIN
		IF .IMAHPTR[I0LFCNT] NEQ 0 THEN			! IF THE LEAF COUNT IS NON-ZERO OUTPUT THE LEAVES
			SENDLF(.IMAHPTR,.DATE,.TIME,.ENDI);

		IF .IMAHPTR[I0MCLSS] EQL MCLASSDEF AND .TRMCNT NEQ 0 THEN
				! IF DEFERRED AND AT LEAST ONE SOURCE TYPE
		    BEGIN
			IMAHPTR[I0DSKMH] _ MHPTR[M0DSKADDR] _ DROLMSG( .MHPTR[M0INCHNK], 0 );	! ROLL OUT 1ST MSG
			MHPTR[M0CHNKS] _ 0
		    END;

		INCR I FROM 0 TO .TRMCNT - 1 DO				! FOR ALL TERMINALS
		    BEGIN
			IF .IMAHPTR[I0DSTSIZE] EQL 0 THEN
				DEST _ .DTAB			! IF ONLY ONE DESTINATION THEN DTAB IS THE DEST
			    ELSE DEST _ .(.DTAB)[.I]<RH>;	! ELSE DTAB IS A POINTER TO THE DEST TABLE

			IF .IMAHPTR[I0MCLSS] EQL MCLASSDEF THEN		! IF DEFERRED
			    BEGIN
				GHPTR _ CREATEGH(DEST[S0DGHS]);		! CREATE A GROUP HEADER
									! AND LINK TO DEFERRED MESSAGES
				DEST[S0DFGCNT] _ .DEST[S0DFGCNT] + 1;	! INCREMENT THE DEFERRED MESSAGE COUNT
				LTTYNO _ (.DEST<RH>-(.SRCTAB+1))/.S0SIZE; ! FIGURE TTY NUMBER
				GHPTR[G0DSKGHADDR] _ DROLGH( .LTTYNO, .MHPTR[M0DSKADDR],
								.IMAHPTR[I0TSN],.DEST);
										! SAVING THE DISK ADDRESS IN THE GH
				GHPTR[G0ONDSK] _ TRUE				! MARK IT AS ON DSK
			    END
			    ELSE					! IF IMMEDIATE
			    BEGIN
				MSGCHN(.DEST);				! INSERT INTO OUTPUT CHAIN
				GHPTR _ CREATEGH(DEST[S0IGHS]);		! THEN CREATE A GROUP HEADER AND LINK IT
									! TO THE IMMEDIATE MESSAGES
				DEST[S0DEFSENDING] _ FALSE		! SET THINGS SO WE CAN SEND
									! PIGGYBACK DEFERRED'S AGAIN
			    END;

			GHPTR[G0OUTMH] _ .MHPTR;			! MHPTR,
			GHPTR[G0SENDER] _ .DEST;			! SENDER,
			GHPTR[G0DSTCNT] _ .TRMCNT;			! DESTINATION COUNT IN THE GH
			GHPTR[G0TSN] _ .IMAHPTR[I0TSN];			! AND THE TRANSACTION SEQUENCE NUMBER

			IF .I EQL 0 THEN
			    BEGIN
				IMAHPTR[I0GHPTR] _ .GHPTR;	! FOR THE FIRST DESTINATION SAVE THE GHPTR
				LINK( GHPTR[G0MHS], .MHPTR);	! LINK THE MESSAGES TOGETHER
				MHS _ .GHPTR[G0MHS];		! AND REMEMBER THE LINKAGE
			    END
			    ELSE GHPTR[G0MHS] _ .MHS;		! ELSE STORE THE REMEMBERED LINKAGE

			IF .ENDI GTR EMI THEN GHPTR[G0ENDI] _ TRUE;	! IF EGI OR EPI, MARK THE GROUP COMPLETE

			IF .IMAHPTR[I0DSTSIZE] NEQ 0 THEN		! IF MORE THAN ONE DEST THEN
				(.DTAB)[.I] _ .GHPTR			! STORE THE GH IN THE DESTINATION TABLE
			    ELSE IMAHPTR[ I0DESTAB ] _ .GHPTR		! STORE THE GH IN THE IMAH FOR A SINGLE DEST
		    END;

		SENDEXIT(.JOBSLOT,.IMAHPTR,.ENDI);		! ALL DONE WITH "IF NO GH" SO FINISH UP
		RETURN .VALUE					! AND RETURN
	    END;

	IF .TRMCNT NEQ 0 THEN ATTACH(.IMAHPTR,.ENDI,.GHPTR,.MHPTR);	! LINK THE MESSAGE

	IF .IMAHPTR[I0LFCNT] NEQ 0 THEN			! IF LEAF DESTINATION(S) THEN
	    BEGIN

		INCR I FROM IF .TRMCNT EQL 0 THEN 2 ELSE 1 TO .IMAHPTR[I0LFCNT] DO
			BEGIN
				DEST _ .(.DTAB)[.IMAHPTR[I0DSTSIZE] - .I];
				IF .ENDI GTR EMI THEN DEST[G0ENDI] _ TRUE;
				IF .DEST[G0PURGING] THEN BEGIN
				    IF .ENDI GTR EMI THEN DELGH(0,.DEST)
				    END ELSE BEGIN
					MHPTR _ COPYMSG(.IMAHPTR[I0MHPTR],.DATE,.TIME);
					LOGANDCHKORROLL;
					INSERT(.MHPTR,.DEST)
				    END
			END;
		IF .TRMCNT EQL 0 THEN
		    BEGIN
			MHPTR _ .IMAHPTR[I0MHPTR];	!GET THE MH BACK AFTER COPIES
			MHPTR[M0DATE] _ .DATE;				! DATE AND TIME STAMP THE MESSAGE
			MHPTR[M0TIME] _ .TIME;
			DEST _ .IMAHPTR[I0GHPTR];			! SET UP FOR LOGANDCHKORROLL
			IF .ENDI GTR EMI THEN  DEST[G0ENDI] _ TRUE;
			IF .DEST[G0PURGING] THEN BEGIN
			    IF .ENDI GTR EMI THEN DELGH(0,.DEST);
			    DELMH(0,.MHPTR)
			END ELSE BEGIN
			    LOGANDCHKORROLL;
			    INSERT(.MHPTR,.DEST)				! ATTACH IT TO A GROUP
			END
		    END
	    END;

	IF .IMAHPTR[I0MCLSS] EQL MCLASSDEF AND .TRMCNT NEQ 0 THEN
				! IF DEFERRED AND AT LEAST ONE SOURCE TYPE
	    BEGIN
		MHPTR _ .IMAHPTR[I0MHPTR];			! MAKE SURE WE HAVE THE RIGHT MHPTR
		MHPTR[M0DSKADDR] _ DROLMSG( .MHPTR[M0INCHNK], .IMAHPTR[I0DSKMH]);	! ROLL THE MSG OUT
		MHPTR[M0CHNKS] _ 0
	    END;

	SENDEXIT(.JOBSLOT,.IMAHPTR,.ENDI);		! FINISH UP

	.VALUE						! RETURN WHAT TO DO WITH THE PAGE

    END;
END;