Pfad: Home => AVR-Überblick => Programmiertechniken => Ganzzahlen    (This page in English: Flag EN) Logo

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