|
Wir erzeugen ein Lauflicht mit AVISE4.3
|
.
Am Port C des ATMEGA32 sind über Vorwiderstände (330 Ohm) 8 Leuchtdioden angeschlossen. Im Bild ist stattdessen eine Leuchtdiodenzeile aus 10 LEDs zu sehen, deren oberste und unterste LED nicht angeschlossen sind.
Tokencodes für AVISE4.1 und AVISE4.3
Wir wollen die LEDs des Lauflichts über Port C ansteuern. Dann muss Port C erst einmal auf Ausgang geschaltet werden:
$ 0 C WDDR ("Write to data direction register) . Mit dem $-Zeichen muss nur ein einziges Mal auf Hexadezimaldarstellung aller Zahlen umgeschaltet werden.
Probieren Sie dann FF WPORT aus: Alle 8 LEDs an Port C müssten leuchten, denn FF(16) = 1111 1111(2). Oder 81 WPORT: LED 0 und LED 7 leuchten.
Also müssen wir für ein Lauflicht durch geschickte Wahl von Ausgabewerten einzelne Leuchtdioden abwechselnd zum Leuchten bringen und ausschalten. Hilfreich zur Überlegung ist die Darstellung der Ausgabewerte als Dual- und dann erst als Hexadezimalzahlen:
1000 0000(2) = 80(16)
0100 0000(2) = 40(16)
0010 0000(2) = 20(16)
.
.
.
0000 0001(2) = 1(16)
Die einfachste Möglichkeit wäre es also, 80(16) auf den Stapel zu legen, über Port C auszugeben. Dann 40 über Port C ausgeben, wodurch die vorherige LED auch wieder ausgeschaltet wird, usw., also
$ 80 C WPORT 40 C WPORT 20 C WPORT 10 C WPORT 8 C WPORT 4 C WPORT 2 C WPORT 1 C WPORT
Würden Sie das machen, würden Sie vielleicht nicht einmal das kurze Aufblitzen der LEDs sehen; die letzte würde dauernd leuchten. Wir müssen nach jeder Ausgabe unserem Auge etwas Zeit gönnen, bis die Anzeige geändert wird. Das könnte geschehen mit 100 MS WAIT. Dann würde der Programmlauf nach jeder Ausgabe um 100(16) = 256(10) ms angehalten werden.
Es ist lästig, immer das gleiche zu schreiben. Definieren wir doch zur Abkürzung ein neues Ausgabewort AUS:
: AUS C WORT 100 MS WART RET
Es erwartet einen Wert auf dem TOS, gibt ihn aus und wartet 256 ms.
Dann hieße die Befehlskette:
$ 80 AUS 40 AUS 20 AUS 10 AUS 8 AUS 4 AUS 2 AUS 1 AUS
Probieren Sie's aus. Es ist umständlich, aber es funktioniert.
Der Eingabewert 80 wird offensichtlich 7 x halbiert; wir nennen diesen Vorgang Rechtslauf. Für den umgekehrten Weg müsste 1 auf den Stapel gelegt werden und dann 7 x verdoppelt werden; das ist der Linkslauf.
Wenn man jetzt aber ein hin und herlaufendes Lauflicht haben möchte, macht sich ein Schönheitsfehler bemerkbar: Der Rechtslauf endet mit 1, der Linkslauf beginnt mit 1. Auch, wenn der Linkslauf bei 80 angekommen ist, folgt vom Rechtslauf noch einmal 80. Es ist also zweckmäßig die Schleifen von beiden Läufen nur von 7 bis 2 laufen zu lassen: Rechtslauf mit Anfangswert 80, 1. Halbierung: 40, 2. H: 20, 3. H: 10, 4. H: 8, 5. H: 4, 6. H: 2. Dann Linkslauf mit Anfangswert 1, 1. Verdoppelung: 2, ... 6. Verdoppelung: 40. Dann wieder Rechtslauf mit Anfangswert 80 ...
Für die Verschiebung des besetzten Bits gibt es aber elegantere Verfahren: Rotations- und Schiebebefehle. Beide wirken auf die zwei Bytes des TOS. Also:
1000 0000 1000 0010 1 ROR liefert den neuen TOS 0100 0000 0100 0001. Die grüne 1 besagt also, dass die Rotation um 1 Bit erfolgen soll. Wird noch einmal 1 ROR eingegeben, entsteht 1010 0000 0010 0000, usw. Bei einem Rotationsvorgang wird also das auf der einen Seite durch die Bitverschiebung herausfallende Bit (der Übertrag) auf der anderen Seite wieder eingefügt. Entsprechendes gilt für die Linksrotation mit dem Wort ROL. Anders ist es dagegen bei den Schiebeworten (>> und <<). Das herausfallende Bit geht verloren, auf der anderen Seite wird eine 0 nachgezogen.
Also: 1000 0000 1000 0010 1 >> liefert wie beim Rotationsbefehl den neuen TOS 0100 0000 0100 0001. Eine zweite Rechtsschiebeaktion erzeugt aber 0010 0000 0010 0000.
Für die ersten 7 Rotations- und Schiebeoperationen entstehen die gleichen niederwertigen Bytes. Da wir in der Regel nur einen 8-Bit-Ausgabe-Port benutzen und immer wieder neue Anfangswerte einsetzen, sind für diese Zweck beide Operationen gleichwertig. Damit die End- oder Anfangswerte nicht doppelt ausgegeben werden, sind für Rechtslauf oder Linkslauf nur 6 Rotations- oder Shiftvorgänge einzusetzen.
Für die FOR-NEXT-Schleife (in AVISE4.3) muss der Laufindex vorher als Variable definiert werden, zugleich schalten wir - wenn noch nicht geschehen - auf Hexadezimaldarstellung um:
$ VAR L
Wir könnten also ein Wort RL für den Rechtslauf definieren durch eine der folgenden Zeilen:
: RL 7 2 FOR L DUP AUS 2 / NEXT DROP RET
: RL 7 2 FOR L DUP AUS 1 ROR NEXT DROP RET
: RL 7 2 FOR L DUP AUS 1 >> NEXT DROP RET
Was soll denn jeweils halbiert oder verdoppelt werden? Der entsprechende Wert muss zuvor auf den Stapel gelegt werden, also z.B. mit 80 RL . Im ersten Schleifendurchlauf würde dann 80 mit DUP ein zweites Mal auf den Stapel darüber gelegt werden. Wozu? Weil nach der ersten Ausgabe mit AUS das oberste Stapelelement wieder entfernt wird; wir brauchen aber 80 noch für die Halbierung. Am Schluss steht noch DROP. Andernfalls würde das Wort mit dem Ergebnis der letzten Halbierung auf dem Stapel beendet werden. Der Stapel könnte bei mehreren Durchläufen überlaufen.
AVISE4.1 kennt aus Platzgründen keine FOR-NEXT-Schleife; wir müssten eine REPEAT-UNTIL-Schleife einsetzen:
: RL REPEAT DUP AUS 1 ROR DUP 1 = UNTIL DROP RET
Diese REPEAT - UNTIL-Schleife wird abgebrochen, wenn auf dem Stapel ein flag = 1 liegt. Dazu wird abgefragt, ob durch die Halbierung eine Zahl = 1 entstanden ist. Wenn ja, wird der TOS 1erzeugt, sonst 0. Durch den Vergleich mit 1 würde aber das Ergebnis der Halbierung verbraucht werden und im nächsten Durchlauf für eine weitere Halbierungen nicht mehr zur Verfügung stehen. Deswegen wird es mit DUP dupliziert (2 x auf den Stapel gelegt). Nach Beendigung der Schleife ist es einmal noch übrig geblieben. Es muss mit DROP entfernt werden.
Ganz entsprechend Varianten für den Linkslauf:
: LL 7 2 FOR L DUP AUS 2 * NEXT DROP RET
: LL 7 2 FOR L DUP AUS 1 ROL NEXT DROP RET
: LL 7 2 FOR L DUP AUS 1 << NEXT DROP RET
oder auch in AVISE4.1:
: LL REPEAT DUP AUS 1 ROL DUP 80 = UNTIL DROP RET
Der Eingabewert muss wieder über den TOS angeboten werden: 1 LL
Jetzt kann man das Lauflicht fast fertig programmieren. Es soll durch einen Tastendruck beendet werden:
: IMMER REPEAT 80 RL 1 LL KEY? UNTIL DROP RET
KEY? fragt ab, ob eine Taste gedrückt wurde. Wenn nein, wird das flag 0 auf den Stapel gelegt und UNTIL bricht nicht ab. Wenn ja, wird der Code der gedrückten Taste auf den Stapel gelegt und darüber das flag 1. UNTIL bricht ab. Der Tastecode bliebe auf dem TOS zurück, wenn er nicht durch DROP entfernt würde.
Es funktioniert!
Statt 80 bzw. 1 können andere Werte eingegeben werden. Dann sollten andere Leuchtmuster über die Leuchtdiodenzeile laufen.
Probieren wir Folgendes, wobei wir gleich annehmen, dass diese beiden Werte auf dem Stapel liegen:
: IMMER REPEAT DUP RL SWAP DUP LL SWAP KEY? UNTIL DROP DROP DROP RET
Ob es mit 1 80 IMMER geht? Weil RL und LL jeweils den TOS verbrauchen, muss er vorher dupliziert werden. Mit SWAP wird wieder die richtige Reihenfolge auf dem Stapel hergestellt. Nach Beendigung der Schleife bleiben, vom TOS her gelesen, der Tastencode und die beiden Eingabewerte übrig. Sie müssen mit dem dreimaligen DROP entfernt werden.
Probieren Sie's aus!
Funktioniert es auch mit anderen Eingabewerten? Korrigieren Sie evtl. RL und LL!
Schäden werden bei sachgemäßem Umgang nicht erwartet.
Dennoch wird keinerlei Haftung übernommen, z.B. bzgl. Gefährdung
der Prozessoren, der PCs und vor allem der mit dem System experimentierenden
Personen.
Ein Haftungsausschluss von Herrn Schemmert wird hier einkopiert:
|
(zuletzt aktualisiert 2013)