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