× The internal search function is temporarily non-functional. The current search engine is no longer viable and we are researching alternatives.
As a stop gap measure, we are using Google's custom search engine service.
If you know of an easy to use, open source, search engine ... please contact support@midrange.com.


  • Subject: An introduction to MI by example III (part A)...
  • From: "Phil Hall" <hallp@xxxxxxxx>
  • Date: Mon, 22 Nov 1999 15:03:31 -0600

Here's another edition in the on going 'Introducing MI...'
series, to try and help the non-MI programmers (or the
beginners) on the list to understand MI 'code in action'

=============================================

You can request copies of all "Introducing MI..." postings
by clicking on the following link;

    hallp@ssax.com?SUBJECT=Introducing_MI_Bundle_Request

Or if your email client doesn't support the above link, by
sending an email to;

    hallp@ssax.com

with;

    Introducing MI Bundle Request

in the SUBJECT line.

==============================================

This time we'll take a look at Leif's sorting program. The reason I
chose this one is that it introduces how MI branch statements work.

Again, here's the complete source;

DCL SPCPTR .PARM1 PARM;
DCL DD PARM1 CHAR(5) BAS(.PARM1);
    DCL DD PARM-NBR-ELEMENTS PKD(7,0) DEF(PARM1) POS(1);
    DCL DD PARM-DIRECTION    CHAR(1)  DEF(PARM1) POS(5); /* A OR D */

DCL SPCPTR .PARM2 PARM;
DCL DD ELEMENT(1) CHAR(20) BAS(.PARM2);

DCL OL PARMS(.PARM1, .PARM2) EXT PARM MIN(2);

DCL DD SWAP-FLAG  CHAR(1);
DCL DD JUMP-SIZE   BIN(4);
DCL DD SWEEP-END   BIN(4);
DCL DD ITEM-NBR    BIN(4);
DCL DD COMP-NBR    BIN(4);

ENTRY * (PARMS) EXT;
    OVRPGATR   1, 2; /* DON'T CONSTRAIN ARRAY REFS */
    CPYNV      JUMP-SIZE, PARM-NBR-ELEMENTS;
SORT-JUMP:
    CMPNV(B)   JUMP-SIZE,  1 /HI(SORT-SWEEP);
    CMPBLA(B)  SWAP-FLAG, "S"/NEQ(RETURN);
SORT-SWEEP:
    MULT(S)    JUMP-SIZE, 10;
    ADDN(S)    JUMP-SIZE,  3;
    DIV(S)     JUMP-SIZE, 13;
    SUBN       SWEEP-END, PARM-NBR-ELEMENTS, JUMP-SIZE;
    CPYBLA     SWAP-FLAG, " ";
    CPYNV(B)   ITEM-NBR, 0/ZER(SORT-COMPARE);

SORT-SWAP:
    EXCHBY     ELEMENT(ITEM-NBR), ELEMENT(COMP-NBR);
    CPYBLA     SWAP-FLAG, "S";
SORT-COMPARE:
    ADDN(S)    ITEM-NBR, 1;
    CMPNV(B)   ITEM-NBR, SWEEP-END/HI(SORT-JUMP);
    ADDN       COMP-NBR, ITEM-NBR, JUMP-SIZE;
    CMPBLA(B)  PARM-DIRECTION, "D"/EQ(DESCENDING-SORT-COMPARE);
ASCENDING-SORT-COMPARE:
    CMPBLA(B)  ELEMENT(ITEM-NBR), ELEMENT(COMP-NBR)
               /HI(SORT-SWAP),    NHI(SORT-COMPARE);
DESCENDING-SORT-COMPARE:
    CMPBLA(B)  ELEMENT(ITEM-NBR), ELEMENT(COMP-NBR)
               /LO(SORT-SWAP),    NLO(SORT-COMPARE);

RETURN:
    RTX        *;

PEND;


OK, there are a number of new concepts introduced in this code,
hopefully as we break the code down and follow it's execution, we see
what these are and how they're used.

To start with, a new data type is being used. We've met SPCPTR, CHAR and
BIN before, the new type here is PKD.

    DCL SPCPTR .PARM1 PARM;
    DCL DD PARM1 CHAR(5) BAS(.PARM1);
        DCL DD PARM-NBR-ELEMENTS PKD(7,0) DEF(PARM1) POS(1);
        DCL DD PARM-DIRECTION    CHAR(1)  DEF(PARM1) POS(5); /* A OR D
*/

So what do we have in the above code ? Well a variable called 'PARM1' is
being declared, with 'PARM-NBR-ELEMENTS' of type PKD taking the first 4
bytes, and 'PARM-DIRECTION' taking the last byte, mapped across it. Plus
'PARM1' is being based on the space pointer '.PARM1' and the pointer
'.PARM1' is being defined as PARM, so the compiler/run-time will point
'.PARM1' to one of the incoming parameters.

    DCL SPCPTR .PARM2 PARM;
    DCL DD ELEMENT(1) CHAR(20) BAS(.PARM2);

In the above code, a variable called ELEMENT is being declared, as an
array with one element, with each element (although there is only one
element defined, you'll see later why I say 'each element') being
defined as 20 characters long. We also see that this variable is  based
on the space pointer '.PARM2' and the pointer '.PARM1' is being defined
as PARM as was '.PARM1' so the compiler/run-time will point '.PARM2' to
one of the incoming parameters.

How does the compiler/run-time determine which pointer to point to which
incoming variable ? Well, we see how in the next line of code, which
details the order of the mapping;

    DCL OL PARMS(.PARM1, .PARM2) EXT PARM MIN(2);

Now, you may be saying "That's obvious, why tell us ?", well, sometimes
you may see code that doesn't use variable names that are as self
documenting as in Leif's code !

Finally we have a few more variables being declared;

    DCL DD SWAP-FLAG  CHAR(1);
    DCL DD JUMP-SIZE   BIN(4);
    DCL DD SWEEP-END   BIN(4);
    DCL DD ITEM-NBR    BIN(4);
    DCL DD COMP-NBR    BIN(4);

Nothing new to cover in the above code.

Next is the program entry point;

    ENTRY * (PARMS) EXT;

And then the MI instruction OVRPGATR (OVeRride ProGram ATtRibutes);

        OVRPGATR   1, 2; /* DON'T CONSTRAIN ARRAY REFS */

This allows the program to reference subscripts of arrays that are out
side of the range of the array as specified when the array was defined.
More on this later.

Next we see;

        CPYNV      JUMP-SIZE, PARM-NBR-ELEMENTS;

This is the MI instruction CoPY Numeric Value in it's basic form. And it
does what it's named - copies a numeric value from one variable into
another. In this case, the program is copying one of the incoming
parameters 'PARM-NBR-ELEMENTS' which represents the number of elements
in the array we're about to sort in to a local variable 'JUMP-SIZE'.

    SORT-JUMP:
        CMPNV(B)   JUMP-SIZE,  1 /HI(SORT-SWEEP);
        CMPBLA(B)  SWAP-FLAG, "S"/NEQ(RETURN);

In the above first line, we see something new, the 'SORT-JUMP:'. These
are what, in MI speak, are called 'branch targets'. Branch targets are
used in MI by many of the MI instructions as a way of determining where
to go next in the code based on results of the MI instruction. Anyone
who says "Hey, these are like GOTO labels in BASIC" will be asked to
leave ;-)

Then we see the MI instruction CMPNV(B). This uses a new concept, well
more of a new notation. A large number of the MI instructions (but
mostly the instructions that deal with numbers plus the compare
instructions) have 4 different forms.

    1. The standard form denoted by <instruction>
    2. A short form denoted by <instruction>(S)
    3. A branching form denoted by <instruction>(B)
    4. A rounding form denoted by <instruction>(R)
    5. A indicator form denoted by <instruction>(I)

The short form:
This allows you reduce the number of operands in the standard form by
one. Instead of coding the standard form as;

ADDN   <operand_X> <operand_X> <operand_Y>

Which means add <operand_Y> to <operand_X> and place the result in
<operand_X>, you can code the short form as;

ADDN(S) <operand_X> <operand_Y>

Both mean exactly the same thing.

The branching form:
When you see an MI instruction with the (B) notation, then the
instruction is going to branch to a target (either a defined branch
target, or a relative offset) if the test condition is met. If the
condition is not met, then the program continues with the next line of
code. The notation is;

<instruction>(B) <operand_1> <operand_2> /<test>(<branch_target>)

The rule is <operand_1> is compared with <operand_2>, if <test> is met,
then branch to <branch_target>.

For example, in the above code;

CMPNV(B)   JUMP-SIZE,  1 /HI(SORT-SWEEP);

The test is if 'JUMP-SIZE' is HIgher (i.e. larger) than 1. If this test
condition is true, then the program jumps to the branch target
'SORT-SWEEP'. If the test in not true, then execution continues with the
next instruction. There are a number of conditions you can test for such
as;

    /HI, /EQ, /LO, /ZER, etc.

All of the possible test conditions, the rounding form and indicator
form are covered in the MI Functional Reference. If code comes up on
this list that uses them, I'll cover them then.

Let's get back to the code. Firstly, a test is made to see if JUMP-SIZE
is greater than 1. If this is false, then the next instruction
(CMPBLA(B)  SWAP-FLAG, "S"/NEQ(RETURN)) compares 'SWAP-FLAG' with "S".
If 'SWAP-FLAG' is not equal to "S", then the program jumps to the branch
target label 'RETURN'. In most cases when you invoke this program,
'JUMP-SIZE' will always be greater than 1 (unless you're trying to sort
an array with one or zero elements !) so execution will normally jump to
'SORT-SWEEP'. Let's take a look at that code;

    SORT-SWEEP:
        MULT(S)    JUMP-SIZE, 10;
        ADDN(S)    JUMP-SIZE,  3;
        DIV(S)     JUMP-SIZE, 13;
        SUBN       SWEEP-END, PARM-NBR-ELEMENTS, JUMP-SIZE;
        CPYBLA     SWAP-FLAG, " ";
        CPYNV(B)   ITEM-NBR, 0/ZER(SORT-COMPARE);

OK, nothing too difficult here. Firstly 'JUMP-SIZE' is MULTiplied by 10,
then 3 is added to it, and finally 'JUMP-SIZE' is divided by 13, with
the result stored in 'JUMP-SIZE'. Next we see that  'JUMP-SIZE' is
subtracted from 'PARM-NBR-ELEMENTS' and the result stored in
'SWEEP-END'. Then the value of 'SWAP-FLAG' is set to " ", by using the
CPYBLA (CoPY Bytes Left Aligned) MI instruction. In the last line of
this section, we start to see some of the 'power' of MI programming. We
have a CPYNV (CoPY Numeric Value) instruction, but the branching form of
it. This means that the instruction will perform the copy, then test the
result and branch if needed. This ability of MI to allow you to have
instructions that perform their function and then also test for a
branch, really reduce the lines of code in MI programs - and I feel
increases readability of the code. So in the program, the code is
copying 0 into 'ITEM-NBR' and then straight away testing if 'ITEM-NBR'
is equal to zero, and if true, jumping to 'SORT-COMPARE'. The actual
usage in this code will always jump to 'SORT-COMPARE' and the reason is
so that the program can jump over some code, that will be jumped back to
later.

Although first time through the program, the program will always jump to
'SORT-COMPARE', we'll continue following the code line by line. Next
code we see is;

    SORT-SWAP:
        EXCHBY     ELEMENT(ITEM-NBR), ELEMENT(COMP-NBR);
        CPYBLA     SWAP-FLAG, "S";

Another branch target 'SORT-SWAP' is defined. Then the MI instruction
EXCHBY (EXCHange BYtes). This instruction swaps byte for byte the data
in <operand_2> with the data in <operand_1>. It's a useful instruction,
as you don't need to worry about the length of the operands because the
compiler takes care of this, as it's determined at compile time. In the
code, it's used to swap one element in the array with another. The last
line of this section copies "S" in to 'SWAP-FLAG'.

Now we get to the next section of code;

    SORT-COMPARE:
        ADDN(S)    ITEM-NBR, 1;
        CMPNV(B)   ITEM-NBR, SWEEP-END/HI(SORT-JUMP);
        ADDN       COMP-NBR, ITEM-NBR, JUMP-SIZE;
        CMPBLA(B)  PARM-DIRECTION, "D"/EQ(DESCENDING-SORT-COMPARE);

Another branch target 'SORT-COMPARE' is defined. Following this,
'ITEM-NBR' is incremented by 1. 'ITEM-NBR' is the iterator through the
passed array of items. Then 'ITEM_NBR' is compared with the value of
'SWEEP-END'. If 'ITEM-NBR' is higher in value than 'SWEEP-END' then
branch to 'SORT-JUMP' else continue. Next we're adding 'JUMP-SIZE' to
'ITEM-NBR' and placing the result in to 'COMP-NBR'. Finally, a check is
made to see which way the array is to be sorted; ascending or
descending. A branch is made on this test.

Lastly, the code has the following lines;

    ASCENDING-SORT-COMPARE:
        CMPBLA(B)  ELEMENT(ITEM-NBR), ELEMENT(COMP-NBR)
                   /HI(SORT-SWAP),    NHI(SORT-COMPARE);

    DESCENDING-SORT-COMPARE:
        CMPBLA(B)  ELEMENT(ITEM-NBR), ELEMENT(COMP-NBR)
                   /LO(SORT-SWAP),    NLO(SORT-COMPARE);

Here, the current element 'ITEM-NBR' is compared with the selected
element 'COMP-NBR'. Depending upon the sort sequence they're swapped (a
branch to 'SORT-SWAP') or if they are already in the sort order, a
branch to 'SORT-COMPARE' is made.

Finishing with the program returning control to the caller, and the end
of program marker for the compiler;

    RETURN:
        RTX        *;

    PEND;

An explanation of the sort routine itself, can be found at;

http://www.etk.com/papers/sorting/sorting.htm#combsort

--phil

+---
| This is the MI Programmers Mailing List!
| To submit a new message, send your mail to MI400@midrange.com.
| To subscribe to this list send email to MI400-SUB@midrange.com.
| To unsubscribe from this list send email to MI400-UNSUB@midrange.com.
| Questions should be directed to the list owner/operator: dr2@cssas400.com
+---


As an Amazon Associate we earn from qualifying purchases.

This thread ...


Follow On AppleNews
Return to Archive home page | Return to MIDRANGE.COM home page

This mailing list archive is Copyright 1997-2024 by midrange.com and David Gibbs as a compilation work. Use of the archive is restricted to research of a business or technical nature. Any other uses are prohibited. Full details are available on our policy page. If you have questions about this, please contact [javascript protected email address].

Operating expenses for this site are earned using the Amazon Associate program and Google Adsense.