M.S.S. Inc LOGO HLASM Assembler Tip'o the Month

McColley Systems Software Inc.
If it's assembler, WE CAN HELP!
" Elegant Solutions for Your Processing Needs "


Home   Shared Spool Mods   ESSM!   Tip of the Month   Articles   Free Source   LINKS   ASSEMBLER SURVIVAL GUIDE   ABOUT US  


Skip Directly to the Index.

Quick Quote -
"There are 10 types of people in the world, those that understand binary, and those that do not."

I am going to try to do my best to update at least one ASSEMBLER PROGRAMMING TIP or TECHNIQUE every month. You can judge if I am keeping my word or not after a few more months. I might suggest that instead of you having to bother coming back to this page, fill in the information on the form (form yet to be devised - I told you it was a new web site!) and I will fire off an e-mail to everyone on my mailing list every month when I have a new tip or technique to add. I promise never to rent or sell your names or e-mail addresses to anyone else - I will of course shamelessly promote my own business services and products - sounds like a fair trade to ME!

Go ahead, add ME to your monthly Tips Mailing List

Stephen G. McColley            
Owner McColley Systems Software    
Feel free to Contact Us.        

Stephen.McColley@MVSProgrammer.com


OK let's start off with some basic tips and techniques that I believe everyone should observe;

click a topic to go directly there, or scroll through them all.


General practices concerning Comments   May 2011

DSECTs - they are essential!   May 2011

Subroutine Linkage Techniques   June 2011

Exotic Instructions   May 2011

What is NOT CLEVER!   May 2011

Register Zero Shortcuts   May 2011

Break Your Program Down Into Subroutines   May 2011

Now for some additional tips, techniques, and specific tricks used from assembler code...

Automatically Centering Heading Text on a Print Line   July 2011

Exchanging Two Variables Without a Temporary Third Location   May 2011

Using a return code with a Branch Table   July 2011

Formatting register content for printing (hex representation)   June 2011

Generating a TR or TRT starter TABLE   June 2011

A Simple Technique to Improve Your Searches   August 2011

Locating JOBNAME / STEPNAME information (coming soon)

Dynamically expanding in storage tables (coming soon)

Only use STANDARD Documented Interfaces (coming soon)

Always test return codes from MACRO calls (coming soon)

Singly linked sorted lists (coming soon)

Doubly linked sorted lists (coming soon)

The 5 Major MVS Control Blocks (coming soon)

Estae routines (coming soon)

Send me your ideas - I'll give you credit and add them if I can (still waiting).


Back to TOP of Page.










General Practices Concerning Comments

Use em!!
If ever I hear someone in my employ state that assembler code is 'Self-Documenting' meaning that Assembler code needs no comments, as I have heard elsewhere, he or she would be in danger of being terminated on the spot!
I hope that was strong enough to get the point across. All assembler code should be documented, but then again there is sensible documentation, and mindless restating of what each instruction or macro does. There is no since in performing the later activity. Simply commenting an instruction like I have done in the next line, does no one any good.

DATA LABEL OP OPERANDS COMMENTS
    LA R8,30(R3,R7) Add the address in R3 to R7 + 30 = R8

You see, that really didn't add any new information, but if we comment it like this -

DATA LABEL OP OPERANDS COMMENTS
    LA R8,30(R3,R7) add table disp(r3) + employee index1(r7) +30 to get employee phone#

Now you see how that might be more helpful, at least if you had the full context, what the indexes meant and so on.

As a general rule, I comment every line of code. I comment every macro written, and if any of the passed parms are not clear or are at all questionable, I make a comment about the individual parms passed to the macro. I comment, in a separate flower box, the entire program, at the top of the code, including general register usage, basic mainline logic, file inputs and outputs, amode and rmode requirements and reentrancy intended for the code. I comment each subroutine at the top of the subroutine itself - detailing what the routine does, what it expecst as input, what outputs are generated. I make comments about what the routine does and how it does it. Finally I comment the linkage used to get to and from the sub-routine. If in doubt, add a comment. You never know who the next person will be that tries to read your code. It might be the new guy just starting out who has a hard time reading your code, it may be an old-timer that just expects things to be a certain way - that you have not observed, or it may even be YOU! If your memory is like mine then you really better write some comments. Comments cost you nothing - other than the storage associated with each one, so spend liberally, at least when it comes to comments.
Return to Index.


DSECTs they are essential!

There are a lot of programmers out there that simply do not like to use DSECTs. I know because I have read a lot of code. If you want to improve your coding skills the single best piece of advice I can give anyone is to read other peoples code - read a lot. Some of it you won't like, some of it you will decide is not good code, but other times you will come across a good technique, or trick, or even just a commenting style that you find really helpful. At least you will be exposed to new coding ideas. Anyway, back to topic, DSECTs, I know many of you do not like them, or perhaps you simply do not understand them well. DSECTs really are very simple, they simply attach a displacement value a data type and data length value to a label. When the data type, length, and displacement from a dsect are combined with the value in a base register that you specify with a simple USING statement, the assembler has everything it needs to generate an instruction using all of those factors based on your simple statement of the name. That's all they do but for some people that seems to be a difficult thing to understand. In any case, it is essential that you learn to use them. You can write code that uses information from system control blocks, let say for instance you need information from the CVT. It is possible to load the address of the CVT into a register, then go to the z/OS DATA AREAs manuals and locate the displacement from the top of the CVT to whatever you need to reference and then code hard displacements in your instructions to address the data. It works! It works fine. You will see other people do it all the time. You will see that same code fail to run after an upgrade to the OS is taken as well - that also happens all the time! Usually the reason is that the displacements that you looked up in the DATA AREAs manuals have changed. The hard fix is to find every place in your code that you referenced a system area and update the displacement, then hope it doesn't change again for a long time.

The easy way to deal with this common problem is to have used a DSECT in the first place and then to routinely re-assemble your code with every system upgrade. The re-assembly will fix 98% of all compatibility issues - IF - you have used DSECTs properly to begin with. Those that it does not fix, automatically you should find when the assembly fails. Better to find a problem while you are still working on a new system install and before you get a call at 4am telling you that your program if broken (again).

If you really are uncomfortable with DSECTs, pick up a good text about how to use DSECTs and read it thoroughly. They really are not that complicated. I use system provided dsects ANY time I reference system areas in my own code, and I frequently find it helpful (for the same reasons I use system DSECTs) to create and use my own dsects for data areas in my own programs. This is especially true of areas that I design to be shared between several different pieces of code.

Make it part of your personal programming code - use DSECTS! They were provided to you for a reason. Use them now and save yourself some grief later.


Return to Index.


Sub-routine Linkage

What a topic! This could go on, and on, for several thousand words or more. What I want to do here though is examine the simple common linkage used for most sub-routines within a single csect. The most common linkage instructions used are BAS, BAL, BALR, BASR, and BAKR. I prefer to keep things very simple so I generally restrict myself to the BAS instruction (BAL can get you into trouble if you bounce between 24 and 31 bit addressing). BAKR is an excellent instruction for linkage, but it does a lot of work, it saves literally everything you can think of, and so it has a good deal of overhead - all instructions are not the same (see the article "the MYTH of MIPs" in the ARTICLES section of this web site). It used to be that I always reserved a single register when I started writing a new csect, simply to use for sub-routine linkage with the BAS or BASR instruction - say r9 almost any one is as good as any other, as long as you don't use R0, R1, R2, R14, or R15 since using any of those will restrict your subroutines from using many macros. In the case of R2, just don't even try to use a TR instruction or you might wipe it out. Ok, so you chose a non-base, non-standard-linkage register to save the return address from a sub-routine in, you are all ready to go, and you really are - but what if you want to call a sub-routine from a sub-routine? Opps, now you need another register, well that's not so bad, a second register reserved for linkage. You can just remember when it's safe to use it and when it's not... are you kidding - now you really have first and second level linkages to deal with? I have found it much cleaner - a bit more overhead - but much clearer to simply use a stack area.

It is a simple technique that I have documented at the following LINK .


Return to Index.

Exotic Instructions

There is nothing explicitly wrong with using an 'exotic' instruction. But, I have seen code where the author was simply trying to find a way to fit some of the 'newer' more exotic instructions into the code, and that is just bad coding. Remember you should alway strive to write clear, easily understood code. There is a place for the 'EX' (execute) instruction, particularly when working with variable length data, but to simply use it for it's own sake is ill advised. Please understand I am not suggesting you never learn or use any new instructions - far from it - just use them for the purposes they were clearly intended for, and if one or two older simpler instructions will accomplish the same thing as one exotic instruction, carefully consider using the simpler instructions instead. By the way some of the newest instructions are favorites of mine - AHI (add halfword immediate) is great for direct manipulation of address or index values( because of the halfword instead of 1 byte immediate value), and everyone needs to be able to use the somewhat older jump and relative addressing instructions if you plan on dealing with JES2 very much.
Return to Index.


What is NEVER Clever!

I recently went through an exercise where I had to go behind someone else and document their code, as well as figure out what it did - or was supposed to do in some cases. To understand what I found you have to first imagine a very paranoid and insecure individual who happens to be able to write assembler code - now he is paranoid, so he doesn't really want anyone to know what he does or to be able to go behind him. Since he is terribly insecure, he believes that he must come up with the most complex, difficult to follow logic that he can, even to perform the most mundane tasks. In just one example of what I came across, he must have spent hours, with his mind just spinning - devising the most complex method possible to perform the very basic task of loading the address of the CVT into a register. Just to make things even more difficult to follow, he didn't get the real CVT address, he got the CVT address plus one, then used fixed offsets (all adjusted by 1 of course) to reference the CVT data.

It took me a few minutes, mostly because I couldn't believe that someone had coded approximately 30 lines of code, including a subroutine to accomplish the same thing that any other systems programmer can tell you should have been done with a single line of code;

                      L         R5,16(R0,R0)         LOAD CVT ADDRESS INTO R5

Now I'm sure that when the original author completed the coding, he probably said to himself something like "Wow, how clever was that? - NOBODY (except me because I'm sooo smart) will ever be able to figure out what that does." and just so that no one gets the wrong idea, he clearly was capable of producing clear code - it's just that what he had done could best be described as "PURPOSEFULLY EVASIVE". LET ME SAY RIGHT HERE AND NOW - THIS IS SOMETHING THAT CAN BE DONE BY ANY FAIR PROGRAMMER - IT IS NEITHER CLEVER NOR WISE - IF YOU ARE EVER TEMPTED TO TRY TO PULL SOMETHING LIKE THIS - JUST DON'T DO IT!   Eventually you will have to answer to someone about it, and it simply can not be defended.

Return to Index.


Register Zero Shortcuts

The assembler does a lot of nice things for us, one of the nice things it does is to complete our incomplete coding techniques such as missing register values. If we simply omit a register that is used by the instruction, the assembler slips in a register zero specification for us. This gives us a shortcut, a shorthand of sorts, so that we don't have to code everything for every instruction. This is simply a bad programming practice, and not because I'm a 'purest' there are good reasons not to take this particular shortcut. So whenever I teach assembler programming, when I get to Indexed Base/Displacement addressing, I always have to tell my students to do what I say - not what I do, or at least not what I may have coded in the past. You see, it is an easy habit to fall into - when you code an index base/displacement instruction, like     L     (LOAD) you find that most of the time you don't need the index, and you simply don't code it - you code something like;

            L       R1,24(R4)       LOAD R1 WITH WORD FROM 24 PAST R4

and that works fine, and I see it in other peoples code all the time - but what you and I have really coded, or what gets generated is;

            L       R1,24(R4,R0)

The assembler will take the first register you code and use that as the INDEX register and since the second register was omitted R0 will be substituted by default. This has two real problems, first you tend to forget that you CAN use an index register with these instructions, the second problem is really a trap in waiting we'll get to that part of it in a just a second. For now you already know that the instruction works just the way you want it to, regardless of what I may be saying - you know it works - and it does. It works because you are simply (in this case) developing an address to load from, and whether you use an index (off of zero) or a real base with an index of zero makes no difference, EXCEPT when you eventually get around to writing code that will run in AR, ACCESS REGISTER MODE, and then it makes a difference. If you setup AR4 to reference a specific address space and then write the instruction the way I first showed you, it probably will NOT work because when in AR mode, the access register redirection to a different address space is effective ONLY for the BASE, and NEVER for index registers which is the way we used R4 in that first example. What you will in fact get, is the right address, but in the wrong address space. This is not cool at all, and it is very hard to spot, especially since you KNOW that the LOAD instruction works, and you have by now probably forgotten that the instruction can even use an index, much less that that is exactly what you are doing.

So save yourself some future grief and get into the habit of explicitly coding both the base and the index register when you code. You may have to check that quick reference of yours to 'remember' which instructions really are indexed, and which are not, this is the way to correct the first problem (forgetting you can index), and you won't fall into the AR mode trap later and have to discover that the instruction you have been coding for years suddenly doesn't work that way you have always thought of it as working. If you simply MUST save yourself a keystroke here or there - try inserting a simple comma, as in the following line of code.

            L       R1,24(,R4)       LOAD R1 WITH WORD FROM 24 PAST R4

You still take a shortcut, you still save a couple of keystrokes, but this way you actually coded a BASE register and defaulted the index register to zero as intended.
Happy coding.


Return to Index.


Break Your Program Down Into Subroutines

I am not a fan of STRUCTURED PROGRAMMING, silly rules like never coding a hard branch instruction are unnecessary in my opinion, but I am a fan of simple, straight forward easy to understand coding, and one of the basic tenet of structured programming is that you break things down into lots of little subroutines, and that does help to make your code easier to understand. Strictly from a performance standpoint, calling a sub-routine, even with a simple low-overhead calling and return sequence, is not as efficient as simply coding the 'routine' in-line. But the advantages of properly structuring your code will usually far outweigh the small cost in efficiency.

I would say in general, that unless you are writing an operating system exit that will be executed hundreds of thousands of times a day (maybe per hour) that the organizational benefits provided by creating many subroutines in your program outweigh any performance benefits you may glean from coding inline to avoiding the overhead required to make a subroutine call and return from it. In short, unless you are in very intensively used code where you have to squeeze all the possible speed out of it you can - break your code down into subroutines. When you take even the most complex task and break it down into smaller, simpler tasks it simply becomes easier to read, easier to understand, and easier to maintain. So it simply makes sense to do so. You will also find that when your code is broken down into many smaller sub-routines, that your mainline logic will become clearer and simpler. I found that after I started breaking my code down properly my mainline logic became a simple list of BAS (branch and save) instructions, with maybe a few loop control instructions built into the mainline - maybe no loop control instructions need to be in your mainline at all - that really depends on what you are comfortable with. In any case, what this does, besides making your code easier to maintain, is that it forces you to look at your logic more closely. It is a matter of being able to see the forest in spite of the trees. A typical mainline may look something like -

MAINLINE BAS R9,INIT01 INITIAL HOUSEKEEPING
  BAS R9,OPENFLSO OPEN OUTPUT FILES
  BAS R9,OPENFLSI OPEN INPUT FILES
LOOP01 BAS R9,GETINPUT READ AN INPUT REC.
  BAS R9,TSTREC DO WE WANT THIS ONE?
  JZ LOOP01 IF CC=0 SKIP RECORD
  BAS R9,PROCESS MANIPULATE DATA
  BAS R9,TESTWRT TEST, TIME TO WRITE OUTPUT
  JNZ LOOP01 IF CC NE 0 SKIP OUTPUT
  BAS R9,WRITE CREATE OUTPUT RPT
  J LOOP01 LOOP TILL DONE
EODRTN BAS R9,WRTFINAL WRITE FINAL RPTS
  BAS R9,CLOSEALL CLOSE ALL FILES NOW
  BAS R9,CLEANUP RELEASE STG ETC.
  $EXIT RC=00 MY PGMS ALL END WELL!

You see even a very small simple report program can be, should be, broken down into many smaller routines. If this program has been written without any subroutines, and it certainly could have been, you would need to read the entire program before you really knew what it was doing. With this style of coding you have a very good idea (unless the comments are incorrect) what the program is doing after you read the first 20 or so lines of code. Further if you need to modify a specific portion of it, you have a very good idea of where to go to begin looking for the place to make the change. If you want to change the logic where you decide if a detail is written or not, go find label 'TSTWRT' and start looking there. If the TSTWRT routine is at all complex, it should probably also be broken down into simpler routines, that will further help refine where a change should be made. Even the apparently simple routines alluded to in this example probably need to be broken down into yet smaller routines. Let's take for example the routine 'WRITE' to write an output record, let's say that really is a report detail we are writing. Well, the report probably needs report headings - that's a separate routine, maybe footers (a confidentiality note or copyright notice), that should be a separate routine, spacing down before you print footers - that's probably a separate routine.

Now, every time I introduce this idea to a new Software of people, someone always want to know, how big should you allow a section of code get before you break it up into multiple routines - what is a good rule of thumb? Well, this is largely a matter of taste, and what you are comfortable with. I tend to think if a piece of code runs to more than 40 or 50 or so lines it needs to be broken down - unless those 50 lines are doing something very simple and repetitive, like moving one field after another from place 'A' to place 'B'. If it takes 100 lines to populate a new record by moving first one field then the next, then the next into the record, then that is a single logical unit of work and if it runs to 250 lines, that's ok and there is no need to break it up.

After you have been writing like this for just a short time, you will see that small routines that are used to create larger logical elements are quite easy to write, and to maintain. You will start seeing that many of your small routines, like the ones I just mentioned to handle report writing (headers, footers, spacing for footers, etc.) are easily re-usable. No sense in reinventing the wheel and the more basic your sub-routines, the more likely you can reuse them. I have at times been able to write fairly large, complex programs by simply copying blocks of old code from here and there.... and adding the logic that was particular to the specific program, and I was done. How much testing do you need to do when you use already proven blocks of code? Not as much as something written from scratch, but testing and unit testing is an entirely different subject.

In the meantime,
Happy coding.


Return to Index.

Automatically Centering Heading Text on a Print Line

Centering report headings or column headings in the center of a report if it goes on paper, or on a screen if it is just a display you are providing, is never a difficult thing to do, but it can be tedious, and it can turn into quite a bother when you need to make changes to a report or display format. The procedure, which any first year programming student can tell you, is quite easy; just take the length of the title, subtract that value from the entire length of the display or print line length, and divide that result by 2. That will give you the starting position to move your report title to, so that it ends up being centered in the line. But, counting spaces, doing math, why bother, I'm lazy! I would much rather have the assembler do it for me. Besides, if I let the assembler figure out the print position for me, when I later change the size of the print output, or the length of the title, just the act of re-assembling will 're-center' the title for me again. The 'trick' to doing this is to use the " L' " (length of) assembler notation for the print line and print title fields and then let the assembler do some simple math to resolve LENGTH values (using the L' notation) into a displacement with our print line to move our print literals (headings) to.
Here is a simple example: (YOU MAY NEED TO MAXIMIZE YOUR BROWSER FOR IT TO DISPLAY CORRECTLY)

LINE# LABEL OP OPERANDS COMMENTS
001   MVI PRNTLINE,C' ' BLANK OUT THE PRINT LINE
002   MVC PRNTLINE+1(L'PRNTLINE-1),PRNTLINE  
003 CNTRHD1 MVC PRNTLINE+((L'PRNTLINE-L'HEAD1)/2)(L'HEAD1),HEAD1 Center head1 in prntline
004 CNTR2 EQU ((L'PRINTLINE-L'HEAD2)/2) JUST FOR READABILITY
005   MVI PRNTLINE,C' ' BLANK OUT THE PRINT LINE
006   MVC PRNTLINE+1(L'PRNTLINE-1),PRNTLINE  
007   MVC PRNTLINE+CNTR2(L'HEAD2),HEAD2 SAME AS ABOVE ... BUT EASIER
008 HEAD1 DC C'THIS IS THE FIRST HEADING' THE FIRST HEADING LITERAL
009 HEAD2 DC C'AND THE SECOND' THE SECOND HEADING LITERAL
010 PRNTLINE DS CL132 PRINT LINE (PRINTED DATA HERE)

In this sample we define our print line on LINE# 010, and our two heading literals on lines 8 and 9. In the example we first clear our print line using the MVI and MVC on lines 1 and 2 and then center the HEAD1 literal on the PRNTLINE field using the MVC statement on line 003. The phrase "((L'PRNTLINE-L'HEAD1)/2)" which is added to the beginning location of the PRNTLINE location is what does the centering for us - (the length of the print line minus the length of the heading, all divided by two is where we must start moving our literal value to, so that it will be centered. NOTICE, the trailing " (L'HEAD1) " that specifies the length of our move, otherwise we would default to the length of the printline (133), resulting in a mess.

Now this all works fine, it's just a bit hard to read, and hard to even fit on the line at times, so I have used an EQU on LINE# 4 to calculate the beginning location to move TO, for the second heading HEAD2. Of course we need to blank out our print line before we simply reuse it again, so we do that in lines 5 & 6, just like we did in lines 1 and 2 above, then when we write our MVC using the EQUATED centering value (LINE# 7), it is a little more readable. The MVC on line 7 is equivelent to the MVC on line 3, except it moves HEAD2 instead of HEAD1 and uses a centering value adjusted for HEAD2 instead of HEAD1. Either way you are more comfortable coding it is fine, they are after all equivelent to each other. Now, if we need to change the length of either header, we can just change the DC literal, and everything else about centering the heading is already handled for us. It really does make changes and maintenance a lot easier.

Happy coding...
Return to Index.

Exchanging Two Variables Without a Temporary Third Location

Everyone who has ever written a program in a language other than Assembler (hlasm) knows exactly how to swap two variables around, the procedure is very simple; save variable 1 in a temp location, copy variable 2 to the variable 1 location and finally copy the temp location to the variable 2 location. Now, what if you don't have a temp area? Well, in assembler we have the Boolean operations and while they are usually used to flip bits one way or the other, there is a neat little trick that you can use with the "exclusive or character" instruction (XC). If you exclusive or variable 1 to variable 2, then exclusive or variable 2 to variable 1, and finally exclusive or variable 1 to variable 2 - then say abracadabra, you have just swapped the two variables without the need of a temporary variable location (it even works when no one says abracadabra - I know, I've done it before). When coded it, it will look very simply like this -
          XC       VAR1,VAR2
          XC       VAR2,VAR1
          XC       VAR1,VAR2
It works as long as the two variables are the same length. I can imagine the look of disbelief on your face, because I have seen it before whenever I show this to someone, so go ahead, break out your programmers calculator with the exclusive OR function and try it for yourself.

Happy coding...
Return to Index.

Formatting register content for printing (hex representation)

There are any number of ways to skin this particular cat, but let's make sure we both understand what I mean here. What I intend to do is take a fullword value, perhaps a fullword saved as a result of a ST (store) instruction, and reformat those four bytes into eight printable character digits that provide a hexadecimal representation of the original 4 byte value.

In the following example - a code snippet - the value in R3 is saved and reformatted into the area named ANSWER.

DATA LABEL OP OPERANDS COMMENTS
    ST R3,SAVER3 SAVE HEX VALUE IN SAVER3
    UNPK ANSWER(9),SAVER3(5) 1ST 8 BYTES NOW HAVE 1 HEX DIGIT EACH IN THE FORM
  *   X'F?' WHERE ? IS A HEX DIGIT FROM SAVER3;
  *     THE 9TH BYTE IS A SIGN PLUS WHATEVER FOLLOWED SAVER3
  *       WE REALLY DON'T CARE ABOUT THE 9TH BYTE
    TR ANSWER(8),TRTHEX-240 XLATE F0 TO C'0';  x'FA' TO x'A';   x'FF' TO C'F' ETC.
  *   THAT IS IT. THE EIGHT BYTES WE WANT ARE IN ASNWER(8)
  SAVER3 DC F  
    DC XL1 ANY BYTE FOLLOWING SAVER3 WORKS FINE.
  ANSWER; DC CL9 WE JUST NEED THE FIRST 8 BYTES, BUT WILL USE ALL 9
  TRTHEX DC C'0123456789ABCDEF' SINCE ALL INPUT BYTES ARE X'F0' - X'FF' THIS IS ENOUGH

The UNPK instruction is very mechanical - it doesn't care if it has good or bad data - except for the low order byte of input, and output, every input byte is broken in half, and then each half (or nibble) is prefaced with a x'F'. Therefore, except for the low order (right-most) byte that is unpacked, the only possible hex values are x'F0', x'F1', x'F2', x'F3'..... x'F9', x'FA', x'FB', X'FC', X'FD', x'FE' and x'FF'. The low order byte the one that a sign nibble was unpacked into - we don't care about - we only want the first 8 result bytes since they were built directly from the 4 byte value we want to make readable. Since there are only 16 possible character values (and the numbers (x'F0' - x'F9') are already set for printing) we only need to translate the values of x'FA' to c'A' and x'FB' to c'B' etc. which is exactly what the TR (TRANSLATE) instruction is meant to do. Normally the TR instruction needs a 256 byte translate table, but in this case we know there are only 16 possible values to be translated, and they happen to be the 16 last bytes, we just set those bytes in our table and 'point' our TR table operand to 240 bytes before our where our table actually starts. This is ok, since the lowest value to be translated will be a x'F0' (table beginning +x'F0' (or 240 bytes)).

A neat trick - really just takes two instructions - UNPK and TR - the rest is 'alignment'. - of course if you have an easier way to handle this - you let me know.

Happy coding...
Return to Index.

Using a return code with a Branch Table

Did you ever wonder why return codes from routines and utility programs tend to be multiples of 4? You know they seem to have return codes of 4,8,12,16,24,32 etc. I don't know if I have ever read any program documentation that stated the return codes were 1,2,3,4,5 etc. You know, there is a reason for this, and it is a handy little technique that you can use yourself. It is specically useful when you call a utility program from your own program and then take different action depending on the different return codes. Here is a little code snippet that takes advantage of the return codes that are a multiple of four - we will assume that on entry to this snippet the return code is in R15 and is either zero, four, eight, tweleve, or sixteen.

DATA LABEL OP OPERANDS COMMENTS
    B BRTABLE(R15) BRANCH TO BRTABLE INDEXED BY R15
  BRTABLE B DORC00 RETURN CODE WAS ZERO J DORC00
    B DORC04 RETURN CODE WAS 4 - J DORC04
    B DORC08 IF RC = 8 - TAKE THIS BRANCH DORC08
    B DORC12 THE RC WAS 12 IF WE BRANCHED HERE - GO DORC12
    B DORC16 RC WAS 16 -JUMP TO DORC16

Of course you still should validate that the return code was between zero and 16. This works becasue each BRANCH instruction is 4 bytes long and we branched to BRTABLE INDEXED by R15, the return code. If the return code had been 64 we would have branched way down the line somewhere. But assuming the return codes are all between 0 and 16, and are all multiples of 4, this is a very effecient way of branching to specific routines based on the return codes. We didn't even do a test - we just took two quick branches right to the routine that was specified for each return code!

Now you know why most return codes are 4,8,12,16 and so on... Programmers are both tricky - and LAZY of course you can take advantage of the same properties with your routines / return codes / branch tables. If you ever do run across some code that sets return codes, 1,2,3,4,etc. you can still use the same method, just multiply the return code by 4 before you take your first indexed branch, and an easy way to multiply by four is to SLL (shift left logical) two. That will multiply by 4 for you.

Happy coding...
Return to Index.


Generating a TR or TRT starter TABLE

What do I mean by a starter TABLE for a TR or TRT instruction? Well it is frequently necessary to translate or translate and test, while changing only one or two characters values. One way to do this is to create 16 DC statements the first with the values
      x'00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F'
and the next with the same values all x'10' larger than the previous line, and so on, and on, until you have 16 lines with 16 bytes of hex data typed into each line - a total of 256 bytes- each with a number value just one more than the previous byte.

A much easier way to code the same thing, and far less likely to cause carpal tunnel syndrome (or introduce a typo) - is to code a line like the following one;
      TRTABLE    DC       256AL1(*-TRTABLE)        
Which generates the same thing - all 256 bytes of it. Exactly how does that work you ask? Well, let's look at the statement carefully, we are defining an adcon that is one byte long, with a value that is the current address (that's what the asterisk inside an adcon definition means - give us the current address) we take that address, and subtract from it the address of the TRTABLE label. Well, for the first byte that can be restated to mean the address of HERE minus the address of HERE, and let that result be just one byte long - that is a zero. BUT - we replicated it 256 times, so for the next adcon that will also be 1 byte long, but one byte further away from the label TRTABLE, we get a result of 1, the next byte is obviously 2, and so on, up to x'ff'.
Ok - so now we have an easy way to create a TR table that is loaded with the values x'00' - x'ff' each byte containing a value one larger than the previous byte. If you use a TR instruction with such a table, it basically won't do anything, or more specifically this table can be used to translate any string into it's original value (no change). Well, I wouldn't suggest bothering with that useless operation, but what if you wanted to translate all of the open and close parenthesis to blanks? The way most people are taught, is to get a reference card out and see what hex value a printable open parenthesis "(" is and then go 'into' the original table that many bytes and change that hex value to whatever you want to translate to - let's say you want it turned into a blank - so you replace it with x'40'. There is an easier way - the ORG instruction. Let's see how you use it to replace the open and close parenthesis, and the character 'Z', all into spaces - (I don't like Z's).

DATA LABEL OP OPERANDS COMMENTS
  TRTAB1 DC 256AL1(*-TRTAB1) SET UP A TRANSPARENT TABLE FIRST
    ORG TRTAB1+C'(' REPOSITION COUNTER TO THE C'(' POSITION
    DC X'40' REPLACE OPEN PAREN WITH A BLANK (X'40')
    ORG TRTAB1+C')' REPOSITION COUNTER TO THE C')' POSITION
    DC X'40' REPLACE CLOSE PAREN WITH A BLANK (X'40')
    ORG TRTAB1+C'Z' REPOSITION COUNTER TO THE C'Z'TH POSITION
    DC X'40' REPLACE THE Z WITH A BLANK (X'40')
    ORG TRTAB1+256 RESET THE LOCATION COUNTER WHERE IT BELONGS.

See, that way we don't have to know what number represents a Z or an open paren, or a close paren - we let the assembler figure that out for us. It also serves the purpose of being somewhat - dare I say it, self documenting. That is, is becomes clear exactly which bytes in the TR table have been altered from their original value.
Just remember that last ORG instruction that resets the pointer to 'table+256', just past the 256 byte table, so that the next DC or DS instruction you use doesn't overlay the end of your table.

Happy coding...
Return to Index.


A Simple Technique to Improve Your Searches

Searching a table is one of the basic things we must from time to time accomplish. Whether it is scanning an input control card for keywords, or parsing an input parameter, it is one task that can, if we let it, can take an awful lot of cpu time. We must do all that we can to improve these types of processes. One thing that can help significantly especially when dealing with a long list that must be searched, is to simply make sure that the list is sorted, and then when searching the table for a match to a potential keyword we can simply assume no match as soon as the table entry becomes greater than the object we are looking for a table match for we can simply stop seaching and assume no match will be found. Assuming a random distribution of potential items that may need to be matched against the list, and also assuming a random distribution of the table elements that are to be searched, the average search time can be cut in half. Of course that type of change really needs to be setup as part of the original program design.

For a simple fix, that can potentially cut the search time to a fraction of the original search time let".s first take a look at a typical search routine, and then we can see how to significantly improve it.

Here is the original, and typical search routine.

DATA LABEL OP OPERANDS COMMENTS
  SRCHRTN LA R4,KEY1 R4 = INPUT KEY TO BE MATCHED TO TABLE
    LA R5,TABLE1 R5 => TABLE OF 8 BYTE ENTRIES
  TEST1 CLC 0(8,R4),0(R5) SEE IF THIS KEY MATCHES A TABLE ELEMENT
    JE MATCHED IF A MATCH IS FOUND - JUMP OUT OF LOOP
    LA R5,8(R0,R5) BUMP R5 TO NEXT TABLE ELEMENT
    CLC 0(2,R5),=X'FFFF' TEST FOR END OF TABLE
    JE NOTFOUND IF END OF TABLE - IT WAS NOT FOUND
    J TEST1 CONTINUE TESTING UNTIL FOUND
         
         

As you see this is a typical search routine. R5 points to the fixed table of eight byte long table entries. The table is delimited by a two byte entry of high values, this way we can add new entries to the table without making any code changes. so, let's look at the first change that will improve the search the most - remember, we still do not know that this is a sorted table so we must continue searching the entire table with each search.

Here is the first improvement.

DATA LABEL OP OPERANDS COMMENTS
  SRCHRTN LA R4,KEY1 R4 = INPUT KEY TO BE MATCHED TO TABLE
    LA R5,TABLE1 R5 => TABLE OF 8 BYTE ENTRIES
NEW ==> TEST0 CLC 0(1,R4),0(R5) SEE IF JUST THE 1ST CHARACTER MATCHES
NEW ==>   JNE BUMP8 IF 1ST CHAR DOES NOT MATCH, CHECK NEXT
  TEST1 CLC 0(8,R4),0(R5) SEE IF ENTIRE KEY MATCHES TABLE ELEMENT
    JE MATCHED IF A MATCH IS FOUND - JUMP OUT OF LOOP
  BUMP8 LA R5,8(R0,R5) BUMP R5 TO NEXT TABLE ELEMENT
    CLC 0(2,R5),=X'FFFF' TEST FOR END OF TABLE
    JE NOTFOUND IF END OF TABLE - IT WAS NOT FOUND
    J TEST0 CONTINUE TESTING UNTIL FOUND
         
         

What we have done here is to add two new instructions, and a label for a previously existing instruction. By testing only a single byte from each key, at least those that do not match on the first byte, we reduce the number of charaters that are compared. By reducing the total number of characters that must be fetched and that must be compared, we reduce the total work that must be done by the routine, and improve the overall run time of the routine. The longer the elements are, the more effective this type of change becomes. For a large table, the search time can be cut to under 1/4 of the original in many cases. Of course depending one the distribution of the keys, and the key length, etc. etc. "your mileage my vary".

By defining the table so that the number of items in the table can be loaded into a THIRD register we can refine the search even further. Here is the way it looks when we know the length of the table before we start the search.

DATA LABEL OP OPERANDS COMMENTS
  SRCHRTN LA R4,KEY1 R4 = INPUT KEY TO BE MATCHED TO TABLE
    LA R5,TABLE1 R5 => TABLE OF 8 BYTE ENTRIES
NEWEST   L R6,=A(NUMENTS) GET NUM ON ENTRIES IN R6
  TEST0 CLC 0(1,R4),0(R5) SEE IF JUST THE 1ST CHARACTER MATCHES
    JNE LOOPEND IF 1ST CHAR DOES NOT MATCH, CHECK NEXT
  TEST1 CLC 0(8,R4),0(R5) SEE IF ENTIRE KEY MATCHES TABLE ELEMENT
    JE MATCHED IF A MATCH IS FOUND - JUMP OUT OF LOOP
NEWEST LOOPEND LA R5,8(R0,R5) BUMP R5 TO NEXT TABLE ENTRY
    BCT R6,TEST0 LOOP TILL DONE
    J NOTFOUND IF END OF TABLE - IT WAS NOT FOUND
    LTORG    
  TABLE1 DC CL8'ABCDEFGH'  
    DC CL8'PQRSTUVW'  
    . . . .   MANY MORE ENTIRES GO IN HERE...
  TABLAST  DC CL8'45678ZSDF' THE LAST TABLE ENTRY
  NUMENTS EQU (*-TABLE1)/8 NUMBER OF ENTRIES IN TABLE

By defining the table with the equate at the end (numents) we can still add table entries without requiring any code changes. By loading an additional register with the number of entries in the table, we can eliminate the additional checks for a table delimiter, and simply decrementing a register is many times faster than retrieving data from storage for a comparison. Of course with a table of fixed length elements we could also have used a BXLE instruction and reduced the total number of instructions even further, but I find that most people are not really comfortable with BXLE. I am not sure why, BXLE is a perfectly good instruction, and when used properly will save instructions in critical path code. But, as long as people are not comfortable with it, I am going to fall back on the axiom that we must keep it simple - who knows it may be YOU that has to go back and change it next year. Well, that's it for simple fixes to searches - the more elegant search and sorting techniques require much more explination, and initial design work. But only the most elegant techniques can yield improvements better than what we have discussed above.



Happy coding...
Return to Index.
















Last updated July 2011 - copyright © 2011 McColley Systems Software Inc.
Return to top of Page.