Forum: Mikrocontroller und Digitale Elektronik Register sichern - guter Stil?


von Ronny Schulz (Gast)


Lesenswert?

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.

von Josef (Gast)


Lesenswert?

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

von Ronny Schulz (Gast)


Lesenswert?

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.

von Martin Weber (Gast)


Lesenswert?

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.

von Sebastian Wille (Gast)


Lesenswert?

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

von Ronny Schulz (Gast)


Lesenswert?

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.

von Michael (Gast)


Lesenswert?

>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.

von Fiffi (Gast)


Lesenswert?

Hallo,

ich habe auch ähnliche Probleme im Assembler ...

Ist es sinnvoll ein Register fest mit Null zu setzen ?


Gruß

Fiffi

von Peter D. (peda)


Lesenswert?

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

von Josef (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.