$TITLE ('RMX DEMO PROGRAM, F. S. MGR. EXERCISER')
/*
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

  MODULE: GET.

  LAST CHANGED: 5 OCTOBER 1977.

  THIS MODULE PROVIDES AN EXERCISE TOOL FOR THE FREE SPACE MANAGER, AS
  WELL AS A DEMONSTRATION OF AN RMX SYSTEM.

  CONTAINED IN THIS MODULE ARE 4 TASKS:

  GET <MSG LENGTH>

    REQUEST A MESSAGE OF <MSG LENGTH> BYTES FROM THE FREE SPACE
    MANAGER.

  POOL

    DISPLAY THE CURRENT STATE OF THE FREE SPACE LIST.

  MSG

    DISPLAY THE CURRENT STATE OF THE ALLOCATED MESSAGE LIST.

  PUT <MSG ADDRESS>

    RETURN A MESSAGE AT <MSG ADDRESS> FROM THE ALLOCATED MESSAGE
    LIST TO THE FREE SPACE LIST.

*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
*/
GET:
DO;

  /*
    GET AND PUT EXERCISE ROUTINES FOR THE MEMORY MANAGER.
  */

$NOLIST
$INCLUDE (:F1:COMMON.ELT)
$INCLUDE (:F1:CHAR.ELT)
$INCLUDE (:F1:TASK.ELT)
$INCLUDE (:F1:EXCH.ELT)
$INCLUDE (:F1:IED.ELT)
$INCLUDE (:F1:STATIC.ELT)
$INCLUDE (:F1:MSG.ELT)
$INCLUDE (:F1:THMSG.ELT)
$INCLUDE (:F1:MSGTYP.ELT)
$INCLUDE (:F1:SYNCH.EXT)
$INCLUDE (:F1:OBJMAN.EXT)
$INCLUDE (:F1:CLI.EXT)
$INCLUDE (:F1:FSMGR.EXT)
$INCLUDE (:F1:THDINI.EXT)
$INCLUDE (:F1:THDINO.EXT)
$INCLUDE (:F1:SCANIN.PEX)
$INCLUDE (:F1:NUMOUT.PEX)
$LIST

/*
  DATA STRUCTURES COMMON TO GET,PUT,POOL,MSG
*/

DECLARE GOT$XCH EXCHANGE$DESCRIPTOR;

/*
  GET TASK DECLARATIONS.
*/

DECLARE GET$XCH EXCHANGE$DESCRIPTOR;
DECLARE GET$ID STRUCTURE (
        MSGHDR,
        NAME(6) BYTE) DATA (
        0,15,WRITE$TYPE,.GET$XCH,.GET$XCH,
        'GET   ');
DECLARE GET$MSG STRUCTURE (
        MSG$HDR,
        NAME(6) BYTE);
DECLARE GET$IX EXCHANGE$DESCRIPTOR;
DECLARE GET$ROM STRUCTURE (
        MSGHDR,
        MSG$LEN ADDRESS) DATA (
        0,11,FS$REQ$TYPE,.GET$IX,.GET$IX,0);
DECLARE GET$REQ STRUCTURE (
        MSGHDR,
        MSG$LEN ADDRESS);
DECLARE G$PTR ADDRESS;
DECLARE GET$RESP BASED G$PTR STRUCTURE (
        MSGHDR,
        MSG$LEN ADDRESS);
DECLARE GET$DISP STRUCTURE (
        MSGHDR,
        STATUS ADDRESS,
        BUFADR ADDRESS,
        COUNT ADDRESS,
        ACTUAL ADDRESS,
        H1(7) BYTE,
        ADR(4) BYTE,
        H2 BYTE,
        AL(4) BYTE,
        H3(2) BYTE);
DECLARE GET$ACK STRUCTURE (
        MSGHDR,
        STATUS ADDRESS,
        BUFADR ADDRESS,
        COUNT ADDRESS,
        ACTUAL ADDRESS,
        DAT(18) BYTE) DATA (
        0,37,WRITE$TYPE,.GET$IX,.GET$IX,
        0,.GET$DISP.H1,18,0,
        'FS$REQ XXXX XXXX',CR,LF);
DECLARE GET$NAK STRUCTURE (
        MSGHDR,
        STATUS ADDRESS,
        BUFADR ADDRESS,
        COUNT ADDRESS,
        ACTUAL ADDRESS,
        DAT(13) BYTE) DATA (
        0,30,WRITE$TYPE,.GET$IX,.GET$IX,
        0,.GET$DISP.H1,13,0,
        'FS$NAK XXXX',CR,LF);

/*
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

  LOCAL PROCEDURE: GET.

  THIS PROCEDURE IS AN RMX TASK WHICH IS INVOKED BY WAY OF A CONSOLE
  COMMAND:

                 GET <MSG LENGTH>

  GET REQUESTS THE FREE SPACE MANAGER TO BUILD A MESSAGE
  OF THE SPECIFIED LENGTH.  IF SPACE IS AVAILABLE, GET
  PRINTS THE MESSAGE

  FS$REQ <MSG ADDRESS> <MSG LENGTH>

  OTHERWISE, GET PRINTS

  FS$NAK <MSG LENGTH>

  THE MESSAGE IS FORWARDED (SENT) TO THE ALLOCATED MESSAGE LIST, GOT$XCH.

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
GET:
  PROCEDURE;
    DECLARE BPTR ADDRESS;
    DECLARE MSG$PTR ADDRESS;
    DECLARE MSG BASED MSG$PTR TH$MSG;

    /*
      CREATE GET'S LISTENING POST, GET$IX.
    */
    CALL RQCXCH(.GETIX);
    /*
      INITIALIZE MESSAGES.
    */
    CALL MOVE(SIZE(GET$REQ),.GET$ROM,.GET$REQ);
    CALL MOVE(SIZE(GET$MSG),.GET$ID,.GET$MSG);
    /*
      SEND KEYWORD 'GET' AND EXCHANGE 'GET$IX' TO CLI.
    */
    CALL RQSEND(.KEY$XCH,.GET$MSG);
    DO FOREVER;
      /*
        WAIT FOR MESSAGE FROM CLI.
      */
      MSG$PTR = RQWAIT(.GET$XCH,0);
      /*
        POSITION SCANNER BEYOND KEYWORD IN MESSAGE.
      */
      BPTR = .MSG.REMAINDER(MSG.STATUS);
      /*
        CONVERT NUMBER FROM ASCII TO BINARY.
      */
      GET$REQ.MSG$LEN = SCANINTEGER(.BPTR);
      /*
        RETURN MESSAGE TO HOME$EXCHANGE(RQFSRX)
      */
      CALL RQSEND(MSG.HOME$EXCHANGE,MSG$PTR);
      /*
        MAKE FREE SPACE REQUEST.
      */
      GET$REQ.TYPE = FS$REQ$TYPE;
      CALL RQSEND(.RQFSAX,.GET$REQ);
      G$PTR = RQWAIT(.GET$IX,0);
      IF GET$RESP.TYPE = FS$REQ$TYPE THEN
      DO;
        /*
          FREE SPACE MANAGER HAD THE SPACE.
        */
        MSG$PTR = GET$RESP.MSG$LEN;
        CALL MOVE(SIZE(GET$ACK),.GET$ACK,.GET$DISP);
        CALL NUMOUT(GET$RESP.MSG$LEN,16,'0',.GET$DISP.ADR,4);
        CALL NUMOUT(MSG.LENGTH,16,'0',.GET$DISP.AL,4);
        /*
          PRINT 'FS$REQ XXXX XXXX'
        */
        CALL RQSEND(.RQOUTX,.GET$DISP);
        G$PTR = RQWAIT(.GET$IX,0);
        /*
          SEND THE ALLOCATED MESSAGE TO THE ALLOCATED MESSAGE LIST.
        */
        CALL RQSEND(.GOT$XCH,MSG$PTR);
      END;
      ELSE
      DO;
        /*
          INSUFFICIENT FREE SPACE AVAILABLE.
        */
        CALL MOVE(SIZE(GET$NAK),.GET$NAK,.GET$DISP);
        CALL NUMOUT(GET$RESP.MSG$LEN,16,'0',.GET$DISP.ADR,4);
        /*
          PRINT 'FS$NAK XXXX'
        */
        CALL RQSEND(.RQOUTX,.GET$DISP);
        G$PTR = RQWAIT(.GET$IX,0);
      END;
    END;
  END GET;

DECLARE GET$TD TASK$DESCRIPTOR;
DECLARE GET$PRI LITERALLY '131';
DECLARE GET$STL LITERALLY '64';
DECLARE GET$STK(GET$STL) BYTE;
DECLARE GET$STD STATIC$TASK$DESCRIPTOR DATA (
        'GET   ',
        .GET,
        .GET$STK,
        GET$STL,
        GET$PRI,
        .GET$XCH,
        .GET$TD);

/*
  POOL TASK DECLARATIONS.
*/

DECLARE POOL$XCH EXCHANGE$DESCRIPTOR;
DECLARE POOL$ID STRUCTURE (
        MSGHDR,
        NAME(6) BYTE) DATA (
        0,15,WRITE$TYPE,.POOL$XCH,.POOL$XCH,
        'POOL  ');
DECLARE POOL$MSG STRUCTURE (
        MSG$HDR,
        NAME(6) BYTE);
DECLARE POOL$IX EXCHANGE$DESCRIPTOR;
DECLARE P$PTR ADDRESS;
DECLARE POOL$DISP BASED P$PTR STRUCTURE (
        MSGHDR,
        STATUS ADDRESS,
        BUFADR ADDRESS,
        COUNT ADDRESS,
        ACTUAL ADDRESS,
        H1(5) BYTE,
        ADR(4) BYTE,
        H2 BYTE,
        AL(4) BYTE,
        H3(2) BYTE);
DECLARE POOL$ACK STRUCTURE (
        MSGHDR,
        STATUS ADDRESS,
        BUFADR ADDRESS,
        COUNT ADDRESS,
        ACTUAL ADDRESS,
        DAT(16) BYTE) DATA (
        0,33,WRITE$TYPE,.POOL$IX,.POOL$IX,
        0,.POOL$DISP.H1,16,0,
        'POOL XXXX XXXX',CR,LF);

/*
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

  LOCAL PROCEDURE: POOL.

  THIS PROCEDURE ALLOWS THE USER TO DISPLAY THE FREE SPACE LIST.  THE
  LIST WILL CHANGE DYNAMICALLY EACH TIME IT IS INVOKED, SINCE
  CLI IS USING THE FREE SPACE MANAGER FOR ALLOCATION AND
  DEALLOCATION OF MESSAGES.  ALSO, SINCE POOL IS OF HIGHER
  PRIORITY THAN FREE SPACE COMPACTIFICATION, POOL MAY
  PREEMPT COMPACTIFICATION TO DO A DISPLAY.


+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
POOL:
  PROCEDURE;
    DECLARE TEMP TH$MSG;
    DECLARE A$PTR ADDRESS;
    DECLARE A BASED A$PTR STRUCTURE (
            MSGHDR);
    DECLARE MARKER STRUCTURE (
            MSGHDR);

    /*
      INITIALIZE POOL TASK.
    */
    MARKER.LENGTH = 4;
    /*
      CREATE POOL'S LISTENING POST.
    */
    CALL RQCXCH(.POOLIX);
    /*
      SEND 'POOL  ', POOL$IX TO CLI.
    */
    CALL MOVE(SIZE(POOL$MSG),.POOL$ID,.POOL$MSG);
    CALL RQSEND(.KEY$XCH,.POOL$MSG);
    DO FOREVER;
      /*
        WAIT FOR MESSAGE FROM CLI.
      */
      P$PTR = RQWAIT(.POOL$XCH,0);
      /*
        SAVE HEADER PORTION OF MESSAGE.
      */
      CALL MOVE(SIZE(TEMP),P$PTR,.TEMP);
      /*
        INITIALIZE DISPLAY MESSAGE.
      */
      CALL MOVE(SIZE(TEMP),P$PTR,.TEMP);
      CALL MOVE(SIZE(POOL$ACK),.POOL$ACK,P$PTR);
      POOL$DISP.BUFADR = .POOL$DISP.H1;
      /*
        SEND MARKER TO RQFREE EXCHANGE TO MARK
        THE END OF THE LIST AS A TERMINATION CONDITION.
      */
      CALL RQSEND(.RQFREE,.MARKER);
      DO WHILE (A$PTR:=RQWAIT(.RQFREE,0)) <> .MARKER;
        /*
          BUILD DISPLAY FOR 1 LIST ELEMENT.
        */
        CALL NUMOUT(A$PTR,16,'0',.POOL$DISP.ADR,4);
        CALL NUMOUT(A.LENGTH,16,'0',.POOL$DISP.AL,4);
        /*
          RETURN MESSAGE TO FREE LIST.
        */
        CALL RQSEND(.RQFREE,A$PTR);
        /*
          PRINT 'POOL XXXX XXXX'
        */
        CALL RQSEND(.RQOUTX,.POOL$DISP);
        P$PTR = RQWAIT(.POOL$IX,0);
      END;
      CALL MOVE(SIZE(TEMP),.TEMP,P$PTR);
      CALL RQSEND(POOL$DISP.HOME$EXCHANGE,P$PTR);
    END;
  END POOL;

DECLARE POOL$TD TASK$DESCRIPTOR;
DECLARE POOL$PRI LITERALLY '131';
DECLARE POOL$STL LITERALLY '64';
DECLARE POOL$STK(POOL$STL) BYTE;
DECLARE POOL$STD STATIC$TASK$DESCRIPTOR DATA (
        'POOL  ',
        .POOL,
        .POOL$STK,
        POOL$STL,
        POOL$PRI,
        .POOL$XCH,
        .POOL$TD);

/*
  MSG TASK DECLARATIONS.
*/

DECLARE MSG$XCH EXCHANGE$DESCRIPTOR;
DECLARE MSG$ID STRUCTURE (
        MSGHDR,
        NAME(6) BYTE) DATA (
        0,15,WRITE$TYPE,.MSG$XCH,.MSG$XCH,
        'MSG   ');
DECLARE MSG$MSG STRUCTURE (
        MSG$HDR,
        NAME(6) BYTE);
DECLARE MSG$IX EXCHANGE$DESCRIPTOR;
DECLARE M$PTR ADDRESS;
DECLARE MSG$DISP BASED M$PTR STRUCTURE (
        MSGHDR,
        STATUS ADDRESS,
        BUFADR ADDRESS,
        COUNT ADDRESS,
        ACTUAL ADDRESS,
        H1(4) BYTE,
        ADR(4) BYTE,
        H2 BYTE,
        AL(4) BYTE,
        H3(2) BYTE);
DECLARE MSG$ACK STRUCTURE (
        MSGHDR,
        STATUS ADDRESS,
        BUFADR ADDRESS,
        COUNT ADDRESS,
        ACTUAL ADDRESS,
        DAT(15) BYTE) DATA (
        0,32,WRITE$TYPE,.MSG$IX,.MSG$IX,
        0,.MSG$DISP.H1,15,0,
        'MSG XXXX XXXX',CR,LF);

/*
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

  LOCAL PROCEDURE: MSG.

  THIS PROCEDURE LIST THE MESSAGES QUEUED ON THE ALLOCATED MESSAGE LIST.

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
MSG:
  PROCEDURE;
    DECLARE TEMP TH$MSG;
    DECLARE A$PTR ADDRESS;
    DECLARE A BASED A$PTR STRUCTURE (
            MSGHDR);
    DECLARE MARKER STRUCTURE (
            MSGHDR);

    MARKER.LENGTH = 4;
    /*
      CREATE MSG'S LISTENING POST.
    */
    CALL RQCXCH(.MSGIX);
    CALL MOVE(SIZE(MSG$MSG),.MSG$ID,.MSG$MSG);
    /*
      SEND 'MSG   ', MSGIX TO CLI.
    */
    CALL RQSEND(.KEY$XCH,.MSG$MSG);
    DO FOREVER;
      /*
        WAIT FOR MESSAGE FROM CLI.
      */
      M$PTR = RQWAIT(.MSG$XCH,0);
      /*
        SAVE MESSAGE HEADER IN TEMPORARY STORAGE.
      */
      CALL MOVE(SIZE(TEMP),M$PTR,.TEMP);
      /*
        INITIALIZE DISPLAY MESSAGE.
      */
      CALL MOVE(SIZE(MSG$ACK),.MSG$ACK,M$PTR);
      MSG$DISP.BUFADR = .MSG$DISP.H1;
      /*
        SEND TERMINATION CONDITION TO ALLOCATED MESSAGE LIST.
      */
      CALL RQSEND(.GOT$XCH,.MARKER);
      DO WHILE (A$PTR:=RQWAIT(.GOT$XCH,0)) <> .MARKER;
        /*
          BUILD DISPLAY MESSAGE.
        */
        CALL NUMOUT(A$PTR,16,'0',.MSG$DISP.ADR,4);
        CALL NUMOUT(A.LENGTH,16,'0',.MSG$DISP.AL,4);
        /*
          RETURN MESSAGE TO ALLOCATED LIST.
        */
        CALL RQSEND(.GOT$XCH,A$PTR);
        /*
          PRINT 'MSG XXXX XXXX'
        */
        CALL RQSEND(.RQOUTX,.MSG$DISP);
        M$PTR = RQWAIT(.MSG$IX,0);
      END;
      /*
        RESTORE ORIGINAL MESSAGE FROM CLI, RETURN IT
        TO ITS HOME EXCHANGE.
      */
      CALL MOVE(SIZE(TEMP),.TEMP,M$PTR);
      CALL RQSEND(MSG$DISP.HOME$EXCHANGE,M$PTR);
    END;
  END MSG;

DECLARE MSG$TD TASK$DESCRIPTOR;
DECLARE MSG$PRI LITERALLY '131';
DECLARE MSG$STL LITERALLY '64';
DECLARE MSG$STK(MSG$STL) BYTE;
DECLARE MSG$STD STATIC$TASK$DESCRIPTOR DATA (
        'MSG   ',
        .MSG,
        .MSG$STK,
        MSG$STL,
        MSG$PRI,
        .MSG$XCH,
        .MSG$TD);

/*
  PUT TASK DECLARATIONS.
*/

DECLARE PUT$XCH EXCHANGE$DESCRIPTOR;
DECLARE PUT$ID STRUCTURE (
        MSGHDR,
        NAME(6) BYTE) DATA (
        0,15,WRITE$TYPE,.PUT$XCH,.PUT$XCH,
        'PUT   ');
DECLARE PUT$MSG STRUCTURE (
        MSG$HDR,
        NAME(6) BYTE);
DECLARE PUT$PTR ADDRESS;
DECLARE PUT$DISP BASED PUT$PTR TH$MSG;

/*
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

  PUBLIC PROCEDURE: PUT.

  THIS PROCEDURE IS THE INITIALIZATION TASK FOR THIS MODULE.  IT CREATES
  3 SUBORDINATE TASKS AS WELL AS EXCHANGES USED FOR INTERTASK
  COMMUNICATION BETWEEN TASKS IN THIS MODULE.  WHEN IT 
  INITIALIZATION JOB IS COMPLETE, THIS TASK BECOMES THE PUT
  TASK, IMPLEMENTING THE FUNCTION OF PUTTING BACK MESSAGES
  ALLOCATED BY GET.  THIS TASK ALSO INITIALIZES THE FREE SPACE POOL BY SENDING
  A LARGE MESSAGE TO RQFSRX FOR RECLAMATION.


*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
*/
PUT:
  PROCEDURE PUBLIC;
    DECLARE SEARCHING BOOLEAN;
    DECLARE (A$PTR,B$PTR,C$PTR) ADDRESS;
    DECLARE A BASED A$PTR STRUCTURE (
            MSGHDR);
    DECLARE MSG STRUCTURE (
            MSGHDR,
            REMAINDER(1000) BYTE);

    /*
      CREATE INTERTASK EXCHANGES.
    */
    CALL RQCXCH(.PUT$XCH);
    CALL RQCXCH(.GOT$XCH);
    CALL RQCXCH(.GET$XCH);
    CALL RQCXCH(.POOL$XCH);
    CALL RQCXCH(.MSG$XCH);
    /*
      CREATE TASKS.
    */
    CALL RQCTSK(.GET$STD);
    CALL RQCTSK(.MSG$STD);
    CALL RQCTSK(.POOL$STD);
    /*
      INITIALIZE FREE SPACE MANAGER BY GIVING IT
      SPACE TO WORK WITH.
    */
    MSG.LENGTH = SIZE(MSG);
    CALL RQSEND(.RQFSRX,.MSG);
    /*
      SEND 'PUT   ', PUT$IX TO CLI.
    */
    CALL MOVE(SIZE(PUT$MSG),.PUT$ID,.PUT$MSG);
    CALL RQSEND(.KEY$XCH,.PUT$MSG);
    DO FOREVER;
      /*
        WAIT FOR MESSAGE FROM CLI.
      */
      PUT$PTR = RQWAIT(.PUT$XCH,0);
      /* 
        SET SCANNER TO POINT BEYOND KEYWORD SCANNED BY CLI.
      */
      BPTR = .PUT$DISP.REMAINDER(PUT$DISP.STATUS);
      /*
        CONVERT MESSAGE ADDRESS FROM ASCII TO BINARY.
      */
      C$PTR = SCANINTEGER(.BPTR);
      /*
        SEND MESSAGE BACK TO POOL.
      */
      CALL RQSEND(PUT$DISP.HOME$EXCHANGE,PUT$PTR);
      /*
        WAIT FOR A MESSAGE TO BE SENT TO THE ALLOCATED EXCHANGE.
      */
      SEARCHING = TRUE;
      B$PTR = RQWAIT(.GOT$XCH,0);
      /*
        SEND IT BACK AS A TERMINATION MARKER.
      */
      CALL RQSEND(.GOT$XCH,BPTR);
      /*
        ATTEMPT TO FIND MESSAGE WHOSE ADDRESS MATCHES THE VALUE
        IN THE MESSAGE FROM CLI.  IF A MATCH IS FOUND, SEND THE
        MESSAGE HOME.
      */
      DO WHILE SEARCHING;
        IF (A$PTR:=RQWAIT(.GOT$XCH,0)) = BPTR THEN SEARCHING = FALSE;
        IF A$PTR = C$PTR THEN
        DO;
          /*
            FOUND A MATCH, RELEASE MESSAGE.
          */
          CALL RQSEND(A.HOME$EXCHANGE,A$PTR);
          SEARCHING = FALSE;
        END;
        ELSE
        DO;
          /*
            NO MATCH, PUT BACK ON ALLOCATED LIST.
          */
          CALL RQSEND(.GOT$XCH,A$PTR);
        END;
      END;
    END;
  END PUT;

END GET;

EOF