Zur deutschen Version.

Legal issues

Copyright (C) 2019 Dr. Thomas Redelberger, All rights reserved.

The information here is provided as-is, with no warranty of any kind. The source code provided may be used freely in any project. The only condition being that my name and e-mail address must be quoted in a publicly available "credits" section of your project and/or product.

Utility Macros for AVRASM2

While AVRASM is quite a full featured macro assembler, some features are missing:

Furthermore there are often similar tasks that need to be done in most projects, like:

I have written a set of utility macros to cover those items. They are to be used in AVR assembly language projects. I have documented how to use advanced assembler features.

Download source code


Macro ldiw

Quite often, you need to load a register pair with a 16‑bit immediate value, for example a RAM address, like

        ldi     zl,  LOW(address)
        ldi     zh, HIGH(address)

The macro ldiw allows to shorten this to

        ldiw    z, address

Thus this looks like the processor had a ldiw instruction, but of course after macro expansion this becomes again the two processor instructions shown above.

Macro MtrClrMem

The state of RAM in an AVR device is not guaranteed to be zero after power-up let alone after a reset or after other restart conditions (e.g. "brown-out"). Hence you usually need to clear variables in RAM explicitly at program initialisation time.

MtrClrMem clears 1 to 256 bytes to zero.

        MtrClrMem   CONSTANT

Macro MtrCheckDSEGspace

AVRASM2 calculates the used RAM based on the use of the .byte assembler directive only. This is explicitly stated in the assembler listing file. However, to allocate variables using macros ‑ which is needed in object orientated programming ‑ I often use the .org assembler directive.



at the end of the code will check for the remaining RAM and throw an assembler error message if too much RAM has been allocated.

A set of macros to provide C-language like switch constructs

If ... Then ... Else constructs are relatively easy to code in assembly language. However Switch Case constructs are difficult. Hence I developed a set of assembler macros to provide Switch Case functionality.

I wanted to have the same functionality available in assembler like in the C-language:

An example use case is handling the different status register values of the AVR TWI module.

There are different options to implement switch constructs. The fastest but most complex to generate are jump-tables. The most simple are comparisons plus branch instructions.

I opted for the latter, even though it is slower for complex cases.

This is example C-source-code for a switch with three cases and a default case (NA, NB, NC be symbolic constants, for example using the C-pre-processor).

    switch variable {
    case    NA:
        ...Statements A...;

    case    NB:
        ...Statements B...;

    case    NC:
        ...Statements C...;

        ...default case Statements...;

A naive implementation in assembler could look like:

        cmp r24, NA
        breq    A

        cmp r24, NB
        breq    B

        cmp r24, NC
        breq    C

        ...default case Statements...
        rjmp    ends

        ...Statements A...
        rjmp    ends

        ...Statements B...
        rjmp    ends

        ...Statements C...
;       rjmp    ends


This is simple and easy to understand. But it only works like this if the code fragments can be reached by the breq instructions i.e. they are a maximum 64 bytes away.

When the code is longer, you could instead write

        cmp r24, NA
        brne    PC+2
        rjmp    A

But the code gets slower.

To avoid the issue you could write like

        cmp r24, NA
        brne    tB

        ...Statements A...
        rjmp    ends

tB:     cmp r24, NB
        brne    tC

        ...Statements B...
        rjmp    ends

tC:     cmp r24, NC
        brne    default

        ...Statements C...
        rjmp    ends

        ...default case Statements...


But this is harder to read and understand, especially when the code blocks get longer. The main reason is that the labels do not any more stand for the case code blocks but for the compare instructions.

And this gets worse when you have multiple switch constructs in the same procedure or even nested switch statements.

My main objective for the macro solution was to keep the code readable. For above example it looks like:

        MtrSwitch   Ident

        cpi r24, NA
        MtrCaseEq   Ident
        ...Statements A...
        MtrCaseEqEndBrk Ident

        cpi r24, NB
        MtrCaseEq   Ident
        ...Statements B...
        MtrCaseEqEndBrk Ident

        cpi r24, NC
        MtrCaseEq   Ident
        ...Statements C...
        MtrCaseEqEndBrk Ident

        ...default case Statements...

        MtrSwitchEnd    Ident

There are no more labels to clutter the source code. Instead the labels are generated automatically by the macros.

There has to be a unique identifier for the whole switch construct. This is the literal "Ident" in above example. Using such identifiers allows to nest the switch constructs. The identifier has to be unique in the complete source code.

Last change: 2021-12-10
© 2002-2024 Dr. Thomas Redelberger redetho(a‍t)

Close menu