As a self-professed lazy programmer I've stolen -- er, learned -- a lot of Assembler tricks to keep from counting...
bytes or typing those two extra instructions. I hope other people find these tricks useful, thus freeing up time they can use to watch their investments go down the tubes.
Some machine instructions require operand lengths. In the case of the Compare Logical Character (CLC) instruction, the Assembler gets the length from the left operand. This can be a little irritating when you want to compare the first few bytes of the left operand to a shorter literal. To do this in classic style you must count the literal's length and code put it into an explicit length on the left:
But, there is no reason why the literal can't be first:
It's a little disconcerting at first, but you don't have to count bytes and code an explicit length. In addition, there's one less thing to remember if the literal's length changes.
DSECT Hokey Pokey
A lot of system-level code involves chaining control blocks. Thankfully, most of these blocks have dummy sections (DSECTs) describing their layouts. The bad news is using the DSECTs involves lots of temporary USING statements followed the same number of DROPs. It gets even more awkward inside of macros where code must be confined to using one or two registers. Take, for instance, this sequence of instructions to get the job step program name:
L R15,548 Get ASCB USING ASCB,R15 Get ASCB DSECT L R15,ASCBRCTP Get region control TCB DROP R15 Lose ASCB DSECT USING TCB,R15 Use TCB DSECT L R15,TCBLTC Point to daughter TCB L R15,TCBLTC Point to jobstep TCB L R15,TCBRBP Point to RB DROP R15 Lose TCB DSECT USING RBSECT,R15 Use RB DSECT L R15,RBCDFLGS Address RB's CDE DROP R15 Lose RB DSECT USING CDENTRY,R15 Use CDE DSECT MVC PROGNAME,CDNAME Copy program name
While the code is straightforward, it is also cluttered with USING and DROP statements. We can take those statements out by remembering that the Assembler can do math with labels to calculate a data item's offset as long as the equation resolves to an absolute number. Instead of repetitive USING and DROP statements, subtracting the field you need from the DSECT's label produces the field's offset. Using this technique, the code above becomes:
L R15,548 Get ASCB L R15,ASCBRCTP-ASCB(,R15) Get region control TCB L R15,TCBLTC-TCB(,R15) Point to daughter TCB L R15,TCBLTC-TCB(,R15) Point to jobstep TCB L R15,TCBRBP-TCB(,R15) Point to RB L R15,RBCDFLGS-RBSECT(,15) Address RB's CDE MVC PROGNAME,CDNAME-CDENTRY(,R15) Copy program name
The second is more streamlined and easier to include in a macro, although you still have to define the DSECTs somewhere in the source
The length attribute
The Assembler provides attributes enabling macro writers to test the qualities symbols, instructions and other language objects. The LENGTH attribute returns the number of bytes a labeled field occupies in storage. You invoke the LENGTH attribute with the string L' and it can be used inside of macros for conditional assembly. For instance, the following code sets the symbol &LEN to 28.
LABEL DC C'This error should not happen' &LEN SETA L'LABEL
But the LENGTH attribute can be used in open (non-macro) code as well. For example, assume we have a string we want to copy into buffer without padding it with blanks. Instead of counting bytes, use the LENGTH attribute to generate the length for the move instruction:
MVC BUFFER(L'MESSAGE),MESSAGE MESSAGE DC C'Consult your systems programmer'
Best of all, if you ever change the message's length you don't have to recount the bytes and change all the references. Instead, just change the message's contents and recompile.
Fun with address constants
The Assembler also does math inside of constant definitions. The one I find the most useful is the local address ("A" type) constant. Not only can it define absolute and relative values, it can have a length from one to four bytes. Here are a few examples of its uses:
CONST1 DC C'First constant' CONST2 DC C'Second constant' CONST3 DC C'Semprini' LISTEND EQU * * One byte length of CONST1 LIT1L DC AL1(L'CONST1) * Two byte length of CONST1 and CONST2 LIT1AND2 DC AL2(L'CONST1+L'CONST2) * Length of all three constants LITSLEN DC AL4(LISTEND-CONST1)
From the above examples you can see how to use the LENGTH attribute and some math to create constants of varying length. Again, if the length of any of the constants changes, the address constants will be recomputed at assembly time. But this can get even more complicated. Assume you need to search a table of fixed length entries. With the proper statements, you can set up a low-maintenance, easy-to-use table as follows:
TABENTS DC AL4((TABEND-TABBEG)/4) TABBEG EQU * DC CL4'AAAA' DC CL4'BBBB' DC CL4'YYYY' DC CL4'ZZZZ' TABEND EQU *
The above example shows a simple table of four-byte entries. The trick is in the TABENTS address constant that subtracts the table's end from the table's beginning to find its entire length. Then, by dividing by an individual entry's length, it computes the number of entries.
To use the table, the program loads the number of entries from TABENTS to control a loop through the individual entries. With this setup, entries can be added or removed and the value of TABENTS will be recalculated when you recompile the program.
The high-level Assembler provides a very flexible platform for coding in relatively crude machine code. In addition, there are lots of tricks available for making this code more maintainable and easier to understand. My advice is to break out the High-Level Assembler Language Reference and find out what you can.
ABOUT THE AUTHOR: For 24 years, Robert Crawford has worked off and on as a CICS systems programmer. He is experienced in debugging and tuning applications and has written in COBOL, Assembler and C++ using VSAM, DLI and DB2.
Did you find this helpful? Write to Matt Stansberry about your data center concerns at firstname.lastname@example.org.