× 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 II...
  • From: "Phil Hall" <hallp@xxxxxxxx>
  • Date: Thu, 11 Nov 1999 13:06:22 -0600

Welcome to 'Introducing MI By Example II' !

Firstly, let me thank Leif for providing the example code. One of the
great things about Leif's code is that it's very compact and so there's
very little 'not needed' code to get in the way of describing what the
program does - makes my life easier !

Secondly, these 'Introducing MI By Example...' emails are really
intended to help those on the list who just want to know how MI works in
a general sense - these emails are no substitute for actually reading
the manuals and playing with your own code. For those on the list who
already know MI, just ignore me !

Thirdly, why am I doing these emails ? Well, I personally like to learn
by example code, I think it helps understand a language if you see it in
action.

Fourthly, my intention is not to be picky on the code - I'm just telling
you what it's doing.

OK, so what was Leif's last MI program posted ? It was the code to find
out from which library your program was executed. This, (as Leif pointed
out in his original note) is a very useful utility when you're a
software developer and you allow the customer to install the application
into any library, but you as a developer won't know the name of the
library until runtime, and you can't rely on the fact that the library
will be in the library list. Qualified calls are a real no-no, so you
need something like this utility to tell you the library your program
executed from.

OK, here's a copy of Leif's MI code;

DCL SPCPTR .PARM1 PARM;
DCL DD PARM1 CHAR(10) BAS(.PARM1);
    DCL DD PARM-LIB-NAME CHAR(10) DEF(PARM1) POS(1);
DCL OL PARMS(.PARM1) EXT PARM MIN(1);

DCL SPCPTR .PROGRAM INIT(PROGRAM);
DCL DD      PROGRAM CHAR(77) BDRY(16);
    DCL DD  PGM-BYTES-PRV      BIN(4) DEF(PROGRAM) POS( 1) INIT(77);
    DCL DD  PGM-BYTES-AVL      BIN(4) DEF(PROGRAM) POS( 5);
    DCL DD  PGM-PTR-TYPE      CHAR(1) DEF(PROGRAM) POS( 9);
    DCL DD  PGM-LIB-TYPE      CHAR(2) DEF(PROGRAM) POS(10);
    DCL DD  PGM-LIB-NAME     CHAR(30) DEF(PROGRAM) POS(12);
    DCL DD  PGM-PGM-TYPE      CHAR(2) DEF(PROGRAM) POS(42);
    DCL DD  PGM-PGM-NAME     CHAR(30) DEF(PROGRAM) POS(44);

DCL SPCPTR .THE-STACK INIT(THE-STACK);
DCL DD      THE-STACK CHAR(12816) BDRY(16);
    DCL DD  STK-BYTES-PRV      BIN(4) DEF(THE-STACK) POS( 1);
    DCL DD  STK-BYTES-AVL      BIN(4) DEF(THE-STACK) POS( 5);
    DCL DD  STK-NBR-OF-ENTRIES BIN(4) DEF(THE-STACK) POS( 9);
    DCL DD  STK-THREAD-COUNTER BIN(4) DEF(THE-STACK) POS(13);
    DCL DD  STK-ENTRY(100)  CHAR(128) DEF(THE-STACK) POS(17);

DCL DD THE-ENTRY CHAR(128) BDRY(16);
    DCL SYSPTR THE-ENTRY-PGM DEF(THE-ENTRY) POS(33);

DCL DD CALLING-PGM-NBR BIN(4);

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

ENTRY * (PARMS) EXT;
    ADDN       STK-BYTES-PRV, 12800, 16;
    MATINVS   .THE-STACK, *;
    SUBN       CALLING-PGM-NBR, STK-NBR-OF-ENTRIES, 1;
    CPYBWP     THE-ENTRY, STK-ENTRY(CALLING-PGM-NBR);
    MATPTR    .PROGRAM, THE-ENTRY-PGM; /* CALLER */
    CPYBLA     PARM-LIB-NAME, PGM-LIB-NAME;
    RTX        *;

PEND;

Before we break the code apart, look how compact this code is, there are
only 6, yes 6 !, lines of code in this program, if you ignore the
variable declaring and the entry and exit code.

Let's start with the first line of code;

    DCL SPCPTR .PARM1 PARM;

We've seen code like this before, in the MI program for getting the
serial number. We're declaring a variable called .PARM1, of type SPCPTR
(a space pointer). The final token in this line is 'PARM' this means
that our pointer .PARM1 will point to the incoming parameter(s). The MI
compiler will take care of setting the address of where the pointer will
point to, which is why you'll not see .PARM1 being assigned a value in
the code. As you use MI more and more, you'll see the compiler 'help
out' more often.

The next two lines of code;

    DCL DD PARM1 CHAR(10) BAS(.PARM1);
        DCL DD PARM-LIB-NAME CHAR(10) DEF(PARM1) POS(1);

Is declaring one of those 'scalar-data-object' things again. But as we
saw in the first 'Introducing MI...' these are very similar to RPG data
structures. In this case, the code is declaring a variable called PARM1,
type character, length 10, with it's addressability based on the
variable .PARM1. The final part, the addressability, is telling the
compiler to use the address of .PARM1 as the address of PARM1. Confused
? Well pointers are the difficult part of most languages that use them,
but in this case all we're doing is creating an overlay of the memory
that the pointer .PARM1 points to, so we can access the same data either
by using the pointer .PARM1 or using the character variable PARM1
because they both refer to the same data in the same memory location.

Following .PARM and PARM being declared, we see the line;

    DCL OL PARMS(.PARM1) EXT PARM MIN(1);

This is declaring a 'operand list', hence the 'OL'. The operand list is
to be called PARMS (anyone getting any ideas what this operand list will
be used for ?); is to be made up of the .PARM1 variable type; it's to be
externally visible; it's to be an operand list of type PARM and there is
to be a minimum of 1 entry in the list. OK, so you've probably guessed
by now that we're creating a way to have a parameter passed to this
program. So the MIN(1) is specifying there must be one parameter passed,
and the PARMS(.PARM1) is specifying that the parameter passed be of the
same variable type as .PARM1, i.e. a space pointer. But this line is
only describing the parameter list, later in the code we'll see how it's
used in the program entry point.

Onward we go. The next section of code is declaring more variables, that
are similar to RPG data structures - in fact, as an exercise you could
see if you can create the RPG equivalent data structures for these MI
variables. Also in this section we're declaring pointers to point to
these variables, nothing new from what we learned from Leif's first
program apart from the last line of the second structure;

        DCL DD  STK-ENTRY(100)  CHAR(128) DEF(THE-STACK) POS(17);

This line defines an array of 100 elements, that are 128 characters
long, mapped across THE-STACK starting with the first element at
position 17 of THE-STACK. Remember this fact for later.

Finally we hit the 'real' code, starting with;

    ENTRY * (PARMS) EXT;

Here an entry point is being defined. Remember from the last
"Introducing MI...' that all subroutines use this entry point syntax,
but also remember that we can safely assume that an unnamed entry point
defined with the 'EXT' directive is the actual program entry point. In
this code you can see that this entry point is also expecting
parameters, that are to match the parameters defined in the parameter
list 'PARMS'. We all now know how to pass parameters into MI programs -
great !

OK, here's the first of those 6 lines of code;

        ADDN       STK-BYTES-PRV, 12800, 16;

Well, this is a very simple MI instruction, ADDN, ADD Numeric, that has
the basic format (read the MI Functional Reference for the other
formats) of;

    ADDN <result> <operand_1> <operand_2>

Which, don't be to shocked, adds operand_2 to operand_1 and places the
result in result. Easy !

Next line is;

        MATINVS   .THE-STACK, *;

This is a call to the MATINVS MI instruction, which MATerialzes the
INVocation Stack, into the variable .THE-STACK. You can think of this
materialized invocation stack is a memory version of what you see on the
DSPJOB option 11 screen.

Following the MATINVS, we have;

        SUBN       CALLING-PGM-NBR, STK-NBR-OF-ENTRIES, 1;

Which is using the MI instruction SUBtract Number to subtract 1 from
STK-NBR-OF-ENTRIES, placing the result into CALLING-PM-NBR. Why ? Well
in the invocation stack, the program that called GETOWNLIB is one level
back in the stack. So this line of code is calculating the relative
invocation number of the caller of GETOWNLIB, relative to the position
of GETOWNLIB in the current stack.

The next line of code;

        CPYBWP     THE-ENTRY, STK-ENTRY(CALLING-PGM-NBR);

Uses this invocation stack offset number of the calling program stored
in CALLING-PGM-NBR to look up in the array of stack entries (remember
how the array was defined across the THE-STACK structure ?) and copies,
using the MI instruction CPYBWP (CoPY Bytes With Pointers), the entry
into the variable THE-ENTRY. It uses CPYBWP because the data returned
from MATINVS is a list of pointers to the programs in the call stack.
But the pointer in itself doesn't tell us very much, a 16 byte hex
number is not very user friendly ! So the next line of code;

        MATPTR    .PROGRAM, THE-ENTRY-PGM; /* CALLER */

Uses the MI instruction MATPTR (MATerialze PoinTeR) to materialize the
pointer into something more readable. The MATPTR places the 'readable'
information on the pointer in the variable .PROGAM. So now GETOWNLIB
'knows' from the subfields of .PROGRAM the name of what program called
it, and if you reread the structure definition it also now knows the
library of the calling program. Bingo ! We have the information we were
after. The next line;

        CPYBLA     PARM-LIB-NAME, PGM-LIB-NAME;

Uses the MI instruction CPYBLA (CoPY Bytes Left Adjust) to copy the
library name of the calling program into the parameter the calling
program passed.

Finally;

        RTX        *;

Returns control back to the calling program, which now has a variable
that contains the name of the library it was executed from.

Oh, yes, the compiler must be kept happy, so we see;

    PEND;

As the last line of source.


There we have it, six lines of MI code (very compact this MI stuff ! We
could even make this code be only five lines long - there's another
exercise for you) to determine from the call stack the name of the
library the calling program was executed from. And one more MI program
you can understand - that makes two more than you did last week !

--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.