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