Menu

Go to english version.

Rechtliches

Copyright (C) 2019 Dr. Thomas Redelberger, redethohtCat.svggmx.de.

Die Information hier wird ohne jegliche Gewährleistung und ohne jegliche Garantie gegeben. Der Code kann im jedem Projekt verwendet werden. Die einzige Bedingung ist, dass mein Name und meine E-Mail-Adresse in einem frei zugänglichen "Credits"-Abschnitt des Projekts und/oder des Produkts erscheinen muss.

Nützliche Makros für AVRASM2

AVRASM2 ist ein kompletter Makro-Assembler. Ihm fehlen aber einige nützliche Funktionen:

Zusätzlich gibt es einige Dinge, die man in den meisten Projekten braucht, wie:

Ich habe eine Reihe von Makros geschrieben, um diese Dinge abzubilden. Sie können in AVR-Assembler-Projekten verwendet werden. Ich habe dokumentiert, wie man den AVRASM-Assembler für komplexe Projekte einsetzen kann.

Quelltext herunterladen

Überblick

Makro ldiw

Oft muss man ein Register-Paar mit einem 16‑Bit Wert laden, zum Beispiel einer RAM-Adresse:

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

Das Makro ldiw erlaubt, das kürzer zu schreiben:

        ldiw    z, address

Das schaut dann so aus, als ob der Prozessor eine ldiw Instruktion hat, aber nach der Makro-Ersetzung werden das natürlich wieder dieselben beiden Prozessor-Instruktionen wie oben.

Makro MtrClrMem

Es ist nicht zugesichert, dass das RAM eines AVR-Prozessors nach dem Einschalten Null ist, geschweige denn nach einem Reset oder einer anderen Neustart-Bedingung (z.B. "brown-out"). Daher muss man Variablen im RAM meistens ausdrücklich auf Null setzen.

MtrClrMem löscht ein bis 256 Bytes.

        MtrClrMem   CONSTANT

Makro MtrCheckDSEGspace

AVRASM2 berechnet das verwendete RAM ausschließlich auf Basis der .byte Assembler-Anweisung. Das kann man im Assembler-Listing ausdrücklich genau so lesen. Um aber Variablen mittels Makros anzulegen ‑ was für objektorientierte Programmierung unerlässlich ist ‑ benutze ich oft die .org Assembler-Anweisung.

Indem ich

        MtrCheckDSEGspace

ans Ende des Source-Codes setze, prüfe ich auf das verbleibende RAM ab. Das Makro gibt eine Fehlermeldung aus, falls zu viel RAM verbraucht wurde.

Ein Satz von Makros für Switch-Konstrukte wie in C

If ... Then ... Else sind in Assembler leicht zu erstellen, Switch Case-Konstrukte sind dagegen schwierig. Deshalb habe ich einen Weg gesucht, wie man das mit Assembler-Makros nachbauen kann.

In Assembler möchte ich die gleiche Funktionalität zur Verfügung haben, wie in C:

Ein typischer Anwendungsfall sind einfache Fall-Unterscheidungen, wie z.B. die verschiedenen Status-Register-Fälle beim AVR-TWI-Modul.

Es gibt verschiedene Möglichkeiten, Switch-Konstrukte umzusetzen. Am schnellsten aber auch aufwändigsten sind Sprungtabellen. Die einfachsten sind Abfragen mit Vergleichs- und Sprung-Anweisungen.

Ich habe mich für letzteres entschieden, auch wenn das bei komplexen Fällen langsamer ist, als mit Sprungtabellen.

Es folgt Beispiel-C-Source-Code für ein Switch mit drei Fällen und Default-Zweig: (NA, NB, NC seien symbolische Konstanten, die man zum Beispiel mit dem C-Prä-Prozessor definieren kann).

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

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

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

    default:
        ...default case Statements...;
    }

Eine naive Implementierung in Assembler könnte so aussehen:

        cmp r24, NA
        breq    A

        cmp r24, NB
        breq    B

        cmp r24, NC
        breq    C

        ...default case Statements...
        rjmp    ends

A:
        ...Statements A...
        rjmp    ends

B:
        ...Statements B...
        rjmp    ends

C:
        ...Statements C...
;       rjmp    ends


ends:

Diese Implementierung ist einfach und übersichtlich. Sie funktioniert aber nur, solange die Code-Teile durch die breq-Anweisungen erreichbar sind und das sind nur 64 Byte.

Falls der Code länger ist, könnte man schreiben

        cmp r24, NA
        brne    PC+2
        rjmp    A

Aber dadurch verliert man an Geschwindigkeit.

Um das zu vermeiden, könnte man schreiben

        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:
        ...default case Statements...

ends:

Diese Variante ist meines Erachtens aber viel schlechter zu lesen, besonders, wenn die Code-Stücke länger sind. Denn die Label bezeichnen jetzt nicht mehr die Code-Alternativen, sondern die Vergleichs-Anweisungen.

Es wird noch viel unübersichtlicher, wenn man mehrere Switch-Konstrukte im selben Programm-Teil hat oder gar verschachtelte Switch-Konstrukte.

Also musste eine Makro-Lösung her, die die Übersicht gewährleistet. Für obiges Beispiel sieht meine Lösung so aus:

        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

Es gibt hier keine Labels mehr, die den Code unübersichtlich machen. Die Labels werden von den Makros automatisch erzeugt.

Ein Bezeichner (das muss ein Literal sein, hier "Ident") identifiziert das Switch-Konstrukt. Damit sind auch geschachtelte Switch-Konstrukte möglich. Der Bezeichner muss im Source-Code eindeutig sein.


Last change: 2021-12-10
© 2002-2023 Dr. Thomas Redelberger redethogmx.de

Close menu