Der Programmzähler zeigt auf die Stelle im Quellcode , die gerade abgearbeitet wird. Wenn das Programm keine Sprung- oder Entscheidungsbefehle enthält (auch keine Interrupts), dann wird Zeile für Zeile des Programms abgearbeitet und der Programmzähler entsprechend laufend erhöht.
Bei einem Unterprogrammaufruf mit RCALL wird
Die Programmzeilen des UP werden abgearbeitet und am Ende wird die auf dem Stapel abgelegte Rücksprungadresse wieder in den PC geladen und das Programm an der Stelle nach dem UP-Aufruf weiter abgearbeitet.
Erinnern wir uns.
In den folgenden Übungen wird die neue Programmstruktur konsequent aufgebaut.
Die zugrunde liegende Schaltung in dieser Übung ist für uns nicht neu; sie besteht aus einem Taster T0 und einer LED. Beide Bauteile befinden sich auf dem STK200 Board, können aber auch separat auf einem Steckbrett nach Schaltskizze aufgebaut werden.
Taster und LED | |
Aufgaben |
Programmkopf, Hauptprogramm, Unterprogramme (optional) ausweisen. |
Material |
|
Schaltskizze
Vorbemerkungen zum Programmaufbau
Beginnen wir mit dem Programmkopf. Was man dort alles hineinpackt ist in Teilbereichen Geschmachssache. Bei mir besteht der Programmkopf aus
der Beendigung aller nicht benutzten Interrupts.
Wie das praktisch umgesetzt wird, zeigt das Programm zu dieser Übung. Der Programmkopf kann zukünftig in jedes neues Assemblerprogramm kopiert werden und muss im Einzelfall nur noch um weitere Interrupthandler und/oder Variablennamen erweitert werden, so wie es die Programmanforderung vorsieht.
Das Programm
Wie arbeitet das Programm?
Der Programmkopf endet mit dem Befehl RETI. Alle bis dort getroffenen Festsetzungen gelten zukünftig für alle weiteren Programme und werden ggf. bei anderen Voraussetzungen angepasst bzw. ergänzt.
Bei den Interrupthandlern habe ich nur den Timer0 berücksichtigt; es lassen sich natürlich auch alle anderen Interrupt-Handler hier auflisten. Das ist Geschmackssache und muss jeder für sich entscheiden.
Im RESET-Block wird standardmäßig der Stackpointer eingerichtet und werden die Ein- und Ausgänge festgelegt.
Im MAIN-Block wird hier die Ansteuerung der LEDs bei Tastendruck an T0 definiert. Die beiden Befehle
überprüfen, ob Taster T0 gedrückt wurde. Bei nicht gedrücktem Taster liegt am Eingang PIND.0 eine 1, die über den Befehl IN im hreg_1 abgelegt wird. Die logische UND Verknüpfung ergibt nur bei gleichen Eingangsgrößen HIGH am Ausgang ein HIGH oder 1. Das Z-Flag im Statusregister wird gelöscht oder LOW.
Bei gedrücktem Taster (logisch 0) wird dieser Wert mit logisch 1 AND-verknüpft; das Ergebnis ist immer logisch LOW oder 0; gleichzeitig wird das Z-Flag im Statusregister gesetzt oder ist HIGH.
Der BRNE-Befehl überprüft nun das Z-Flag und verzweigt nur dann, wenn es gelöscht (LOW) ist; mit anderen Worten, wenn der Taster T0 nicht gedrückt wurde.
Wie oben bereits besprochen, wurde bei dem Sprung ins Unterprogramm das Statusregister vorher "gerettet". Dies erfolgt über die beiden Befehle
Am Ende des Unterprogramms werden die Daten das Statusregister vom Stack zurückgelesen.
Was wo im Speicher während des Programmablaufs passiert, lässt sich mit Hilfe des Debuggers genauer untersuchen.
Damit ist die Struktur der Assemblerprogramme für die Zukunft festgelegt.
Boolsche Daten
Die Bits in einem Byte lassen sich mit ihren Nullen und Einsen als eine binäre Zahl interpretieren. Mit acht Bit lassen sich dann alle positiven ganzen Zahlen zwischen 0 und 255 darstellen.
Ordnet man den einzelnen Bits Wahrheitswerte in Form von TRUE und FALSE oder 1 und 0 zu, dann sprechen wir von sogenannten boolschen Daten.
Boolsche Daten lassen sich mit boolschen Operatoren wie AND, OR, NAND, NOR, EXOR und NOT softwaremäßig verknüpfen. Die Ergebnisse der Verknüpfung boolscher Daten mit den genannten Operatoren werden jeweils in einer Wahrheitstabelle zusammengefasst (siehe Grundlagen / Logik-Gatter / Kapitel "Wahrheitstafeln erstellen").
Logische Operatoren wirken immer auf das ganze Byte. Soll ein logischer Operator nur auf bestimmte Bits innerhalb eines Bytes wirken, benutzt man dazu spezielle Bitmasken oder kurz gesagt, man maskiert das Byte.
Bits maskieren
Mit dem Operator AND lassen sich Bits in einem Byte maskieren und auf Null setzen.Die Maske muss an den zu maskierenden Stellen Null und an allen anderen Stellen eine 1 haben.
Beispiel
Das obere Nibble des Bytes 0b1011 0011 soll maskiert (auf Null gesetzt) werden. Die AND-Maske muss dann 0b0000 1111 sein. Überall, wo in der Maske eine Null steht, wird das Ergebnis auf Null gesetzt, überall wo eine 1 steht, wird das Ergebnis des Bytes übernommen. Das Ergebnis lautet damit: 0b0000 0011.
Bits löschen oder auf Null ziehen
Mit dem AND-Operator lassen sich sehr einfach und gezielt einzelne Bits in einem Register löschen oder auf LOW ziehen. Dazu muss in der Maske an der betreffenden Stelle nur eine Null eingetragen werden und alle anderen Stellen auf 1 oder HIGH gesetzt sein.
Beispiel
Die Maske 0b1101 1111 = $DF setzt Bit 5 in einem Byte auf Null.
Bits umschalten (toggeln)
Mit dem XOR-Operator lassen sich einzelne Bits oder alle Bits in einem Byte umschalten: aus HIGH (1) wird LOW (0) und umgekehrt. An den Stellen, an denen die XOR-Maske eine 1 ausweist, werden die Bits umgeschaltet, dort wo eine 0 steht, passiert gar nichts.
Beispiel
Die XOR-Maske 0b1111 1111 = $FF angewendet auf das Byte 0b10101010 ergibt 0b01010101.
Bits setzen
Mit dem OR-Operator lassen sich gezielt einzelne Bits in einem Byte setzen. Dazu muss in der OR-Maske an der betreffenden Stelle eine 1 und der Rest 0 gesetzt werden.
Beispiel
Die OR-Maske 0b0010 0000 = $20 setzt im Byte 0b0001 0110 das 5. Bit auf 1. Nach der Maskierung hat das Byte die Darstellung 0b0011 0110.
Zusammenfassung | |
Bit n in einem Byte auf 0 setzen/löschen |
n AND 0 |
schließt Bit n von einer Aktion aus | n AND 1 oder n OR 0 oder n XOR 0 |
Bit n in einem Byte auf 1 setzen |
n OR 1 |
Bit n in einem Byte toggeln/invertieren |
n XOR 1 |
Das Maskieren, Setzen, Löschen und Toggeln von Bits wird in Übung 2 praktisch untersucht.
In dieser Übung machen wir uns mit den logischen Instruktionen vertraut. Die Bargrafanzeige auf dem STK-200 dient als Anzeige der logischen Ergebnisszustände. Insgesamt sind vier verschiedene Aufgaben zu lösen.
Logische Verknüpfungen | |
Material |
|
Aufgaben |
|
Bevor es ans Programmieren geht, sind zunächst ein paar Dinge im Datenblatt des Prozessors ATmega8515 nachzuschlagen. Beantworte zunächst die folgenden Fragen:
Die Antworten findest du am Ende dieses Kapitels.
Das Programm avr_teil5_2.asm zu Teilaufgabe 1
Ausgabe auf der Bargrafanzeige
Das Programm avr_teil5_3.asm für die zweite Teilaufgabe
Bit2 wird AND-maskiert; dazu muss eine Programmzeile im Hauptprogramm geändert werden, der Rest bleibt gleich.
Ausgabe auf der Bargrafanzeige
Das Programm avr_teil5_4.asm für die dritte Teilaufgabe
Bit3 muss OR-maskiert werden; dazu werden zwei Programmzeilen im Hauptprogramm geändert.
Ausgabe auf der Bargrafanzeige
Das Programm avr_teil5_5.asm für die vierte Teilaufgabe
Das Byte wird EOR-maskiert. Insgesamt werden drei Programmzeilen geändert/ausgetauscht.
Ausgabe auf der Bargrafanzeige
Wie arbeiten die Programm avr_teil5_2-5.asm?
Der Programmkopf und das Reset-Modul wurden bereits früher besprochen. Änderungen wurden dort nicht vorgenommen. Kommen wir also zum Hauptprogramm.
Wird der Taster nicht gedrückt, dann verzweigt das Programm zum Unterprogramm an der Adresse T0_offen. Das Statusregister wird gesichert und mit
werden die LEDs in der Bargrafanzeige angesprochen, die später leuchten sollen; es sind LED1, LED2, LED4 und LED7. Steht die 1 für LED AN, dann sähe das Binärmuster so aus: 0b1001 0110. Dieser Binärzahl entspricht die Hexadezimalzahl $96. Über die Direktive .EQU wurde genau dieser Wert der Variablen leds im Programmkopf zugewiesen.
Mit der Instruktion
wird das Komplement des Registerinhalts von hreg_1 gebildet. Aus Einsen werden Nullen und umgekehrt. Dieser Befehl muss sein, da die LEDs low-active sind und die 1 bei unseren bisherigen Betrachtungen für eine eingeschaltete LED stand.
Überträgte man die Bitfolge von hreg_1 jetzt an das Ausgangsregister PORTB, dann leuchten die entsprechenden LEDs 7, 4, 2 und 1.
Aus dem Unterprogramm wird anschließend mit RJMP zurückgesprungen in das Hauptprogramm main.
Wird der Taster T0 gedrückt, dann macht das Programm weiter bei der Instruktion
Die Binärzahl 0b1111 0000 (Bitmaske) wird in das Register hreg_1 geladen und mit dem Inhalt von leds über
logisch-AND maskiert. Das Ergebnis wird in hreg_1 abgelegt.
Die folgende Instruktion COM berücksichtigt, dass die LEDs low-active sind und bildet das Komplement zur Binärzahl 0b1001 0000; das ist 0b0110 1111. Dieses Byte wird an den Ausgangsport PORTB übergeben. Überall dort, wo eine 0 steht, leuchtet die entsprechende LED, dort wo eine 1 steht, ist sie aus.
Zweite Teilaufgabe
Das zweite Bit muss logisch-AND maskiert werden.
Alles Weitere ist bekannt.
Dritte Teilaufgabe
Das dritte Bit muss logisch-OR maskiert werden, weil es zusätzlich zu den bereits bestehenden Bits gesetzt werden soll. Das wird erledigt durch
Vierte Teilaufgabe
Das obere Nibbel soll bei Tastendruck getoggelt und das untere beibehalten werden. Das Byte muss logisch-EXOR maskiert werden. Das erledigt der Befehl EOR, der nur über zwei Register angesprochen werden kann. Deshalb wird ein kleiner Umweg im Programm genommen. Die Maske und das Byte leds werden vorher in je ein Hilfsregister abgelegt und anschließend als Argument für die Instruktion EOR benutzt.
Antworten zu den Fragen aus Übung 2
1. Es werden fünf logische Instruktionen bereitgestellt: AND, ANDI, OR, ORI und EOR.
2. Die Befehle unterscheiden sich in der Art, welche Daten miteinander verknüpft werden können. Die Instruktionen AND, OR und EOR führen logische Verknüpfungen von zwei Registerinhalten durch, während die Instruktionen ANDI und ORI den Registerinhalt mit einer Konstanten logisch verknüpfen.
3. Es werden drei Statusregister beeinflusst: Z, N und V.