Pfad:
Home =>
AVR-Überblick =>
Programmiertechniken => Ganzzahlen
(This page in English:
)
Programmiertechnik für Anfänger in AVR Assemblersprache
Ganzzahlenberechnungen in Assemblersprache
Nur Ganzzahlen, bitte
Für den Anfänger verwirrend, aber der Ausdruck
.equ zahl=3/2
führt nicht zu dem erwarteten Ergebnis (zahl=1,5). Stattdessen
listet der Assembler gavrasm in seiner Symbolliste am Ende des Listings
folgendes auf:
List of symbols:
Type nDef nUsed Decimalval Hexval Name
C 1 0 1 01 ZAHL
(Hinweis: die Liste der Symbole macht nur der Assembler gavrasm, andere
Assembler machen das nicht!)
Statt 1,5 steht da 1 in der Konstanten zahl. Schon die Einteilung
der Spalten in der Symboltabelle sieht nur ganze Zahlen vor und kann mit
gebrochenen Dezimalzahlen rein gar nix anfangen.
Das liegt daran, dass Assembler nur Ganzzahlen kann. Das macht in der
Binärwelt auch einen ziemlichen Sinn: wer nur Null und Eins kennt,
kann schon gar keine Halben oder Viertel kennen und darstellen. Wer
versucht, gebrochene Dezimalzahlen in Assembler einzuführen, um
auch noch notorischen C-Programmierern das Denken zu erleichtern, macht
damit nur Unsinn: der Teiler in einem Timer kann nur Ganzzahlen und kann
niemals durch 1,5 teilen!
Aufrunden beim Teilen
Und nicht mal Aufrunden kann der Assembler: er schneidet beim Teilen
einfach die Kommastellen ab. Wenn wir die 0,5 aufgerundet haben wollen,
müssen wir das .equ schon so berechnen:
.equ zahl=(3+1)/2
Vor dem Teilen durch Zwei muss dem zu teilenden noch die Hälfte
des Teilers hinzuaddiert werden, erst dann rundet er korrekt auf und
kriegt zwei heraus:
List of symbols:
Type nDef nUsed Decimalval Hexval Name
C 1 0 2 02 ZAHL
Bei etwas komplizierteren Ausdrücken kann es zu empfehlen sein,
den Teiler separat zu berechnen. Nehmen wir als Beispiel die Berechnung
der Frequenz eines mit einem Teiler geteilten Taktes. Der Vorteiler
des Timers teilt den Takt durch z. B. 64, der Timer dann durch
15. Nehmen wir ferner an, der Timer lasse bei Erreichen von 15 einen
Ausgangspin torkeln, dann teilt das die am Pin erzeugte Frequenz noch
mal durch zwei. Mit
.equ teiler=15*64*2
haben wir die gesamte Teilerrate, durch die der Teiler mit dem Timer
die Taktfrequenz dividiert.
Berechnen wir aus einer Taktfrequenz von z. B. 1 MHz die Frequenz
des Torkel-Pins und wollen das Ergebnis auch noch gleich aufrunden,
dann können wir das so machen:
.equ frequenz = (1000000 + teiler/2) / teiler
Wegen der Regel "Punktrechnung vor Strichrechnung" müssen
wir den Ausdruck teiler/2 nicht extra in Klammern setzen, dieser Ausdruck
wird vom Assembler sowieso zuerst berechnet und dann zu dem 1 MHz Takt
dazugezählt. Dieser Ausdruck braucht dann aber unbedingt Klammern,
damit das Addieren vor dem Teilen durch teiler erfolgt.
Macht man das, dann kommt Folgendes heraus:
List of symbols:
Type nDef nUsed Decimalval Hexval Name
C 1 2 1920 0780 TEILER
C 1 0 521 0209 ZAHL
Beim Rechnen mit gebrochenen Dezimalzahlen käme ohne Aufrunden
520,83333 (Periode) heraus, das Aufrunden auf 521 ist daher korrekt.
Was tun, wenn uns aber die Dezimalzahlen hinter dem Komma interessieren?
Zum Beispiel, wenn unser Timer mit einem Vorteiler von 1.024 arbeitet
und durch 256 teilt. Dann macht er eine Rechteckspannung von
ftoggle1.000.000 / 1.024 / 256 / 2 = 1,907 Hz
und die 0,907 hinter dem Komma täten uns dann schon interessieren.
Nehmen wir doch einfach das Tausendfache unserer Rechnung, also
.equ teiler = 1024 * 256 * 2
.equ fmHz = (1000 * 1000000 + teiler / 2) / teiler
Machen wir das, dann spuckt gavrasm im Listing folgendes aus:
List of symbols:
Type nDef nUsed Decimalval Hexval Name
C 1 2 524288 080000 TEILER
C 1 0 1907 0773 FMHZ
Die Hz sind jetzt mHz und die drei Dezimalziffern hinter dem Komma sind
erkennbar. Leider ist durch die in Assembler nicht unterschiedene Klein-
und Grossschreibung aus dem Milli-Hertz ein Mega-Hertz geworden, aber
das müssen wir hinnehmen (es sei denn, wir nennen die Konstante
nicht fmHz sondern fmillihertz, dann bleibt es auch in Großschreibung
noch korrekt).
Nebenfolgen der Ganzzahlen-Mathematik
Tückisch sind Ganzzahlen-Berechnungen, wenn beim Teilen eine Null
herauskommt, weil der Teiler größer ist als die zu teilende
Zahl. Das kriegt man z. B. bei einem 16-Bit-Timer (der durch bis
zu 65.536 teilt) und einem Vorteiler von 1.024. Das sind zusammen schon
65.536.000, und viel größer als jede realistische Taktfrequenz.
Rechnet man dann mit einer Frequenz von Null weiter, und rechnet z. B.
daraus die Zyklusdauer aus, z. B. mit t in µ-Sekunden so:
.equ teiler = 65536 * 1024 * 2
.equ f=(1000000 + teiler/2) / teiler
.equ tus=(1000000 + f/2) / f
dann kriegt man die Fehlermeldung
===> Error 101: Division by Zero
Line: .equ tus=(1000000+f/2) / f
weil f in der Ganzzahlenberechnung Null geworden ist:
List of symbols:
Type nDef nUsed Decimalval Hexval Name
C 1 2 1342189568 50003000 TEILER
C 1 2 0 00 F
C 1 0 0 00 TUS
Mit einem Blick in die Symbolliste ist ein solcher Fehler schnell
aufgeklärt.
Der Zahlenraum bei Assembler
Frühere Assembler waren auf 24 oder 32 Bit breite Ganzzahlen
beschränkt. Da das oberste Bit der Zahl das Vorzeichen enthielt,
blieben daher Zahlenwerte zwischen +2^23-1 bis -2^23, ca.
+/-8 Millionen bzw. +2^31-1 bis -2^31 (ca. 2 Milliarden)
zulässig.
Fast alle neueren Assembler verwenden 64-Bit-Zahlen und es gibt kaum
noch Fälle, bei denen man an diese Grenzen auch nur annähernd
herankommt. Man kann also locker die Frequenz auch noch in Pico-Hertz
angeben, wenn man viel Wert auf nutzlose Stellen hinterm Komma legt.
Merken
In Assembler gibt es nur Ganzzahlen. Wenn man beim Teilen vor dem
Abrunden die Ganzzahl noch aufrunden will, macht man das durch Addieren
des halben Teilers.
Zum Seitenanfang
©2002-2020 by http://www.avr-asm-tutorial.net