The Relogix translation

Note: This is a translation exactly as produced by Relogix, with no human intervention to clean up the code after translation. Guided by the user, Relogix has automatically chosen the types of variables, detected the use of structures and pointers, created proper C function calls, and has placed the assembler comments in the translated source allowing for the re-arrangement of the code flow.

Note that MicroAPL currently offers Relogix/MF as a porting service. The output of the translator as shown here would be cleaned up by our engineers before delivery to the customer. Typical post-translation cleanup work includes removing EBCDIC and endian dependencies from the code, improving the choice of variable names and slight changes to improve code readability.




/************************************************************************
*                                                                       *
*       "zoofood.c" - Translated from file "zoofood.asm"                *
*                                                                       *
*-----------------------------------------------------------------------*
*                                                                       *
*       Relogix Code Translator Copyright (c) MicroAPL 1990-2018        *
*                   All Rights Reserved Worldwide                       *
*                                                                       *
*************************************************************************
*/

/*
** Downloaded from:
** http://www.psc-consulting.ca/fenske/cpmis04s.txt
**
**
**
** Program Name:  SHOW:ZOOFOOD
**
** Program Title:  Feeding Animals in a Zoo
**
** Author:  Keith Fenske, Department of Mathematics, University
**        of Alberta.
**
** Date:  16 July 1977.  Copyright (c) 1977 by Keith Fenske.  All
**        rights reserved.
**
** Source Language:  IBM 370 Assembler
**
** Routine Type:  Single use, meaning that Zoofood modifies its data
**        regions in such a way that a new copy of the program
**        must be loaded before it will run properly.
**
** Purpose:  To play a not-event game, in which the user wins if
**        a certain event does not occur.
**
** Description:  Being the zookeeper for a small collection of
**        animals, the user is required to feed them periodically.
**        Each of the seven animals may be fed any six of seven
**        foods.  The seventh means disaster as the animal will
**        attack.  The animals are randomly associated with the
**        touchy foods, leaving the user the choice of how
**        he is going to allocate the food types.
**
**        Animal names are:  bear, child, elephant, giraffe, monkey,
**              rhinoceros and seal.
**
**        Foods are:  banana, chocolate, fish, grass, leaf,
**              marshmallow and peanut.
**
** Restrictions:  Nothing special.
**
** Size:  About 2112 bytes.
**
** Timing:  0.01 to 0.1 seconds (Amdahl 470 V/6).
**
** Usage:  $RUN SHOW:ZOOFOOD
**
** External References:
**        MTS Resident System
**              scards, setpfx, sprint
**
**        SHOW:LIB Subroutines
**              cmdscan, copy, random
**
** Assembler Macro References:
**        *SYSMAC Macros
**              call, enter, exit, litaddr, requ, scards, sprint
**
** Logical I/O Units Referenced:
**        SCARDS - input from the user.
**        SPRINT - messages and prompting.
**
** Input Lines:  All input from the user is via direct prompts,
**        asking for a YES or NO response, or for a food name.
**        Food may be specified by any proper abbreviation.
**
** Parameters:  None.
**
** Error Messages:  Self-explanatory.
**
** Chances of Winning:  Zoofood is essentially a matching game.
**        There are "n" objects to be tagged by "n" tags.  On the
**        first try, the user has a one in "n" chance of tagging
**        the wrong object.  Thus, he has a (n-1)/n chance of
**        getting to the next round.  The interesting point is this:
**        if the user tags a safe object, the corresponding
**        dangerous tag is now safe also.  This released tag may
*
**        of getting by.  Repeat this process until slightly over
**        half the objects have been tagged; the rest are "safe".
**        Example:
**
**              p(7) = 6/7 * 5/6 * 4/5 * 3/4
**
**        p(n) = 1/2, when "n" is even.  p(n) = (n-1)/(2*n), when
**        "n" is odd.  p(odd) has 1/2 as a limit.  As a table:
**
**              objects         win probability
**
**                  3                 1/3
**                  5                 2/5
**                  7                 3/7
**                  9                 4/9
**                 11                 5/11
**
** References:  SHOW:LIB.W documents the subroutine library.
**        SHOW:ZOOFOOD.W has this program's writeup.
**
*/


/* User-specified #include directives */
#include "rlx370.h"
#include "zoodemo.h"

#define OBJECTS              7                   /* prime number of animals/foods */
#define FEDMSG1L             (sizeof(fedmsg1))
#define FEDMSG2L             (sizeof(fedmsg2))
#define FEDMSG3L             (sizeof(fedmsg3))
#define FEDMSG4L             (sizeof(fedmsg4))
#define FEDMSG5L             (sizeof(fedmsg5))



/* Private file-scope variables */

/** Program Data Regions
  *
  * Equates that control the allocation of data regions.
  *
  *
  * Double-word regions.  The ANIMALS and FOOD tables have 16-byte
  * entries, of which the first halfword is the true length of the
  * name.
  *
  clean alignment */
static Name animals [] = 
{   {   4,  { "BEAR          " }  }, 
    {   5,  { "CHILD         " }  }, 
    {   8,  { "ELEPHANT      " }  }, 
    {   7,  { "GIRAFFE       " }  }, 
    {   6,  { "MONKEY        " }  }, 
    {   10,  { "RHINOCEROS    " }  }, 
    {   4,  { "SEAL          " }  }
};
static char copyunit [8] = "SPRINT  ";           /* "copy" output unit name */
static Name food [] = 
{   {   6,  { "BANANA        " }  }, 
    {   9,  { "CHOCOLATE     " }  }, 
    {   4,  { "FISH          " }  }, 
    {   5,  { "GRASS         " }  }, 
    {   4,  { "LEAF          " }  }, 
    {   11,  { "MARSHMALLOW   " }  }, 
    {   6,  { "PEANUT        " }  }
};
static char input [] = { "                                                                                                                                                                                                                                                                " };
                                                 /* terminal input buffer */
static char output [120] = "                                                                                                                        ";
                                                 /* terminal output buffer */

/** Fullword-aligned data regions.
  */
static unsigned long choicem = OBJECTS - 1;      /* possible shuffle multipliers */
static unsigned long choices = OBJECTS;          /* choices of animal/food types */
static char copyflag [] = 
{
  0x00, 0x00, 0x00, 0x06
};                                                /* file to unit copy with ...
                                                    attention interrupts enabled */
static unsigned long one = 1;

/** Halfword-aligned data regions.
  */
static short fed [] = 
{
  -1, -1, -1, -1, -1, -1, -1
};                                                /* food given to this animal,
                                                    -1 for not fed */
static unsigned short inlen = 0;                 /* length of input text */
static unsigned short outlen = 0;                /* length of output text */
static short pairings [7];                       /* dangerous food for animal */
static short where [] = 
{
  -1, -1, -1, -1, -1, -1, -1
};                                                /* which animal got this food,
                                                    -1 for not yet used */

/** Byte-aligned data regions.
  */
static char copyfile [15] = "SHOW:ZOOFOOD.W ";   /* program write-up file */
static char pfxin = '?';                         /* terminal input prefix */
static char pfxout = ' ';                        /* terminal output prefix */

/** Messages.  Each text string has a length equate making it easy
  * to move around.
  */
static char fedmsg1 [32] = "0What are you going to feed the ";
static char fedmsg2 = '?';
static char fedmsg3 [29] = "-You have been attacked by a ";
static char fedmsg4 [10] = "-wielding ";
static char fedmsg5 [11] = " Pick from ";


/*
*************************************************************************
* zoofood                                                               *
*************************************************************************
*
*  Save the caller's registers, prepare a new save area, and
*  establish R12 as our base register.
*
*
*  Initialize the terminal I/O prefix.  Then introduce ourselves
*  on the logical I/O unit SPRINT.
*
*
* Parameters:
*
*
* Result:
*
*    int                                            [Originally in r15]
*/
int zoofood (void)
{
    unsigned int base;                           /* [Originally in r9] */
    Boolean break_to_l12;
    Name *current_animal;                        /* [Originally in r2] */
    short food_chosen;                           /* [Originally in r3] */
    Name *food_item;                             /* [Originally in r2] */
    long i;                                      /* [Originally in r7] */
    unsigned int idx;                            /* [Originally in r8] */
    long length_keyword;                         /* [Originally in r1] */
    int length_remaining;                        /* [Originally in r0] */
    Boolean match;
    unsigned int num_animals;                    /* [Originally in r9] */
    char *p;                                     /* [Originally in r1] */
    unsigned long pseudo_random;                 /* [Originally in r8] */
    char *q;                                     /* [Originally in r2] */

    /** Save the caller's registers, prepare a new save area, and
      * establish R12 as our base register.
      *
      *
      * Initialize the terminal I/O prefix.  Then introduce ourselves
      * on the logical I/O unit SPRINT.
      */
    setpfx (&pfxout, &one);                      /* begin a main program
                                                    set a blank terminal prefix */
    sprint ("1Zoofood, a game for future zookeepers", NULL);

    /** Prompt the user as to whether he requires a copy of this
      * program's writeup.
      */
    sprint ("0Have you run this zoo before?", NULL);

    /** Read the user's response from the logical I/O unit SCARDS.
      * Modifiers are set for uppercase and no implicit concatenation
      * ($CONTINUE WITH lines).  An end-of-file will cause program
      * termination.
      */
    setpfx (&pfxin, &one);                       /* question mark input prefix */

    /* read away */
    if (scards (input, &inlen) == 0) {
        /** stop
          *
          * Return back to MTS, normally terminating the program.
          */
        return 0;                                /* restores registers, etc. */
    }

    setpfx (&pfxout, &one);                      /* blank the I/O prefix */

    /** Using the SHOW:LIB subroutine CMDSCAN, look for any form of a
      * YES reply.  If one abbreviation is found, the command scanning
      * table will cause execution to advance to SHUFFLE.
      *
      input text length
      input text address
      branch and link to scanner */
    if (cmdscan (input, inlen, "YES", 3) == NULL) {
        /** Control comes to this point if the input was null, empty or
          * not an abbreviation of YES. A call to the SHOW:LIB COPY subroutine
          * is used to print SHOW:ZOOFOOD.W as the program writeup.
          * COPY will process its own attention interrupts.
          */
        copy (copyflag, copyfile, copyunit);     /* info for you */
    }

    /** shuffle
      *
      * We now shuffle the assignments between the animals and their
      * restricted foods.  By design, there are seven of each.  Since
      * seven is a prime number, we may multiply the digits 0 through
      * 6 by any integer, and still have 0 through 6 upon division by
      * 7.  The pairings are decided by the vector PAIRINGS.  The
      * zero-origin n'th halfword gives the zero-origin dangerous food
      * index for the n'th animal.
      *
      * Initially, PAIRINGS is set to 0, 1, ..., 6 by the statement which
      * defines it.  To shuffle the assignments, we
      * (1) select a base from 0 to 6.
      * (2) select a multiplier from 1 to 6.
      * (3) starting at the first animal:
      *     (a) place the base in PAIRINGS.
      *     (b) base = base + multiplier.
      *     (c) base = remainder of base upon division by 7.
      *         i.e. base = base (mod 7)
      *
      * This process generates a new order of the elements 0 through 6.
      *
      *
      * Call the SHOW:LIB subroutine RANDOM to obtain a scaled pseudo-
      * random integer from 0 to 6.  This will be our base value.
      */
    base = random_number (&choices);             /* returns zero-origin result in r0
                                                    save for the future */

    /** Now a multiplier (or increment) must be selected from 1 through
      * 6.  Zero makes little sense here.
      */
    pseudo_random = random_number (&choicem) + 1;
                                                 /* gets 0 to 5
                                                    move the pseudo-random
                                                    add one for 1 to 6 */

    /** Store the base as the assignment for the first animal.  Then
      * continuously increment and modularize the base for each
      * following animal.
      */
    for (i = 0; i < OBJECTS; i++) {
        pairings [i] = base;                     /* save animal/food index */
        base = (base + pseudo_random) % choices; /* clear for division
                                                    next base (non-modular)
                                                    divide by 7 */
    }

    /** feed
      *
      * Now we begin to ask the user what he wants to feed each animal.
      * As we prompt him with a name, he must respond with a food type
      * that has not already been used.  The food can be named by any
      * abbreviation of its true name.  Should the user fail to
      * give us a valid name, a message is printed listing the
      * remaining foods.  Feeding a dangerous food to its paired
      * animal provokes an immediate attack, and user failure.
      * When all of the animals have been fed properly, the user wins.
      */
    idx = 0;                                     /* zero-origin index of first animal */
    num_animals = OBJECTS;                       /* total number of animals */

    while (True) {
        /** Ask the user what he is going to feed the current animal.  We do
          * this by selectively moving pieces of text into the OUTPUT
          * buffer.  R1 is used as a pointer to the next available byte
          * in the buffer.  Eventually, we subtract the address of OUTPUT
          * from R1 to obtain the length used for output.
          */
        memcpy (output, fedmsg1, FEDMSG1L);      /* "what are you ..." */
        current_animal = &animals [idx];         /* index of the animal
                                                    offset into animal names
                                                    address of animal entry */
        _rlx_move_mem (&output [FEDMSG1L], current_animal->name, 14);
                                                 /* move the full name */
        p = &output [(short) current_animal->length + FEDMSG1L];
                                                 /* next output location */
        p [0] = fedmsg2;                         /* a question mark or so */
        outlen = p + FEDMSG2L - output;          /* next output location
                                                    starting output address
                                                    length of text used
                                                    save for sprint call */
        sprint (output, &outlen);                /* pop the question */

        /** Get a response from the user.  We must set the terminal I/O prefix.
          * An end-of-file while reading causes us to branch to the chunk of
          * code which displays the final animal/food assignments.
          */
        setpfx (&pfxin, &one);                   /* question mark input prefix */

        /* read from terminal */
        if (scards (input, &inlen) == 0)
            break;
        setpfx (&pfxout, &one);                  /* blank for an output prefix */

        /** Look for the beginning of a keyword in our input buffer.  In
          * particular, we skip as many blanks as necessary in our
          * search for a non-blank character.  A null input, or all-blank
          * (empty) line causes us to print the available choices.
          */
        length_remaining = (short) inlen;        /* length of input text */

        /* anything there?
           quit on null input */
        if (length_remaining > 0) {
            p = input;                           /* address of input buffer */
            break_to_l12 = False;
            do {
                /* looking at a non-blank?
                   branch if so */
                if (p [0] != ' ') {
                    break_to_l12 = True;
                    break;
                }
                p++;                             /* next input location
                                                    loop again
                                                    quit if all blank */
            } while (--length_remaining != 0);

            if (break_to_l12) {
                /** Now find the end of the keyword, either by another blank or
                  * running out of characters.
                  */
                q = p;                           /* save the starting address */
                /* at a blank yet?
                   skip around if blank */
                while (p [0] != ' ') {
                    p++;                         /* next input location
                                                    keep trying */
                    if (--length_remaining == 0)
                        break;
                }

                /** Compute the keyword length as the difference of the final scanning
                  * address, and the start of the keyword.  In addition, the length
                  * will be truncated to 14 characters, if necessary.
                  */
                length_keyword = p - q;          /* length of input keyword */

                /* maximum acceptable length
                   how do the lengths compare?
                   branch if all right */
                if (length_keyword > 14)
                    length_keyword = 14;         /* swap registers to truncate */

                /** Using variable length comparisons, a check is now made to
                  * see if a matching keyword can be found in our FOOD table.  Note
                  * the use of zero-origin (IBM) lengths with the EX instruction.
                  */
                food_chosen = 0;                 /* index of first food */
                match = False;

                for (i = 0; i < OBJECTS; i++) {
                    /* IBM (zero origin) keyword length
                       do input and table match?
                       branch if so */
                    if (memcmp (q, food [i].name, length_keyword) == 0) {
                        match = True;
                        break;
                    }
                    food_chosen++;               /* next food index */
                }

                if (match) {
                    /** Check if the input-given food has already been handed out.  If it
                      * has not, save the animal index to tell us WHERE the food went,
                      * and note what we FED this animal.
                      *
                      who has been given this food?
                      negative for nobody?
                      quit if already taken */
                    if (where [food_chosen] < 0) {
                        where [food_chosen] = idx;
                                                 /* put current animal code */
                        fed [idx] = food_chosen; /* twice animal code
                                                    what animal was given */

                        /** Ensure that the user did not feed the animal his danger food.
                          *
                          do given and bad match?
                          go if safe */
                        if (food_chosen != pairings [idx]) {
                            /** Loop back to feed the next animal.  If all of them are fed
                              * successfully, the user wins the game.
                              */
                            idx++;               /* next animal index
                                                    feed all the animals */
                            if (--num_animals == 0) {
                                sprint ("-Jolly good, you fed the entire zoo without problems", NULL);
                                break;
                            }
                            continue;
                        }

                        /** Tell the user that he was attacked by a food-wielding animal,
                          * and dump the animal/food list.  Once again, we build up our
                          * output image by attaching message pieces to our OUTPUT buffer.
                          */
                        memcpy (output, fedmsg3, FEDMSG3L);
                                                 /* "You've been attacked" */
                        food_item = &food [food_chosen];
                                                 /* index of food type
                                                    offset into food names
                                                    address of food entry */
                        _rlx_move_mem (&output [FEDMSG3L], food_item->name, 14);
                                                 /* move the entire name */
                        p = &output [(short) food_item->length + FEDMSG3L];
                                                 /* next output location */
                        _rlx_move_mem (p, fedmsg4, FEDMSG4L);
                                                 /* "-wielding" */
                        p += FEDMSG4L;           /* next output location */
                        current_animal = &animals [idx];
                                                 /* index of animal type
                                                    offset into animal names
                                                    address of animal entry */
                        _rlx_move_mem (p, current_animal->name, 14);
                                                 /* move the entire name */
                        outlen = (unsigned short) &p [current_animal->length] - (long) output;
                                                 /* next output location
                                                    starting output address
                                                    length of text
                                                    how many output bytes */
                        sprint (output, &outlen);
                                                 /* write on sprint */
                        break;
                    }
                }
            }
        }

        /** Either due to invalid input, or a food already being in use,
          * we now tell the user what food types he can choose from.
          * Any entries that are non-negative in WHERE have already been
          * given away.  The resulting message is built in the OUTPUT
          * buffer.
          */
        memcpy (output, fedmsg5, FEDMSG5L);      /* "pick from" */
        p = &output [FEDMSG5L];                  /* next output location */

        for (i = 0; i < OBJECTS; i++) {
            /* is the where code negative?
               skip around if in use */
            if (where [i] < 0) {
                _rlx_move_mem (p, food [i].name, 14);
                                                 /* move full food name */
                p += (short) food [i].length + 1;
                                                 /* next output location
                                                    leave a blank byte */
            }
        }

        outlen = p - output;                     /* starting output address
                                                    output bytes used
                                                    length of text */
        sprint (output, &outlen);                /* write on sprint */
    }

    /** show
      *
      * Inform the user about the final state of the animal feedings.  The
      * prefix character is blanked, in case we came here from a SCARDS
      * end-of-file. Two header lines are printed, then the report starts.
      * For each animal, PAIRINGS is used to obtain the dangerous
      * food name, and FED gives the actual feeding.
      */
    setpfx (&pfxout, &one);                      /* blank terminal prefix */
    sprint ("0Following are the animals, what not to feed them,", NULL);
    sprint (" and what they were actually given by you:", NULL);

    /** Each animal loops to this point in telling its story.  We use
      * the usual OUTPUT buffer technique to prepare our text.
      */
    output [0] = ' ';                            /* blank the carriage control */

    for (i = 0; i < OBJECTS; i++) {
        _rlx_move_mem (&output [1], animals [i].name, 14);
                                                 /* move the animal's name */
        _rlx_move_mem (&output [15], food [pairings [i]].name, 14);
                                                 /* index for defensive food
                                                    offset into food names
                                                    address of food entry
                                                    move the food's name */

        /* index for actual food
           was animal fed?
           skip if still hungry */
        if (fed [i] < 0)
            p = &output [29];                    /* next output location */
        else {
            _rlx_move_mem (&output [29], food [fed [i]].name, 14);
                                                 /* offset into food names
                                                    address of food entry
                                                    move the food's name */
            p = &output [43];                    /* next output location */
        }

        /** Print the formed output, and loop back for the next animal.
          */
        outlen = p - output;                     /* initial output location
                                                    actual bytes used
                                                    save text length */
        sprint (output, &outlen);                /* write on sprint */
    }

    /** stop
      *
      * Return back to MTS, normally terminating the program.
      */
    return 0;                                    /* restores registers, etc. */
}