home *** CD-ROM | disk | FTP | other *** search
- CHAPTER 11 MACROS AND CONDITIONAL ASSEMBLY
-
-
- Macro Facility
-
- A86 contains an easy-to-use, but very powerful macro facility.
- The facility subsumes the capabilities of most assemblers,
- including operand concatenation, repeat, indefinite repeat (often
- called IRP), indefinite repeat character (IRPC), passing macro
- operands by text or by value, comparing macro operands to
- strings, and detecting blank macro operands. Unlike other
- assemblers, A86 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. A86 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 AL,#2
- MOV #1,AL
- #EM
-
- VAR1 DB ?
- VAR2 DB ?
-
- MOVM VAR1,VAR2 ; generates MOV AL,VAR2 followed by MOV VAR1,AL
- 11-2
-
- 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: A86 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 hash sign # (a hex 23 byte). The letters following the hash
- sign can be given in either upper case or lower case. Hash-sign
- operators are recognized even within quoted strings. If you wish
- the hash sign to be treated literally, and not as the start of a
- special macro operator, you must give 2 consecutive hash 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. A86 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 A86 source
- lines, a semi-colon occurring outside of a quoted string is the
- start of a comment, ignored by A86. 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". As its default, A86 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 A86 only after a complete
- line is expanded and as it is assembled.
- 11-3
-
- If the first non-blank character after the macro name is a comma,
- then the first operand is null: any occurrences 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, consisting 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 hash
- sign. Examples:
- 11-4
-
- 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
-
- FETCH_CHAR MACRO
- LODSB
- #1
- CALL PROCESS_CHAR
- #EM
-
- FETCH_CHAR STOSB ; generates STOSB as second instruction
- FETCH_CHAR #'INC DI' ; generates INC DI as second instruction
-
-
-
- Looping by Operands in Macros
-
- A86's 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.
- 11-5
-
- The #L Last Operator and Indefinite Repeats
-
- A86 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 ; generates both SUB AX,AX and SUB BX,BX in one macro!
-
- 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-- a digit,
- the letter L, another one of W,X, Y, or Z defined in an outer
- loop, or one of the more complicated specifiers defined later in
- this chapter. 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 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 hash sign
- in this case. If you do, the hash sign will be passed as the
- first character.
- 11-6
-
- 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: for example, BBL is the
- third-to-last operand.
-
- Note that any operand specifier can appear in contexts other than
- by itself following a # within a macro. For example, BBL could
- appear as the upper limit to an R-loop: #RZ1BBL loops with Z
- running from the first operand to the third-to-last operand.
-
- 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 the #L-variable,
- 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 same line!
- 11-7
-
- 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: A86 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 generalize 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 goes 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 A86. The macro shows how you would implement it
- if you did not already have this facility.
- 11-8
-
- Nesting of Loops in Macros
-
- A86 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, for example,
- by having two nested R-loops that use W is its indexing letter.
- The only restriction to this is that you cannot refer to the W of
- the outer loop from within the inner W loop. (I challenge anyone
- to come up with an application in which these limitations /
- restrictions cause a genuine inconvenience!)
-
-
- 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. A86 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-- A86 automatically
- places an #ER code into the macro definition for you.
-
-
- Passing Operands by Value
-
- As already stated, A86's defualt mode for passing operands is by
- text-- the characters of the operand are copied to the macro
- expansion line as-is, without any evaluation. You may override
- this with the #V operator. When A86 sees #Vn in a macro
- definition, it will evaluate the expression given in the text of
- operand n, and pass a string representing the decimal constant
- answer, instead of the original text. The operand must evaluate
- to an absolute constant value, less than 65536. For example:
-
- JLV MACRO
- J#1 LABEL#V2
- #EM
-
- JINDEX = 3
- JLV NC,JINDEX+1 ; generates JNC LABEL4
- JINDEX = 6
- JLV Z,JINDEX+2 ; generates JZ LABEL8
-
-
- Passing Operand Size
-
- The construct #Sn is translated by A86 into the decimal string
- representing the number of characters in operand n. One use of
- this would be to make a conditional-assembly test of whether an
- operand was passed at all, as we'll see later in this chapter.
- Another use is to generate a length byte preceding a string, as
- required by some high-level languages such as Turbo Pascal.
- Example:
- 11-9
-
- LSTRING MACRO
- DB #S1,'#1'
- #EM
-
- LSTRING SAMPLE ; generates DB 6,'SAMPLE'
-
-
- Generating the Number of an Operand
-
- The construct #Nn is translated by A86 into the decimal string
- represented by the position number n of the macro operand. Note
- that this value does not depend on the contents of the operand
- that was passed to the macro. Thus, for example, #N2 would
- translate simply to 2; so this usage of #N is silly. #N achieves
- usefulness when n is variable: W,X,Y,Z, or L. I give an example
- of #N with a loop-control variable in the next section. Here is
- an example of #NL, used to generate an array of strings, preceded
- by a byte telling how many strings are in the array:
-
- ZSTRINGS MACRO
- DB #NL ; generates the number of operands passed
- #RX1L
- DB '#X',0
- #EM
-
- ZSTRINGS TOM,DICK,HARRY ; generates DB 3 followed by strings
-
-
- Parenthesized Operand Numbers
-
- We've seen that macro operands are usually specified in your
- macro definition by a single character: either a single digit or
- one of the special letters W,X,Y,Z, or L. A86 also allows you to
- specify a constant operand number up to 255. You do so by giving
- an expression enclosed in parentheses, rather than a single
- character. The expession must evaluate at the time the macro is
- defined, to a constant between 0 and 255. You can use this
- feature to translate many programs that use MASM's REPT
- directive. For example, if the following REPT construct occurs
- within a MASM macro:
-
- TEMP = 0
- REPT 100
- TEMP = TEMP + 1 ; MASM needs an explicitly-set-up counter
- DB TEMP
- ENDM
-
- you may translate it into an A86 loop, as follows:
-
- #RX1(100) ; the counter X is built into the A86 loop
- DB #NX
- #ER
-
- If the REPT does not occur within a macro, you must define a
- macro containing the loop, which you may then immediately call.
- 11-10
-
- Note that the expression enclosed in praentheses must not itself
- contain any macro operators. Thus, for example, you cannot
- specify #(#NY+1) to represent the operand after Y-- you must use
- #AY.
-
-
- Exiting from the Middle of a Macro
-
- For MASM compatibility, A86 offers the #EX operator, which is
- equivalent to MASM's EXITM directive. #EX is typically used in a
- conditional assembly block within a loop, to terminate the loop
- early. When the #EX code is seen in a macro expansion, the
- expansion ceases at that point, and assembly returns to the
- source file (or to the outer macro in a nested call). You
- couldn't use #EM to do this, because that would signal the end of
- the macro definition, not just the call.
-
-
- 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 A86, 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:
-
- ; "JHASH label" checks to see if AL is a hash sign. If it is,
- ; it processes the hash sign term, and jumps to label.
- ; Otherwise, it drops through to the following code.
-
- JHASH MACRO
- CMP AL,'##' ; is the scanned character a hash sign?
- JNE >M1 ; skip if not
- CALL MDEF_HASH ; process the hash sign
- JMP #1 ; jump to the label provided
- M1:
- #EM
- 11-11
-
- ...
- L3: ; loop here to eat empty lines, leading blanks
- CALL SKIP_BLANKS ; skip over the leading blanks of a line
- INC SI ; advance source ptr beyond the next non-blank
- JHASH L3 ; if hash sign then process, and eat 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
- JHASH >L6 ; if hash sign then process it; get next 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 Chapter 13 for details.
-
-
- Conditional Assembly
-
- A86 has a conditional assembly feature, that allows you to
- specify that blocks of source code will or will not be assembled,
- according to the values of equated user symbols. The controlling
- symbols can be declared in the program (and can thus be the
- result of assembly-time expressions), or they can be declared in
- the assembler invocation.
-
- You should keep in mind the difference between conditional
- assembly, invoked by #IF, and the structured-programming feature,
- invoked by IF without the hash sign. #IF tests a condition at
- assembly time, and can cause code to not be assembled and thus
- not appear in the program. IF causes code to be assembled that
- tests a condition at run time, possibly jumping over code. The
- skipped code will always appear in the program.
-
- All conditional assembly lines are identified by a hash sign # as
- the first non-blank character of a line. The hash sign is
- followed by one of the four keywords IF, ELSEIF, ELSE or ENDIF.
-
- #IF starts a conditional assembly block. On the same line,
- following the #IF, you provide either a single name, or an
- arbitrary expression evaluating to an absolute constant. In this
- context, a single name evaluates to TRUE if it is defined and not
- equal to the absolute constant zero. A name is FALSE if it is
- undefined, or if it has been equated to zero. An expression is
- TRUE if nonzero, FALSE if zero.
- 11-12
-
- If the #IF expression evaluates to FALSE, then the following
- lines of code are skipped, up to the next matching #ELSEIF,
- #ELSE, or #ENDIF. If the expression is TRUE, then the following
- lines of code are assembled normally. If a subsequent matching
- #ELSEIF or #ELSE is encountered, then code is skipped up to the
- matching #ENDIF.
-
- #ELSEIF provides a multiple-choice facility for #IF-blocks. You
- can give any number of #ELSEIFs between an #IF and its matching
- #ENDIF. Each #ELSEIF has a name or expression following it on
- the same line. If the construct following the #IF is FALSE, then
- the assembler looks for the first TRUE construct following an
- #ELSEIF, and assembles that block of code. If there are no TRUE
- #ELSEIFs, then the #ELSE-block (if there is one) is assembled.
-
- You should use the ! instead of the NOT operator in conditional
- assembly expressions. The ! operator performs the correct
- translation of names into TRUE or FALSE values, and handles the
- case !undefined without reporting an error.
-
- #ELSE marks the beginning of code to be assembled if all the
- previous blocks of an #IF have been skipped over. There is no
- operand after the #ELSE. There can be at most one #ELSE in an
- #IF-block, and it must appear after any #ELSEIFs.
-
- #ENDIF marks the end of an #IF-block. There is no operand after
- #ENDIF.
-
- It is legal to have nested #IF-blocks; that is, #IF-blocks that
- are contained within other #IF-blocks. #ELSEIF, #ELSE, and
- #ENDIF always refer to the innermost nested #IF-block.
-
- As an example of conditional assembly, suppose that you have a
- program that comes in three versions: one for Texas, one for
- Oklahoma, and one for the rest of the nation. The three programs
- differ in a limited number of places. Instead of keeping three
- different versions of the source code, you can keep one version,
- and use conditional assembly on the boolean variables TEXAS and
- OKLAHOMA to control the assembler output. A sample block would
- be:
-
- #if TEXAS
- DB 0,1,2,3
- #elseif OKLAHOMA
- DB 4,5,6,7
- #else
- DB 8,9,10,11
- #endif
-
- If a block of code is to be assembled only if TEXAS is false,
- then you would use the exclamation point operator:
-
- #if !TEXAS
- DB 0FF
- #endif
- 11-13
-
- Conditional Assembly and Macros
-
- You may have conditional assembly blocks either in macro
- definitions or in macro expansions. The only limitation is that
- if you have an #IF-block in a macro expansion, the entire block
- (i.e., the matching #ENDIF) must appear in the same macro
- expansion. You cannot, for example, define a macro that is a
- synonym for #IF.
-
- To have your conditional assembly block apply to the macro
- definition, you provide the block normally within the definition.
- For example:
-
- X1 EQU 0
- BAZ MACRO
- #if X1
- DB 010
- #else
- DB 011
- #endif
- #EM
- BAZ
- X1 EQU 1
- BAZ
-
- In the above sequence of code, the conditional assembly block is
- acted upon when the macro BAZ is defined. The macro therefore
- consists of the single line DB 011, with all the conditional
- assembly lines removed from the definition. Thus, both
- expansions of BAZ produce the object-code byte of 011, even
- though the local label X1 has turned non-zero for the second
- invocation.
-
- To have your conditional assembly block appear in the macro
- expansion, you must literalize the hash sign on each conditional
- assembly line by giving two hash signs:
-
- X1 EQU 0
- BAZ MACRO
- ##if X1
- DB 010
- ##else
- DB 011
- ##endif
- #EM
- BAZ
- X1 EQU 1
- BAZ
-
- Now the entire conditional assembly block is stored in the macro
- definition, and acted upon each time the macro is expanded. Thus,
- the two invocations of BAZ will produce the different object
- bytes 011 and 010, since X1 has become non-zero for the second
- expansion.
- 11-14
-
- You will usually want your conditional assembly blocks to be
- acted upon at macro definition time, to save symbol table space.
- You will thus use the first form, with the single hash signs.
-
-
- Simulating MASM's Conditional Assembly Constructs
-
- Microsoft's MASM assembler has an abundance of confusing
- conditional assembly directives, all of which are subsumed by
- A86's #IF expression evaluation policies. IF and IFDEF are both
- covered by A86's #IF directive. IFE and IFNDEF are duplicated by
- #IF followed by the exclamation-point (boolean negation)
- operator. IFB and IFNB test whether a macro operand has been
- passed as blank-- they can be simulated by testing the size of
- the operand with the #Sn operator. Finally, IFIDN and IFDIF do
- string comparisons of macro operands. This is more generally
- subsumed by the string-comparison capabilities of the operators
- EQ, NE, and =.
-
- Examples of translation of each of these constructs is given in
- the next chapter, on compatibility with other assemblers.
-
-
- Conditional Assembly and the XREF Program
-
- Previous versions of A86 contained a warning, that XREF will not
- correctly handle conditional-assembly blocks controlled by
- variables whose values change during assembly. Starting with
- V3.12, this has been corrected, by writing to the SYM file a log
- of each conditional-assembly test result. XREF will consult the
- log to determine which blocks to consider.
-
-
- Declaring Variables in the Assembler Invocation
-
- To facilitate the effective use of conditional assembly, A86
- allows you to declare boolean (true-false) symbols in the command
- line that invokes the assembler. The declarations can appear
- anywhere in the list of source file names. They are
- distinguished from the file names by a leading equals sign =. To
- declare a symbol TRUE (value = 1), give the name after the equals
- sign. DO NOT put any spaces between the equals sign and the
- name! To declare a symbol FALSE (value = 0), you can give an
- equals sign, an exclamation point, then the name. Again, DO NOT
- embed any blanks! Example: if your source files are src1.8,
- src2.8, and src3.8, then you can assemble with TEXAS true by
- invoking A86 as follows:
-
- a86 =TEXAS src1.8 src2.8 src3.8
-
- You can assemble with TEXAS explicitly set to FALSE as follows:
-
- a86 =!TEXAS src1.8 src2.8 src3.8
- 11-15
-
- Note that if TEXAS is used only as a conditional-assembly
- control, then you do not need to include the =!TEXAS in the
- invocation, because an undefined TEXAS will automatically be
- interpreted as false.
-
- A user pointed out to me that it's impossible to get an
- equals-sign into an environment variable. So A86 now accepts an
- up-arrow (hex 5E) character in place of an equals-sign for an
- invocation variable.
-
-
- Null Invocation Variable Names
-
- A86 will ignore an equals-sign by itself in the invocation line,
- without error. This allows you to generate assembler invocation
- lines using parameters that could be either boolean variable
- names, or null strings. For example, in the previously-mentioned
- TEXAS-OKLAHOMA-nation example, the program could be invoked via a
- .BAT file called "AMAKE.BAT", coded as follows:
-
- A86 =%1 *.8
-
- You invoke A86 by typing one of the following:
-
- amake texas
- amake oklahoma
- amake
-
- The third line will produce the assembler invocation A86 = *.8;
- causing no invocation variables to be declared. Thus both TEXAS
- and OKLAHOMA will be false, which is exactly what you want for
- the rest-of-the-nation version of the program.
-
-
- Changing Values of Invocation Variables
-
- The usual prohibition against changing the value of a symbol that
- is not a local label does not apply to invocation variables. For
- example, suppose you have a conditional control variable DEBUG,
- which will generate diagnostic code for debugging when it is
- true. Suppose further that you have already debugged source
- files src1.8 and src3.8; but you are still working on src2.8. You
- may invoke A86 as follows:
-
- A86 src1.8 =DEBUG src2.8 =!DEBUG src3.8
-
- The variable DEBUG will be TRUE only during assembly of src2.8,
- just as you want.
-
-