The coding of macro definitions often receives less attention than the coding of programs - both in programming and in discussion about programming.
This page aims to shed a little light on the subject, particularly for someone new to the subject who comes across some during their maintenance work.
It is not intended to be a comprehensive manual or FAQ on the subject.
|Caveat:||The code samples below are intended as a starting
point for your own use, it is your responsibility to ensure they works
correctly in your situation.
For instance, the code below assumes that 0 through 15 have been equated to R0 through R15, whereas sometimes equates are set-up so that 0 through 15 become R0 through R9, RA through RF.
Frequently macro definitions can be difficult to read, difficult to maintain, difficult to debug. However, just as much, if not more, care and consideration should be given to the coding of macro definitions, as is given to the coding of programs, for the following reasons:
The macro definition writer should bear these difficulties constantly in mind, giving particular attention to the use of global and local variable symbols, AIF statements and MNOTE statements.
Macro definitions that are required, or are likely to be required, by more than one separately assembled program, should be placed in the appropriate 'public' library. Otherwise, there might be more than one occurrence of the definition, and future changes might have to be made in each occurrence instead of just once.
On the other hand, macro definitions which are definitely not intended for general use, but which serve a particular purpose for one program, should be kept within that program. This keeps the listing of the macro definition in the most useful place, and also, prevents the proliferation of esoteric macros on the 'public' library.
If the macro can be called by executable code, always include a variable symbol in the name field, so that the macro call can be branched to. If this variable symbol is anything but null, provide a labeled DS OH as the first generated statement with this variable symbol specifying the label, for example:
MACRO &N MYMACRO , Prototype, no operands * .... * .... AIF (&N EQ ).NOLABEL Label provided? &N DS OH Yes, then generate it .NOLABEL ANOP * etc
Put each symbolic parameter on a separate line. Each symbolic parameter can then have its own comment, and new parameters can be added more easily, for example:
MACRO &N FREEMAIN C &TYPE, Indicate parameter format C &LV=, Specifies length value C &A=, Address of area to be freed C &SP=O Subpool number
It is not a good idea to normally have more than one positional parameter. Either have one positional parameter and all the others keyword parameters (like the above example), or have all keyword parameters. Keyword parameters are easier to remember, and make macro calls easier to understand. Consider, for example, the OS macro GET.
In the following example of a macro call:
GET (R2),(R3) Read next record
it is difficult to say, with certainty, which is the DCB address and which is the area address, whereas the following code would have removed the doubt.
GET DCB=(R2), Read next record C AREA=(R3) Area provided by user
There are two exceptional cases where positional parameters are preferable:
When complementary pairs of macros occur (such as GETMAIN/FREEMAIN, WAIT/POST, ENQ/DEQ, GET/PUT, OPEN/CLOSE), avoid inconsistencies in corresponding parameters (e.g. WAIT uses keyword parameters but POST uses positional parameters).
It is a good idea for every macro to contain a boxed prologue, in the same style as program prologues, except that each statement will start with .* instead of just a single* as in normal assembler.
The macro may generate a descriptive header, particularly if the macro's purpose is to provide a common data area definition, but such a header is distinct from the internal commentary which forms the prologue. The example following illustrates this distinction, without actually showing how a prologue should be coded. Note the use of ANOPs, merely to provide gaps in the listing.
MACRO MYCB , PROTOTYPE STATEMENT .******************************************* .* This is the prologue, explaining * .* that this is the macro to be used * .* to generate the description of the * .* control block MYCB, etc. etc. * .******************************************* .* This is the modification box to be * .* updated with details of any changes * .******************************************* ANOP .* Generate Header ANOP .******************************************* .* This is the descriptive header, * .* saying that what follows is a * .* description of the control block * .* MYCB, any giving any other information * .* that may be useful to the reader of * .* the program calling the macro * ********** ** ********* ******************** ANOP .* .* Now generate the actual description .* SPACE 1 MYCB DSECT etc.
All variable symbols should be explicitly declared (by use of statements like LCLC, GBLB), even if the assembler in use does not insist on it. Using implicit declarations can lead to errors due to mis-spellings.
Don't forget that names of variable symbols may need to conform to any relevant local naming in your installation. Special care is required to avoid duplicate global symbol names, it may be a good idea to have some convention for the use of a prefix to avoid clashes.
Conditional assembly statements (such as AIF, SETA, SETB, SETC) should be explained by remarks and comments, just like ordinary code.
These are the statements that get converted by the macro processor into generated code. All the standards that apply to ordinary code must still be adhered to. OS macros which, for good reasons of their own, generate instructions like B *+56 or L 15,48(1) should not be taken as good examples!
Never have an EJECT or SPACE as the first or last generated statement. The programmer who calls the macro is likely to code another EJECT of his own it he wants to start a new page after the macro call. Even if the assembler in use allows it, never generate PRINT statements, because of the effect they may have on print controls written by the programmer using the macro.
Avoid generating an unlabelled DS OH as the first generated statement, when the name field of the macro call is null.
Try to keep comments on successive generated statements aligned. If variable symbols are used in model statements, bear in mind that the generated field(s) may not be the same length as in the model statement - pushing or pulling the comment out of alignment (there is not much you can do about it, except to invent variable symbols which are likely to be approximately the same lengths as their replacement values, and to leave room for expansion between the end of the comment and column 72 to avoid generating untidy continuations).
Model statements and conditional assembly statements should be accompanied by a liberal proportion of internal comments, for instance, when it is necessary to explain the use of variable symbols. Bear in mind the distinction between generated comments, which are to help the reader of the program that calls the macro, and comments (starting with .*), which are to help the reader of the macro definition.
In complicated macro definitions which involve a lot of decision-making by means of conditional assembly statements, the code should be split up into code blocks headed by boxed internal comments. These comment boxes can include explanations of the use of variable symbols and macro operands. Unboxed internal commentary sets can also be used to clarify smaller code units within a block,. Refer to the corresponding paragraphs in the Commentary section for further information on this subject.
For the purposes of this discussion, we distinguish three types of inner macro (that is, a macro which may be called by a macro)
Calls, within a macro, to public inner macros, should be preceded by an MNOTE statement repeating the call.
MNOTE GETMAIN R,LV=&LEN GETMAIN R,,LV=&LEN
This is because the generated statement issuing the inner macro call does not appear in the assembly listing. The preceding MNOTE shows the reader what is happening and the variable symbols get replaced in the comment as well as in the generated statement.
System inner macros and public inner macros should always be placed, as separate members, in the macro library.
The definition of a private inner macro should be normally placed within the definition of the macro that calls it. (A private inner macro that is called by a macro and by one of its private inner macros is still considered to be private.)
As with program commentary, don't use abbreviations that may be misunderstood. Stick to the severity codes used by the assembler itself, using the same connotations, so that return codes can be correctly interpreted by job control. Normally, the only codes needed will be:
* Comment or Warning
4 Program will probably execute
8 Program will not execute correctly
Remember that MNOTEs can sometimes be more useful than generated comments, because the message operand can include variable symbols.
could result in:
+ convert FWORD to DECIMAL in DWORD + L RO,FWORD Load binary value + CVD RO,DWORD Obtain decimal value
on the generated listing.
Note that the above MNOTE and remarks are only illustrating a technique. A real macro would need much more explicit commentary.
Public macros should never generate code that requires particular symbols to be explicitly defined by the calling program (but normal register names can be assumed to be available, and should be used).
If a public macro can be called more than once by the same program, duplication of internally defined symbols should be avoided by concatenating the first three or four characters of the macro name to a suitable variable symbol (usually &SYSNDX). Avoid going past column 8 in the label of the model statement - some assemblers don't pull the operation code back to column 10.
Now you've read all about macros try the sections on
Now you've read all about macros try the sections on