ca65-11.html

来自「cc65 的编译器文档」· HTML 代码 · 共 466 行

HTML
466
字号
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML><HEAD> <META NAME="GENERATOR" CONTENT="LinuxDoc-Tools 0.9.20"> <TITLE>ca65 Users Guide: Macros</TITLE> <LINK HREF="ca65-12.html" REL=next> <LINK HREF="ca65-10.html" REL=previous> <LINK HREF="ca65.html#toc11" REL=contents></HEAD><BODY><A HREF="ca65-12.html">Next</A><A HREF="ca65-10.html">Previous</A><A HREF="ca65.html#toc11">Contents</A><HR><H2><A NAME="macros"></A> <A NAME="s11">11.</A> <A HREF="ca65.html#toc11">Macros</A></H2><H2><A NAME="ss11.1">11.1</A> <A HREF="ca65.html#toc11.1">Introduction</A></H2><P>Macros may be thought of as "parametrized super instructions". Macros aresequences of tokens that have a name. If that name is used in the sourcefile, the macro is "expanded", that is, it is replaced by the tokens thatwere specified when the macro was defined.</P><H2><A NAME="ss11.2">11.2</A> <A HREF="ca65.html#toc11.2">Macros without parameters</A></H2><P>In it's simplest form, a macro does not have parameters. Here's anexample:</P><P><BLOCKQUOTE><CODE><PRE>        .macro  asr             ; Arithmetic shift right                cmp     #$80    ; Put bit 7 into carry                ror             ; Rotate right with carry        .endmacro</PRE></CODE></BLOCKQUOTE></P><P>The macro above consists of two real instructions, that are inserted intothe code, whenever the macro is expanded. Macro expansion is simply doneby using the name, like this:</P><P><BLOCKQUOTE><CODE><PRE>        lda     $2010        asr        sta     $2010</PRE></CODE></BLOCKQUOTE></P><H2><A NAME="ss11.3">11.3</A> <A HREF="ca65.html#toc11.3">Parametrized macros</A></H2><P>When using macro parameters, macros can be even more useful:</P><P><BLOCKQUOTE><CODE><PRE>        .macro  inc16   addr                clc                lda     addr                adc     #$01                sta     addr                lda     addr+1                adc     #$00                sta     addr+1        .endmacro</PRE></CODE></BLOCKQUOTE></P><P>When calling the macro, you may give a parameter, and each occurence ofthe name "addr" in the macro definition will be replaced by the givenparameter. So</P><P><BLOCKQUOTE><CODE><PRE>        inc16   $1000</PRE></CODE></BLOCKQUOTE></P><P>will be expanded to</P><P><BLOCKQUOTE><CODE><PRE>                clc                lda     $1000                adc     #$01                sta     $1000                lda     $1000+1                adc     #$00                sta     $1000+1</PRE></CODE></BLOCKQUOTE></P><P>A macro may have more than one parameter, in this case, the parametersare separated by commas. You are free to give less parameters than themacro actually takes in the definition. You may also leave intermediateparameters empty. Empty parameters are replaced by empty space (that is,they are removed when the macro is exanded). If you have a look at ourmacro definition above, you will see, that replacing the "addr" parameterby nothing will lead to wrong code in most lines. To help you, writingmacros with a variable parameter list, there are some control commands:</P><P><CODE><A HREF="ca65-10.html#.IFBLANK">.IFBLANK</A></CODE> tests the rest of the line andreturns true, if there are any tokens on the remainder of the line. Sinceempty parameters are replaced by nothing, this may be used to test if a givenparameter is empty. <CODE><A HREF="ca65-10.html#.IFNBLANK">.IFNBLANK</A></CODE> tests theopposite.</P><P>Look at this example:</P><P><BLOCKQUOTE><CODE><PRE>        .macro  ldaxy   a, x, y        .ifnblank       a                lda     #a        .endif        .ifnblank       x                ldx     #x        .endif        .ifnblank       y                ldy     #y        .endif        .endmacro</PRE></CODE></BLOCKQUOTE></P><P>This macro may be called as follows:</P><P><BLOCKQUOTE><CODE><PRE>        ldaxy   1, 2, 3         ; Load all three registers        ldaxy   1, , 3          ; Load only a and y        ldaxy   , , 3           ; Load y only</PRE></CODE></BLOCKQUOTE></P><P>There's another helper command for determining, which macro parameters arevalid: <CODE><A HREF="ca65-8.html#.PARAMCOUNT">.PARAMCOUNT</A></CODE> This command isreplaced by the parameter count given, <EM>including</EM> intermediate empty macroparameters:</P><P><BLOCKQUOTE><CODE><PRE>        ldaxy   1               ; .PARAMCOUNT = 1        ldaxy   1,,3            ; .PARAMCOUNT = 3        ldaxy   1,2             ; .PARAMCOUNT = 2        ldaxy   1,              ; .PARAMCOUNT = 2        ldaxy   1,2,3           ; .PARAMCOUNT = 3</PRE></CODE></BLOCKQUOTE></P><H2><A NAME="ss11.4">11.4</A> <A HREF="ca65.html#toc11.4">Detecting parameter types</A></H2><P>Sometimes it is nice to write a macro that acts differently depending on thetype of the argument supplied. An example would be a macro that loads a 16 bitvalue from either an immediate operand, or from memory. The <CODE><A HREF="ca65-9.html#.MATCH">.MATCH</A></CODE> and <CODE><A HREF="ca65-9.html#.XMATCH">.XMATCH</A></CODE>functions will allow you to do exactly this:</P><P><BLOCKQUOTE><CODE><PRE>        .macro  ldax    arg                .if (.match (.left (1, arg), #))                    ; immediate mode                    lda     #&lt;(.right (.tcount (arg)-1, arg))                    ldx     #>(.right (.tcount (arg)-1, arg))                .else                    ; assume absolute or zero page                    lda     arg                    ldx     1+(arg)                .endif        .endmacro</PRE></CODE></BLOCKQUOTE></P><P>Using the <CODE><A HREF="ca65-9.html#.MATCH">.MATCH</A></CODE> function, the macro is able tocheck if its argument begins with a hash mark. If so, two immediate loads areemitted, Otherwise a load from an absolute zero page memory location isassumed. So this macro can be used as</P><P><BLOCKQUOTE><CODE><PRE>        foo:    .word   $5678        ...                ldax    #$1234          ; X=$12, A=$34        ...                ldax    foo             ; X=$56, A=$78</PRE></CODE></BLOCKQUOTE></P><H2><A NAME="ss11.5">11.5</A> <A HREF="ca65.html#toc11.5">Recursive macros</A></H2><P>Macros may be used recursively:</P><P><BLOCKQUOTE><CODE><PRE>        .macro  push    r1, r2, r3                lda     r1                pha        .if     .paramcount > 1                push    r2, r3        .endif        .endmacro</PRE></CODE></BLOCKQUOTE></P><P>There's also a special macro to help writing recursive macros: <CODE><A HREF="ca65-10.html#.EXITMACRO">.EXITMACRO</A></CODE> This command will stop macro expansionimmidiately:</P><P><BLOCKQUOTE><CODE><PRE>        .macro  push    r1, r2, r3, r4, r5, r6, r7        .ifblank        r1                ; First parameter is empty                .exitmacro        .else                lda     r1                pha        .endif                push    r2, r3, r4, r5, r6, r7        .endmacro</PRE></CODE></BLOCKQUOTE></P><P>When expanding this macro, the expansion will push all given parametersuntil an empty one is encountered. The macro may be called like this:</P><P><BLOCKQUOTE><CODE><PRE>        push    $20, $21, $32           ; Push 3 ZP locations        push    $21                     ; Push one ZP location</PRE></CODE></BLOCKQUOTE></P><H2><A NAME="ss11.6">11.6</A> <A HREF="ca65.html#toc11.6">Local symbols inside macros</A></H2><P>Now, with recursive macros, <CODE><A HREF="ca65-10.html#.IFBLANK">.IFBLANK</A></CODE> and<CODE><A HREF="ca65-8.html#.PARAMCOUNT">.PARAMCOUNT</A></CODE>, what else do you need?Have a look at the inc16 macro above. Here is it again:</P><P><BLOCKQUOTE><CODE><PRE>        .macro  inc16   addr                clc                lda     addr                adc     #$01                sta     addr                lda     addr+1                adc     #$00                sta     addr+1        .endmacro</PRE></CODE></BLOCKQUOTE></P><P>If you have a closer look at the code, you will notice, that it could bewritten more efficiently, like this:</P><P><BLOCKQUOTE><CODE><PRE>        .macro  inc16   addr                inc     addr                bne     Skip                inc     addr+1        Skip:        .endmacro</PRE></CODE></BLOCKQUOTE></P><P>But imagine what happens, if you use this macro twice? Since the label"Skip" has the same name both times, you get a "duplicate symbol" error.Without a way to circumvent this problem, macros are not as useful, asthey could be. One solution is, to start a new lexical block inside themacro:</P><P><BLOCKQUOTE><CODE><PRE>        .macro  inc16   addr        .proc                inc     addr                bne     Skip                inc     addr+1        Skip:        .endproc        .endmacro</PRE></CODE></BLOCKQUOTE></P><P>Now the label is local to the block and not visible outside. However,sometimes you want a label inside the macro to be visible outside. To makethat possible, there's a new command that's only usable inside a macrodefinition: <CODE><A HREF="ca65-10.html#.LOCAL">.LOCAL</A></CODE>. <CODE>.LOCAL</CODE> declares oneor more symbols as local to the macro expansion. The names of local variablesare replaced by a unique name in each separate macro expansion. So we couldalso solve the problem above by using <CODE>.LOCAL</CODE>:</P><P><BLOCKQUOTE><CODE><PRE>        .macro  inc16   addr                .local  Skip            ; Make Skip a local symbol                clc                lda     addr                adc     #$01                sta     addr                bcc     Skip                inc     addr+1        Skip:                           ; Not visible outside        .endmacro</PRE></CODE></BLOCKQUOTE></P><H2><A NAME="ss11.7">11.7</A> <A HREF="ca65.html#toc11.7">C style macros</A></H2><P>Starting with version 2.5 of the assembler, there is a second macro typeavailable: C style macros using the <CODE>.DEFINE</CODE> directive. These macros aresimilar to the classic macro type described above, but behaviour is sometimesdifferent:</P><P><UL><LI>    Macros defined with <CODE><A HREF="ca65-10.html#.DEFINE">.DEFINE</A></CODE> may notspan more than a line. You may use line continuation (see <CODE><A HREF="ca65-10.html#.LINECONT">.LINECONT</A></CODE>) to spread the definition overmore than one line for increased readability, but the macro itselfmay not contain an end-of-line token.</LI><LI>    Macros defined with <CODE><A HREF="ca65-10.html#.DEFINE">.DEFINE</A></CODE> sharethe name space with classic macros, but they are detected and replacedat the scanner level. While classic macros may be used in every place,where a mnemonic or other directive is allowed, <CODE><A HREF="ca65-10.html#.DEFINE">.DEFINE</A></CODE> style macros are allowed anywhere in a line. Sothey are more versatile in some situations.</LI><LI>    <CODE><A HREF="ca65-10.html#.DEFINE">.DEFINE</A></CODE> style macros may takeparameters. While classic macros may have empty parameters, this isnot true for <CODE><A HREF="ca65-10.html#.DEFINE">.DEFINE</A></CODE> style macros.For this macro type, the number of actual parameters must matchexactly the number of formal parameters.To make this possible, formal parameters are enclosed in braces whendefining the macro. If there are no parameters, the empty braces maybe omitted.</LI><LI>    Since <CODE><A HREF="ca65-10.html#.DEFINE">.DEFINE</A></CODE> style macros may notcontain end-of-line tokens, there are things that cannot be done. Theymay not contain several processor instructions for example. So, whilesome things may be done with both macro types, each type has specialusages. The types complement each other.</LI></UL></P><P>Let's look at a few examples to make the advantages and disadvantagesclear.</P><P>To emulate assemblers that use "<CODE>EQU</CODE>" instead of "<CODE>=</CODE>" you may use thefollowing <CODE>.DEFINE</CODE>:</P><P><BLOCKQUOTE><CODE><PRE>        .define EQU     =        foo     EQU     $1234           ; This is accepted now</PRE></CODE></BLOCKQUOTE></P><P>You may use the directive to define string constants used elsewhere:</P><P><BLOCKQUOTE><CODE><PRE>        ; Define the version number        .define VERSION         "12.3a"        ; ... and use it        .asciiz VERSION</PRE></CODE></BLOCKQUOTE></P><P>Macros with parameters may also be useful:</P><P><BLOCKQUOTE><CODE><PRE>        .define DEBUG(message)  .out    message        DEBUG   "Assembling include file #3"</PRE></CODE></BLOCKQUOTE></P><P>Note that, while formal parameters have to be placed in braces, this isnot true for the actual parameters. Beware: Since the assembler cannotdetect the end of one parameter, only the first token is used. If youdon't like that, use classic macros instead:</P><P><BLOCKQUOTE><CODE><PRE>        .macro  message                .out    message        .endmacro</PRE></CODE></BLOCKQUOTE></P><P>(This is an example where a problem can be solved with both macro types).</P><H2><A NAME="ss11.8">11.8</A> <A HREF="ca65.html#toc11.8">Characters in macros</A></H2><P>When using the <A HREF="ca65-2.html#option-t">-t</A> option, characters are translatedinto the target character set of the specific machine. However, this happensas late as possible. This means that strings are translated if they are partof a <CODE><A HREF="ca65-10.html#.BYTE">.BYTE</A></CODE> or <CODE><A HREF="ca65-10.html#.ASCIIZ">.ASCIIZ</A></CODE> command. Characters are translated as soon as they areused as part of an expression.</P><P>This behaviour is very intuitive outside of macros but may be confusing whendoing more complex macros. If you compare characters against numeric values,be sure to take the translation into account.</P><HR><A HREF="ca65-12.html">Next</A><A HREF="ca65-10.html">Previous</A><A HREF="ca65.html#toc11">Contents</A></BODY></HTML>

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?