/************************************************************************

        test.c

        These programs are designed to test the OS502 functionality     

        Read Appendix B about test programs and Appendix C concerning
        system calls when attempting to understand these programs.      

        Revision History:       
                1.0 August 1990
                1.1 December 1990: Tests cleaned up; 1b, 1e - 1k added
                                   Portability attempted.
                1.2 December 1991: Still working on portabililty.
                1.3 July     1992: tests1i/1j made re-entrant.
                1.4 December 1992: Minor changes - portability
                                   tests2c/2d added.  2f/2g rewritten
                1.5 August   1993: Produced new test2g to replace old
                                   2g that did a swapping test.
                1.6 June     1995: Test Cleanup.
************************************************************************/

#include                 "global.h"
#include                 "syscalls.h"
#include                 "z502.h"
#include                 "protos.h"

#include                 "stdio.h"
#include                 "string.h"
#include                 "stdlib.h"
#include                 "math.h"

extern INT16             Z502_REG_PROGRAM_COUNTER;
extern INT32             SYS_CALL_CALL_TYPE;
extern Z502_ARG          Z502_REG_ARG1;
extern Z502_ARG          Z502_REG_ARG2;
extern Z502_ARG          Z502_REG_ARG3;
extern Z502_ARG          Z502_REG_ARG4;
extern Z502_ARG          Z502_REG_ARG5;
extern Z502_ARG          Z502_REG_ARG6;
extern INT32             Z502_REG_1;
extern INT32             Z502_REG_2;
extern INT32             Z502_REG_3;
extern INT32             Z502_REG_4;
extern INT32             Z502_REG_5;
extern INT32             Z502_REG_6;
extern INT32             Z502_REG_7;
extern INT32             Z502_REG_8;
extern INT32             Z502_REG_9;

/*      Prototypes for internally called routines.                  */

void                    test1x( void );
void                    test1j_echo( void );
void                    test2gx( void );
void                    error_expected( INT32, char[] );
void                    success_expected( INT32, char[] );
void                    get_pink_random_number( long *, long );


/**************************************************************************

        Test1a 
        
        Exercises GET_TIME_OF_DAY and SLEEP and TERMINATE_PROCESS

        Z502_REG_1              Start time
        Z502_REG_2              End time
        Z502_REG_9              Error returned

**************************************************************************/

void    test1a( void )
    {
    static INT32          sleep_time = 100;

    SELECT_STEP
        {
       STEP( 0 )
                printf( "This is Release %s:  Test 1a\n", CURRENT_REL );
                GET_TIME_OF_DAY( &Z502_REG_1 );
       STEP( 1 )
                SLEEP ( sleep_time );

       STEP( 2 )
                GET_TIME_OF_DAY( &Z502_REG_2 );

       STEP( 3 )
                printf( "sleep time= %ld, elapsed time= %ld\n",
                        sleep_time, Z502_REG_2 - Z502_REG_1 );
                TERMINATE_PROCESS( -1, &Z502_REG_9 );

       STEP( 4 )
                printf( "ERROR: Test should be terminated but isn't.\n");
                break;
    }                                           /* End of SELECT    */
}                                               /* End of test1a    */

/**************************************************************************

        Test1b 
        
        Exercises the CREATE_PROCESS and GET_PROCESS_ID  commands. 

        This test tries lots of different inputs for create_process.
        In particular, there are tests for each of the following:

                1. use of illegal priorities
                2. use of a process name of an already existing process.
                3. creation of a LARGE number of processes, showing that 
                   there is a limit somewhere ( you run out of some
                   resource ) in which case you take appropriate action.

        Test the following items for get_process_id:

                1. Various legal process id inputs.
                2. An illegal/non-existant name.

        Z502_REG_1, _2          Used as return of process id's.
        Z502_REG_3              Cntr of # of processes created.
        Z502_REG_9              Used as return of error code.

**************************************************************************/

#define         ILLEGAL_PRIORITY                -3
#define         LEGAL_PRIORITY                  10

void    test1b( void)
    {
    static char         process_name[16];
    static INT32        sleep_time = 500;

    while (1)
        {
        SELECT_STEP
            {
        /* Try to create a process with an illegal priority.    */

           STEP( 0 )
                printf( "This is Release %s:  Test 1b\n", CURRENT_REL );
                CREATE_PROCESS( "test1b_a", test1x, ILLEGAL_PRIORITY, 
                                            &Z502_REG_1, &Z502_REG_9 );

           STEP( 1 )
                error_expected( Z502_REG_9, "CREATE_PROCESS" );

        /* Create two processes with same name - 1st succeeds, 2nd fails */

                CREATE_PROCESS( "two_the_same", test1x, LEGAL_PRIORITY, 
                                            &Z502_REG_2, &Z502_REG_9 );

           STEP( 2 )
                success_expected( Z502_REG_9, "CREATE_PROCESS" );
                CREATE_PROCESS( "two_the_same", test1x, LEGAL_PRIORITY, 
                                            &Z502_REG_1, &Z502_REG_9 );


           STEP( 3 )
                error_expected( Z502_REG_9, "CREATE_PROCESS" );
                TERMINATE_PROCESS( Z502_REG_2, &Z502_REG_9 );

           STEP( 4 )
                success_expected( Z502_REG_9, "TERMINATE_PROCESS" );
                break;

        /*      Loop until an error is found on the create_process.  
                Since the call itself is legal, we must get an error 
                because we exceed some limit.                           */

           STEP( 5 )
                Z502_REG_3++;                   /* Generate next prog name*/
                sprintf( process_name, "Test1b_%ld", Z502_REG_3 );
                printf( "Creating process \"%s\"\n", process_name );
                CREATE_PROCESS( process_name, test1x, LEGAL_PRIORITY, 
                                            &Z502_REG_1, &Z502_REG_9 );

           STEP( 6 )
                if ( Z502_REG_9 == ERR_SUCCESS )
                    {
                    GO_NEXT_TO( 5 )                        /* LOOP BACK */
                }
                break;

        /*      When we get here, we've created all the processes we can.*/

           STEP( 7 )
                error_expected( Z502_REG_9, "CREATE_PROCESS" );
                printf( "%ld processes were created in all.\n", Z502_REG_3 );

        /*      Now test the call GET_PROCESS_ID                        */

                GET_PROCESS_ID( "", &Z502_REG_1, &Z502_REG_9 );  /* Legal */

           STEP( 8 )
                success_expected( Z502_REG_9, "GET_PROCESS_ID" );
                printf( "The PID of this process is %ld\n", Z502_REG_1 );
                strcpy( process_name, "Test1b_1" );
                GET_PROCESS_ID( process_name, &Z502_REG_1, 
                                                &Z502_REG_9 );  /* Legal */

           STEP( 9 )
                success_expected( Z502_REG_9, "GET_PROCESS_ID" );
                printf( "The PID of target process is %ld\n", Z502_REG_1 );
                GET_PROCESS_ID( "bogus_name", &Z502_REG_1, 
                                                &Z502_REG_9 );  /* Illegal */

           STEP( 10 )
                error_expected( Z502_REG_9, "GET_PROCESS_ID" );
                SLEEP( sleep_time );

           STEP( 11 )
                TERMINATE_PROCESS( -2, &Z502_REG_9 )

        }                                       /* End of SELECT     */
    }                                           /* End of while      */
}                                               /* End of test1b     */


/**************************************************************************

        Test1c

        Tests multiple copies of test1x running simultaneously.
        Test1c runs these with the same priority in order to show
        FCFS scheduling behavior; Test1d uses different priorities
        in order to show priority scheduling.                     

        WARNING:  This test assumes tests 1a - 1b run successfully 

        Z502_REG_1, 2, 3, 4, 5  Used as return of process id's.
        Z502_REG_6              Return of PID on GET_PROCESS_ID
        Z502_REG_9              Used as return of error code.

**************************************************************************/

#define         PRIORITY1C              10

void    test1c( void)
    {
    static INT32      sleep_time      = 1000;

    while( 1 )
        {
        SELECT_STEP
            {
           STEP( 0 )
                printf( "This is Release %s:  Test 1c\n", CURRENT_REL );
                CREATE_PROCESS( "test1c_a", test1x, PRIORITY1C, 
                                                &Z502_REG_1, &Z502_REG_9 );
                
           STEP( 1 )
                success_expected( Z502_REG_9, "CREATE_PROCESS" );
                CREATE_PROCESS( "test1c_b", test1x, PRIORITY1C, 
                                                &Z502_REG_2, &Z502_REG_9 );
                
           STEP( 2 )
                CREATE_PROCESS( "test1c_c", test1x, PRIORITY1C, 
                                                &Z502_REG_3, &Z502_REG_9 );
                
           STEP( 3 )
                CREATE_PROCESS( "test1c_d", test1x, PRIORITY1C, 
                                                &Z502_REG_4, &Z502_REG_9 );
        
           STEP( 4 )
                CREATE_PROCESS( "test1c_e", test1x, PRIORITY1C, 
                                                &Z502_REG_5, &Z502_REG_9 );
 


        /*      In these next three cases, we will loop until the target
                process ( test1c_a ) has terminated.  We know it
                terminated because for a while we get success on the call
                GET_PROCESS_ID, and then we get failure when the process
                no longer exists.                                       */

           STEP( 5 )
                SLEEP ( sleep_time );
                
           STEP( 6 )
                GET_PROCESS_ID( "test1c_a", &Z502_REG_6, &Z502_REG_9 );
                
           STEP( 7 )
                if ( Z502_REG_9 == ERR_SUCCESS )
                    GO_NEXT_TO( 5 )                /* Loop back */
                break;

           STEP( 8 )
                TERMINATE_PROCESS( -1, &Z502_REG_9 );
                
        }                                       /* End switch           */
    }                                           /* End while            */
}                                               /* End test1c           */


/**************************************************************************


        Test 1d   
        
        Tests multiple copies of test1x running simultaneously.
        Test1c runs these with the same priority in order to show
        FCFS scheduling behavior; Test1d uses different priorities
        in order to show priority scheduling.                       

        WARNING:  This test assumes tests 1a - 1b run successfully 

        Z502_REG_1, 2, 3, 4, 5  Used as return of process id's.
        Z502_REG_6              Return of PID on GET_PROCESS_ID
        Z502_REG_9              Used as return of error code.

**************************************************************************/

#define         PRIORITY1               10
#define         PRIORITY2               11
#define         PRIORITY3               11
#define         PRIORITY4               90
#define         PRIORITY5               40

void    test1d( void)
    {
    static INT32      sleep_time      = 1000;

    while( 1 )
        {
        SELECT_STEP
            {
           STEP( 0 )
                printf( "This is Release %s:  Test 1d\n", CURRENT_REL );
                CREATE_PROCESS( "test1d_1", test1x, PRIORITY1, 
                                                &Z502_REG_1, &Z502_REG_9 );
        
           STEP( 1 )
                success_expected( Z502_REG_9, "CREATE_PROCESS" );
                CREATE_PROCESS( "test1d_2", test1x, PRIORITY2, 
                                                &Z502_REG_2, &Z502_REG_9 );
                
           STEP( 2 )
                CREATE_PROCESS( "test1d_3", test1x, PRIORITY3, 
                                                &Z502_REG_3, &Z502_REG_9 );

           STEP( 3 )
                CREATE_PROCESS( "test1d_4", test1x, PRIORITY4, 
                                                &Z502_REG_4, &Z502_REG_9 );
                
           STEP( 4 )
                CREATE_PROCESS( "test1d_5", test1x, PRIORITY5, 
                                                &Z502_REG_5, &Z502_REG_9 );
                


/*  We will loop until the target process ( test1d_5 ) has terminated.  
    We know it terminated because for a while we get success on the call
    GET_PROCESS_ID, and then failure when the process no longer exists. */

           STEP( 5 )
                SLEEP ( sleep_time );
                
           STEP( 6 )
                GET_PROCESS_ID( "test1d_5", &Z502_REG_6, &Z502_REG_9 );
                
           STEP( 7 )
                if ( Z502_REG_9 == ERR_SUCCESS )
                    GO_NEXT_TO( 5 )                    /* Loop back */
                break;

           STEP( 8 )
                TERMINATE_PROCESS( -1, &Z502_REG_9 );
                
        }                                       /* End switch           */
    }                                           /* End while            */
}                                               /* End test1d           */


/**************************************************************************

      Test 1e exercises the SUSPEND_PROCESS and RESUME_PROCESS commands

    
        This test should try lots of different inputs for suspend and resume.
        In particular, there should be tests for each of the following:

                1. use of illegal process id.
                2. what happens when you suspend yourself - is it legal?
                3. suspending an already suspended process.
                4. resuming a process that's not suspended.

        there are probably lots of other conditions possible.
    
        Z502_REG_1              Target process ID
        Z502_REG_2              OUR process ID
        Z502_REG_9              Error returned

**************************************************************************/

#define         LEGAL_PRIORITY_1E               10

void    test1e( void)
    {
    SELECT_STEP
        {
           STEP( 0 )                            /* Get OUR PID          */
                 GET_PROCESS_ID( "", &Z502_REG_2, &Z502_REG_9 ); 
 
           STEP( 1 )                            /* Make legal target    */
                 printf( "Release %s:Test 1e: Pid %ld\n", 
                                                CURRENT_REL, Z502_REG_2 );
                 CREATE_PROCESS( "test1e_a", test1x, LEGAL_PRIORITY_1E,
                                        &Z502_REG_1, &Z502_REG_9);
                
           STEP( 2 )
                success_expected( Z502_REG_9, "CREATE_PROCESS" );
                                                /* Suspend Illegal PID  */
                SUSPEND_PROCESS( (INT32)9999, &Z502_REG_9);
                
           STEP( 3 )
                error_expected( Z502_REG_9, "SUSPEND_PROCESS" );
                                                /* Resume Illegal PID   */
                RESUME_PROCESS( (INT32)9999, &Z502_REG_9);
                
           STEP( 4 )
                error_expected( Z502_REG_9, "RESUME_PROCESS" );
                                                /* Suspend legal PID    */ 
                SUSPEND_PROCESS(Z502_REG_1, &Z502_REG_9); 

                
           STEP( 5 ) 
                success_expected( Z502_REG_9, "SUSPEND_PROCESS" );
                                        /* Suspend already suspended PID */
                SUSPEND_PROCESS(Z502_REG_1, &Z502_REG_9);
                
           STEP( 6 )
                error_expected( Z502_REG_9, "SUSPEND_PROCESS" );
                                                /* Do a legal resume    */      
                RESUME_PROCESS(Z502_REG_1, &Z502_REG_9);
                
           STEP( 7 )
                success_expected( Z502_REG_9, "RESUME_PROCESS" );
                                /* Resume an already resumed process */
                RESUME_PROCESS(Z502_REG_1, &Z502_REG_9);
                
           STEP( 8 )
                error_expected( Z502_REG_9, "RESUME_PROCESS" );
                                        /* Try to resume ourselves      */
                RESUME_PROCESS(Z502_REG_2, &Z502_REG_9);

           STEP( 9 )
                error_expected( Z502_REG_9, "RESUME_PROCESS" );
                                        /* It may or may not be legal to
                                           suspend ourselves; architectural
                                           decision.                    */
                SUSPEND_PROCESS(-1, &Z502_REG_9);
                
           STEP( 10 )
                /* If we returned "SUCCESS" here, then there is an
                   inconsistancy, because success implies that the 
                   process should have gone away.                       */

                error_expected( Z502_REG_9, "SUSPEND_PROCESS" );
                TERMINATE_PROCESS(-1, &Z502_REG_9);
                
        default: 
                success_expected( Z502_REG_9, "TERMINATE_PROCESS" );
                break;
    }                                             /* End of SELECT     */
}                                                 /* End of test1e     */
                


/**************************************************************************

      Test1f  Successfully suspend and resume processes.          
    
      In particular, show what happens to scheduling when processes
      are temporarily suspended.

      This test works by starting up a number of processes at different
      priorities.  Then some of them are suspended.  Then some are resumed.

        Z502_REG_1              Loop counter
        Z502_REG_2              OUR process ID
        Z502_REG_3,4,5,6,7      Target process ID
        Z502_REG_9              Error returned

**************************************************************************/


#define         PRIORITY_1F1             5
#define         PRIORITY_1F2            10
#define         PRIORITY_1F3            15
#define         PRIORITY_1F4            20
#define         PRIORITY_1F5            25


void    test1f( void)
    {

    static INT32          sleep_time = 200;

    SELECT_STEP
        {
           STEP( 0 )                            /* Get OUR PID          */
                 Z502_REG_1 = 0;                /* Initialize */
                 GET_PROCESS_ID( "", &Z502_REG_2, &Z502_REG_9 ); 
 
           STEP( 1 )                            /* Make legal target    */
                 printf( "Release %s:Test 1f: Pid %ld\n", 
                                                CURRENT_REL, Z502_REG_2 );
                 CREATE_PROCESS( "test1f_a", test1x, PRIORITY_1F1,
                                        &Z502_REG_3, &Z502_REG_9);
                
           STEP( 2 )                            /* Make legal target    */
                 CREATE_PROCESS( "test1f_b", test1x, PRIORITY_1F2,
                                        &Z502_REG_4, &Z502_REG_9);
                
           STEP( 3 )                            /* Make legal target    */
                 CREATE_PROCESS( "test1f_c", test1x, PRIORITY_1F3,
                                        &Z502_REG_5, &Z502_REG_9);
                
           STEP( 4 )                            /* Make legal target    */
                 CREATE_PROCESS( "test1f_d", test1x, PRIORITY_1F4,
                                        &Z502_REG_6, &Z502_REG_9);

                
           STEP( 5 )    /* Make legal target    */
                 CREATE_PROCESS( "test1f_e", test1x, PRIORITY_1F5,
                                        &Z502_REG_7, &Z502_REG_9);
                
                /* Let the 5 pids go for a bit                          */

           STEP( 6 )     
                SLEEP ( sleep_time );
                
                /* Suspend 3 of the pids and see what happens - we 
                   should see scheduling behavior where the processes
                   are yanked out of the ready and the waiting states,
                   and placed into the suspended state.                 */

           STEP( 7 )
                SUSPEND_PROCESS(Z502_REG_3, &Z502_REG_9);

           STEP( 8 )
                SUSPEND_PROCESS(Z502_REG_5, &Z502_REG_9);

           STEP( 9 )
                SUSPEND_PROCESS(Z502_REG_7, &Z502_REG_9);

           STEP( 10 )    
                SLEEP ( sleep_time );
                
           STEP( 11 )
                RESUME_PROCESS(Z502_REG_3, &Z502_REG_9);
                
           STEP( 12 )
                RESUME_PROCESS(Z502_REG_5, &Z502_REG_9);
                
           STEP( 13 )
                if ( Z502_REG_1 < 5 )
                    GO_NEXT_TO( 6 )             
                Z502_REG_1++;                   /* Inc the loop counter */
                RESUME_PROCESS(Z502_REG_7, &Z502_REG_9);

        /*      Wait for children to finish, then quit                  */

           STEP( 14 )    
                SLEEP ( (INT32)10000 );

           STEP( 15 )    
                TERMINATE_PROCESS(-1, &Z502_REG_9);
                

    }                                           /* End of SELECT        */
}                                               /* End of test1f     */



/**************************************************************************

      Test1g  Generate lots of errors for CHANGE_PRIORITY    

      Try lots of different inputs: In particular, some of the possible
        inputs include:

                1. use of illegal priorities
                2. use of an illegal process id.

    
        Z502_REG_1              Target process ID
        Z502_REG_2              OUR process ID
        Z502_REG_9              Error returned

**************************************************************************/

#define         LEGAL_PRIORITY_1G               10
#define         ILLEGAL_PRIORITY_1G            999

void    test1g( void)
    {
    SELECT_STEP
        {
           STEP( 0 )                            /* Get OUR PID          */
                 GET_PROCESS_ID( "", &Z502_REG_2, &Z502_REG_9 ); 
 
           STEP( 1 )                            /* Make legal target    */
                 printf( "Release %s:Test 1g: Pid %ld\n", 
                                                CURRENT_REL, Z502_REG_2 );
                 CREATE_PROCESS( "test1g_a", test1x, LEGAL_PRIORITY_1G,
                                        &Z502_REG_1, &Z502_REG_9);
                
           STEP( 2 )
                success_expected( Z502_REG_9, "CREATE_PROCESS" );
                                                /* Target Illegal PID  */
                CHANGE_PRIORITY( (INT32)9999, LEGAL_PRIORITY_1G, &Z502_REG_9);
                
           STEP( 3 )
                error_expected( Z502_REG_9, "CHANGE_PRIORITY" );
                                                /* Use illegal priority */
                CHANGE_PRIORITY( Z502_REG_1, ILLEGAL_PRIORITY_1G, 
                                                        &Z502_REG_9);
                
           STEP( 4 )
                error_expected( Z502_REG_9, "CHANGE_PRIORITY" );
                TERMINATE_PROCESS(-1, &Z502_REG_9);
                
        default: 
                success_expected( Z502_REG_9, "TERMINATE_PROCESS" );
                break;
    }                                             /* End of SELECT     */
}                                                 /* End of test1g     */
                

/**************************************************************************

        Test1h  Successfully change the priority of a process       

        When you change the priority, it should be possible to see
        the scheduling behaviour of the system change; processes
        that used to be scheduled first are no longer first.

    
        Z502_REG_2              OUR process ID
        Z502_REG_3 - 5          Target process IDs
        Z502_REG_9              Error returned

**************************************************************************/

#define         MOST_FAVORABLE_PRIORITY         1
#define         FAVORABLE_PRIORITY             10
#define         NORMAL_PRIORITY                20
#define         LEAST_FAVORABLE_PRIORITY       30

void    test1h( void)
    {
    INT32       ourself;
    SELECT_STEP
        {
           STEP( 0 )    /* Get OUR PID          */
                 GET_PROCESS_ID( "", &Z502_REG_2, &Z502_REG_9 ); 
 
           STEP( 1 )    /* Make our prior high */
                 printf( "Release %s:Test 1h: Pid %ld\n", 
                                                CURRENT_REL, Z502_REG_2 );
                 ourself = -1;
                 CHANGE_PRIORITY( ourself, MOST_FAVORABLE_PRIORITY,
                                                        &Z502_REG_9);

           STEP( 2 )    /* Make legal targets   */
                 CREATE_PROCESS( "test1h_a", test1x, NORMAL_PRIORITY,
                                        &Z502_REG_3, &Z502_REG_9);
                
           STEP( 3 )    /* Make legal targets   */
                 CREATE_PROCESS( "test1h_b", test1x, NORMAL_PRIORITY,
                                        &Z502_REG_4, &Z502_REG_9);
                
           STEP( 4 )    /* Make legal targets   */
                 CREATE_PROCESS( "test1h_c", test1x, NORMAL_PRIORITY,
                                        &Z502_REG_5, &Z502_REG_9);


        /*      Sleep awhile to watch the scheduling                    */

           STEP( 5 )    
                 SLEEP( 200 );

        /*      Now change the priority - it should be possible to see
                that the priorities have been changed for processes that
                are ready and for processes that are sleeping.          */

           STEP( 6 )    
                 CHANGE_PRIORITY( Z502_REG_3, FAVORABLE_PRIORITY, 
                                        &Z502_REG_9);

           STEP( 7 )    
                 CHANGE_PRIORITY( Z502_REG_5, LEAST_FAVORABLE_PRIORITY, 
                                        &Z502_REG_9);

        /*      Sleep awhile to watch the scheduling                    */

           STEP( 8 )    
                 SLEEP( 200 );

        /*      Now change the priority - it should be possible to see
                that the priorities have been changed for processes that
                are ready and for processes that are sleeping.          */

           STEP( 9 )    
                 CHANGE_PRIORITY( Z502_REG_3, LEAST_FAVORABLE_PRIORITY, 
                                        &Z502_REG_9);

           STEP( 10 )    
                 CHANGE_PRIORITY( Z502_REG_4, FAVORABLE_PRIORITY, 
                                        &Z502_REG_9);

        /*      Sleep awhile to watch the scheduling                    */

           STEP( 11 )    
                 SLEEP( 600 );

           STEP( 12 )    
                 TERMINATE_PROCESS(-1, &Z502_REG_9);

    }                                             /* End of SELECT     */
}                                                 /* End of test1h     */
                

/**************************************************************************

        Test1i   SEND_MESSAGE and RECEIVE_MESSAGE with errors.      

        This has the same kind of error conditions that previous 
        tests did; bad PIDs, bad message lengths, illegal buffer 
        addresses, etc.  Your imagination can go WILD on this one.

    
        Z502_REG_1              Pointer to data private to each process
                                   running this routine.
        Z502_REG_2              OUR process ID
        Z502_REG_3              Target process IDs
        Z502_REG_9              Error returned

**************************************************************************/

#define         LEGAL_MESSAGE_LENGTH           (INT16)64
#define         ILLEGAL_MESSAGE_LENGTH         (INT16)1000

#define         MOST_FAVORABLE_PRIORITY         1
#define         NORMAL_PRIORITY                20

typedef struct
    {
    INT32    target_pid;
    INT32    source_pid;
    INT32    actual_source_pid;
    INT32    send_length;
    INT32    receive_length;
    INT32    actual_send_length;
    INT32    loop_count;
    char     msg_buffer[LEGAL_MESSAGE_LENGTH];
} TEST1I_DATA;

void    test1i( void)
    {
    TEST1I_DATA *td;                            /* Use as ptr to data */

    /* Here we maintain the data to be used by this process when running
       on this routine.  This code should be re-entrant.                */

    if ( Z502_REG_1 == 0 )
        {
        Z502_REG_1 = (INT32)calloc( 1, sizeof ( TEST1I_DATA ));
        if ( Z502_REG_1 == 0 )
            {
            printf( "Something screwed up allocating space in test1i\n" );
        }
        td = ( TEST1I_DATA *)Z502_REG_1;
        td->loop_count = 0;
    }
    td = ( TEST1I_DATA *)Z502_REG_1;


while ( 1 )
    {
    SELECT_STEP
        {
           STEP( 0 )    /* Get OUR PID         */
                GET_PROCESS_ID( "", &Z502_REG_2, &Z502_REG_9 ); 
 
           STEP( 1 )    /* Make our prior high */
                 printf( "Release %s:Test 1i: Pid %ld\n", 
                                                CURRENT_REL, Z502_REG_2 );
                CHANGE_PRIORITY( -1, MOST_FAVORABLE_PRIORITY,
                                                        &Z502_REG_9);
           STEP( 2 )    /* Make legal targets  */
                CREATE_PROCESS( "test1i_a", test1x, NORMAL_PRIORITY,
                                        &Z502_REG_3, &Z502_REG_9);
                
           STEP( 3 )    

                /*      Send to illegal process                     */

                td->target_pid = 9999;
                td->send_length= 8;
                SEND_MESSAGE( td->target_pid, "message", td->send_length,
                                        &Z502_REG_9 );
                

           STEP( 4 )    
                error_expected( Z502_REG_9, "SEND_MESSAGE" );

                /* Try an illegal message length                        */

                td->target_pid = Z502_REG_3;
                td->send_length= ILLEGAL_MESSAGE_LENGTH;
                SEND_MESSAGE( td->target_pid, "message", td->send_length,
                                        &Z502_REG_9 );
                
           STEP( 5 )    
                error_expected( Z502_REG_9, "SEND_MESSAGE" );

                /*      Receive from illegal process                    */

                td->source_pid = 9999;
                td->receive_length = LEGAL_MESSAGE_LENGTH;
                RECEIVE_MESSAGE( td->source_pid, td->msg_buffer, 
                        td->receive_length, &(td->actual_send_length), 
                        &(td->actual_source_pid), &Z502_REG_9 );

           STEP( 6 )    
                error_expected( Z502_REG_9, "RECEIVE_MESSAGE" );

                /*      Receive with illegal buffer size                */

                td->source_pid = Z502_REG_3;
                td->receive_length = ILLEGAL_MESSAGE_LENGTH;
                RECEIVE_MESSAGE( td->source_pid, td->msg_buffer, 
                        td->receive_length, &(td->actual_send_length), 
                        &(td->actual_source_pid), &Z502_REG_9 );

           STEP( 7 )    
                error_expected( Z502_REG_9, "RECEIVE_MESSAGE" );

                /*      Send a legal ( but long ) message to self       */

                td->target_pid = Z502_REG_2;
                td->send_length= LEGAL_MESSAGE_LENGTH;
                SEND_MESSAGE( td->target_pid, "a long but legal message", 
                        td->send_length, &Z502_REG_9 );

           STEP( 8 )    
                success_expected( Z502_REG_9, "SEND_MESSAGE" );
                td->loop_count++;

                /*      Receive this long message, which should error
                        because the receive buffer is too small         */

                td->source_pid = Z502_REG_2;
                td->receive_length = 10;
                RECEIVE_MESSAGE( td->source_pid, td->msg_buffer, 
                        td->receive_length, &(td->actual_send_length), 
                        &(td->actual_source_pid), &Z502_REG_9 );

           STEP( 9 )    
                error_expected( Z502_REG_9, "RECEIVE_MESSAGE" );
                break;

        /*      Keep sending legal messages until the architectural 
                limit for buffer space is exhausted.  In order to pass
                the next test, this number should be at least EIGHT     */

           STEP( 10 )    
                td->target_pid = Z502_REG_3;
                td->send_length= LEGAL_MESSAGE_LENGTH;
                SEND_MESSAGE( td->target_pid, "message", td->send_length, 
                                                &Z502_REG_9 );
                
           STEP( 11 )    
                if (Z502_REG_9 == ERR_SUCCESS)
                    GO_NEXT_TO( 10 )      /* Loop back    */
                td->loop_count++;
                break;

           STEP( 12 )    
                printf( "A total of %ld messages were enqueued.\n",
                                                td->loop_count - 1 );
                TERMINATE_PROCESS(-1, &Z502_REG_9);

        }                                         /* End of SELECT     */
    }                                             /* End of while      */
}                                                 /* End of test1i     */
                

/**************************************************************************

        Test1j   SEND_MESSAGE and RECEIVE_MESSAGE Successfully.

        Creates three other processes, each running their own code.
        RECEIVE and SEND messages are winged back and forth at them.
    
        Z502_REG_1              Pointer to data private to each process
                                   running this routine.
        Z502_REG_2              OUR process ID
        Z502_REG_3 - 5          Target process IDs
        Z502_REG_9              Error returned

        The SEND and RECEIVE system calls as implemented by this test
        imply the following behavior:

          SENDER = PID A          RECEIVER = PID B, 

                               Designates source_pid =
           target_pid =          A            C             -1
          ----------------+------------+------------+--------------+
                          |            |            |              |
               B          |  Message   |     X      |   Message    |
                          |Transmitted |            | Transmitted  |
          ----------------+------------+------------+--------------+
                          |            |            |              |
               C          |     X      |     X      |       X      |
                          |            |            |              |
          ----------------+------------+------------+--------------+
                          |            |            |              |
              -1          |   Message  |     X      |   Message    |
                          | Transmitted|            | Transmitted  |
          ----------------+------------+------------+--------------+
        A broadcast ( target_pid = -1 ) means send to everyone BUT yourself.

**************************************************************************/

#define         LEGAL_MESSAGE_LENGTH            (INT16)64
#define         ILLEGAL_MESSAGE_LENGTH          (INT16)1000

#define         MOST_FAVORABLE_PRIORITY         1
#define         NORMAL_PRIORITY                20

typedef struct
    {
    INT32    target_pid;
    INT32    source_pid;
    INT32    actual_source_pid;
    INT32    send_length;
    INT32    receive_length;
    INT32    actual_send_length;
    INT32    send_loop_count;
    INT32    receive_loop_count; 
    char     msg_buffer[LEGAL_MESSAGE_LENGTH];
    char     msg_sent[LEGAL_MESSAGE_LENGTH];
} TEST1J_DATA;


void    test1j( void)
    {
    TEST1J_DATA *td;                            /* Use as ptr to data */

    /* Here we maintain the data to be used by this process when running
       on this routine.  This code should be re-entrant.                */

    if ( Z502_REG_1 == 0 )
        {
        Z502_REG_1 = (INT32)calloc( 1, sizeof ( TEST1J_DATA ));
        if ( Z502_REG_1 == 0 )
            {
            printf( "Something screwed up allocating space in test1j\n" );
        }
        td = ( TEST1J_DATA *)Z502_REG_1;
        td->send_loop_count = 0;
        td->receive_loop_count = 0;
    }
    td = ( TEST1J_DATA *)Z502_REG_1;


while ( 1 )
    {
    SELECT_STEP
        {
           STEP( 0 )    /* Get OUR PID         */
                GET_PROCESS_ID( "", &Z502_REG_2, &Z502_REG_9 ); 
 
           STEP( 1 )    /* Make our prior high */
                printf( "Release %s:Test 1j: Pid %ld\n", 
                                                CURRENT_REL, Z502_REG_2 );
                CHANGE_PRIORITY( -1, MOST_FAVORABLE_PRIORITY,
                                                        &Z502_REG_9);

           STEP( 2 )    /* Make legal targets  */
                success_expected( Z502_REG_9, "CHANGE_PRIORITY" );
                CREATE_PROCESS( "test1j_1", test1j_echo, NORMAL_PRIORITY,
                                        &Z502_REG_3, &Z502_REG_9);
                
           STEP( 3 )    /* Make legal targets  */
                success_expected( Z502_REG_9, "CREATE_PROCESS" );
                CREATE_PROCESS( "test1j_2", test1j_echo, NORMAL_PRIORITY,
                                        &Z502_REG_4, &Z502_REG_9);
                
           STEP( 4 )    /* Make legal targets  */
                success_expected( Z502_REG_9, "CREATE_PROCESS" );
                CREATE_PROCESS( "test1j_3", test1j_echo, NORMAL_PRIORITY,
                                        &Z502_REG_5, &Z502_REG_9);


           STEP( 5 )    
                success_expected( Z502_REG_9, "CREATE_PROCESS" );

                /*      Send/receive a legal message to each child    */

                td->target_pid = Z502_REG_3;
                td->send_length= 20;
                strcpy( td->msg_sent, "message to #3" );
                SEND_MESSAGE( td->target_pid, td->msg_sent, 
                              td->send_length, &Z502_REG_9 );

           STEP( 6 )    
                success_expected( Z502_REG_9, "SEND_MESSAGE" );
                td->source_pid = -1;
                td->receive_length = LEGAL_MESSAGE_LENGTH;
                RECEIVE_MESSAGE( td->source_pid, td->msg_buffer, 
                        td->receive_length, &(td->actual_send_length), 
                        &(td->actual_source_pid), &Z502_REG_9 );

           STEP( 7 )    
                success_expected( Z502_REG_9, "RECEIVE_MESSAGE" );
                if ( strcmp( td->msg_buffer, td->msg_sent ) != 0 )
                    printf("ERROR - msg sent != msg received.\n");
                if ( td->actual_source_pid != Z502_REG_3 )
                    printf( "ERROR - source PID not correct.\n" );
                if ( td->actual_send_length != td->send_length )
                    printf( "ERROR - send length not sent correctly.\n" );

                td->target_pid = Z502_REG_4;
                td->send_length= 20;
                strcpy( td->msg_sent, "message to #4" );
                SEND_MESSAGE( td->target_pid, td->msg_sent, 
                              td->send_length, &Z502_REG_9 );

           STEP( 8 )    
                success_expected( Z502_REG_9, "SEND_MESSAGE" );
                td->source_pid = -1;
                td->receive_length = LEGAL_MESSAGE_LENGTH;
                RECEIVE_MESSAGE( td->source_pid, td->msg_buffer, 
                        td->receive_length, &(td->actual_send_length), 
                        &(td->actual_source_pid), &Z502_REG_9 );

           STEP( 9 )    
                success_expected( Z502_REG_9, "RECEIVE_MESSAGE" );

                td->target_pid = Z502_REG_5;
                td->send_length= 20;
                strcpy( td->msg_sent, "message to #5" );
                SEND_MESSAGE( td->target_pid, td->msg_sent, 
                              td->send_length, &Z502_REG_9 );

           STEP( 10 )    
                success_expected( Z502_REG_9, "SEND_MESSAGE" );
                td->source_pid = -1;
                td->receive_length = LEGAL_MESSAGE_LENGTH;
                RECEIVE_MESSAGE( td->source_pid, td->msg_buffer, 
                        td->receive_length, &(td->actual_send_length), 
                        &(td->actual_source_pid), &Z502_REG_9 );

           STEP( 11 )    
                success_expected( Z502_REG_9, "RECEIVE_MESSAGE" );

        /*      Keep sending legal messages until the architectural 
                limit for buffer space is exhausted.                    */

           STEP( 12 )    
                td->target_pid = -1;
                sprintf( td->msg_sent, "This is message %ld", 
                                                td->send_loop_count );
                td->send_length= 20;
                SEND_MESSAGE( td->target_pid, td->msg_sent, 
                              td->send_length, &Z502_REG_9 );
                
           STEP( 13 )    
                if (Z502_REG_9 == ERR_SUCCESS)
                    GO_NEXT_TO( 12 )      /* Loop back    */
                td->send_loop_count++;
                break;

           STEP( 14 )    
                td->send_loop_count--;
                printf( "A total of %ld messages were enqueued.\n",
                                                td->send_loop_count );
                break;

           STEP( 15 )    
                td->source_pid = -1;
                td->receive_length = LEGAL_MESSAGE_LENGTH;
                RECEIVE_MESSAGE( td->source_pid, td->msg_buffer, 
                        td->receive_length, &(td->actual_send_length), 
                        &(td->actual_source_pid), &Z502_REG_9 );

           STEP( 16 )    
                success_expected( Z502_REG_9, "RECEIVE_MESSAGE" );
                printf( "Receive from PID = %ld: length = %ld: msg = %s:\n",
                        td->actual_source_pid, td->actual_send_length, 
                        td->msg_buffer );
                td->receive_loop_count++; 
		if ( td->receive_loop_count <= td->send_loop_count )
                    GO_NEXT_TO( 15 )      /* Loop back    */
                break;

           STEP( 17 )    
                printf( "A total of %ld messages were received.\n",
                                        td->receive_loop_count - 1 );
                TERMINATE_PROCESS(-2, &Z502_REG_9);


        }                                         /* End of SELECT     */
    }                                             /* End of while      */
}                                                 /* End of test1j     */
                



/**************************************************************************

      Test1k  Test other oddities in your system.   


        There are many other strange effects, not taken into account
        by the previous tests.  Some of these include:

                1. an illegal system call number.
                2. executing a privileged instruction from a user
                   program

        Z502_REG_2              OUR process ID
        Z502_REG_9              Error returned
    
**************************************************************************/

void    test1k( void)
    {
    SELECT_STEP
        {
           STEP( 0 )
                GET_PROCESS_ID( "", &Z502_REG_2, &Z502_REG_9 );
             
           STEP( 1 )
                printf( "Release %s:Test 1k: Pid %ld\n", 
                                                CURRENT_REL, Z502_REG_2 );

                /* Hand the system an illegal system call number */

                SYS_CALL_CALL_TYPE = 9999;
                break;

        /*      Do an illegal hardware instruction - we will 
                not return from this.                                   */

           STEP( 2 )
                Z502_DELAY_TIMER( (INT32)1);
    }                                             /* End of SELECT     */
}                                                 /* End of test1k     */


/**************************************************************************

      Test1x 
      
        is used as a target by the process creation programs.
        It has the virtue of causing lots of rescheduling activity in
        a relatively random way.                                         

    
        Z502_REG_1              Loop counter
        Z502_REG_2              OUR process ID
        Z502_REG_3              Starting time
        Z502_REG_4              Ending time
        Z502_REG_9              Error returned

**************************************************************************/


#define         NUMBER_OF_TEST1X_ITERATIONS     20

void    test1x( void )
    {
    static INT32          sleep_time = 17;

    while( 1 )
        {
        SELECT_STEP
            {
           STEP( 0 )
                    GET_PROCESS_ID( "", &Z502_REG_2, &Z502_REG_9 );
             
           STEP( 1 )
                    printf( "Release %s:Test 1x: Pid %ld\n", 
                                                CURRENT_REL, Z502_REG_2 );
                    break;
           STEP( 2 )
                    GET_TIME_OF_DAY( &Z502_REG_3 );
                    
           STEP( 3 )
                    SLEEP ( ( sleep_time * Z502_REG_3 ) % 43 );  /* random*/
                    
           STEP( 4 )
                    GET_TIME_OF_DAY( &Z502_REG_4 );
                    
           STEP( 5 )
                    if ( Z502_REG_1 <= NUMBER_OF_TEST1X_ITERATIONS )
                        GO_NEXT_TO( 2 )
                    else
                        {
                        TERMINATE_PROCESS( -1, &Z502_REG_9 );
                    }
                    Z502_REG_1++;                       /* Inc loop cntr */
                    break; 

           STEP( 6 )
                    printf( "ERROR: Test should be terminated but isn't.\n");
                    break;
        }                                       /* End of while     */
    }                                           /* End of SELECT    */
}                                               /* End of test1x    */


/**************************************************************************

      Test1j_echo
      
        is used as a target by the message send/receive programs.
        All it does is send back the same message it received to the
        same sender.
    
        Z502_REG_1              Pointer to data private to each process
                                   running this routine.
        Z502_REG_2              OUR process ID
        Z502_REG_3              Starting time
        Z502_REG_4              Ending time
        Z502_REG_9              Error returned
**************************************************************************/

typedef struct
    {
    INT32    target_pid;
    INT32    source_pid;
    INT32    actual_source_pid;
    INT32    send_length;
    INT32    receive_length;
    INT32    actual_senders_length;
    char     msg_buffer[LEGAL_MESSAGE_LENGTH];
    char     msg_sent[LEGAL_MESSAGE_LENGTH];
} TEST1J_ECHO_DATA;

void    test1j_echo( void)
    {
    TEST1J_ECHO_DATA *td;                               /* Use as ptr to data */

    /* Here we maintain the data to be used by this process when running
       on this routine.  This code should be re-entrant.                */

    if ( Z502_REG_1 == 0 )
        {
        Z502_REG_1 = (INT32)calloc( 1, sizeof ( TEST1J_ECHO_DATA ));
        if ( Z502_REG_1 == 0 )
            {
            printf( "Something screwed up allocating space in test1j_echo\n" );
        }
    }
    td = ( TEST1J_ECHO_DATA *)Z502_REG_1;


    while( 1 )
        {
        SELECT_STEP
            {
           STEP( 0 )
                GET_PROCESS_ID( "", &Z502_REG_2, &Z502_REG_9 );
             
           STEP( 1 )
                success_expected( Z502_REG_9, "GET_PROCESS_ID" );
                printf( "Release %s:Test 1j_echo: Pid %ld\n", 
                                                CURRENT_REL, Z502_REG_2 );
           STEP( 2 )
                td->source_pid = -1;
                td->receive_length = LEGAL_MESSAGE_LENGTH;
                RECEIVE_MESSAGE( td->source_pid, td->msg_buffer, 
                        td->receive_length, &(td->actual_senders_length), 
                        &(td->actual_source_pid), &Z502_REG_9 );
             
           STEP( 3 )
                success_expected( Z502_REG_9, "RECEIVE_MESSAGE" );
                printf( "Receive from PID = %ld: length = %ld: msg = %s:\n",
                        td->actual_source_pid, td->actual_senders_length, 
                        td->msg_buffer );
                                                        
                td->target_pid = td->actual_source_pid;
                strcpy( td->msg_sent, td->msg_buffer );
                td->send_length= td->actual_senders_length;
                SEND_MESSAGE( td->target_pid, td->msg_sent, 
                              td->send_length, &Z502_REG_9 );

           STEP( 4 )
                success_expected( Z502_REG_9, "SEND_MESSAGE" );
                GO_NEXT_TO( 2 )
                break;
        }                                       /* End of while     */
    }                                           /* End of SELECT    */
}                                               /* End of test1j_echo*/

/**************************************************************************

      error_expected    and    success_expected

        These routines simply handle the display of success/error data.

**************************************************************************/

void    error_expected( INT32 error_code, char sys_call[] )
    {
    if ( error_code == ERR_SUCCESS)
        {
        printf( "An Error SHOULD have occurred.\n" );
        printf("????: Error( %ld ) occurred in case %d (%s)\n",
              error_code, Z502_REG_PROGRAM_COUNTER-2, sys_call );
    }
    else
        printf( "Program correctly returned an error: %ld\n", error_code );

}                                               /* End of error_expected */



void    success_expected( INT32 error_code, char sys_call[] )
    {
    if ( error_code != ERR_SUCCESS)
        {
        printf( "An Error should NOT have occurred.\n" );
        printf("????: Error( %ld ) occurred in case %d (%s)\n",
              error_code, Z502_REG_PROGRAM_COUNTER-2, sys_call );
    }
    else
        printf( "Program correctly returned success.\n" );

}                                            /* End of success_expected */


/**************************************************************************

      Test2a exercises a simple memory write and read             

        Use:  Z502_REG_1                data_written
              Z502_REG_2                data_read
              Z502_REG_3                address
              Z502_REG_4                process_id
              Z502_REG_9                error                    

**************************************************************************/

void    test2a( void )
    {

    SELECT_STEP
        {
       STEP( 0 )
            GET_PROCESS_ID( "", &Z502_REG_4, &Z502_REG_9 );
           
       STEP( 1 )
            printf( "Release %s:Test 2a: Pid %ld\n", 
                                                CURRENT_REL, Z502_REG_4 );
            Z502_REG_3 = 412;
            Z502_REG_1 = Z502_REG_3 + Z502_REG_4;
            MEM_WRITE( Z502_REG_3, &Z502_REG_1 );
            
       STEP( 2 )
            MEM_READ( Z502_REG_3, &Z502_REG_2 );
            
       STEP( 3 ) 
            printf("PID= %ld  address= %ld   written= %ld   read= %ld\n",
                        Z502_REG_4, Z502_REG_3, Z502_REG_1, Z502_REG_2);
            if ( Z502_REG_2 != Z502_REG_1 )
                    printf( "AN ERROR HAS OCCURRED.\n" );
            TERMINATE_PROCESS( -1, &Z502_REG_9 );
            
    }                                           /* End of SELECT    */
}                                               /* End of test2a    */


/**************************************************************************

        Test2b 
        
        exercises simple memory writes and reads.  Watch out,
        the addresses used are diabolical and are designed to show
        unusual features of your memory management system.          

        Use:  Z502_REG_1                data_written
              Z502_REG_2                data_read
              Z502_REG_3                address
              Z502_REG_4                process_id
              Z502_REG_5                test_data_index             
              Z502_REG_9                error

        The following registers are used for sanity checks - after each
        read/write pair, we will read back the first set of data to make
        sure it's still there.

              Z502_REG_6                First data written
              Z502_REG_7                First data read
              Z502_REG_8                First address                   

**************************************************************************/

#define         TEST_DATA_SIZE          (INT16)7

void    test2b( void )
    {
    static INT32        test_data[TEST_DATA_SIZE] = {
                        0, 4, PGSIZE - 2, PGSIZE, 3 * PGSIZE - 2,
                        (VIRTUAL_MEM_PGS - 1) * PGSIZE,
                        VIRTUAL_MEM_PGS * PGSIZE - 2};

    while(1)
        {
        SELECT_STEP
            {
           STEP( 0 )
                GET_PROCESS_ID( "", &Z502_REG_4, &Z502_REG_9 );
                
           STEP( 1 )
                Z502_REG_8 = test_data[ (INT16)Z502_REG_5] ;
                Z502_REG_6 = Z502_REG_3 + Z502_REG_4;
                printf( "\n\nRelease %s:Test 2b: Pid %ld\n", 
                                                CURRENT_REL, Z502_REG_4 );
                break;

           STEP( 2 )
                Z502_REG_3 = test_data[ (INT16)Z502_REG_5 ];
                Z502_REG_1 = Z502_REG_3 + Z502_REG_4;
                MEM_WRITE( Z502_REG_3, &Z502_REG_1 );
                
           STEP( 3 )
                MEM_READ( Z502_REG_3, &Z502_REG_2 );


           STEP( 4 )
                printf("PID= %ld  address= %ld  written= %ld   read= %ld\n",
                        Z502_REG_4, Z502_REG_3, Z502_REG_1, Z502_REG_2);
                if (Z502_REG_2 != Z502_REG_1 )
                    printf( "AN ERROR HAS OCCURRED.\n" );

                /*      Go back and check earlier write                 */

                MEM_READ( Z502_REG_8, &Z502_REG_7 );
                
           STEP( 5 )
                GO_NEXT_TO( 2 )
                printf("PID= %ld  address= %ld   written= %ld   read= %ld\n",
                        Z502_REG_4, Z502_REG_8, Z502_REG_6, Z502_REG_7);
                if (Z502_REG_6 != Z502_REG_7 )
                    printf( "AN ERROR HAS OCCURRED.\n" );
                Z502_REG_5++;
                break;
        }                                       /* End of SELECT    */
    }                                           /* End of while     */
}                                               /* End of test2b    */


/**************************************************************************

        Test2c causes usage of disks.  The test is designed to give
        you a chance to develop a mechanism for handling disk requests.

    Z502_REG_1  - data that was written.
    Z502_REG_2  - data that was read from memory.
    Z502_REG_3  - address where data was written/read.
    Z502_REG_4  - process id of this process.
    Z502_REG_6  - number of iterations/loops through the code.
    Z502_REG_7  - which page will the write/read be on. start at 0  
    Z502_REG_9  - returned error code.

**************************************************************************/

#define         DISPLAY_GRANULARITY2c           20

typedef         union
    {
    char        char_data[ PGSIZE ];
    long        long_data[ PGSIZE/4 ];
} DISK_DATA;

void    test2c( void )
    {
    DISK_DATA   *data_written;
    DISK_DATA   *data_read;
    INT16       disk_id;
    INT16       cylinder;
    INT16       sector;

    if ( Z502_REG_1 == 0 )
        {
        Z502_REG_1 = ( INT32 )calloc( 1, sizeof( DISK_DATA ) );
        Z502_REG_2 = ( INT32 )calloc( 1, sizeof( DISK_DATA ) );
        if ( Z502_REG_2 == 0 )
            printf( "Something screwed up allocating space in test2c\n");
    }
    data_written = (DISK_DATA *)Z502_REG_1;
    data_read    = (DISK_DATA *)Z502_REG_2;


    while(1)
        {
        SELECT_STEP
            {
           STEP( 0 )
                GET_PROCESS_ID( "", &Z502_REG_4, &Z502_REG_9 );
                
           STEP( 1 )
                Z502_REG_6 = Z502_REG_4;
                printf( "\n\nRelease %s:Test 2c: Pid %ld\n", 
                                                CURRENT_REL, Z502_REG_4 );
                break;
           STEP( 2 )
                disk_id  = ( Z502_REG_4 / 2 ) % MAX_NUMBER_OF_DISKS + 1;
                cylinder = Z502_REG_6 / NUM_SECTORS;
                sector   = Z502_REG_6 % NUM_SECTORS;
                data_written->long_data[0] = disk_id;
                data_written->long_data[1] = cylinder;
                data_written->long_data[2] = sector;
                data_written->long_data[3] = Z502_REG_4;
                DISK_WRITE( disk_id, cylinder, sector, 
                                 (char*)(data_written->char_data ) );
                
           STEP( 3 )
                disk_id  = ( Z502_REG_4 / 2 ) % MAX_NUMBER_OF_DISKS + 1;
                cylinder = Z502_REG_6 / NUM_SECTORS;
                sector   = Z502_REG_6 % NUM_SECTORS;
                DISK_READ( disk_id, cylinder, sector, 
                                 (char*)(data_read->char_data ) );
                
           STEP( 4 )
                disk_id  = ( Z502_REG_4 / 2 ) % MAX_NUMBER_OF_DISKS + 1;
                cylinder = Z502_REG_6 / NUM_SECTORS;
                sector   = Z502_REG_6 % NUM_SECTORS;
                if ( Z502_REG_7 % DISPLAY_GRANULARITY2c == 0 )
                  printf("PID= %ld  disk_id =%d, cylinder = %d, sector = %d\n",
                        Z502_REG_4, disk_id, cylinder, sector );
                if (  ( data_read->long_data[0] != data_written->long_data[0] )
                   || ( data_read->long_data[1] != data_written->long_data[1] )
                   || ( data_read->long_data[2] != data_written->long_data[2] )
                   || ( data_read->long_data[3] != data_written->long_data[3]))
                    printf( "AN ERROR HAS OCCURRED.\n" );

                Z502_REG_6 += 2;
                if ( Z502_REG_6 < 50 )
                    GO_NEXT_TO( 2 )                     /* Go write/read */
                break;


                /* Now read back the data we've written and paged */

           STEP( 5 )
                printf( "Reading back data: test 2c, PID %ld.\n", 
                                                        Z502_REG_4 );
                Z502_REG_6 = Z502_REG_4;
                break;
           STEP( 6 )
                disk_id  = ( Z502_REG_4 / 2 ) % MAX_NUMBER_OF_DISKS + 1;
                cylinder = Z502_REG_6 / NUM_SECTORS;
                sector   = Z502_REG_6 % NUM_SECTORS;
                data_written->long_data[0] = disk_id;
                data_written->long_data[1] = cylinder;
                data_written->long_data[2] = sector;
                data_written->long_data[3] = Z502_REG_4;
                DISK_READ( disk_id, cylinder, sector, 
                                 (char*)(data_read->char_data ) );
                
           STEP( 7 )
                disk_id  = ( Z502_REG_4 / 2 ) % MAX_NUMBER_OF_DISKS + 1;
                cylinder = Z502_REG_6 / NUM_SECTORS;
                sector   = Z502_REG_6 % NUM_SECTORS;
                if ( Z502_REG_7 % DISPLAY_GRANULARITY2c == 0 )
                  printf("PID= %ld  disk_id =%d, cylinder = %d, sector = %d\n",
                        Z502_REG_4, disk_id, cylinder, sector );
                if (  ( data_read->long_data[0] != data_written->long_data[0] )
                   || ( data_read->long_data[1] != data_written->long_data[1] )
                   || ( data_read->long_data[2] != data_written->long_data[2] )
                   || ( data_read->long_data[3] != data_written->long_data[3]))
                    printf( "AN ERROR HAS OCCURRED.\n" );

                Z502_REG_6 += 2;
                if ( Z502_REG_6 < 50 )
                    GO_NEXT_TO( 6 )                     /* Go write/read */
                break;
            STEP( 8 )
                TERMINATE_PROCESS( -1, &Z502_REG_9 );

        }                                       /* End of SELECT    */
    }                                           /* End of while     */
}                                               /* End of test2c    */



/**************************************************************************

        Test2d runs several disk programs at a time.  The purpose here
               is to watch the scheduling that goes on for these
               various disk processes.  The behavior that should be seen
               is that the processes alternately run and do disk
               activity - there should always be someone running unless
               ALL processes happen to be waiting on the disk at some
               point.
               This program will terminate when all the test2c routines
               have finished.

    Z502_REG_4  - process id of this process.
    Z502_REG_5  - returned error code.
    Z502_REG_6  - pid of target process.
    Z502_REG_8  - returned error code from the GET_PROCESS_ID call.

**************************************************************************/

#define           MOST_FAVORABLE_PRIORITY                       1

void    test2d( void )
    {
    static INT32         trash;

    while(1)
        {
        SELECT_STEP
            {
           STEP( 0 )
                GET_PROCESS_ID( "", &Z502_REG_4, &Z502_REG_5 );
                
           STEP( 1 )
                printf( "\n\nRelease %s:Test 2d: Pid %ld\n", 
                                                CURRENT_REL, Z502_REG_4 );
                CHANGE_PRIORITY( -1, MOST_FAVORABLE_PRIORITY,
                                                        &Z502_REG_9);
           STEP( 2 )
                CREATE_PROCESS( "first", test2c, 5, &trash, &Z502_REG_5 );
                
           STEP( 3 )
                CREATE_PROCESS( "second", test2c, 5, &trash, &Z502_REG_5 );
                
           STEP( 4 )
                CREATE_PROCESS( "third", test2c, 7, &trash, &Z502_REG_5 );
     

           STEP( 5 )
                CREATE_PROCESS( "fourth", test2c, 7, &trash, &Z502_REG_5 );
                
           STEP( 6 )
                CREATE_PROCESS( "fifth", test2c, 7, &trash, &Z502_REG_5 );
                
           STEP( 7 )
                SLEEP ( 50000 );
                
           STEP( 8 )
                TERMINATE_PROCESS( -1, &Z502_REG_5 );
                

        }                                       /* End of SELECT    */
    }                                           /* End of while     */
}                                               /* End of test2d    */


/**************************************************************************

        Test2e causes extensive page replacement.  It simply advances
        through virtual memory.  It will eventually end because
        using an illegal virtual address will cause this process to
        be terminated by the operating system.                                                  

    Z502_REG_1  - data that was written.
    Z502_REG_2  - data that was read from memory.
    Z502_REG_3  - address where data was written/read.
    Z502_REG_4  - process id of this process.
    Z502_REG_6  - number of iterations/loops through the code.
    Z502_REG_7  - which page will the write/read be on. start at 0  
    Z502_REG_9  - returned error code.

**************************************************************************/

#define         STEP_SIZE               VIRTUAL_MEM_PGS/(2 * PHYS_MEM_PGS )
#define         DISPLAY_GRANULARITY2e     16 * STEP_SIZE

void    test2e( void )
    {

    while(1)
        {
        SELECT_STEP
            {
           STEP( 0 )
                GET_PROCESS_ID( "", &Z502_REG_4, &Z502_REG_9 );
                
           STEP( 1 )
                printf( "\n\nRelease %s:Test 2e: Pid %ld\n", 
                                                CURRENT_REL, Z502_REG_4 );
                break;
           STEP( 2 )
                Z502_REG_3 = PGSIZE * Z502_REG_7;       /* Generate address*/
                Z502_REG_1 = Z502_REG_3 + Z502_REG_4;   /* Generate data */
                MEM_WRITE( Z502_REG_3, &Z502_REG_1 );   /* Write the data */
                
           STEP( 3 )
                MEM_READ( Z502_REG_3, &Z502_REG_2 );    /* Read back data */
                
           STEP( 4 )
                if ( Z502_REG_7 % DISPLAY_GRANULARITY2e == 0 )
                  printf("PID= %ld  address= %ld   written= %ld   read= %ld\n",
                        Z502_REG_4, Z502_REG_3, Z502_REG_1, Z502_REG_2 );
                if (Z502_REG_2 != Z502_REG_1 )          /* Written = read? */
                    printf( "AN ERROR HAS OCCURRED.\n" );
                Z502_REG_7 += STEP_SIZE;
                if ( Z502_REG_7 < VIRTUAL_MEM_PGS )
                    GO_NEXT_TO( 2 )                     /* Go write/read */
                break;


                /* Now read back the data we've written and paged */

           STEP( 5 )
                printf( "Reading back data: test 2e, PID %ld.\n", 
                                                        Z502_REG_4 );
                Z502_REG_7 = 0;
                break;
           STEP( 6 )
                Z502_REG_3 = PGSIZE * Z502_REG_7;       /* Generate address*/
                Z502_REG_1 = Z502_REG_3 + Z502_REG_4;   /* Data expected  */
                MEM_READ( Z502_REG_3, &Z502_REG_2 );    /* Read back data */
                
           STEP( 7 )
                if ( Z502_REG_7 % DISPLAY_GRANULARITY2e == 0 )
                  printf("PID= %ld  address= %ld   written= %ld   read= %ld\n",
                        Z502_REG_4, Z502_REG_3, Z502_REG_1, Z502_REG_2 );
                if (Z502_REG_2 != Z502_REG_1 )          /* Written = read? */
                    printf( "AN ERROR HAS OCCURRED.\n" );
                Z502_REG_7 += STEP_SIZE;
                GO_NEXT_TO( 6 )                         /* Go write/read */
                break;
        }                                       /* End of SELECT    */
    }                                           /* End of while     */
}                                               /* End of test2e    */



/**************************************************************************

        Test2f causes extensive page replacement, but reuses pages. 
                This program will terminate, but it might take a while.

    Z502_REG_1  - data that was written.
    Z502_REG_2  - data that was read from memory.
    Z502_REG_3  - address where data was written/read.
    Z502_REG_4  - process id of this process.
    Z502_REG_5  - holds the pointer to the record of page touches
    Z502_REG_6  - counter/index - starts at 0.
    Z502_REG_8  - iteration count
    Z502_REG_9  - returned error code.

**************************************************************************/

#define                 NUMBER_OF_ITERATIONS            3
#define                 LOOP_COUNT                    400
#define                 DISPLAY_GRANULARITY2         1000
#define                 LOGICAL_PAGES_TO_TOUCH       2 * PHYS_MEM_PGS

typedef struct
    {
    INT16       page_touched[LOGICAL_PAGES_TO_TOUCH];
} MEMORY_TOUCHED_RECORD;

void    test2f( void )
    {
    MEMORY_TOUCHED_RECORD       *mtr;
    short                       index;

    if ( Z502_REG_5 == 0 )
        {
        Z502_REG_5 = ( INT32 )calloc( 1, sizeof(MEMORY_TOUCHED_RECORD) );
        if ( Z502_REG_5 == 0 )
            {
            printf("Something screwed up allocating space in test2f\n");
        }
        mtr = ( MEMORY_TOUCHED_RECORD *)Z502_REG_5;
        for ( index = 0; index < LOGICAL_PAGES_TO_TOUCH; index++ )
            mtr->page_touched[index] = 0;
    }
    mtr = ( MEMORY_TOUCHED_RECORD *)Z502_REG_5;


    while(1)
        {
        SELECT_STEP
            {
           STEP( 0 )
                GET_PROCESS_ID( "", &Z502_REG_4, &Z502_REG_9 );
                

           STEP( 1 )
                printf( "\n\nRelease %s:Test 2f: Pid %ld\n", 
                                                CURRENT_REL, Z502_REG_4 );
                break;

           STEP( 2 )
                get_pink_random_number( &Z502_REG_7, LOGICAL_PAGES_TO_TOUCH );
                Z502_REG_3 = PGSIZE * Z502_REG_7;  /* Convert page to addr.*/
                Z502_REG_1 = Z502_REG_3 + Z502_REG_4;
                MEM_WRITE( Z502_REG_3, &Z502_REG_1 );
                
           STEP( 3 )
                MEM_READ( Z502_REG_3, &Z502_REG_2 );
                
           STEP( 4 )
                if ( Z502_REG_6 % DISPLAY_GRANULARITY2 == 0 )
                  printf("PID= %ld  address= %ld   written= %ld   read= %ld\n",
                        Z502_REG_4, Z502_REG_3, Z502_REG_1, Z502_REG_2 );
                Z502_REG_6++;
                if (Z502_REG_2 != Z502_REG_1 )
                    printf( "AN ERROR HAS OCCURRED: READ NOT EQUAL WRITE.\n" );
                mtr->page_touched[ (short)Z502_REG_7 ]++;
                if ( Z502_REG_6 <= LOOP_COUNT )
                    GO_NEXT_TO( 2 )    /* There are more pages to read/write */
                else
                    {
                    printf( "PID %ld starting re-read.\n", Z502_REG_4 );
                    Z502_REG_6 = 0;
                }
                break;


           STEP( 5 )
                /* We can only read back from pages we've previously
                   written to, so find out which pages those are.           */

                while( mtr->page_touched[(short)Z502_REG_6] == 0 )
                     {
                     Z502_REG_6++;
                     if ( Z502_REG_6 >= LOGICAL_PAGES_TO_TOUCH ) 
                         {
                         GO_NEXT_TO( 7 )
                         break;
                    }
                }
                Z502_REG_3 = PGSIZE * Z502_REG_6;  /* Convert page to addr.*/   
                Z502_REG_1 = Z502_REG_3 + Z502_REG_4;   /* Expected read*/
                MEM_READ( Z502_REG_3, &Z502_REG_2 );

           STEP( 6 )
                if ( Z502_REG_6 % DISPLAY_GRANULARITY2 == 0 )
                  printf("PID= %ld  address= %ld   written= %ld   read= %ld\n",
                        Z502_REG_4, Z502_REG_3, Z502_REG_1, Z502_REG_2 );
                Z502_REG_6++;
                if (Z502_REG_2 != Z502_REG_1 )
                    printf( "ERROR HAS OCCURRED: READ NOT SAME AS WRITE.\n" );
                if ( Z502_REG_6 < LOGICAL_PAGES_TO_TOUCH )
                    GO_NEXT_TO( 5 )     /* There's more to read back    */
                break;

            STEP( 7 )
                /* We've completed reading back everything              */

                Z502_REG_8++;
                printf( "TEST 2f, PID %ld, HAS COMPLETED %ld ITERATIONS\n",
                                Z502_REG_4, Z502_REG_8 );
                if ( Z502_REG_8 >= NUMBER_OF_ITERATIONS )
                    GO_NEXT_TO( 8 )                 /* All done     */
                else
                    GO_NEXT_TO( 2 )     /* Set up to start ALL over     */
                printf( "PID %ld starting read/write.\n", Z502_REG_4 );
                Z502_REG_6 = 0;
                break;

           STEP( 8 )
                TERMINATE_PROCESS( -1, &Z502_REG_9 );

        }                                       /* End of SELECT    */
    }                                           /* End of while     */
}                                               /* End of test2f    */


/**************************************************************************

      Test2g starts up a number of programs who do tests of shared area.

    Z502_REG_4  - process id of this process.
    Z502_REG_5  - returned error code.
    Z502_REG_6  - pid of target process.
    Z502_REG_8  - returned error code from the GET_PROCESS_ID call.

**************************************************************************/

#define           MOST_FAVORABLE_PRIORITY                       1

void    test2g( void )
    {
    INT32         trash;

    while(1)
        {
        SELECT_STEP
            {
           STEP( 0 )
                GET_PROCESS_ID( "", &Z502_REG_4, &Z502_REG_5 );
                
           STEP( 1 )
                printf( "\n\nRelease %s:Test 2g: Pid %ld\n", 
                                                CURRENT_REL, Z502_REG_4 );
                CHANGE_PRIORITY( -1, MOST_FAVORABLE_PRIORITY,
                                                        &Z502_REG_5);
           STEP( 2 )
                CREATE_PROCESS( "first", test2gx, 5, &trash, &Z502_REG_5 );
                
           STEP( 3 )
                CREATE_PROCESS( "second", test2gx, 6, &trash, &Z502_REG_5 );
                
           STEP( 4 )
                Z502_REG_8 = 0;
                CREATE_PROCESS( "third", test2gx, 7, &trash, &Z502_REG_5 );
                
           STEP( 5 )
                Z502_REG_8 = 0;
                CREATE_PROCESS( "fourth", test2gx, 8, &trash, &Z502_REG_5 );
                
           STEP( 6 )
                Z502_REG_8 = 0;
                CREATE_PROCESS( "fifth", test2gx, 9, &trash, &Z502_REG_5 );



                        /* Loop here until the "2gx" processes terminate. */
           STEP( 7 )
                GET_PROCESS_ID( "fifth", &Z502_REG_6, &Z502_REG_8 );
                
           STEP( 8 )
                if ( Z502_REG_8 != 0 )
                    GO_NEXT_TO( 9 )
                else
                    {
                    GO_NEXT_TO( 7 )
                    SLEEP ( 10000 );
                }
                break;
           STEP( 9 )
                TERMINATE_PROCESS( -1, &Z502_REG_5 );

        }                                       /* End of SELECT    */
    }                                           /* End of while     */
}                                               /* End of test2g    */


/**************************************************************************

      Test2gx - test shared memory usage.
      
      This test runs as multiple instances of processes; there are several
      processes who in turn manipulate shared memory.

      NOTE:  This test is new in Release 1.5 and has not been tested very
             thoroughly - be careful and trust nothing!!

      The algorithm used here flows as follows:

      o Get our PID and print it out.
      o Use our PID to determine the address at which to start shared
        area - every process will have a different starting address.
      o Define the shared area.
      o Fill in initial portions of the shared area by:
        + Locking the shared area
        + Determine which location in shared area is ours by using the
          number of processes that are already holding the region.
          For this discussion, call it the shared_index.
        + Fill in portions of the shared area.
        + Unlock the shared area.
      o Sleep to let all 2gx PIDs start up.
      o If (shared_index > 0) goto INSIDE_LOOP   *** NOT first DEFINER  ***

      o LOOP forever doing the following steps:
          + Lock shared area
          + Determine the "next" process, where 
             next = ( my_shared_index + 1 ) mod number_of_2gx_processes.
          + Put N + 1 ( initially N = 0 ) into mailbox of next process
          + Put sender's PID into Target's mailbox.
          + Get PID of "next" process.
          + Unlock shared area.
          + SEND_MESSAGE( "next", ..... );
   
      o INSIDE_LOOP 
        + RECEIVE_MESSAGE( "-1", ..... )
        + Lock shared area
        + Read my mailbox
        + Print out lots of stuff
        + Do lots of sanity checks
        + If  N < MAX_ITERATIONS then go to LOOP.

      o If (shared_index == 0)   ***** the first DEFINER   *****
        + sleep                      ***** let others finish   *****
        + print the whole shared structure

      o Terminate the process.
      
**************************************************************************/


#define           MAX_NUM_2GX_ITERATIONS      26
#define           MAX_NUM_2GX_PROCS           10
#define           SHARED_MEM_NAME             "almost_done!!"

/*      The following structure will be laid on shared memory by using
        the MEM_ADJUST   macro                                          */

typedef  struct
   {
   INT32        pid;
   INT32        mailbox;
   INT32        writer_of_mailbox;
} PROC_INFO;

typedef  struct
    {
    INT32       number_2gx_procs;
    INT32       lock_word;
    PROC_INFO   proc_info[ MAX_NUM_2GX_PROCS ];
} SHARED_DATA;

/*  This MEM_ADJUST macro allows us to overlay the SHARED_DATA structure
    onto the shared memory we've defined.  It generates an address
    appropriate for use by READ and MEM_WRITE.                          */

#define         MEM_ADJUST( arg )                                       \
(int)&(shared_ptr->arg) - (int)(shared_ptr)                             \
                      + (int)starting_address_of_shared_area

void    test2gx( void )
    {
    static INT32          starting_address_of_shared_area;
    static INT32          pages_in_shared_area; 
    static char           area_tag[] = SHARED_MEM_NAME;
    static INT32          number_previous_sharers; 
    static INT32          error_returned;
    static INT32          already_locked;
    static INT32          memory_info;
    static INT32          our_index;
    static INT32          next_index;
    static INT32          next_pid;
    static INT32          data_being_passed = 0;

    static INT32          source_pid;
    static char           receive_buffer[20];
    static INT32          message_receive_length;
    static INT32          message_send_length;
    static INT32          message_sender_pid;

    /* The declaration of shared_ptr is only for use by MEM_ADJUST macro.
       It points to a bogus location - but that's ok because we never
       actually use the result of the pointer.                      */

    static  SHARED_DATA   *shared_ptr = 0;       


    while(1)
        {
        SELECT_STEP
            {
           STEP( 0 )
                GET_PROCESS_ID( "", &Z502_REG_4, &Z502_REG_5 );
                
           STEP( 1 )
                printf( "\n\nRelease %s:Test 2gx: Pid %ld\n", 
                                                CURRENT_REL, Z502_REG_4 );
                /* As an interesting wrinkle, each process should start
                   its shared region at a somewhat different address;
                   determine that here.                               */

                starting_address_of_shared_area = 
                                            ( Z502_REG_4 % 17 ) * PGSIZE;
                pages_in_shared_area = sizeof( SHARED_DATA )/PGSIZE + 1;

                DEFINE_SHARED_AREA( starting_address_of_shared_area, 
                                    pages_in_shared_area, 
                                    area_tag, 
                                    &number_previous_sharers, 
                                    &error_returned );

           STEP( 2 )
                success_expected( error_returned, "DEFINE_SHARED_AREA" );

                /*  Put stuff in shared area - lock it first        */

                READ_MODIFY( MEM_ADJUST( lock_word ), &already_locked );

           STEP( 3 )
                if ( already_locked == TRUE )
                    GO_NEXT_TO( 2 )   /* Couldn't get lock - try again */
                SLEEP( 10 );

           STEP( 4 )
                /* We now have the lock and so own the shared area  */

                /* Increment the number of users of shared area     */

                MEM_READ( MEM_ADJUST( number_2gx_procs ), &memory_info );

           STEP( 5 )
                memory_info++;
                MEM_WRITE( MEM_ADJUST( number_2gx_procs ), &memory_info );

           STEP( 6 )
                memory_info = Z502_REG_4;  /* Store PID in our slot */
                our_index = number_previous_sharers;
                MEM_WRITE( MEM_ADJUST(proc_info[our_index].pid), 
                                                       &memory_info );



           STEP( 7 )
                memory_info = 0;                    /* Free lock */
                MEM_WRITE( MEM_ADJUST( lock_word ), &memory_info );

           STEP( 8 )
                if ( our_index == 0 )
                    {
                    GO_NEXT_TO( 12 );         /* This is the master */
                    SLEEP( 1000 );             /* Wait for slaves to start */
                }
                else
                    GO_NEXT_TO( 30 );         /* These are the slaves */
                break;

           STEP( 12 )
               /* This is the top of the loop - return here for each
                  send - receive pair                                */

                /*  Put stuff in shared area - lock it first        */

                READ_MODIFY( MEM_ADJUST( lock_word ), &already_locked );

           STEP( 13 )
                if ( already_locked == TRUE )
                    GO_NEXT_TO( 12 )   /* Couldn't get lock - try again */
                SLEEP( 10 );

           STEP( 14 )
                MEM_READ( MEM_ADJUST( number_2gx_procs ), &memory_info );

           STEP( 15 )
                next_index = ( our_index + 1 ) % memory_info;
                MEM_WRITE( MEM_ADJUST(proc_info[next_index].mailbox), 
                                                  &data_being_passed );
                
           STEP( 16 )
                MEM_WRITE( MEM_ADJUST(proc_info[next_index].writer_of_mailbox), 
                                                  &Z502_REG_4 );


           STEP( 17 )
                MEM_READ( MEM_ADJUST(proc_info[next_index].pid), &next_pid );

           STEP( 18 )
                memory_info = 0;                    /* Free lock */
                MEM_WRITE( MEM_ADJUST( lock_word ), &memory_info );

           STEP( 19 )
                printf( "Sender %d to Receiver %d passing data %d\n",
                                Z502_REG_4, next_pid, data_being_passed );
                SEND_MESSAGE( next_pid, " ", 0, &error_returned );

           STEP( 20 )
                success_expected( error_returned, "SEND_MESSAGE" );
                if ( data_being_passed < MAX_NUM_2GX_ITERATIONS )
                    {
                    GO_NEXT_TO( 30 );
                }
                else
                    {
                    GO_NEXT_TO( 40 );
                }
                break;

           STEP( 30 )
                source_pid = -1;                /* From anyone  */
                message_receive_length = 20;
                RECEIVE_MESSAGE( source_pid, 
                                 receive_buffer,
                                 message_receive_length,
                                 &message_send_length,
                                 &message_sender_pid,
                                 &error_returned );

           STEP( 31 )
                success_expected( error_returned, "RECEIVE_MESSAGE" );

                READ_MODIFY( MEM_ADJUST( lock_word ), &already_locked );

           STEP( 32 )
                if ( already_locked == TRUE )
                    GO_NEXT_TO( 31 )   /* Couldn't get lock - try again */
                SLEEP( 10 );

           STEP( 33 )
                MEM_READ( MEM_ADJUST(proc_info[our_index].mailbox), 
                                                  &data_being_passed );

           STEP( 34 )
                printf( "\t\t\tReceiver %d from Sender %d got data %d\n",
                                Z502_REG_4, message_sender_pid, 
                                data_being_passed );
                MEM_READ( MEM_ADJUST(proc_info[our_index].writer_of_mailbox), 
                                                  &memory_info );

           STEP( 35 )
                if ( memory_info != message_sender_pid )
                    {
                    printf( "ERROR: ERROR: The sender PID, given by the \n");
                    printf( "RECIEVE_MESSAGE and the mailbox, don't match\n" );
                }
                data_being_passed++;
                GO_NEXT_TO( 12 );
                break;


           STEP( 40 )
                if ( our_index > 0 )
                    GO_NEXT_TO( 60 );
                break;

           /* The first sharer prints out the entire shared area    */

           STEP( 41 )
                SLEEP( 5000 );           /* Wait for msgs to finish  */

           STEP( 42 )
                MEM_READ( MEM_ADJUST( number_2gx_procs ), &memory_info );

           STEP( 43 )
                printf( "number_2gx_processes = %d\n", memory_info );
                Z502_REG_7 = 0;
                break;

           STEP( 44 )
                MEM_READ( MEM_ADJUST( proc_info[ Z502_REG_7].pid ), 
                                                        &Z502_REG_1 );

           STEP( 45 )
                MEM_READ( MEM_ADJUST( proc_info[ Z502_REG_7].mailbox ), 
                                                        &Z502_REG_2 );

           STEP( 46 )
                MEM_READ( MEM_ADJUST( proc_info[Z502_REG_7].writer_of_mailbox),
                                                        &Z502_REG_3 );

           STEP( 47 )
                printf( "Mailbox info for index %d:\n", Z502_REG_7 );
                printf( "\t\t\t%d   %d   %d\n", 
                             Z502_REG_1, Z502_REG_2, Z502_REG_3 );
                Z502_REG_7++;
                if ( Z502_REG_7 >= memory_info )
                    {
                    GO_NEXT_TO( 44 );
                }
                else
                    {
                    GO_NEXT_TO( 60 );
                }
                break;

           STEP( 60 )
                TERMINATE_PROCESS( -1, &Z502_REG_9 );

        }                                       /* End of SELECT    */
    }                                           /* End of while     */
}                                               /* End of test2gx   */

/**************************************************************************

      get_pink_random_number   is a homegrown deterministic random
      number generator.  It produces "pink" numbers rather than "white"
      numbers.  This is useful in picking page locations so that pages
      get reused and makes a LRU algorithm meaningful.

**************************************************************************/

#define                 SKEWING_FACTOR          0.95
void    get_pink_random_number( long *random_number, long range )
    {
    long         temp;

    temp = rand();
    if ( temp < 0 ) 
        temp = -temp;
    temp = temp % range;
    temp = (long)pow((double)temp, (double)SKEWING_FACTOR);
    *random_number = range - 1 - temp;
}                               /* End get_pink_random_number */
