Pfad: Home => AVR-Überblick => Programmiertechniken => Register

Programmiertechnik für Anfänger in AVR Assemblersprache

Was ist ein Register?

Register sind besondere Speicher mit je 8 Bit Kapazität. Sie sehen bitmäßig daher etwa so aus:
7654 3210

Man merke sich die Nummerierung der Bits: sie beginnt immer bei Null.
In einen solchen Speicher passen Zahlen von 0 bis 255 (Ganzzahl ohne Vorzeichen), von -128 bis +127 (Ganzzahl mit Vorzeichen in Bit 7), ein Acht-Bit- ASCII-Zeichen wie z.B. 'A' oder auch acht einzelne Bits, die sonst nix miteinander zu tun haben (z.B. einzelne Flaggen oder Flags).
Das Besondere an diesen Registern (im Gegensatz zu anderen Speichern) ist, dass sie Es gibt 32 davon in jedem AVR. Auch der kleinste Typ hat schon so viele davon. Das macht die AVR ziemlich einzigartig, da dadurch viele Kopieraktionen und der langsamere Zugriff auf andere Speicherarten oft nicht nötig ist. Die Register werden mit R0 bis R31 bezeichnet, man kann ihnen mit einer Assemblerdirektive aber auch einen etwas wohlklingenderen Namen verpassen, wie z.B.

.DEF MeinLieblingsregister = R16

Assemblerdirektiven gibt es einige (mehr Assemblerdirektiven gibt es hier), sie stellen Regie-Anweisungen an den Assembler dar und erzeugen selbst keinen ausführbaren Code. Sie beginnen immer mit einem Punkt.

Statt des Registernamens R16 wird dann fürderhin immer der neue Name verwendet. Das könnte also ein schreibintensives Programm werden.

Mit dem Befehl

   LDI MeinLieblingsRegister, 150

was in etwa bedeutet: Lade die Zahl 150 in das Register R16, aber hurtig, (in englisch: LoaD Immediate) wird ein fester Wert oder eine Konstante in mein Lieblingsregister geladen. Nach dem Übersetzen (Assemblieren) ergibt das im Programmspeicher etwa folgendes Bild:

000000 E906

In E906 steckt sowohl der Load-Befehl als auch das Zielregister (R16) als auch die Konstante 150, auch wenn man das auf den ersten Blick nicht sieht. Auch dies macht Assembler bei den AVR zu einer höchst effektiven Angelegenheit: Instruktion und Konstante in einem einzigen Befehlswort und schnell und effektiv ausgeführt. Zum Glück müssen wir uns um diese Übersetzung des Befehlsworts nicht kümmern, das macht der Assembler für uns.

In einem Befehl können auch zwei Register vorkommen. Der einfachste Instruktion dieser Art ist der Kopierbefehl MOV. Sie kopiert den Inhalt des einen Registers in ein anderes Register. Also etwa so:

.DEF MeinLieblingsregister = R16
.DEF NochEinRegister = R15
   LDI MeinLieblingsregister, 150
   MOV NochEinRegister, MeinLieblingsregister


Die ersten beiden Zeilen dieses großartigen Programmes sind Direktiven, die ausschließlich dem Assembler mitteilen, dass wir anstelle der beiden Registernamen R16 und R15 andere Benennungen zu verwenden wünschen. Sie erzeugen keinen Code! Die beiden Programmzeilen mit LDI und MOV erzeugen Code, nämlich:

000000 E906
000001 2F01


Die zweite Instruktion schiebt die 150 im Register R16 in das Rechenwerk und kopiert dessen Inhalt in das Zielregister R15. MERKE:

Das erstgenannte Register im Assemblerbefehl ist immer das Zielregister, das das Ergebnis aufnimmt!

(Also so ziemlich umgekehrt wie man erwarten würde und wie man es ausspricht. Deshalb sagen viele, Assembler sei schwer zu lernen!)


Zum Seitenanfang

Unterschiede der Register

Schlaumeier würden das obige Programm vielleicht eher so schreiben:

.DEF NochEinRegister = R15
   LDI NochEinRegister, 150


Und sind reingefallen: Nur die Register R16 bis R31 lassen sich hurtig mit einer Konstante laden, die Register R0 bis R15 nicht! Diese Einschränkung ist ärgerlich, ließ sich aber bei der Konstruktion der Assemblersprache für die AVRs wohl kaum vermeiden.

Es gibt eine Ausnahme, das ist das Nullsetzen eines Registers. Diese Instruktion

    CLR MeinLieblingsRegister

ist für alle Register zulässig.

Diese zwei Klassen von Registern gibt es ausser bei LDI noch bei folgenden Instruktionen: Rx muss bei diesen Instruktionen ein Register zwischen R16 und R31 sein! Wer also vorhat, solche Instruktionen zu verwenden, sollte ein Register oberhalb von R15 dafür auswählen. Das programmiert sich dann leichter. Noch ein Grund, die Register mittels .DEF umzubenennen: in größeren Programmen wechselt sich leichter ein Register, wenn man ihm einen besonderen Namen gegeben hat.

Zum Seitenanfang

Pointer-Register

Noch wichtigere Sonderrollen spielen die Registerpaare R26/R27, R28/R29 und R30/R31. Diese Pärchen sind so wichtig, dass man ihnen in der AVR-Assemblersprache extra Namen gegeben hat: X, Y und Z. Diese Doppelregister sind als 16-Bit-Pointerregister definiert. Sie werden gerne bei Adressierungen für internes oder externes RAM verwendet (X, Y und Z) oder als Zeiger in den Programmspeicher (Z).

Bei den 16-Bit-Pointern befindet sich das niedrigere Byte der Adresse im niedrigeren Register, das höherwertige Byte im höheren Register. Die beiden Teile haben wieder eigene Namen, nämlich ZH (höherwertig, R31) und ZL (niederwertig, R30). Die Aufteilung in High und Low geht dann etwa folgendermaßen:

.EQU Adresse = RAMEND ; In RAMEND steht die höchste SRAM-Adresse des Chips
   LDI YH,HIGH(Adresse)
   LDI YL,LOW(Adresse)


Für die Pointerzugriffe selbst gibt es eine Reihe von Spezial-Zugriffs-Kommandos zum Lesen(LD=Load) und Schreiben (ST=Store), hier am Beispiel des X-Zeigers:
ZeigerVorgangBeispiele
XLese/Schreibe von der Adresse X und lasse den Zeiger unverändertLD R1,X
ST X,R1
X+Lese/Schreibe von der Adresse X und erhöhe den Zeiger anschließend um EinsLD R1,X+
ST X+,R1
-XVermindere den Zeiger um Eins und lese/schreibe dann erst von der neuen AdresseLD R1,-X
ST -X,R1
Analog geht das mit Y und Z ebenso.

Für das Lesen aus dem Programmspeicher gibt es nur den Zeiger Z und die Instruktion LPM. Er lädt das Byte an der Adresse Z in das Register R0. Da im Programmspeicher jeweils Worte, also zwei Bytes stehen, wird die Adresse mit zwei multipliziert und das unterste Bit gibt jeweils an, ob das untere oder obere Byte des Wortes im Programmspeicher gelesen werden soll. Also etwa so:

   LDI ZH,HIGH(2*Adresse)
   LDI ZL,LOW(2*Adresse)
   LPM


Nach Erhöhen des Zeigers um Eins wird das zweite Byte des Wortes im Programmspeicher gelesen. Da die Erhöhung des 16-Bit-Speichers um Eins auch oft vorkommt, gibt es auch hierfür einen Spezialbefehl für Zeiger:

   ADIW ZL,1
   LPM


ADIW heisst soviel wie ADdiere Immediate Word und kann bis maximal 63 zu dem Wort addieren. Als Register wird dabei immer das untere Zeigerregister angegeben (hier: ZL). Der analoge Befehl zum Zeiger vermindern heißt SBIW (SuBtract Immediate Word). Anwendbar sind die beiden Befehle auf die Registerpaare X, Y und Z sowie auf das Doppelregister R24/R25, das keinen eigenen Namen hat und auch keinen Zugriff auf RAM- oder sonstige Speicher erm´┐Żglicht. Es kann als 16-Bit-Wert optimal verwendet werden.

Wie bekommt man aber nun die Werte, die ausgelesen werden sollen, in den Programmspeicher? Dazu gibt es die DB- und DW-Anweisungen für den Assembler. Byteweise Listen werden so erzeugt:

.DB 123,45,67,78 ; eine Liste mit vier Bytes
.DB "Das ist ein Text. " ; eine Liste mit einem Text

Auf jeden Fall ist darauf achten, dass die Anzahl der einzufügenden Bytes pro Zeile geradzahlig sein muss. Sonst fügt der Assembler ein Nullbyte am Ende hinzu, das vielleicht gar nicht erwünscht ist.
Das Problem gibt es bei wortweise organisierten Tabellen nicht. Die sehen so aus:

.DW 12345,6789 ; zwei Worte

Statt der Konstanten können selbstverständlich auch Labels (Sprungadressen) eingefügt werden, also z.B. so:

Label1:
[... hier kommen irgendwelche Befehle...]
Label2:
[... hier kommen noch irgendwelche Befehle...]
Sprungtabelle:
.DW Label1,Label2


Beim Lesen per LPM erscheint übrigens das niedrigere Byte der 16-Bit-Zahl zuerst!

Und noch was für Exoten, die gerne von hinten durch die Brust ins Auge programmieren: Die Register sind auch mit Zeigern lesbar und beschreibbar. Sie liegen an der Adresse 0000 bis 001F. Das kann man nur gebrauchen, wenn man auf einen Rutsch eine Reihe von Registern in das RAM kopieren will oder aus dem RAM laden will. Lohnt sich aber erst ab 5 Registern.

Zum Seitenanfang

Empfehlungen zur Registerwahl

  1. Register immer mit der .DEF-Anweisung festlegen, nie direkt verwenden. Das ist praktischer, weil Verwechslungen vermieden und Doppelverwendungen erkannt werden. Wer es noch praktischer haben will, setzt vor den Namen ein kleines r, also z. B. rZaehler.
  2. Werden Pointer-Register für RAM u.a. benötigt, R26 bis R31 dafür reservieren.
  3. 16-Bit-Zähler oder ähnliches realisiert man am besten im Registerpaar R24/R25, weil es ADIW und SBIW und nicht als Pointer verwendet werden kann.
  4. Soll aus dem Programmspeicher gelesen werden, Z (R30/31) und R0 dafür reservieren.
  5. Werden oft konstante Werte oder Zugriffe auf einzelne Bits in einem Register verwendet, dann die Register R16 bis R23 dafür vorzugsweise reservieren.
  6. Muss bei Interrupts der Inhalt des Flaggenregisters gesichert werden, vorzugsweise R15 für diesen Zweck verwenden.
  7. Für alle anderen Anwendungsfälle vorzugsweise R1 bis R14 verwenden.


Zum Seitenanfang

©2002-2010 by http://www.avr-asm-tutorial.net