Next Previous Contents

9. Inline assembler

The compiler allows to insert assembler statements into the output file. The syntax is

        asm (<string literal>[, optional parameters]) ;
or
        __asm__ (<string literal>[, optional parameters]) ;

The first form is in the user namespace and is disabled by --standard if the argument is not cc65.

The asm statement may be used inside a function and on global file level. An inline assembler statement is a primary expression, so it may also be used as part of an expression. Please note however that the result of an expression containing just an inline assembler statement is always of type void.

The contents of the string literal are preparsed by the compiler and inserted into the generated assembly output, so that the can be further processed by the backend and especially the optimizer. For this reason, the compiler does only allow regular 6502 opcodes to be used with the inline assembler. Pseudo instructions (like .import, .byte and so on) are not allowed, even if the ca65 assembler (which is used to translate the generated assembler code) would accept them. The builtin inline assembler is not a replacement for the full blown macro assembler which comes with the compiler.

Note: Inline assembler statements are subject to all optimizations done by the compiler. There is currently no way to protect an inline assembler statement from being moved or removed completely by the optimizer. If in doubt, check the generated assembler output, or disable optimizations.

The string literal may contain format specifiers from the following list. For each format specifier, an argument is expected which is inserted instead of the format specifier before passing the assembly code line to the backend.

Using these format specifiers, you can access C #defines, variables or similar stuff from the inline assembler. For example, to load the value of a C #define into the Y register, one would use

        #define OFFS  23
        __asm__ ("ldy #%b", OFFS);

Or, to access a struct member of a static variable:

        typedef struct {
            unsigned char x;
            unsigned char y;
            unsigned char color;
        } pixel_t;
        static pixel_t pixel;
        __asm__ ("ldy #%b", offsetof(pixel_t, color));
        __asm__ ("lda %v,y", pixel);

The next example shows how to use global variables to exchange data between C an assembler and how to handle assembler jumps:

        unsigned char globalSubA, globalSubB, globalSubResult;

        /* return a-b, return 255 if b>a */
        unsigned char sub (unsigned char a, unsigned char b)
        {
            globalSubA = a;
            globalSubB = b;
            __asm__ ("sec");
            __asm__ ("lda %v", globalSubA);
            __asm__ ("sbc %v", globalSubB);
            __asm__ ("bcs %g", jumpSubNoError);
            __asm__ ("lda #$FF");
        jumpSubNoError:
            __asm__ ("sta %v", globalSubResult);
            return globalSubResult;
        }

Arrays can also be accessed:

        unsigned char globalSquareTable[] = {
            0, 1, 4, 9, 16, 25, 36, 49, 64, 81,
            100, 121, 144, 169, 196, 225
        };
        unsigned char globalSquareA, globalSquareResult;

        /* return a*a for a<16, else 255 */
        unsigned char square (unsigned char a)
        {
            if (a>15){
                return 255;
            }
            globalSquareA = a;
            __asm__ ("ldx %v", globalSquareA);
            __asm__ ("lda %v,x", globalSquareTable);
            __asm__ ("sta %v", globalSquareResult);
            return globalSquareResult;
        }

Note: Do not embed the assembler labels that are used as names of global variables or functions into your asm statements. Code like this

        int foo;
        int bar () { return 1; }
        __asm__ ("lda _foo");           /* DON'T DO THAT! */
        ...
        __asm__ ("jsr _bar");           /* DON'T DO THAT EITHER! */

may stop working if the way, the compiler generates these names is changed in a future version. Instead use the format specifiers from the table above:

        __asm__ ("lda %v", foo);        /* OK */
        ...
        __asm__ ("jsr %v", bar);        /* OK */


Next Previous Contents