Benutzer:Haku/Für:Assembler

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

Darum Assembler!

Ja, Assembler. Total altmodisch, umständlich und uncool.

Es folgen einige Beispiele, weshalb ich persönlich der festen Überzeugung bin, dass jeder, der ernsthaft mit Mikrocontrollern leben möchte, Assembler verstehen sollte.


Befehlssatz

Gerade auf Mikroprozessoren hat man nicht beliebig viele Ressourcen und Rechenzeit, wie das mittlerweile auf jedem PC der Fall ist. Mit einer Hochsprache hat man schnell und elegant etwas programmiert, was für die Zielplattform aber völlig ungeeignet ist. Da hilft es, wenn man seinen Prozessor verstanden hat und sich in etwa vorstellen kann, welche Möglichkeiten der Compiler hat, um den Code zu übersetzen. Dazu muss man aber den Assembler verstanden haben.

Beispiele?

Auf dem AVR wird folgendes ziemlich umständlich:

int schieben(int x, int d) {
  return x << d;
}

Oder, immer wieder gerne gesehen:

#include <util/delay.h>
/* ... */
void warten(int sek) {
  _delay_ms(1000 * sek);
}


Nebenläufigkeit

Richtig Spaß machen Mikroprozessoren erst mit Interrupts. Vorallem, wenn man dann Daten zwischen dem Hauptprogramm (main-Schleife) und den Interrupts austauscht. Man sollte dann wissen, was im Assembly passiert. Andernfalls handelt man sich jede Menge Unterhaltung ein, wenn es dann irgendwann zu ganz sporadischen (also auch ganz schlecht reproduzierbaren) Fehlern kommt.

Beispiel?

Dieses knallt auf dem AVR sehr sporadisch:

volatile int zaehler;
ISR(...) {
  zaehler++;   
}

int main() {
  /* ... */
  if (zaehler == 1000) {
    /* Autsch */
  }
}


Read-modify-write

Read-modify-write ist richtig schön im Zusammenspiel mit Interrupts.

Beispiel?

Folgendes funktioniert völlig unproblematisch und wackelt an zwei Pins eines ATmega128:

volatile int zaehler;
ISR(/* ... */) {
  static int flag = 0;
  
  flag = !flag;
  if (flag)
    PORTE |= _BV(PE1);
  else
    PORTE &= ~_BV(PE1);
}

int main() {
  for (;;) {
    PORTE |= _BV(PE0);
    PORTE &= ~_BV(PE0);
  }
}

Folgendes dagegen geht, wieder auf dem ATmega128, sehr sporadisch in die Hose:

volatile int zaehler;
ISR(/* ... */) {
  static int flag = 0;
  
  flag = !flag;
  if (flag)
    PORTF |= _BV(PF1);
  else
    PORTF &= ~_BV(PF1);
}

int main() {
  for (;;) {
    PORTF |= _BV(PF0);
    PORTF &= ~_BV(PF0);
  }
}