Google
 

Trailing-Edge - PDP-10 Archives - AP-D471B-SB_1978 - mgnmpp.bli
There are no other files named mgnmpp.bli in the archive.
!***COPYRIGHT (C) 1974, 1975, 1976, 1977 DIGITAL EQUIPMENT CORPORATION., MAYNARD, MASS.***
MODULE MPP(SREG = #17, FREG = #16,  VREG = #15,

!*** LAST MODIFIED BY  CDO  21-JUN-76

MLIST,TIMER=EXTERNAL(SIX12),FSAVE)=
BEGIN

! THIS MODULE CONTAINS THOSE ROUTINES REQUIRED TO HANDLE MPP GENERATIONS
GLOBAL BIND MPP = 1;
REQUIRE MGNMAC.BLI;
REQ(MGNEXT);
REQ(MGNMC2);

EXTERNAL
	MOVE,
	LINK,
	UNLINK,
	COMPARE,
	GETNODEPTR,

	HASMPP,

	MAXSLOTS;

OWN	LASTMPP;

COMMENT;
! ROUTINE PRINTMPPNAME
! ======= =============
! OUTPUTS MPPNAME TO CONTROLLING TTY

ROUTINE PRINTMPPNAME( MPPNAME ) =
    BEGIN
	TYPE( 'FOR [MPP ]'); OUTSA( .MPPNAME ); TYPE( '[:?M?J]' );
    END;

COMMENT;
! ROUTINE INMPP
! ======= ======
! THIS ROUTINE ASKS FOR AND ACCEPTS A MPP NAME FROM THE USER

ROUTINE INMPP(A)=
    BEGIN
									! TYPE THE QUESTION
	IF ASKL( .A ) THEN RETURN 0;					! ACCEPT AN ALTERNATE LINE ANSWER
								    	! IF THE LINE WAS EMPTY RETURN 0
	IF NOT .ERRORFLG THEN GETNAME(ALINE,ACHAR);			! IF NO ERROR ACCEPTING THE LINE THEN GATHER A NAME
	IF NOT .ERRORFLG THEN TOOMUCHINPUT();				! IF NOT END OF LINE THEN ERROR
	IF NOT .ERRORFLG THEN IF SUBQUEUES() OR .PERIODS NEQ 0 THEN ERROR( 18 );	! IF STILL NO ERRORS, SEE IF THE USER
								    	! PUT PERIODS IN THE MPP NAME
	IF .ERRORFLG THEN 0 ELSE 1					! IF ERROR IN ANY PART, RETURN 0 ELSE 1
    END;
COMMENT;

! ROUTINE NUMMPPS
! ======= ==========
! THIS ROUTINE KILLS ALL MPP SPECS

GLOBAL ROUTINE NUMMPPS =
    BEGIN
	REGISTER
		I,
		MPPADDR;
	MAP	FORMAT MPPADDR;

	MPPADDR _ .MPPTAB<FORE>;
	I _ -1;
	WHILE .MPPADDR NEQ 0 DO
	    BEGIN
		MPPADDR[M0MPPNO] _ I _ .I + 1;
		MPPADDR _ .MPPADDR[M0FORE]
	    END;

	LASTMPP _ .I

    END;
COMMENT;

! ROUTINE CREATEMPP
! ======= ==========
! THIS ROUTINE STUFFS A NEW MPP IN THE MPPTAB
! INPUT POINTER TO MPPNAME
! OUTPUT POINTER TO MPP TABLE ENTRY

ROUTINE CREATEMPP( MPPNAME ) =
    BEGIN
	REGISTER MPPADDR;
	MAP	FORMAT MPPADDR;

	MPPADDR _ GMEM(M0SIZE);
	MPPADDR[M0LINKS] _ 0;
	MOVE( %FROM% .MPPNAME, %TO% MPPADDR[M0NAME], M0NAMELEN %WORDS%);
	LINK( %TO% MPPTAB, MPPADDR[M0LINKS]);

	.MPPADDR
    END;

COMMENT;
! ROUTINE NULLMPPNAME
! ======= ============
! RETURNS TRUE IF THE MPPNAME IS NULL

ROUTINE NULLMPPNAME(LOC)=
    BEGIN
	! RETURNS TRUE IF THE CONTENTS OF LOC IS NULL, ELSE FALSE
	RETURN IF ..LOC EQL 0 THEN TRUE ELSE FALSE
    END;
FORWARD GETMPPADDR(1);

COMMENT;

! ROUTINE MAKUMPP
! ======= ========
! THIS ROUTINE MAKES A MPP TO ATTACH TO A NODE

GLOBAL ROUTINE MAKUMPP( NODEPTR, QUESTION ) =
    BEGIN
	OWN
		MPPADDR;
	LABEL	LOOP;

	MAP	FORMAT	MPPADDR;
	MAP	FORMAT	NODEPTR;

LOOP:	REPEAT						! UNTIL WE GET A GOOD ANSWER OR A LONE <CRLF>
	    BEGIN
		IF INMPP( .QUESTION ) NEQ 0 THEN
			LEAVE LOOP				! GOT A GOOD ONE SO CONTINUE
		    ELSE
		    BEGIN
			IF .ERRORFLG THEN
			  BEGIN
			    ERROR( 75 );			!BAD MPP NAME
			    WARN( 0 );
			    ERRORFLG _ 0
			  END 
			  ELSE
			IF EOL( ACHAR ) THEN RETURN 0		! <CRLF> ONLY ? YES THEN RETURN
		    END
	    END;


	! IF WE GOT A MPP NAME
	IF (MPPADDR _ GETMPPADDR( PRIM<36,7> )) EQL 0 THEN
	    BEGIN
		MPPADDR _ CREATEMPP( PRIM<36,7> );
		MPPADDR[M0TOBEDEFINED] _ TRUE
	    END;

	ATTACH( %TO% MPPADDR[M0LISTPTRS], .NODEPTR, N0MPPOFFSETT );

	.MPPADDR

    END;

FORWARD ACCEPTMPP(1),MAKMPP(1);
COMMENT;

! ROUTINE MAKEMPP
! ======= ========
! GIVEN THE ADDRESS OF A MPPNAME, THIS ROUTINE CAUSE THE APPROPRIATE
! QUESTIONS TO BE ASKED TO GENERATE ONE OR MORE MPPS ( MORE IF
! THE MPP NAME WAS NULL)

GLOBAL ROUTINE MAKEMPP(MPPNAME)=
    BEGIN

	REGISTER MPPADDR;
	MAP	FORMAT MPPADDR;

	ERRORFLG _ 0;

	IF .ALLSWITCH THEN
	    BEGIN
		MPPADDR _ .MPPTAB<FORE>;
		WHILE .MPPADDR NEQ 0 DO
		    BEGIN
			IF .MPPADDR[M0TOBEDEFINED] THEN
			    BEGIN
				PRINTMPPNAME( MPPADDR[M0NAME] );
				MAKMPP( .MPPADDR);
			    END;
			MPPADDR _ .MPPADDR[M0FORE];
			CRLF
		    END;
		ZERO( .MPPNAME, .MPPNAME + M0NAMELEN )
	    END;

	IF SUBQUEUES() OR .PERIODS NEQ 0 THEN RETURN( ERROR( 18 ) );	! PERIODS IN A MPP NAME ARE NO NO'S

	IF NULLMPPNAME(.MPPNAME) THEN		! IF NO MPP SPECIFIED
	    BEGIN				! ASK THE USER FOR MPP NAMES
		REPEAT				! UNTIL HE INPUTS A <CR> BY
		    BEGIN			! ITSELF
			IF INMPP( PAZ '[MPPNAME]?R(<done>,<NAME>?R)[:	??') EQL 0 THEN
			    IF NOT .ERRORFLG THEN RETURN
				ELSE
				BEGIN
							! IF THE ANSWER WAS UNACCEPTABLE
				    ERROR( 75 );			! TELL THE USER IT WAS BAD
				    WARN( 0 );			! AND HAVE HIM TRY AGAIN
				    ERRORFLG _ 0;
				END
		            ELSE ACCEPTMPP(PRIM<36,7>);	! IF OK NAME EXECUTE THE FUNCTION
			CRLF
		    END
	    END;
	ACCEPTMPP(.MPPNAME)				! IF A MPP WAS SPECIFIED, USE IT
    END;
COMMENT;

! ROUTINE GETMPPADDR
! ======= ===========
! SEARCHES THE MPPTAB FOR THE MPPNAME CONTAINED AT THE CONTENTS OF MPPNAME
! IF FOUND, RETURNS THE ADDRESS OF THE MPP TABLE ENTRY
! OTHERWISE RETURNS 0

ROUTINE GETMPPADDR(MPPNAME) =
    BEGIN
	REGISTER MPPADDR;
	MAP FORMAT MPPADDR;

	IF TOOLONG(.MPPNAME,M0NAMESIZE, M0NAMELEN * 5) THEN
	    BEGIN
		WARN( 5 );
		TRUNCATE(.MPPNAME, M0NAMESIZE, M0NAMELEN * 5)
	    END;

	IF ( MPPADDR _ .MPPTAB<FORE> ) EQL 0 THEN RETURN 0;	! START AT THE BEGINNING
							! WHILE THE NAME AT MPPNAME ISN'T THE NAME IN THE TABLE DO
	WHILE NOT COMPARE(.MPPNAME,MPPADDR[M0NAME],M0NAMELEN) DO
							! IF END OF TABLE RETURN 0
		IF (MPPADDR _ .MPPADDR[M0FORE]) EQL 0 THEN RETURN 0;
	.MPPADDR					! A MATCH! SO RETURN THE ADDRESS
    END;

FORWARD MAKMPP(1), MODMPP(1);

COMMENT;

! ROUTINE ACCEPTMPP
! ======= ==========
! THIS ROUTINE CREATES A MPP TABLE ENTRY IF ONE DOES NOT EXIST
! THEN CALLS MAKMPP TO ASK THE QUESTIONS TO FILL THE ENTRY
! IF THE MPP ALREADY EXISTS, THE USER IS ASKED HOW HE WANTS TO
!  HANDLE IT

ROUTINE ACCEPTMPP(MPPNAME)=
    BEGIN
	REGISTER MPPADDR;
	MAP FORMAT MPPADDR;

	PRINTMPPNAME( .MPPNAME );

	! SEE IF ALREADY DEFINED
	IF (MPPADDR _ GETMPPADDR(.MPPNAME)) EQL 0 THEN
		MPPADDR _ CREATEMPP( .MPPNAME )		! MAKE A MPP TABLE ENTRY
	    ELSE
	    BEGIN
		IF NOT .MPPADDR[M0TOBEDEFINED] THEN
		    BEGIN
			ASKSTR( '[MPP ALREADY EXISTS]?R(IGNORE, MODIFY, REPLACE?R)[:	??]',
				PLIT(	ASCII 'REPLA',	MAKMPP,
					ASCII 'MODIF',	MODMPP,
					0,		IGNORE,
					ASCII 'IGNOR',	IGNORE),
				.MPPADDR);
			RETURN
		    END
	    END;

	MAKMPP(.MPPADDR)

    END;
COMMENT;
!!
!!! ROUTINE SETLOCK
!!! ======= =======
!!! SETS THE LOCK BIT TO TRUE INDICATING LOCKING IS DESIRED
!!
!!ROUTINE SETLOCK( MPPADDR ) =
!!    BEGIN
!!	MAP FORMAT MPPADDR;
!!
!!	MPPADDR[M0LOCK] _ TRUE
!!
!!    END;
!!
!!COMMENT;
!!
!!! ROUTINE CLEARLOCK
!!! ======= =========
!!! SETS THE LOCK BIT TO FALSE INDICATING LOCKING IS NOT DESIRED
!!
!!ROUTINE CLEARLOCK( MPPADDR ) =
!!    BEGIN
!!	MAP FORMAT MPPADDR;
!!
!!	MPPADDR[M0LOCK] _ FALSE
!!
!!    END;
!!
COMMENT;

! ROUTINE SETIMMORTAL
! ======= =======
! SETS THE IMMORTAL BIT TO TRUE INDICATING IMMORTAL IS DESIRED

ROUTINE SETIMMORTAL( MPPADDR ) =
    BEGIN
	MAP FORMAT MPPADDR;

	MPPADDR[M0IMMORTAL] _ TRUE

    END;

COMMENT;

! ROUTINE CLEARIMMORTAL
! ======= =========
! SETS THE IMMORTAL BIT TO FALSE INDICATING IMMORTAL IS NOT DESIRED

ROUTINE CLEARIMMORTAL( MPPADDR ) =
    BEGIN
	MAP FORMAT MPPADDR;

	MPPADDR[M0IMMORTAL] _ FALSE

    END;

FORWARD MAKEEGO(2);

COMMENT;

! ROUTINE MAKMPP
! ======= =======
! THIS ROUTINE ASKS THE QUESTIONS TO MAKE ONE (AND ONLY ONE) MPP
! AND RECORDS THE RESPONSES IN THE APPROPRIATE MPP TABLE ENTRY

ROUTINE MAKMPP(MPPADDR)=
    BEGIN
	OWN	SPECBLK[4];
	MAP FORMAT MPPADDR;

	WHILE ASKFSPEC( '[FILE] DESIGNATION ?R(<FILE-SPEC>?R)[:	??]', SPECBLK) EQL CRONLY DO
	    BEGIN
		ERROR( 76 );
		WARN( 0 );
	    END;

	MAKEEGO( SPECBLK, .MPPADDR );

	TYPE( '[BACKUPS]?R(<DONE>, <FILE-SPEC> ?R)[:]' );

	WHILE NOT (ASKFSPEC('[			??]', SPECBLK) EQL CRONLY) DO
	    BEGIN
		MAKEEGO( SPECBLK, .MPPADDR )
	    END;

	MPPADDR[M0MAX] _ ASKDNUM( '[MAX]IMUM[ COPIES] THAT CAN RUN?R(<MAX AVAILABLE JOB SLOTS>, <DECIMAL NUMBER>?R)[:	??]',
				DEFAULTOK,
				BIGNUMBER,
				0,
				.MAXSLOTS);

	MPPADDR[M0MIN] _ ASKDNUM( '[MIN]IMUM[ COPIES] THAT MUST RUN?R(0, <DECIMAL NUMBER>?R)[:	??]',
				DEFAULTOK,
				0,
				0,
				.MPPADDR[ M0MAX ]);

	MPPADDR[M0INITIAL] _ ASKDNUM( '[COPIES] TO START[ INITIALLY]?R(0, <DECIMAL NUMBER>?R)[:	??]',
				DEFAULTOK,
				0,
				0,
				.MPPADDR[ M0MAX ]);

	MPPADDR[M0HPQ] _ ASKDNUM( '[HPQ] TO USE?R(<none>,<DECIMAL NUMBER BETWEEN 1 AND 15 INCLUSIVE>?R)[:	??]',
				DEFAULTOK,
				0,
				1,
				15);

!!	ASKSTR('[LOCK] JOB IN CORE?R(NO,YES?R)[:	??]',
!!		PLIT(	ASCII 'YES',	SETLOCK,
!!			ASCII 'NO',	CLEARLOCK,
!!			0,		CLEARLOCK),
!!		.MPPADDR);

	ASKSTR('CAN THIS [MPP ]BE [KILL(ABLE)]ED BY MCS?R(YES,NO?R)[:	??]',
		PLIT(	ASCII 'YES',	CLEARIMMORTAL,
			ASCII 'NO',	SETIMMORTAL,
			0,		CLEARIMMORTAL),
		.MPPADDR);


	MPPADDR[M0TOBEDEFINED] _ FALSE
    END;
COMMENT;

! ROUTINE MAKEEGO
! ======= ========
! THIS ROUTINE GETS CORE FOR AN ALTEREGO, STORES THE FILE SPEC, AND ASKS
! FOR THE AMOUNT OF CORE REQUIRED BY THE PROGRAM.

ROUTINE MAKEEGO( SPECBLK, MPPADDR ) =
    BEGIN
	REGISTER EGOPTR;

	MAP	FORMAT MPPADDR;
	MAP	FORMAT EGOPTR;
	MAP	FORMAT SPECBLK;

	IF .SPECBLK[ SB0EXT ] NEQ 0 THEN
	  BEGIN
	    WARN(27);		!EXTENSION IGNORED
	    SPECBLK[SB0EXT] _ 0 ! AND FORCE TO ZERO
	  END;
	EGOPTR _ GMEM( E0SIZE );

	EGOPTR[E0EGOS] _ 0;

	LINK( %TO% MPPADDR[M0EGOS], .EGOPTR);

	MOVE( %FROM% .SPECBLK, %TO% EGOPTR[E0SPECBLK], SPECBLKLEN );

	EGOPTR[E0CORE] _ ASKSIZE( '?I?I[CORE] TO START MPP WITH ?R(<AS REQ''D>, <CORE-SPEC>?R)[:	??]');

    END;
FORWARD DISMPP(1);

COMMENT;

! ROUTINE DISPMPP
! ======= ===========
! THIS ROUTINE HANDLES THE INDIVIDUAL CASES OF DISPLAY MPP:...
! CASE 1: IF AN INDIVIDUAL MPP IS REQUESTED ( BY NAME ), DISMPP IS
!	  CALLED WITH THE ADDRESS OF THE MPP TABLE ENTRY
! CASE 2: IF /ALL WAS SPECIFIED, DISMPP IS CALLED FOR EACH ENTRY IN
!	  MPP TABLE
! CASE 3: IF NO NAME OR SWITCH WAS GIVEN, THE USER IS ASKED FOR THE
!	  NAMES OF THE MPPS TO BE DISPLAYED

GLOBAL ROUTINE DISPMPP(MPPNAME)=
    BEGIN
	REGISTER MPPADDR;
	MAP FORMAT MPPADDR;

	IF SUBQUEUES() OR .PERIODS NEQ 0 THEN RETURN( ERROR( 18 ) );	! PERIODS IN A MPP NAME ARE NO NO'S

	IF NULLMPPNAME(.MPPNAME) AND NOT .ALLSWITCH THEN
	    BEGIN				! ASK THE USER FOR MPP NAMES
		REPEAT		! UNTIL HE INPUTS A <CR> BY
		    BEGIN			! ITSELF
			IF INMPP( PAZ '[MPPNAME]?R(<done>,<NAME>?R)[:	??') EQL 0 THEN 
			    IF NOT .ERRORFLG THEN RETURN
				ELSE
				BEGIN
							! IF THE ANSWER WAS UNACCEPTABLE
				    ERROR( 75 );			! TELL THE USER IT WAS BAD
				    WARN( 0 );			! AND HAVE HIM TRY AGAIN
				    ERRORFLG _ 0;
				END
			    ELSE IF (MPPADDR _ GETMPPADDR(PRIM)) EQL 0 THEN
				RETURN(ERROR(16))
				ELSE DISMPP(.MPPADDR);
			CRLF
		    END
	    END;

	IF .ALLSWITCH THEN
	    BEGIN
		MPPADDR _ .MPPTAB<FORE>;
		WHILE .MPPADDR NEQ 0 DO
		    BEGIN
			DISMPP( .MPPADDR);
			MPPADDR _ .MPPADDR[M0FORE]
		    END;
		RETURN
	    END;
	IF (MPPADDR _ GETMPPADDR(.MPPNAME)) EQL 0 THEN RETURN(ERROR(16))
	    ELSE DISMPP(.MPPADDR)
    END;

FORWARD DMPPNAME,DMPPFILE,DMPPBACKUPS,DMPPMAX,DMPPMIN,DMPPINIT,DMPPHPQ,DMPPIMMORTAL;
!!FORWARD	DMPPLOCK;
COMMENT;

! ROUTINE DISMPP
! ======= =======
! THIS ROUTINE DISPLAYS THE MPP'S NAME, AND IF THE MPP IS UNDEFINED,
! THEN "NOT YET DEFINED..." IS DISPLAYED, OTHERWISE THE MPP PARAMETERS
! ARE DISPLAYED

ROUTINE DISMPP(MPPADDR)=
    BEGIN
	MAP FORMAT MPPADDR;
	DMPPNAME(.MPPADDR);
	IF .MPPADDR[M0TOBEDEFINED] THEN
	    BEGIN
		OUTPUT('NOT YET DEFINED, PLEASE MAKE OR MODIFY IT');
	    END
	    ELSE
	    BEGIN
		DMPPFILE( .MPPADDR );
		DMPPBACKUPS( .MPPADDR );
		DMPPMAX(.MPPADDR);
		DMPPMIN(.MPPADDR);
		DMPPINIT(.MPPADDR);
		DMPPHPQ(.MPPADDR);
!!		DMPPLOCK(.MPPADDR);
		DMPPIMMORTAL(.MPPADDR);
	    END;
	OUTPUTCRLF
    END;

COMMENT;

! ROUTINE DMPPNAME
! ======= =========
! THIS ROUTINE DISPLAYS THE MPP NAME

ROUTINE DMPPNAME(MPPADDR)=
    BEGIN
	MAP FORMAT MPPADDR;
	OUTPUT('MPPNAME:	'); XOUTPUT(MPPADDR[M0NAME]);
	OUTPUTCRLF
    END;

COMMENT;

! ROUTINE DMNAME
! ======= =========
! THIS ROUTINE DISPLAYS THE MPP NAME WITHOUT LABEL

GLOBAL ROUTINE DMNAME(MPPADDR)=
    BEGIN
	MAP FORMAT MPPADDR;
	XOUTPUT(MPPADDR[M0NAME])
    END;

FORWARD	DEGO(1);

COMMENT;

! ROUTINE DMPPFILE
! ======= =========
! THIS ROUTINE DISPLAYS THE MPP'S PRIMARY FILE SPECIFICATION

ROUTINE DMPPFILE(MPPADDR)=
    BEGIN
	REGISTER EGOPTR;

	MAP	FORMAT EGOPTR;
	MAP	FORMAT MPPADDR;

	EGOPTR _ .MPPADDR[M0FEGO];

	OUTPUT('FILE DESIGNATION:	');
	DEGO( .EGOPTR );

    END;

! ROUTINE DMPPBACKUPS
! ======= =========
! THIS ROUTINE DISPLAYS THE MPP'S BACKUPS' SPECIFICATIONS

ROUTINE DMPPBACKUPS(MPPADDR)=
    BEGIN
	REGISTER EGOPTR;

	MAP	FORMAT EGOPTR;
	MAP	FORMAT MPPADDR;

	EGOPTR _ .MPPADDR[M0FEGO];

	OUTPUT('BACKUPS:	');

	IF .EGOPTR[E0FORE] EQL 0 THEN OUTPUT('<none>?M?J')
	    ELSE
		WHILE ( EGOPTR _ .EGOPTR[E0FORE] ) NEQ 0 DO
		    BEGIN
			DEGO( .EGOPTR );
			IF .EGOPTR[E0FORE] NEQ 0 THEN OUTPUT( '		' )
		    END

    END;

COMMENT;

! ROUTINE DEGO
! ======= ====
! THIS ROUTINE DISPLAYS THE FILE SPEC OF AN ALTEREGO, GIVEN A POINTER
! TO THE ALTEREGO

ROUTINE DEGO( EGOPTR ) =
    BEGIN
	MAP	FORMAT EGOPTR;

	OUTPUTFSPEC( EGOPTR[E0SPECBLK] );
	OUTPUT('?I( CORE: ');
	IF .EGOPTR[E0KCORE] EQL 0 THEN
		OUTPUT( '<as req''d> ')
	    ELSE
	    BEGIN
		OUTPUTD( .EGOPTR[E0KCORE] );
		OUTPUTC( IF .EGOPTR[E0KFLAG] THEN "K" ELSE "P" );
	    END;
	OUTPUTC( ")" );
	OUTPUTCRLF

    END;

COMMENT;

! ROUTINE DMPPMAX
! ======= ========
! THIS ROUTINE DISPLAYS THE MAXIMUM NUMBER OF COPIES OF THE MPP TO RUN

ROUTINE DMPPMAX(MPPADDR)=
    BEGIN
	MAP FORMAT MPPADDR;
	OUTPUT('MAXIMUM COPIES TO RUN:	');
	IF .MPPADDR[M0MAX] EQL BIGNUMBER THEN OUTPUT('<MAXIMUM AVAILABLE JOB SLOTS>')
	    ELSE OUTPUTD( .MPPADDR[M0MAX] );
	OUTPUTCRLF
    END;

COMMENT;

! ROUTINE DMPPMIN
! ======= ========
! THIS ROUTINE DISPLAYS THE MINIMUM NUMBER OF COPIES OF THE MPP TO RUN

ROUTINE DMPPMIN(MPPADDR)=
    BEGIN
	MAP FORMAT MPPADDR;
	OUTPUT('MINIMUM COPIES TO RUN:	');
	OUTPUTD( .MPPADDR[M0MIN] );
	OUTPUTCRLF
    END;

COMMENT;

! ROUTINE DMPPINIT
! ======= ========
! THIS ROUTINE DISPLAYS THE NUMBER OF COPIES OF THE MPP TO START INITIALLY

ROUTINE DMPPINIT(MPPADDR)=
    BEGIN
	MAP FORMAT MPPADDR;
	OUTPUT('COPIES TO START INITIALLY:	');
	OUTPUTD( .MPPADDR[M0INITIAL] );
	OUTPUTCRLF
    END;

COMMENT;

! ROUTINE DMPPHPQ
! ======= =======
! THIS ROUTINE DISPLAYS THE HPQ THAT THE MPP IS TO BE RUN IN

ROUTINE DMPPHPQ( MPPADDR ) =
    BEGIN
	MAP	FORMAT MPPADDR;

	OUTPUT('HPQ TO BE USED:	');
	IF .MPPADDR[M0HPQ] EQL 0 THEN OUTPUT('<none>')
	    ELSE OUTPUTD( .MPPADDR[M0HPQ] );
	OUTPUTCRLF
   END;

COMMENT;

!!! ROUTINE DMPPLOCK
!!! ======= =========
!!! THIS ROUTINE DISPLAYS THE LOCK CAPABILITY OF THE MPP
!!
!!ROUTINE DMPPLOCK(MPPADDR)=
!!    BEGIN
!!	MAP FORMAT MPPADDR;
!!	OUTPUT('LOCK JOB IN CORE:	');
!!	XOUTPUT(IF .MPPADDR[M0LOCK] THEN PLIT ASCII 'YES' ELSE PLIT ASCIZ 'NO');
!!	OUTPUTCRLF
!!    END;

COMMENT;

! ROUTINE DMPPIMMORTAL
! ======= =========
! THIS ROUTINE DISPLAYS THE IMMORTAL CAPABILITY OF THE MPP

ROUTINE DMPPIMMORTAL(MPPADDR)=
    BEGIN
	MAP FORMAT MPPADDR;
	OUTPUT('JOB KILLABLE BY MCS:	');
	XOUTPUT(IF NOT .MPPADDR[M0IMMORTAL] THEN PLIT ASCII 'YES' ELSE PLIT ASCIZ 'NO');
	OUTPUTCRLF
    END;

! ROUTINE MODIMPP
! ======= ===========
! THIS ROUTINE HANDLES THE INDIVIDUAL CASES OF MODIFY MPP:...
! CASE 1: IF AN INDIVIDUAL MPP IS REQUESTED ( BY NAME ), MODMPP IS
!	  CALLED WITH THE ADDRESS OF THE MPP TABLE ENTRY
! CASE 2: IF /ALL WAS SPECIFIED, MODMPP IS CALLED FOR EACH ENTRY IN
!	  MPP TABLE
! CASE 3: IF NO NAME OR SWITCH WAS GIVEN, THE USER IS ASKED FOR THE
!	  NAMES OF THE MPPS TO BE MODIFIED

GLOBAL ROUTINE MODIMPP(MPPNAME)=
    BEGIN
	REGISTER MPPADDR;
	MAP FORMAT MPPADDR;

	IF SUBQUEUES() OR .PERIODS NEQ 0 THEN RETURN( ERROR( 18 ) );	! PERIODS IN A MPP NAME ARE NO NO'S

	IF NULLMPPNAME(.MPPNAME) AND NOT .ALLSWITCH THEN
	    BEGIN				! ASK THE USER FOR MPP NAMES
		REPEAT		! UNTIL HE INPUTS A <CR> BY
		    BEGIN			! ITSELF
			IF INMPP( PAZ '[MPPNAME]?R(<done>,<NAME>?R)[:	??') EQL 0 THEN 
			    IF NOT .ERRORFLG THEN RETURN
				ELSE
				BEGIN
							! IF THE ANSWER WAS UNACCEPTABLE
				    ERROR( 75 );			! TELL THE USER IT WAS BAD
				    WARN( 0 );			! AND HAVE HIM TRY AGAIN
				    ERRORFLG _ 0;
				END
			    ELSE IF (MPPADDR _ GETMPPADDR(PRIM)) EQL 0 THEN
				RETURN(ERROR(16))
				ELSE MODMPP(.MPPADDR);
			CRLF
		    END
	    END;

	IF .ALLSWITCH THEN
	    BEGIN
		MPPADDR _ .MPPTAB<FORE>;
		WHILE .MPPADDR NEQ 0 DO
		    BEGIN
			MODMPP( .MPPADDR);
			MPPADDR _ .MPPADDR[M0FORE]
		    END;
		RETURN
	    END;

	IF (MPPADDR _ GETMPPADDR(.MPPNAME)) EQL 0 THEN RETURN(ERROR(16))
	    ELSE MODMPP(.MPPADDR);
    END;

FORWARD MMPPNAME,MMPPFILE,MMPPBACKUPS,MMPPMAX,MMPPMIN,
	MMPPINITIAL,MMPPHPQ,MMPPIMMORTAL,MMPPALL,TELLMPPCHANGES;
!!FORWARD	MMPPLOCK;
COMMENT;

! ROUTINE MODMPP
! ======= =======
! THIS ROUTINE TYPES THE NAME OF THE MPP TO BE MODIFIED, AND THEN
! ASKS FOR THE CHANGES TO BE MADE.  MODMPP DOES ONLY ONE MPP AT A
! TIME.

ROUTINE MODMPP(MPPADDR)=
    BEGIN
	OWN	DONE;
	MAP FORMAT MPPADDR;

	%LOCAL% ROUTINE SETDONE = DONE _ TRUE;

	PRINTMPPNAME( MPPADDR[M0NAME] );			! TYPE THE NAME OF THE MPP TO BE MODIFIED

	IF .MPPADDR[M0TOBEDEFINED] THEN			! IF UNDEFINED THEN
	    BEGIN
		MAKMPP(.MPPADDR)				! MAKE IT
	    END
	    ELSE
	    BEGIN
		DONE _ FALSE;
		WHILE NOT .DONE DO				! OTHERWISE, UNTIL THE USER GIVES US A LONE <CR> DO
		    BEGIN
			ASKSTR( '[CHANGE:	??]',		! ASK WHAT CHANGES
				PLIT(	ASCII '??',	TELLMPPCHANGES,
					ASCII 'NAME',	MMPPNAME,
					ASCII 'FILE',	MMPPFILE,
					ASCII 'BACKU',	MMPPBACKUPS,
					ASCII 'MAX',	MMPPMAX,
					ASCII 'MIN',	MMPPMIN,
					ASCII 'INITI',	MMPPINITIAL,
					ASCII 'HPQ',	MMPPHPQ,
!!					ASCII 'LOCK',	MMPPLOCK,
					ASCII 'KILLA',	MMPPIMMORTAL,
					ASCII 'ALL',	MMPPALL,
					0,		SETDONE),
				.MPPADDR);
			CRLF
		    END
	    END
    END;
COMMENT;

! ROUTINE TELLMPPCHANGES
! ======= ===============
! THIS ROUTINE TELLS ( WITH LOTS OF WIND ) WHAT CHANGES CAN BE MADE ON
! AN MPP

ROUTINE TELLMPPCHANGES =
    BEGIN
	TYPE( 'TYPE [NAME] TO CHANGE THE NAME OF THE MPP[,]?J
?MTYPE [FILE] TO CHANGE THE FILE SPECIFICATION OF THE MPP[,]?J
?MTYPE [BACKUPS] TO CHANGE THE BACKUPS TO THE MPP[,]?J
?MTYPE [MAX] TO CHANGE THE MAXIMUM NUMBER OF COPIES OF THE MPP TO RUN[,]?J
?MTYPE [MIN] TO CHANGE THE MINIMUM NUMBER OF COPIES OF THE MPP TO RUN[,]?J
?MTYPE [INITIAL] TO CHANGE THE NUMBER OF COPIES OF THE MPP TO START INITIALLY[,]?J
?MTYPE [HPQ] TO CHANGE THE HPQ TO RUN THE MPP IN[,]?M?J' );
!!	TYPE( 'TYPE [LOCK] TO CHANGE THE LOCKING ABILITY OF THE MPP[,]?M?J' );
	TYPE( 'TYPE [KILLABLE] TO CHANGE THE KILLABILITY OF THE MPP[,]?J
?MTYPE [ALL] TO CHANGE ALL OF THE ABOVE[,]?J
?MTYPE A CARRIAGE RETURN TO FINISH CHANGING THIS MPP[(?R(<CR> WHEN DONE?R))]');
	CRLF;
    END;

COMMENT;

! ROUTINE MMPPALL
! ======= ========
! THIS ROUTINE ASKS ALL THE QUESTIONS TO CHANGE AN MPP

ROUTINE MMPPALL(MPPADDR) =
    BEGIN
	MMPPNAME( .MPPADDR);
	MMPPFILE( .MPPADDR);
	MMPPBACKUPS( .MPPADDR);
	MMPPMAX( .MPPADDR);
	MMPPMIN( .MPPADDR);
	MMPPINITIAL( .MPPADDR);
	MMPPHPQ( .MPPADDR);
!!	MMPPLOCK( .MPPADDR);
	MMPPIMMORTAL( .MPPADDR)
    END;

COMMENT;

! ROUTINE MMPPNAME
! ======= =========
! THIS ROUTINE ASKS THE QUESTION TO CHANGE AN MPP'S NAME

ROUTINE MMPPNAME(MPPADDR)=
    BEGIN
	MAP FORMAT MPPADDR;
	LABEL LOOP;
	IF .SHOW THEN DMPPNAME(.MPPADDR);
LOOP: REPEAT
      BEGIN
	IF INMPP( PAZ 'NEW [MPPNAME]?R(,<NAME>?R)[:	??]') EQL 0 THEN 
	  BEGIN
	    IF .ERRORFLG THEN
	      BEGIN
		ERROR ( 75 );
		WARN  (  0 );
		ERRORFLG _ 0
	      END
	    ELSE RETURN
	  END
	ELSE LEAVE LOOP;
       END;
	IF GETMPPADDR(PRIM) NEQ 0 THEN RETURN (ERROR(17));
	MOVE(PRIM, MPPADDR[M0NAME], M0NAMELEN)
    END;

COMMENT;

! ROUTINE MMPPFILE
! ======= =========
! THIS ROUTINE ASKS THE QUESTIONS NECESSARY TO CHANGE THE FILE
! SPECIFICATION OF THE MPP

ROUTINE MMPPFILE(MPPADDR)=
    BEGIN
	OWN	SPECBLK[ SPECBLKLEN];
	REGISTER
		OLDCORE,
		EGOPTR;

	MAP	FORMAT EGOPTR;
	MAP	FORMAT MPPADDR;

	EGOPTR _ .MPPADDR[M0FEGO];
	OLDCORE _ .EGOPTR[E0CORE];

	IF .SHOW THEN DMPPFILE(.MPPADDR);

	IF ( ASKFSPEC( '[NEW FILE] DESIGNATION ?R(,<FILE-SPEC>?R)[:	??]', SPECBLK) ) NEQ CRONLY THEN
		MOVE( %FROM% SPECBLK, %TO% EGOPTR[E0SPECBLK], SPECBLKLEN );

	EGOPTR[ E0CORE ] _ NEWASKSIZE( '[NEW CORE]?R(, <CORE-SPEC>, "<AS REQ''D>" ?R)[:	??]',
		PLIT( 0, ASCIZ '<AS REQ''D>' ), .OLDCORE );

    END;

COMMENT;

! ROUTINE MMPPBACKUPS
! ======= ========
! THIS ROUTINE ASKS THE QUESTION ABOUT CHANGING THE BACKUPS TO THE MPP

ROUTINE MMPPBACKUPS(MPPADDR)=
    BEGIN
	REGISTER
		EGOPTR,
		NEXTEGO;
	OWN	SPECBLK[SPECBLKLEN];

	MAP	FORMAT EGOPTR;
	MAP	FORMAT MPPADDR;

	IF .SHOW THEN DMPPBACKUPS(.MPPADDR);

	IF ( NEWASKFSPEC( '[NEW BACKUPS]?R(,"<NONE>",<FILE-SPEC>?R)[:	??]',
				 SPECBLK,
				 PLIT( PLIT( SPECBLKLEN : 0 ), ASCIZ '<NONE>' ),
				 EGOPTR[ E0SPECBLK ] ) ) EQL CRONLY THEN RETURN %?? CHANGE CORE ONLY%;

	! ELSE !

	EGOPTR _ .MPPADDR[M0FEGO];
	EGOPTR _ .EGOPTR[E0FORE];		! SKIP THE PRIMARY EGO

	WHILE .EGOPTR NEQ 0 DO			! THE DELETE THE ALTEREGOS
	    BEGIN
		NEXTEGO _ .EGOPTR[E0FORE];
		UNLINK( %FROM% MPPADDR[M0EGOS], .EGOPTR );
		PMEM( .EGOPTR, E0SIZE );
		EGOPTR _ .NEXTEGO
	    END;

	IF NULL( SPECBLK, SPECBLKLEN ) THEN RETURN;	! IF <NONE> THEN DONE

	MAKEEGO( SPECBLK, .MPPADDR );

	WHILE NOT (ASKFSPEC('[			??]', SPECBLK) EQL CRONLY) DO
	    BEGIN
		MAKEEGO( SPECBLK, .MPPADDR )
	    END

    END;

COMMENT;

! ROUTINE MMPPMAX
! ======= =========
! THIS ROUTINE ASKS THE QUESTION ABOUT CHANGING THE MAXIMUM COPIES
! OF THE MPP TO RUN

ROUTINE MMPPMAX(MPPADDR)=
    BEGIN
	MAP	FORMAT MPPADDR;

	IF .SHOW THEN DMPPMAX(.MPPADDR);
	MPPADDR[M0MAX] _ NEWASKDNUM( 'NEW [MAX]IMUM[ COPIES] THAT CAN RUN?R(,"<MAX AVAILABLE JOB SLOTS>", <DECIMAL NUMBER>?R)[:	??]',
				DEFAULTOK,
				.MPPADDR[ M0MAX ],
				PLIT( BIGNUMBER, ASCIZ '<MAX AVAILABLE JOB SLOTS>' ),
				0,
				.MAXSLOTS);

    END;

COMMENT;

! ROUTINE MMPPMIN
! ======= =========
! THIS ROUTINE ASKS THE QUESTION TO CHANGE THE MINIMUM NUMBER OF COPIES OF THE MPP TO RUN

ROUTINE MMPPMIN(MPPADDR)=
    BEGIN
	REGISTER CHANGE;
	MAP	FORMAT MPPADDR;

	IF .SHOW THEN DMPPMIN(.MPPADDR);
	IF (CHANGE _ ASKDNUM( 'NEW [MIN]IMUM[ COPIES] THAT MUST RUN?R(,<DECIMAL NUMBER>?R)[:	??]',
				DEFAULTOK,
				-1,
				0,
				.MPPADDR[ M0MAX ]) ) NEQ -1 THEN MPPADDR[M0MIN] _ .CHANGE;

END;

COMMENT;

! ROUTINE MMPPINITIAL
! ======= =========
! THIS ROUTINE ASKS THE QUESTION TO CHANGE THE NUMBER OF COPIES OF THE MPP TO START INITIALLY

ROUTINE MMPPINITIAL(MPPADDR)=
    BEGIN
	REGISTER CHANGE;
	MAP	FORMAT MPPADDR;

	IF .SHOW THEN DMPPINIT(.MPPADDR);
	IF (CHANGE _ ASKDNUM( 'NEW [COPIES] TO START[ INITIALLY]?R(,<DECIMAL NUMBER>?R)[:	??]',
				DEFAULTOK,
				-1,
				0,
				.MPPADDR[ M0MAX ]) ) NEQ -1 THEN MPPADDR[M0INITIAL] _ .CHANGE;

END;

COMMENT;

! ROUTINE MMPPHPQ
! ======= =========
! THIS ROUTINE ASKS THE QUESTION TO CHANGE THE HPQ TO RUN THE MPP IN

ROUTINE MMPPHPQ(MPPADDR)=
    BEGIN
	MAP	FORMAT MPPADDR;

	IF .SHOW THEN DMPPHPQ(.MPPADDR);
	MPPADDR[ M0HPQ ] _ NEWASKDNUM( 'NEW [HPQ] TO USE?R(,"<none>",<DECIMAL NUMBER BETWEEN 1 AND 15 INCLUSIVE>?R)[:	??]',
				DEFAULTOK,
				.MPPADDR[ M0HPQ ],
				PLIT( 0, ASCIZ '<NONE>' ),
				1,
				15);

END;

COMMENT;

!!! ROUTINE MMPPLOCK
!!! ======= =========
!!! THIS ROUTINE ASKS THE QUESTION ABOUT CHANGING THE LOCK CAPABILITY
!!! OF THE MPP
!!
!!ROUTINE MMPPLOCK(MPPADDR)=
!!    BEGIN
!!	IF .SHOW THEN DMPPLOCK(.MPPADDR);
!!	ASKSTR('NEW [LOCK] JOB IN CORE?R(,NO,YES?R)[:	??]',
!!		PLIT(	ASCII 'YES',	SETLOCK,
!!			ASCII 'NO',	CLEARLOCK,
!!			0,		IGNORE),
!!		.MPPADDR);
!!
!!    END;


COMMENT;

! ROUTINE MMPPIMMORTAL
! ======= =========
! THIS ROUTINE ASKS THE QUESTION ABOUT CHANGING THE IMMORTAL CAPABILITY
! OF THE MPP

ROUTINE MMPPIMMORTAL(MPPADDR)=
    BEGIN
	IF .SHOW THEN DMPPIMMORTAL(.MPPADDR);
	ASKSTR('IS THE MPP NOW [KILLABLE] BY MCS?R(,NO,YES?R)[:	??]',
		PLIT(	ASCII 'YES',	CLEARIMMORTAL,
			ASCII 'NO',	SETIMMORTAL,
			0,		IGNORE),
		.MPPADDR);

END;
FORWARD DELMPP(1),KILLMPPS();

COMMENT;

! ROUTINE DELEMPP
! ======= ===========
! THIS ROUTINE HANDLES THE INDIVIDUAL CASES OF DELETE MPP:...
! CASE 1: IF AN INDIVIDUAL MPP IS REQUESTED ( BY NAME ), DELMPP IS
!	  CALLED WITH THE ADDRESS OF THE MPP TABLE ENTRY
! CASE 2: IF /ALL WAS SPECIFIED, DELMPP IS CALLED FOR EACH ENTRY IN
!	  MPP TABLE
! CASE 3: IF NO NAME OR SWITCH WAS GIVEN, THE USER IS ASKED FOR THE
!	  NAMES OF THE MPPS TO BE DELETED

GLOBAL ROUTINE DELEMPP(MPPNAME)=
    BEGIN
	REGISTER MPPADDR;
	MAP FORMAT MPPADDR;

	IF SUBQUEUES() OR .PERIODS NEQ 0 THEN RETURN( ERROR( 18 ) );	! PERIODS IN A MPP NAME ARE NO NO'S

	IF NULLMPPNAME(.MPPNAME) AND NOT .ALLSWITCH THEN
	    BEGIN				! ASK THE USER FOR MPP NAMES
		REPEAT		! UNTIL HE INPUTS A <CR> BY
		    BEGIN			! ITSELF
			IF INMPP( PAZ '[MPPNAME]?R(<done>,<NAME>?R)[:	??') EQL 0 THEN 
			    IF NOT .ERRORFLG THEN RETURN
				ELSE
				BEGIN
							! IF THE ANSWER WAS UNACCEPTABLE
				    ERROR( 75 );			! TELL THE USER IT WAS BAD
				    WARN( 0 );			! AND HAVE HIM TRY AGAIN
				    ERRORFLG _ 0;
				END
			    ELSE IF (MPPADDR _ GETMPPADDR(PRIM)) EQL 0 THEN
				RETURN(ERROR(16))
				ELSE DELMPP(.MPPADDR);
			CRLF
		    END
	    END;

	IF .DELTYPEFLAG THEN TYPE( 'MPPS DELETED[?M?J]');

	IF .ALLSWITCH THEN
	    BEGIN
		IF NOT(CONFIRMED()) THEN RETURN;
		KILLMPPS();
		RETURN
	    END;
	IF (MPPADDR _ GETMPPADDR(.MPPNAME)) EQL 0 THEN RETURN(ERROR(16))
	    ELSE DELMPP(.MPPADDR);
    END;
COMMENT;

! ROUTINE KILLMPPS
! ======= ==========
! THIS ROUTINE KILLS ALL MPP SPECS

GLOBAL ROUTINE KILLMPPS =
    BEGIN
	REGISTER MPPADDR;
	MAP	FORMAT MPPADDR;

	MPPADDR _ .MPPTAB<FORE>;
	WHILE .MPPADDR NEQ 0 DO
	    MPPADDR _ DELMPP(.MPPADDR)

    END;
COMMENT;

! ROUTINE DELMPP
! ======= =======
! THIS ROUTINE DELETES THE MPP SPECIFIED FROM THE MPP TABLE
! RETURNS THE ADDRESS OF THE NEXT MPP IN THE MPP TABLE

GLOBAL ROUTINE DELMPP(MPPADDR)=
    BEGIN
	OWN NEXT;
	REGISTER
		NEXTNODE,
		NODEPTR,
		EGOPTR,
		NEXTEGO;

	MAP	FORMAT EGOPTR;
	MAP	FORMAT MPPADDR;
	MAP	FORMAT NODEPTR;

	XTYPE( MPPADDR[M0NAME] );			! TYPE THE NAME OF THE MPP DELETED, IF MSGLEVEL EQL LONG
	TYPECRLF;

	NEXT _ .MPPADDR[M0FORE];

	! ZAP ALTEREGOS
	EGOPTR _ .MPPADDR[M0FEGO];

	WHILE .EGOPTR NEQ 0 DO
	    BEGIN
		NEXTEGO _ .EGOPTR[E0FORE];
		PMEM( .EGOPTR, E0SIZE );
		EGOPTR _ .NEXTEGO
	    END;

	NOTE - HOW DO WE WANT TO HANDLE POINTERS IN THE TREE? NOW I DELETE THEM

	IF .MPPADDR[M0LISTPTRS] NEQ 0 THEN		! DELETE THE POINTERS TO THIS MPP IN THE TREE
	    BEGIN
		NODEPTR _ .MPPADDR[M0FLIST];		! GET FIRST NODE ADDR

		WHILE .NODEPTR NEQ 0 DO
		    BEGIN
			NODEPTR[N0MPPPTR] _ 0;
			NEXTNODE _ .NODEPTR[N0MPPFORE];
			NODEPTR[N0MPPLINKS] _ 0;
			NODEPTR _ .NEXTNODE
		    END;

		MPPADDR[M0LISTPTRS] _ 0
	    END;


	UNLINK(%FROM% MPPTAB, .MPPADDR);

	PMEM(.MPPADDR,M0SIZE);

	.NEXT

    END;
FORWARD WCMPPNAME(1), OUTPUTMPPNO, OUTPUTMPPEGO, OUTPUTMM, OUTPUTMPR;

COMMENT;

! ROUTINE WCMPP
! ======= ======
! THIS ROUTINE WRITES THE MPP SECTION OF THE COMPILE FILE

GLOBAL ROUTINE WCMPP=
    BEGIN
    ! IT IS ASSUMED THAT THE MPPS AND LEAVES HAVE BEEN NUMBERED

    ! THEN FOR EACH MPP

	REGISTER
		MPR,
		J,
		THISEGO,
		MPPADDR;
	MAP FORMAT MPPADDR;

	%LOCAL% ROUTINE OUTPUTPAIR( WHICH ) =
	    BEGIN
		OUTPUT( '	XWD	' );
		OUTPUTMPPNO( .WHICH );
		OUTPUTCOMMA;
		OUTPUTMPPNO( .WHICH );
		OUTPUTCRLF
	    END;

	OUTPUT( '?L	SUBTTL	MPPS?M?J?M?J' );


	OUTPUT('EXTERNAL	SON,SOFF,DMR,DCR,ERR?M?J');
	OUTPUT( '?M?JMPPTAB::?M?J' );

	INCR I FROM 0 TO .LASTMPP DO	OUTPUTPAIR( .I );

	OUTPUT( '	M.ERR=.-MPPTAB?M?J' );
	MPR _ .LASTMPP + 1;
	OUTPUTPAIR( .MPR );

	OUTPUT( '	M.SON=.-MPPTAB?M?J' );
	MPR _ .MPR + 1;
	OUTPUTPAIR( .MPR );
	OUTPUT( '	M.SOFF=.-MPPTAB?M?J' );
	MPR _ .MPR + 1;
	OUTPUTPAIR( .MPR );
	OUTPUT( '	M.RDM=.-MPPTAB?M?J' );
	MPR _ .MPR + 1;
	OUTPUTPAIR( .MPR );
	OUTPUT( '	M.RDMC=.-MPPTAB?M?J' );
	MPR _ .MPR + 1;
	OUTPUTPAIR( .MPR );
	OUTPUT( '?M?JFMPPS==:	10?M?J?IBLOCK	FMPPS?M?J?M?JMPPS==:' );
	OUTPUTD( .MPR + 1 );
	OUTPUTCRLF;
	OUTPUTCRLF;

	MPPADDR _ .MPPTAB<FORE>;
	J _ 0;

	INCR I FROM 0 TO .LASTMPP DO
	    BEGIN
		THISEGO _ .MPPADDR[M0FEGO];
		WHILE .THISEGO NEQ 0 DO
		    BEGIN
			THISEGO _ OUTPUTMPPEGO( .I, .J, .THISEGO, .MPPADDR );
			J _ .J + 1
		    END;
		OUTPUTCRLF;
		J _ 0;
		MPPADDR _ .MPPADDR[M0FORE]
	    END;

	OUTPUTCRLF;

	MPR _ .LASTMPP + 1;
	OUTPUTMPR( .MPR, PAZ 'ERR' );

	MPR _ .MPR + 1;
	OUTPUTMPR( .MPR,  PAZ 'SON' );

	MPR _ .MPR + 1;
	OUTPUTMPR( .MPR, PAZ 'SOFF' );

	MPR _ .MPR + 1;
	OUTPUTMPR( .MPR, PAZ 'DMR' );

	MPR _ .MPR + 1;
	OUTPUTMPR( .MPR, PAZ 'DCR' );


    END;

COMMENT;

! ROUTINE WCMPPNAME
! ======= ========
! THIS ROUTINE WRITES THE MPP NAME TO THE COMPILE FILE

ROUTINE WCMPPNAME(MPPADDR)=
    BEGIN
	MAP FORMAT MPPADDR;
	XPUT(MPPADDR[M0NAME])
    END;

COMMENT;

! ROUTINE WCMPPNO
! ======= ========
! THIS ROUTINE WRITES THE MPP NAME TO THE COMPILE FILE

ROUTINE WCMPPNO( MPPADDR ) =
    BEGIN
	MAP	FORMAT MPPADDR;

	OUTPUTMPPNO( .MPPADDR[M0MPPNO] )
    END;

COMMENT;

! ROUTINE WCMPPNUMBER
! ======= ========
! THIS ROUTINE WRITES THE MPP NUMBER TO THE COMPILE FILE

GLOBAL ROUTINE WCMPPNUMBER( MPPADDR ) =
    BEGIN
	MAP	FORMAT MPPADDR;

	OUTPUTD( .MPPADDR[M0MPPNO] )
    END;

COMMENT;

! ROUTINE OUTPUTMPPNO
! ======= ===========
! THIS ROUTINE WRITES AN MPP NAME OF THE FORM "M"NUMBER.0

ROUTINE OUTPUTMPPNO( MAJOR ) =
    BEGIN
	OUTPUTMM(  .MAJOR, 0 )
    END;

COMMENT;

! ROUTINE OUTPUTMM
! ======= ========
! THIS ROUTINE WRITES AN MPPNAME OF THE FORM: "M"NUMBER1.NUMBER2

ROUTINE OUTPUTMM( MAJOR, MINOR ) =
    BEGIN
	OUTPUTC( "M" );
	OUTPUTD( .MAJOR );
	OUTPUTC( "." );
	OUTPUTD( .MINOR );
    END;

COMMENT;

! ROUTINE OUTPUTMPPEGO
! ======= ============
! THIS ROUTINE WRITES AN MPP MACRO CALL FOR AN EGO AND RETURNS
! A POINTER TO THE NEXT EGO

ROUTINE OUTPUTMPPEGO( MAJOR, MINOR, EGO, MPPADDR ) =
    BEGIN
	MAP	FORMAT EGO;
	MAP	FORMAT MPPADDR;

	OUTPUTMM( .MAJOR, .MINOR );

	OUTPUT( ':	%MPP(' );
	OUTPUTD( .MAJOR );
	OUTPUTCOMMA;

	IF .EGO[E0FORE] NEQ 0 THEN
		OUTPUTMM( .MAJOR, .MINOR + 1)
	    ELSE OUTPUTC( "0" );
	OUTPUTCOMMA;

	IF .EGO[ E0DEVICE ] EQL 0 THEN OUTPUT( 'DSK' ) ELSE OUTPUTSWORD( .EGO[E0DEVICE] );
	OUTPUTCOMMA;

	OUTPUTSWORD( .EGO[E0FILENAME] );
	OUTPUTCOMMA;

	OUTPUT( '<^O' );
	OUTPUTO( .EGO[E0PROJ] );
	OUTPUT( ', ^O' );
	OUTPUTO( .EGO[E0PROG] );
	OUTPUTC( ">" );
	OUTPUTCOMMA;

	OUTPUTD( IF .EGO[E0KFLAG] THEN .EGO[E0KCORE] * 2 ELSE .EGO[E0KCORE] );
	OUTPUTCOMMA;

	OUTPUTD( .MPPADDR[M0MIN] );
	OUTPUTCOMMA;

	OUTPUTD( .MPPADDR[M0MAX] );
	OUTPUTCOMMA;

	OUTPUTD( .MPPADDR[M0INITIAL] );
	OUTPUTCOMMA;

	OUTPUTD( .MPPADDR[M0HPQ] );
	OUTPUTCOMMA;

	OUTPUTB( .MPPADDR[M0LOCK] );
	OUTPUTCOMMA;

	OUTPUTB( FALSE );			! NOT LOCAL
	OUTPUTCOMMA;

	OUTPUTB( .MPPADDR[M0IMMORTAL] );

	OUTPUT( ')?M?J' );

	.EGO[E0FORE]

    END;
COMMENT;

! ROUTINE OUTPUTMPR
! ======= ============
! THIS ROUTINE WRITES AN MPP MACRO CALL FOR AN MPR

ROUTINE OUTPUTMPR( MAJOR, MPR ) =
    BEGIN

	OUTPUTMM( .MAJOR, 0 );

	OUTPUT( ':	%MPP(' );
	OUTPUTD( .MAJOR );
	OUTPUTCOMMA;

	OUTPUT( '0,COR,' );

	XOUTPUT( .MPR );
	OUTPUTCOMMA;

	OUTPUT( '<0,0>,0,0,0,0,0,0,TRUE,TRUE)?M?J' );

    END;
FORWARD	MPPATTACH(1);

COMMENT;

! ROUTINE WHATMPPS
! ======= =========
! THIS ROUTINE CHECKS THE MPP TABLE FOR UNDEFINED MPPS AND MPPS
! WHICH DON'T BELONG TO LEAVES
! WHATMPPS RETURNS GOOD IF EVERYTHING IS OK, ELSE BAD

GLOBAL ROUTINE WHATMPPS(TELL)=
    BEGIN
    ! IF TELL THEN ONLY TELL THE USER WHAT IS UNDEFINED, ELSE REQUEST UNDEFINED INFO
    !TWO POSSIBLE PROBLEMS
    ! 1. IF MPP TOBEDEFINED FLAG IS ON
    ! 2. IF THE MPP DOES NOT BELONG TO A LEAF

	REGISTER
		NEXT,
		STATUS,
		MPPADDR;

	MAP FORMAT MPPADDR;

	STATUS _ GOOD;

	IF ( MPPADDR _ .MPPTAB<FORE> ) EQL 0 THEN RETURN GOOD;	! IF THERE ARN'T ANY THEN THEY MUST BE GOOD!

	IF .TELL THEN
	    BEGIN
		DO
		    BEGIN
			IF .MPPADDR[M0TOBEDEFINED] THEN 
			    BEGIN
				ERROR( 49 );
				OUTSA(MPPADDR[M0NAME]);
				TYPE('[ TO BE DEFINED?M?J]');
				STATUS _ BAD
			    END
			    ELSE
			    BEGIN
				IF .MPPADDR[M0LISTPTRS] EQL 0 THEN
				    BEGIN
					WARN( 28 );
					OUTSA(MPPADDR[M0NAME]);
					TYPE('[ HAS NO NODE ASSOCIATED WITH IT?M?J]' );
					! NO STATUS CHANGE / ONLY WARNING
				    END
			    END
		    END
		WHILE (MPPADDR _ .MPPADDR[M0FORE]) NEQ 0
	    END
	    ELSE
	    BEGIN
		DO
		    BEGIN
			NEXT _ .MPPADDR[ M0FORE ];
			IF .MPPADDR[M0TOBEDEFINED] THEN 
			    BEGIN
				ERROR( 49 );
				OUTSA(MPPADDR[M0NAME]);
				TYPE('[ TO BE DEFINED?M?J?M?J]');
				PRINTMPPNAME( MPPADDR[ M0NAME ] );
				MAKMPP(.MPPADDR)
			    END
			    ELSE
			    BEGIN
				IF .MPPADDR[M0LISTPTRS] EQL 0 THEN
				    BEGIN
					WARN( 28 );
					OUTSA(MPPADDR[M0NAME]);
					TYPE('[ HAS NO NODE ASSOCIATED WITH IT?M?J?M?J]' );
					PRINTMPPNAME( MPPADDR[ M0NAME ] );
					ASKSTR( 'DO YOU WISH TO [DELETE THIS MPP OR ATTACH IT TO A NODE]?R(,DELETE,ATTACH?R)[:	??]',
						PLIT(	ASCII 'DELET',	DELMPP,
							ASCII 'ATTAC',	MPPATTACH,
							0,		IGNORE ),
						.MPPADDR )
				    END
			    END
		    END
		WHILE (MPPADDR _ .NEXT) NEQ 0
	    END;
	.STATUS
    END;

COMMENT;

! ROUTINE MPPATTACH
! ======= =========
! THIS ROUTINE ATTACHES THE MPP TO A NODE

ROUTINE MPPATTACH( MPPADDR ) =
    BEGIN

	REGISTER
		NODEPTR;

	LABEL	LOOP;

	MAP	FORMAT MPPADDR;

	TYPE( '[ATTACH TO WHAT NODE:	??]' );

LOOP:	REPEAT						! UNTIL WE GET A GOOD ANSWER
	    BEGIN
		WHILE ( ACHAR _ INPUT( ALINE, ALINELENGTH ) ) EQL CRONLY DO	! GET AN INPUT,  MUST HAVE ONE
		    BEGIN
			ERROR( 32 );
			WARN( 0 );
			TYPE( '[		??]' )
		    END;

		IF ( NODEPTR _ GETNODEPTR( PRIM<0,0> ) ) NEQ 0 THEN		! FIND THE NODE'S ADDRESS
		    BEGIN
			IF NOT HASMPP( .NODEPTR ) THEN LEAVE LOOP
			    ELSE ERROR( 91 );
			NOTE - COULD MAKE THIS ASK REPLACE OR IGNORE ?
		    END
		    ELSE
		    BEGIN
			ERROR( 33 )
		    END;
		RETURN
	    END;

	ATTACH( %TO% MPPADDR[M0LISTPTRS], .NODEPTR, N0MPPOFFSETT )

    END;



END;

! END OF MGNMPP.BLI