Es ist jetzt das erste mal, dass ich etwas größere Programme für einen Mikrocontroller schreibe, genauer gesagt für den ATmega16. Nachdem ich aber eine Menge Codebeispiele schon gesehen habe und die mir natürlich auch sehr weitergeholfen haben (auch wenn man manchmal stundenlang versucht zu interpretieren und zu verstehen), fiel mir immer auf, dass sehr wenig PUSH und POP genutzt wird. Ich selbst sichere alle Register, die ich verändere, sodass der Ursprungszustand nach verlassen der Routine wieder hergestellt ist. Aber ist das ein guter Programmierstil oder sollte man lieber nichts sichern und sich darauf verlassen, dass der aufrufende genau weiss, was verändert wird? Schliesslich können sich ja solche Routinen schnell mal verändern und da vergisst man dann schonmal ein Register. Zumal ich finde, dass der Atmel sowieso massig Register hat. Mein C64 hatte damals nur x,y,z und den Akku soweit ich mich erinnere. Und die Intel-Controller haben auch nicht mehr gehabt.
Nachteile: Push und pop sind sehr langsam (min 2 Takte). Sie brauchen Platz im Stackpointerbereich. Einmal zuviel gepusht und du überschreibst dir ev.wichtige Daten. Einmal gepusht ohne pop - Programm tot. Die Reihenfolge der push's und pop's durcheinander - schwerer Fehler und schwer zu finden. Die Übersicht geht mit den push und pops verloren. Unterprogramme die wiederum pushen - vergiss es. Nimm lieber C bei größeren Progrmmen - hier macht das Sichern der Register der Compiler. Josef
Wenn ich C nehme, dann sind aber die ersten beiden Argumente gegen PUSH und POP schon widerlegt. Mit C ist es sicher noch langsamer und Platz brauche ich sicher auch noch mehr. Das durcheinanderbringen ist natürlich so eine Gefahr. Aber wenn ich das nicht mache, kann ich schnell mal was vergessen und mein Registerinhalt ist weg.
Hi! Ich finde Assembler auch für größere Programme sinnvoll, da der Programmspeicher dank unserer Intelligenz perfekt ausgenutzt wird - Auch wenn C sicher einfacher und vielleicht übersichtlicher ist. Das ist halt meine persönliche Meinung. Um zu vermeiden, daß wichtige Daten überschrieben werden: Der Stack wächst im Datenspeicher für gewöhnlich von oben nach unten. Man kann beim Start des Chips (ich habe einen AT90S2313) den Stackpointer auf einen Wert setzten, der nicht dem obersten Ende des RAMs entspricht. Dadurch ist - sofern man keine Fehler in Reihenfolge oder Anzahl der Pushs und Pops macht - Dieser Datenspeicherbereich für Daten geschützt. Wenn ich mich nicht irre gibt es in dem ATMEL-Assembler auch Direktiven, die Variablen fürs RAM in diesem Bereich sichern (oder verwechsle ich das gerade mit Konstanten im Programmspeicher?). Zumindest funktioniert dieses Vorgehen in jedem Fall, da man den Stackpointer selber initialisieren kann. Guter Stil: Als Programmierer auf "großen" Computern ist das sicher eine Selbstverständlichkeit (wenn man z.B. ein Betriebssystem programmiert). Da in den µC aber die Resourcen doch relativ begrenzt sind, kann man sicher trickreich programmieren. Aber man sollte "sauber" programmieren, also gut auskommentieren. Wenn man die "Prozessumgebung" nicht sichert, dann wird man wie schon richtig erkannt hast unflexibler. Also sollte man für solche Fälle in seinen eigenen Softwarespezifikationen (Kommentare o.ä.) festhalten. Grüße, Martin Weber.
Hi Ronny, ich sichere bei jeder Routine immer die Register per push und pop, die ich verändere. Das klappt bisher wunderbar. Klar - wenn man einmal z.B. pop vergisst gibt's Chaos. Deshalb schreibe ich nach meinen push's sofort meine pop's - und außerdem merkt man Fehler sofort. Das klappt eigentlich ganz gut. Kostet halt Rechenzeit - klar. Aber sonst habe ich keinen richtigen Überblick mehr, wenn eine Routine eine Routine aufruft, die eine Routine aufruft. Für Variablen nehme ich definierte SRAM-Bereich und lds bzw. sts. "Absturzsicherheit" für etwas Rechenzeit. Sebastian
Danke. Das bestätigt mich in meiner Vorgehensweise. Also werde ich, solange ich Platz habe das auch weiterhin so machen. Alerdings muss man doch enorm umdenken, wenn man vorher mit C programmiert hat. Gerade was das ablegen der Daten angeht. Weil man nähmlich nicht einfach, wie in C statische Variablen anlegen kann, die dann für alles zur Verfügung stehen, was man included, sondern weil man sich den Speicherbereich selbst aussuchen muss. Und das sollte dann auch in einer Datei bleiben. Macht man das in mehreren, was natürlich der Übersichtlichkeit halber zu den einzelnen Dateien besser ist, hat man den Nachteil dass man wieder nicht weiss, wo welcher Speicher zugewiesen wurde. Es sei denn das passiert dynamisch, indem man sich eine Funktion baut, die den Speicher reserviert und verwaltet, was aber im Mikrocontroller vielleicht übertrieben ist.
>Die Reihenfolge der push's und pop's durcheinander - schwerer Fehler >und schwer zu finden. Die Übersicht geht mit den push und pops >verloren. Unterprogramme die wiederum pushen - vergiss es. Ein Konzept sollte man schon haben - möglichst vorher. Ein Unterprogramm sollte genau einen Einsprungpunkt (am Anfang) und einen Austrittspunkt haben (RTS, RTE am Ende). Beim C64 war der Ablauf: PHA; TXA; PHA; TYA; PHA; 13 Zyklen, bei 1MHz Takt = 13µS fürs Sichern der Register. Das Wiederherstellen dauerte 16 Zyklen. Beim ATmega mit 16MHz braucht man 125nS / Register. Das ist nicht sehr langsam sondern sehr schnell, Josef ! Es gibt keinen Grund, seine Register nicht ordentlich zu sichern, außer, man liebt die endlose Fehlersuche. Auch der Stackbedarf bleibt in überschaubaren Grenzen solange man keine lokalen Arrays auf dem Stack anlegt. Und wenn mal Codegröße oder 'Rechenzeit' zum Problem werden sollten, kann man das funktionierende Programm Schritt für Schritt verändern und gleich testen. Das ist m.E. der sicherste Weg.
Hallo, ich habe auch ähnliche Probleme im Assembler ... Ist es sinnvoll ein Register fest mit Null zu setzen ? Gruß Fiffi
Wie schon gesagt wurde, mit Push/Pop programmiert man sicher, aber auch groß und langsam. Besser ist es daher, eine Anzahl Register zu vereinbaren, die sozusagen als Schnellspeicher reserviert sind, d.h. zur Parameterübergabe und für Berechnungen, die dann nicht gesichert werden müssen. Und jede Funktion geht dann davon aus, daß deren Inhalt nach einem Return nicht mehr erhalten ist. Das ist sehr praktisch, da Zwischenergebnisse von Berechnungen nur äußerst selten später noch benötigt werden. Da der AVR ja so viele Register hat, kann man auch für die Interrupts eigene Register reservieren. Diese müssen nur dann ge-Pusht werden, wenn Interrupts verschachtelt werden, was aber nur selten benötigt wird. Auch von den 3 Pointern kann einer für die Interrupts reserviert werden. Z.B.: savesreg = r15 ;sichere SREG in Interrupts ia0 = r16 ;4 Arbeitsregister für Interrupts ia1 = r17 ia2 = r18 ia3 = r19 a0 = r20 ;4 Arbeitsregister für Main a1 = r21 a2 = r22 a3 = r23 ixl = r26 ;Pointer in Interrupts ixh = r27 Damit kann man schon sehr codesparend und schnell arbeiten. Alle anderen unteren Register können auch dazu benutzt werden, um Zwischenergebnisse zu sichern, da ein MOV schneller ist als ein PUSH. Besonders bei den Megas können mit dem MOVW sogar 2 Register zugleich gesichert werden, ist dann 75% schneller als 2*PUSH. Peter
Habe ewig lange mit Assembler programmiert. Ist meine Lieblingssprache. Aber ihr bekommt null Aufträge im prof. Programmiergeschäft. Wenn du einem Kunden sagst, du bist Assemblerprogrammierer, bist du leider ein hoffnungsloser Steinzeitprogrammierer. Habe ich selbst erfahren. Kein Mensch popt und pusht mehr, wenn er damit Geld verdienen will. Der beste Assemblerprogrammmierer hat keine Chance gegen einen guten C-Compiler (zB Codevision) bezüglich Codegröße. Diese Zeiten sind vorbei. Sobald Fließkomma usw gefragt sind, ist's aus. Die Portierbarkeit des Codes bei Assembler - unmöglich. Jedes AVR C Programm läuft auch auf 8051 (nur kleine Anpassungen) und vielen andern Prozessoren. Für Auftraggeber (Industrie) ist das überlebenswichtig (Prozessorabkündigungen). Josef
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.