Atmega8, AVR-Studio 4, Simulator2
Viele Beispielprogramme im AVR-Tutorial fangen mit der Initialisierung
des Stackpointers an:
********************
.CSEG
.org 0
ldi r16, low(RAMEND) ; Stackpointer initialisieren
out SPL, r16
ldi r16, high(RAMEND)
out SPH, r16
*************
Viele Programmbeispiele - z.B. die Beispiele aus dem Kapitel EEPROM
verwenden doch garkeinen Stack, SPH und SPL werden garnicht dort
verwendet etc., warum wird das dennoch gemacht? Ich setze nun die
Adresse im Stackpointer auf RAMEND - wozu?
Ich hab mit AVR nicht viel am hut. Ich kenn auch nicht das tutorial.
Der Stack wird für Funktionsaufrufe verwendet. Springst du in eine
Funktion muss irgendwo gesichert werden, was vorher in den Registern
stand, und wo du her kommst. damit der return dann ach wieder richtig
springt.
Der Stack wird in den Hochsprache für variablen innerhalb einer Funktion
verwendet.
und der stack wächst bei vielen systemen von oben nach unten. daher
ramend.
PC-Anfaenger schrieb:> Ich setze nun die> Adresse im Stackpointer auf RAMEND - wozu?
Damit Bytes an definierte Stellen auf Stack gelegt werden können und Dir
nicht die Register, den I/O-Bereich oder andere SRAM-Bereiche mit
ungewollten falschen Werten füllen.
Schau mal in der Befehlsübersicht nach, welche Befehle den Stack
benutzen.
Wenn Du ohne Stackpointer auskommen willst, dann solltest Du einen
AT90S1200, Tiny11, Tiny12 oder Tiny15 benutzen.
...
Jeder call-Befehl und jeder Interrupt schreibt auf den Stack, damit der
ret-Befehl dort die Rücksprungadresse findet.
Somit benutzen nur die allereinfachsten Beispielprogramme den Stack
nicht - diejenigen nämlich, die ohne Subroutinen und Interrupts
auskommen.
PC-Anfaenger schrieb:> Viele Programmbeispiele - z.B. die Beispiele aus dem Kapitel EEPROM> verwenden doch garkeinen Stack, SPH und SPL werden garnicht dort> verwendet etc., warum wird das dennoch gemacht?
Weil es nur ein paar Takte sind, die beim Programmstart benötigt werden
und dann muss man bei Programmerweiterungen, die einen Stack benötigen
nicht daran denken, dass man noch einen Stackpointer einrichten muss.
So was nennt man "defensives Programmieren" - mit einplanen, daß ein
Programmierer auch mal Fehler machen wird und gleich vorausplanen, so
dass solche Fehler nicht gleich zum Desaster ausarten.
> Ich setze nun die> Adresse im Stackpointer auf RAMEND - wozu?
Weil der Stack vom Ende des Speichers nach vorne wächst, so man einen
verwendet.
PC-Anfaenger schrieb:> Viele Programmbeispiele - z.B. die Beispiele aus dem Kapitel EEPROM> verwenden doch garkeinen Stack, SPH und SPL werden garnicht dort> verwendet etc., warum wird das dennoch gemacht?
Jedes dieser Beispiele enthält Unterprogrammaufrufe oder Interrupthand-
ler. Bei beiden wird die Rücksprungadresse auf den Stack gelegt, weswe-
gen dieser richtig initialisiert sein sollte.
Florian schrieb:> Ich dachte immer, dass man sich das bei den neueren AVRs Sparen kann?
Theoretisch ja, da als Initial Value bereits der richtige Wert (RAMEND)
drinsteht.
Das gilt jedoch nur für die neueren Chips, daher ist es kein Schaden, es
einfach generell zu machen (kostet 4 Assemblerbefehle zu
Programmbeginn).
:-)
Hi
>Wenn Du ohne Stackpointer auskommen willst, dann solltest Du einen>AT90S1200, Tiny11, Tiny12 oder Tiny15 benutzen.
Ohne Stackpointer, ja. Aber nicht ohne Stack.
>Viele Programmbeispiele - z.B. die Beispiele aus dem Kapitel EEPROM>verwenden doch garkeinen Stack, SPH und SPL werden garnicht dort>verwendet etc., warum wird das dennoch gemacht? Ich setze nun die>Adresse im Stackpointer auf RAMEND - wozu?
Der Stackpointer wird, ohne das du ihn direkt ansprichst, von 'call',
'rcall', 'ret', 'push', 'pop, 'reti' und Interrupts (was vergessen?)
benutzt. Diese Befehle legen Werte auf der Adresse, auf die der
Stackpointer zeigt, ab und decrementieren den Stackpointer oder
incrementieren den Stackpointer und holen den Wert, auf den der
Stackpointer zeigt. Bei einem 'call' wird zum Beispiel die
Programmadresse des Befehls nach dem 'call' auf dem Stack gespeichert.
Bei einem 'ret' wird der Programmcounter wieder mit diesem Wert geladen
und das Programm macht nach dem Call-Befehl weiter.
MfG Spess
Vielen Dank fuer die Vielzahl von Antworten. Betrachte ich einfach mal
die Befehle push und pop.
Wenn ich also vor dem Aufruf einer Routine einen Register sichern will
und schribe
push r16
Dann müsste ja - wenn ich den Zeiger zum Ende auf RAMEND lege, bei
Speichervorgängen der Zähler dekrementiert werden - weil wenn ich ja
bereits bei RAMEND bin, dann kann ja nichts weiter inkrementiert werden?
Was ist aber, wenn ich den Stack nicht initialisiere? Wenn der dann bei
"RAMSTART" stehen würde und ich dann etwas speichern würde - und dann
dekrementieren täte - so dürfte das ja dann nicht gehen?
Also würde es bedeuten, wenn ich nicht die oben vorgestellte
Initialisierung mache und selber mit .dseg Speicher reserviere, dass es
dann zu Ueberschneidungen kommt, wenn ich push/pops und andere Spruenge
mache?
Hi
>Dann müsste ja - wenn ich den Zeiger zum Ende auf RAMEND lege, bei>Speichervorgängen der Zähler dekrementiert werden - weil wenn ich ja>bereits bei RAMEND bin, dann kann ja nichts weiter inkrementiert werden?
Wird er auch. Aus dem Instruction Set:
This instruction stores the contents of register Rr on the STACK. The
Stack Pointer is post-decremented by 1 after the PUSH.
^^^^^^^^^^^^^^^^
>Was ist aber, wenn ich den Stack nicht initialisiere? Wenn der dann bei>"RAMSTART" stehen würde und ich dann etwas speichern würde - und dann>dekrementieren täte - so dürfte das ja dann nicht gehen?
Nicht initialisiert zeigt der Stackpointer, je nach AVR, entweder auf
$0000 oder RAMEND.
>Also würde es bedeuten, wenn ich nicht die oben vorgestellte>Initialisierung mache und selber mit .dseg Speicher reserviere, dass es>dann zu Ueberschneidungen kommt, wenn ich push/pops und andere Spruenge>mache?
Richtig. Und das endet zu 100% in einem Crash.
MfG Spess