home *** CD-ROM | disk | FTP | other *** search
- ---MACRO.DOC---
-
- Macro Facility
- ----- --------
-
- This assembler contains an easy-to-use, but very powerful macro facility.
- The facility subsumes the capabilities of most assemblers, including operand
- concatenation, indefinite repeat (often called IRP), and indefinite-repeat
- character (IRPC). Unlike other assemblers, this assembler integrates these
- functions into the main macro facility; so they can be invoked without clumsy
- syntax, or strange characters in the macro-call operands.
-
-
-
- Simple Macro Syntax
-
- All macros must be defined before they are used. A macro definition consists
- of the name of the macro, followed by the word MACRO, followed by the text of
- the macro, followed by #EM, which marks the end of the macro.
-
- Many assembly languages require a list of dummy operand-names to follow the
- word MACRO. This assembler does not: the operands are denoted in the text
- with the fixed names #1, #2, #3, ... up to a limit of #9, for each operand
- in order. If there is anything following the word MACRO, it is considered
- part of the macro text.
-
- Examples:
-
- ; CLEAR sets the register-operand to zero.
-
- CLEAR MACRO SUB #1,#1 #EM
-
- CLEAR AX ; generates a SUB AX,AX instruction
- CLEAR BX ; generates a SUB BX,BX instruction
-
- ; MOVM moves the second operand to the first operand. Both operands can be
- ; memory-variables.
-
- MOVM MACRO
- MOV AX,#2
- MOV #1,AX
- #EM
-
- VAR1 DB ?
- VAR2 DB ?
-
- MOVM VAR1,VAR2 ; generates MOV AX,VAR2 followed by MOV VAR1,AX
-
-
-
- Formatting in macro definitions and calls
-
- The format of a macro definition is flexible. If the macro text consists of a
- single instruction, the definition can be given in a single line, as in the
- CLEAR macro given above. There is no particular advantage to doing this,
- however: the assembler prunes all unnecessary spaces, blank lines, and
- comments from the macro text before entering the text into the symbol table.
- I recommend the more spread-out format of the MOVM macro, for program
- readability.
-
- All special macro-operators within a macro definition begin with a pound-sign
- #. The letters following the pound-sign can be given in either upper-case or
- lower-case. Pound-sign operators are recognized even within quoted strings. If
- you wish the pound-sign to be treated literally, and not as the start of a
- special macro-operator, you must give 2 consecutive pound signs: ##. For
- example:
-
- FOO MACRO
- DB '##1'
- DB '#1'
- #em
-
- FOO abc ; produces DB '#1' followed by DB 'abc'
-
- The format of the macro call line is also flexible. A macro call consists of
- the name of the macro, followed by the operands to be plugged into the macro.
- The assembler prunes leading and trailing blanks from the operands of a
- macro call. The operands to a macro call are always separated by commas.
- Also, as in all assembler source lines, a semi-colon occurring outside of
- a quoted-string is the start of a comment, ignored by the assembler. If you
- want to include commas, blanks, or semi-colons in your operands, you must
- enclose your operand in single-quotes.
-
-
-
- Macro operand substitution
-
- Some macro assemblers expect the operands to macro calls to follow the same
- syntax as the operands to instructions. In those assemblers, the operands
- are parsed, and reduced to numeric values before being plugged into the
- macro definition text. This is called "passing by value". This assembler
- does not pass by value, it passes by text. The only parsing of operands
- done by the macro processor is to determine the start and the finish of the
- operand text. That text is substituted, without regard for its contents,
- for the "#n" that appears in the macro definition. The text is interpreted
- by the assembler only after a complete line is expanded and as it is assembled.
-
- If the first non-blank character after the macro name is a comma, then the
- first operand is null: any occurrances of #1 in the macro text will be
- deleted, and replaced with nothing. Likewise, any two consecutive commas
- with no non-blanks between them will result in the corresponding null
- operand. Also, out-of-range operands are null; for example, #3 is a null
- operand if only two operands are provided in the call.
-
- Null operands to macros are not in themselves illegal. They will produce
- errors only if the resulting macro expansion is illegal.
-
- The method of passing by text allows operand-text to be plugged anywhere
- into a macro, even within symbol names. For example:
-
- ; KF_ENTRY creates an entry in the KFUNCS table, consistsing of a pointer
- ; to a KF_-action-routine. It also declares the corresponding CF_-symbol,
- ; which is the index within the table for that entry.
-
- KF_ENTRY MACRO
- CF_#1 EQU ($-KFUNCS)/2+080
- DW KF_#1
- #EM
-
- KFUNCS:
- KF_ENTRY UP
- KF_ENTRY DOWN
-
- ; The above code is equivalent to:
- ;
- ; KFUNCS:
- ; DW KF_UP
- ; DW KF_DOWN
- ;
- ; CF_UP EQU 080
- ; CF_DOWN EQU 081
-
-
-
- Quoted-string operands
-
- As mentioned before, if you want to include blanks, commas, or semicolons in
- your operands, you enclose the operand in single-quotes. In the vast majority
- of cases in which these special characters need to be part of operands, the
- user wants them to be quoted in the final, assembled line also. Therefore, the
- quotes are passed in the operand. To override this, and strip the quotes from
- the string, you precede the quoted string with a pound-sign. Examples:
-
- DBW MACRO
- DB #1
- DW #2
- #EM
-
- DBW 'E', E_POINTER
- DBW 'W', W_POINTER
-
- ; note that if quotes were not passed, the above lines would have to be
- ; DBW '''E''', E_POINTER; DBW '''W''', W_POINTER
-
- GENERAL_PUSH MACRO
- PUSH#1
- #EM
-
- GENERAL_PUSH F ; generates a PUSHF instruction
- GENERAL_PUSH #' AX' ; generates a PUSH AX instruction
-
- The fact that I could not come up with a more useful example than GENERAL_PUSH
- is strong evidence that it is much better to pass the quotes as the default
- action.
-
-
- Looping by operands in macros
-
- This macro facility contains two kinds of loops: you can loop once for each
- operand in a range of operands; or you can loop once for each character within
- an operand. The first kind of loop, the R-loop, is discussed in this section;
- the second kind, the C-loop, is discussed later.
-
- An R-loop is a stretch of macro-definition code that is repeated when the macro
- is expanded. In addition to the fixed operands #1 through #9, you can specify
- a variable operand, whose number changes each time through the loop. You give
- the variable operand one of the 4 names #W, #X, #Y, or #Z.
-
- An R-loop begins with #R, followed immediately by the letter W,X,Y, or Z naming
- the variable, followed by the number of the first operand to be used, followed
- by the number of the last operand to be used. After the #Rxnn is the text to
- be repeated. The R-loop ends with #ER. For example:
-
- STORE3 MACRO
- MOV AX,#1
- #RY24 ; "repeat for Y running from 2 through 4"
- MOV #Y,AX
- #ER
- #EM
-
- STORE3 VAR1,VAR2,VAR3,VAR4
-
- ; the above call produces the 4 instructions MOV AX,VAR1; MOV VAR2,AX;
- ; MOV VAR3,AX; MOV VAR4,AX.
-
-
-
- The #L last operator and indefinite repeats
-
- The macro facility recognizes the special operator #L, which is the last
- operand in a macro call. #L can appear anywhere in macro text; but its big
- power occurs in conjunction with R-loops, to yield an indefinite-repeat
- facility.
-
- A common example is as follows: you can take any macro that is designed
- for one operand, and easily convert it into a macro that accepts any number
- of operands. You do this by placing the command #RX1L, "repeat for X running
- from 1 through L", at the start of the macro, and the command #ER at the end
- just before the #EM. Finally, you replace all instances of #1 in the macro
- with #X. We see how this works with the CLEAR macro:
-
- CLEAR MACRO #RX1L
- SUB #X,#X
- #ER
- #EM
-
- CLEAR AX,BX ; genearates both SUB AX,AX and SUB BX,BX in one macro-call!
-
- It is possible for R-loops to iterate zero times. In this case, the loop-text
- is skipped completely. For example, CLEAR without any operands would produce no
- expanded text.
-
- Character-loops
-
- We have seen the R-loop; now we discuss the other kind of loop in macros,
- the character-loop, or C-loop. In the C-loop, the variable W,X,Y, or Z
- does not represent an entire operand; it represents a character within an
- operand.
-
- You start a C-loop with #C, followed by one of the 4 letters W,X,Y, or Z,
- followed by a single operand-specifier. Following the #Cxn is the text of
- the C-loop. The C-loop ends with #EC. The macro will loop once for every
- character in the operand. That single character will be substituted for each
- instance of the indicated variable-operand. For example:
-
- PUSHC MACRO #CW1
- PUSH #WX
- #EC#EM
-
- PUSHC ABC ; generates the 3 instructions PUSH AX; PUSH BX; PUSH CX
-
- If the C-operand is quoted in the macro call, the quotes ARE removed from
- the operand before passing characters to the loop. It is not necessary
- to precede the quoted string with a pound-sign in this case. If you do,
- the pound-sign will be passed as the first character.
-
- If the C-operand is a null operand (no characters in it), the loop-text
- is skipped completely.
-
-
-
- The "B"-before and "A"-after operators
-
- So far, we have seen that you can specify operands in your macro in fourteen
- different ways: 1,2,3,4,5,6,7,8,9,W,X,Y,Z,L. We now multiply these 14
- possibilities, by introducing the "A" and "B" operators. You can precede any
- of the 14 specifiers with "A" or "B", to get the adjacent operand after
- or before the specified operand. For example, BL means the operand just
- before the last operand; in other words, the second-to-the-last operand.
- AZ means the operand just after the Z operand. You can even repeat, up to
- a limit of 4 "B"s or 3 "A"s: BBL is the third-to-last operand; #AAA9 can be
- used where you would want to (but cannot) use #12.
-
- In the case of the variable operand to a C-loop, the "A" and "B" specifiers
- denote the characters before or after the current looping-character. An
- example of this is given in the next section.
-
-
-
- Multiple-increments within loops
-
- We have seen that you end an R-loop with a #ER, and you end a C-loop with
- a #EC. We now present another way to end these loops; a way that lets you
- specify a larger increment to the macro's loop-counter. You can end your
- loops with one of the 4 additional commands #E1, #E2, #E3, or #E4.
-
- For R-loops terminated by #ER, the variable-operand advances to the next
- operand when the loop is made. If you end your R-loop with #E2, the variable-
- operand advances 2 operands, not just one. For #E3, it advances 3 operands;
- for #E4, 4 operands. The #E1 command is the same as #ER.
-
- The most common usage of this feature is as follows: You will recall that we
- generalized the CLEAR macro with an R-loop, so that it would take an
- indefinite number of operands. Suppose we want to do the same thing with
- the DBW macro. We would like DBW to take any number of operands, and
- alternate DBs and DWs indefinitely on the operands. This is made possible
- by creating an R-loop terminated by #E2:
-
- DBW MACRO #RX1L
- DB #X
- DW #AX
- #E2
- #EM
-
- DBW 'E',E_POINTER, 'W',W_POINTER ; two pairs on the same line!
-
- The #E2 terminator means that we are looping on a pair of operands.
- Note the crucial usage of the "A"-after operator to specify the second
- operand of the operand-pair.
-
- A special note applies to the DBW macro above: the assembler just happens to
- accept a DW directive with no operands (it generates no object code, and
- issues no error). This means that DBW will accept an odd number of operands
- with no error, and do the expected thing (it alternates bytes and words, ending
- with a byte).
-
- You could likewise genralize a macro with 3 or 4 operands, to an indefinite
- number of triples or quadruples; by ending the R-loop with #E3 or #E4. The
- operands in each group would be specified by #X, #AX, #AAX, and, for #E4,
- #AAAX.
-
- For C-loops terminated by #E1 through #E4, the character-pointer is advanced
- the specified number of characters. You use this in much the same way as for
- R-loops, to create loops on pairs, triplets, and quadruplets of characters.
- For example:
-
- PUSHC2 MACRO #CZ1
- PUSH #Z#AZ
- #E2
- #EM
-
- PUSHC2 AXBXSIDI ; generates PUSH AX; PUSH BX; PUSH SI; PUSH DI
-
- Negative R-loops
-
- We now introduce another form of R-loop, called the Q-loop-- the negative
- repeat-loop. This loop is the same as the R-loop, except that the operand
- number decrements instead of increments; and the loop exits when the number
- falls below the finish-number, not above it. The Q-loop is specified by
- #Qxnn instead of #Rxnn, and #EQ instead of #ER. You can also use the
- multiple-decrement forms #E1 #E2 #E3 or #E4 to terminate an Q-loop.
-
- Example:
-
- MOVN MACRO #QXL2 ; "negative-repeat X from L down to 2"
- MOV #BX,#X
- #EQ#EM
-
- MOVN AX,BX,CX,DX ; generates the three instructions:
- ; MOV CX,DX
- ; MOV BX,CX
- ; MOV AX,BX
-
- Note: the above functionality is already built into the MOV instruction of
- the assembler. The macro shows how you would implement it if you did not
- already have this facility.
-
-
-
- Nesting of loops in macros
-
- This macro facility allows nesting of loops within each other. Since we
- provide the 4 identifiers W,X,Y,Z for the loop-operands, you can nest to
- a level of 4 without restriction-- just use a different letter for each
- nesting level. You can nest even deeper, subject to the restriction that
- a letter W,X,Y,Z refers to the innermost containing loop that defines it.
-
-
-
- Implied closing of loops
-
- If you have a loop or loops ending when the macro ends, and if the iteration
- count for those loops is 1, you may omit the #ER, #EC, or #EQ. The assembler
- closes all open loops when it sees #EM, with no error.
-
- For example, if you omit the #ER for the loop-version of the CLEAR macro,
- it would make no difference-- the assembler automatically places an #ER
- code into the macro definition for you.
-
- Local labels in macros
-
- Some assemblers have a LOCAL pseudo-op that is used in conjunction with
- macros. Symbols declared LOCAL to a macro have unique (and bizarre)
- symbol-names substituted for them each time the macro is called. This
- solves the problem of duplicate label definitions when a macro is called
- more than once.
-
- In this assembler, the problem is solved more elegantly, by having a class
- of generic local labels throughout assembly, not just in macros. Recall
- that symbols consisting of a single letter, followed by one or more
- decimal digits, can be redefined. You can use such labels in your macro
- definitions.
-
- I have recommended that local labels outside of macros be designated L1
- through L9. Within macro definitions, I suggest that you use labels
- M1 through M9. If you used an Ln-label within a macro, you would have
- to make sure that you never call the macro within the range of definition
- of another Ln-label with the same name. By using Mn-labels, you avoid
- such potential conflicts.
-
- The following example of a local label within a macro is taken from the
- source of the macro-processor itself:
-
- ; "JPOUND label" checks to see if AL is a pound sign. If it is, it processes
- ; the pound-sign term, and jumps to label. Otherwise, it drops through
- ; to the following code.
-
- JPOUND MACRO
- CMP AL,'##' ; is the scanned character a pound-sign?
- JNE >M1 ; skip if not
- CALL MDEF_POUND ; process the pound sign
- JMP #1 ; jump to the label provided
- M1:
- #EM
-
- ...
- L3: ; loop here to consume empty lines and leading blanks
- CALL SKIP_BLANKS ; skip over the leading blanks of a line
- INC SI ; advance source pointer beyond the next non-blank
- JPOUND L3 ; if pound-sign then process, and consume more blanks
- CMP AL,0A ; were the blanks terminated by a linefeed?
- JE L3 ; loop if yes, nothing on this line
- L5: ; loop here after a line is seen to have contents
- CMP AL,';' ; have we reached the start of a comment?
- JE L1 ; jump if yes, to consume the comment
- JPOUND >L6 ; if pound-sign then process it, and get another char
- ...
- L6:
- LODSB ; fetch the next definition-char from the source
- CMP AL,' ' ; is it blank?
- JA L5 ; loop if not, to process it
- ...
-
-
-
- Debugging macro expansions
-
- There is a tool called EXMAC which will help you troubleshoot program lines
- that call macros. If you are not sure about what code is being generated by
- your macro calls, EXMAC will tell you. See the file EXMAC.DOC for details.
-